1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/memory/weak_ptr.h"
6#include "base/strings/string_number_conversions.h"
7#include "base/strings/string_util.h"
8#include "base/strings/stringprintf.h"
9#include "base/threading/non_thread_safe.h"
10#include "content/public/browser/browser_thread.h"
11#include "content/public/test/browser_test.h"
12#include "content/public/test/test_utils.h"
13#include "net/base/io_buffer.h"
14#include "net/base/ip_endpoint.h"
15#include "net/base/net_errors.h"
16#include "net/socket/stream_socket.h"
17#include "net/socket/tcp_server_socket.h"
18
19using content::BrowserThread;
20
21namespace {
22
23const char kHostTransportPrefix[] = "host:transport:";
24const char kLocalAbstractPrefix[] = "localabstract:";
25
26const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
27const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
28const char kDumpsysCommand[] = "shell:dumpsys window policy";
29const char kListProcessesCommand[] = "shell:ps";
30
31const char kSerialOnline[] = "01498B321301A00A";
32const char kSerialOffline[] = "01498B2B0D01300E";
33const char kDeviceModel[] = "Nexus 6";
34
35const char kJsonVersionPath[] = "/json/version";
36const char kJsonPath[] = "/json";
37const char kJsonListPath[] = "/json/list";
38
39const char kHttpRequestTerminator[] = "\r\n\r\n";
40
41const char kHttpResponse[] =
42    "HTTP/1.1 200 OK\r\n"
43    "Content-Length:%d\r\n"
44    "Content-Type:application/json; charset=UTF-8\r\n\r\n%s";
45
46const char kSampleOpenedUnixSockets[] =
47    "Num       RefCount Protocol Flags    Type St Inode Path\n"
48    "00000000: 00000004 00000000"
49    " 00000000 0002 01  3328 /dev/socket/wpa_wlan0\n"
50    "00000000: 00000002 00000000"
51    " 00010000 0001 01  5394 /dev/socket/vold\n"
52    "00000000: 00000002 00000000"
53    " 00010000 0001 01 11810 @webview_devtools_remote_2425\n"
54    "00000000: 00000002 00000000"
55    " 00010000 0001 01 20893 @chrome_devtools_remote\n"
56    "00000000: 00000002 00000000"
57    " 00010000 0001 01 20894 @chrome_devtools_remote_1002\n"
58    "00000000: 00000002 00000000"
59    " 00010000 0001 01 20895 @noprocess_devtools_remote\n";
60
61const char kSampleListProcesses[] =
62    "USER   PID  PPID VSIZE  RSS    WCHAN    PC         NAME\n"
63    "root   1    0    688    508    ffffffff 00000000 S /init\r\n"
64    "u0_a75 2425 123  933736 193024 ffffffff 00000000 S com.sample.feed\r\n"
65    "nfc    741  123  706448 26316  ffffffff 00000000 S com.android.nfc\r\n"
66    "u0_a76 1001 124  111111 222222 ffffffff 00000000 S com.android.chrome\r\n"
67    "u0_a77 1002 125  111111 222222 ffffffff 00000000 S com.chrome.beta\r\n"
68    "u0_a78 1003 126  111111 222222 ffffffff 00000000 S com.noprocess.app\r\n";
69
70const char kSampleDumpsys[] =
71    "WINDOW MANAGER POLICY STATE (dumpsys window policy)\r\n"
72    "    mSafeMode=false mSystemReady=true mSystemBooted=true\r\n"
73    "    mStable=(0,50)-(720,1184)\r\n" // Only mStable parameter is parsed
74    "    mForceStatusBar=false mForceStatusBarFromKeyguard=false\r\n";
75
76char kSampleChromeVersion[] = "{\n"
77    "   \"Browser\": \"Chrome/32.0.1679.0\",\n"
78    "   \"Protocol-Version\": \"1.0\",\n"
79    "   \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
80    "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n"
81    "   \"WebKit-Version\": \"537.36 (@160162)\"\n"
82    "}";
83
84char kSampleChromeBetaVersion[] = "{\n"
85    "   \"Browser\": \"Chrome/31.0.1599.0\",\n"
86    "   \"Protocol-Version\": \"1.0\",\n"
87    "   \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
88    "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n"
89    "   \"WebKit-Version\": \"537.36 (@160162)\"\n"
90    "}";
91
92char kSampleWebViewVersion[] = "{\n"
93    "   \"Browser\": \"Version/4.0\",\n"
94    "   \"Protocol-Version\": \"1.0\",\n"
95    "   \"User-Agent\": \"Mozilla/5.0 (Linux; Android 4.3; Build/KRS74B) "
96    "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Safari/537.36\",\n"
97    "   \"WebKit-Version\": \"537.36 (@157588)\"\n"
98    "}";
99
100char kSampleChromePages[] = "[ {\n"
101    "   \"description\": \"\",\n"
102    "   \"devtoolsFrontendUrl\": \"/devtools/devtools.html?"
103    "ws=/devtools/page/0\",\n"
104    "   \"id\": \"0\",\n"
105    "   \"title\": \"The Chromium Projects\",\n"
106    "   \"type\": \"page\",\n"
107    "   \"url\": \"http://www.chromium.org/\",\n"
108    "   \"webSocketDebuggerUrl\": \""
109    "ws:///devtools/page/0\"\n"
110    "} ]";
111
112char kSampleChromeBetaPages[] = "[ {\n"
113    "   \"description\": \"\",\n"
114    "   \"devtoolsFrontendUrl\": \"/devtools/devtools.html?"
115    "ws=/devtools/page/0\",\n"
116    "   \"id\": \"0\",\n"
117    "   \"title\": \"The Chromium Projects\",\n"
118    "   \"type\": \"page\",\n"
119    "   \"url\": \"http://www.chromium.org/\",\n"
120    "   \"webSocketDebuggerUrl\": \""
121    "ws:///devtools/page/0\"\n"
122    "} ]";
123
124char kSampleWebViewPages[] = "[ {\n"
125    "   \"description\": \"{\\\"attached\\\":false,\\\"empty\\\":false,"
126    "\\\"height\\\":1173,\\\"screenX\\\":0,\\\"screenY\\\":0,"
127    "\\\"visible\\\":true,\\\"width\\\":800}\",\n"
128    "   \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/"
129    "serve_rev/@157588/devtools.html?ws="
130    "/devtools/page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
131    "   \"faviconUrl\": \"http://chromium.org/favicon.ico\",\n"
132    "   \"id\": \"3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
133    "   \"thumbnailUrl\": \"/thumb/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
134    "   \"title\": \"Blink - The Chromium Projects\",\n"
135    "   \"type\": \"page\",\n"
136    "   \"url\": \"http://www.chromium.org/blink\",\n"
137    "   \"webSocketDebuggerUrl\": \"ws:///devtools/"
138    "page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\"\n"
139    "}, {\n"
140    "   \"description\": \"{\\\"attached\\\":true,\\\"empty\\\":true,"
141    "\\\"screenX\\\":0,\\\"screenY\\\":33,\\\"visible\\\":false}\",\n"
142    "   \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/"
143    "serve_rev/@157588/devtools.html?ws="
144    "/devtools/page/44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
145    "   \"faviconUrl\": \"\",\n"
146    "   \"id\": \"44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
147    "   \"thumbnailUrl\": \"/thumb/44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
148    "   \"title\": \"More Activity\",\n"
149    "   \"type\": \"page\",\n"
150    "   \"url\": \"about:blank\",\n"
151    "   \"webSocketDebuggerUrl\": \"ws:///devtools/page/"
152    "44681551-ADFD-2411-076B-3AB14C1C60E2\"\n"
153    "}]";
154
155static const int kBufferSize = 16*1024;
156static const int kAdbPort = 5037;
157
158static const int kAdbMessageHeaderSize = 4;
159
160class SimpleHttpServer : base::NonThreadSafe {
161 public:
162  class Parser {
163   public:
164    virtual int Consume(const char* data, int size) = 0;
165    virtual ~Parser() {}
166  };
167
168  typedef base::Callback<void(const std::string&)> SendCallback;
169  typedef base::Callback<Parser*(const SendCallback&)> ParserFactory;
170
171  SimpleHttpServer(const ParserFactory& factory, net::IPEndPoint endpoint);
172  virtual ~SimpleHttpServer();
173
174 private:
175  class Connection : base::NonThreadSafe {
176   public:
177    Connection(net::StreamSocket* socket, const ParserFactory& factory);
178    virtual ~Connection();
179
180   private:
181    void Send(const std::string& message);
182    void ReadData();
183    void OnDataRead(int count);
184    void WriteData();
185    void OnDataWritten(int count);
186
187    scoped_ptr<net::StreamSocket> socket_;
188    scoped_ptr<Parser> parser_;
189    scoped_refptr<net::GrowableIOBuffer> input_buffer_;
190    scoped_refptr<net::GrowableIOBuffer> output_buffer_;
191    int bytes_to_write_;
192    bool read_closed_;
193    base::WeakPtrFactory<Connection> weak_factory_;
194
195    DISALLOW_COPY_AND_ASSIGN(Connection);
196  };
197
198  void AcceptConnection();
199  void OnAccepted(int result);
200
201  ParserFactory factory_;
202  scoped_ptr<net::TCPServerSocket> socket_;
203  scoped_ptr<net::StreamSocket> client_socket_;
204  base::WeakPtrFactory<SimpleHttpServer> weak_factory_;
205
206  DISALLOW_COPY_AND_ASSIGN(SimpleHttpServer);
207};
208
209SimpleHttpServer::SimpleHttpServer(const ParserFactory& factory,
210                                   net::IPEndPoint endpoint)
211    : factory_(factory),
212      socket_(new net::TCPServerSocket(NULL, net::NetLog::Source())),
213      weak_factory_(this) {
214  socket_->Listen(endpoint, 5);
215  AcceptConnection();
216}
217
218SimpleHttpServer::~SimpleHttpServer() {
219}
220
221SimpleHttpServer::Connection::Connection(net::StreamSocket* socket,
222                                         const ParserFactory& factory)
223    : socket_(socket),
224      parser_(factory.Run(base::Bind(&Connection::Send,
225                                     base::Unretained(this)))),
226      input_buffer_(new net::GrowableIOBuffer()),
227      output_buffer_(new net::GrowableIOBuffer()),
228      bytes_to_write_(0),
229      read_closed_(false),
230      weak_factory_(this) {
231  input_buffer_->SetCapacity(kBufferSize);
232  ReadData();
233}
234
235SimpleHttpServer::Connection::~Connection() {
236}
237
238void SimpleHttpServer::Connection::Send(const std::string& message) {
239  CHECK(CalledOnValidThread());
240  const char* data = message.c_str();
241  int size = message.size();
242
243  if ((output_buffer_->offset() + bytes_to_write_ + size) >
244      output_buffer_->capacity()) {
245    // If not enough space without relocation
246    if (output_buffer_->capacity() < (bytes_to_write_ + size)) {
247      // If even buffer is not enough
248      int new_size = std::max(output_buffer_->capacity() * 2, size * 2);
249      output_buffer_->SetCapacity(new_size);
250    }
251    memmove(output_buffer_->StartOfBuffer(),
252            output_buffer_->data(),
253            bytes_to_write_);
254    output_buffer_->set_offset(0);
255  }
256
257  memcpy(output_buffer_->data() + bytes_to_write_, data, size);
258  bytes_to_write_ += size;
259
260  if (bytes_to_write_ == size)
261    // If write loop wasn't yet started, then start it
262    WriteData();
263}
264
265void SimpleHttpServer::Connection::ReadData() {
266  CHECK(CalledOnValidThread());
267
268  if (input_buffer_->RemainingCapacity() == 0)
269    input_buffer_->SetCapacity(input_buffer_->capacity() * 2);
270
271  int read_result = socket_->Read(
272      input_buffer_.get(),
273      input_buffer_->RemainingCapacity(),
274      base::Bind(&Connection::OnDataRead, base::Unretained(this)));
275
276  if (read_result != net::ERR_IO_PENDING)
277    OnDataRead(read_result);
278}
279
280void SimpleHttpServer::Connection::OnDataRead(int count) {
281  CHECK(CalledOnValidThread());
282  if (count <= 0) {
283    if (bytes_to_write_ == 0)
284      delete this;
285    else
286      read_closed_ = true;
287    return;
288  }
289  input_buffer_->set_offset(input_buffer_->offset() + count);
290  int bytes_processed;
291
292  do {
293    char* data = input_buffer_->StartOfBuffer();
294    int data_size = input_buffer_->offset();
295    bytes_processed = parser_->Consume(data, data_size);
296
297    if (bytes_processed) {
298      memmove(data, data + bytes_processed, data_size - bytes_processed);
299      input_buffer_->set_offset(data_size - bytes_processed);
300    }
301  } while (bytes_processed);
302  // Posting to avoid deep recursion in case of synchronous IO
303  base::MessageLoop::current()->PostTask(
304      FROM_HERE,
305      base::Bind(&Connection::ReadData, weak_factory_.GetWeakPtr()));
306}
307
308void SimpleHttpServer::Connection::WriteData() {
309  CHECK(CalledOnValidThread());
310  CHECK_GE(output_buffer_->capacity(),
311           output_buffer_->offset() + bytes_to_write_) << "Overflow";
312
313  int write_result = socket_->Write(
314      output_buffer_.get(),
315      bytes_to_write_,
316      base::Bind(&Connection::OnDataWritten, base::Unretained(this)));
317
318  if (write_result != net::ERR_IO_PENDING)
319    OnDataWritten(write_result);
320}
321
322void SimpleHttpServer::Connection::OnDataWritten(int count) {
323  CHECK(CalledOnValidThread());
324  if (count < 0) {
325    delete this;
326    return;
327  }
328  CHECK_GT(count, 0);
329  CHECK_GE(output_buffer_->capacity(),
330           output_buffer_->offset() + bytes_to_write_) << "Overflow";
331
332  bytes_to_write_ -= count;
333  output_buffer_->set_offset(output_buffer_->offset() + count);
334
335  if (bytes_to_write_ != 0)
336    // Posting to avoid deep recursion in case of synchronous IO
337    base::MessageLoop::current()->PostTask(
338        FROM_HERE,
339        base::Bind(&Connection::WriteData, weak_factory_.GetWeakPtr()));
340  else if (read_closed_)
341    delete this;
342}
343
344void SimpleHttpServer::AcceptConnection() {
345  CHECK(CalledOnValidThread());
346
347  int accept_result = socket_->Accept(&client_socket_,
348      base::Bind(&SimpleHttpServer::OnAccepted, base::Unretained(this)));
349
350  if (accept_result != net::ERR_IO_PENDING)
351    base::MessageLoop::current()->PostTask(
352        FROM_HERE,
353        base::Bind(&SimpleHttpServer::OnAccepted,
354                   weak_factory_.GetWeakPtr(),
355                   accept_result));
356}
357
358void SimpleHttpServer::OnAccepted(int result) {
359  CHECK(CalledOnValidThread());
360  ASSERT_EQ(result, 0);  // Fails if the socket is already in use.
361  new Connection(client_socket_.release(), factory_);
362  AcceptConnection();
363}
364
365class AdbParser : SimpleHttpServer::Parser, base::NonThreadSafe {
366 public:
367  static Parser* Create(const SimpleHttpServer::SendCallback& callback) {
368    return new AdbParser(callback);
369  }
370
371  explicit AdbParser(const SimpleHttpServer::SendCallback& callback)
372      : callback_(callback) {
373  }
374
375  virtual ~AdbParser() {
376  }
377
378 private:
379  virtual int Consume(const char* data, int size) OVERRIDE {
380    CHECK(CalledOnValidThread());
381    if (!selected_socket_.empty()) {
382      std::string message(data, size);
383      size_t request_end_pos = message.find(kHttpRequestTerminator);
384      if (request_end_pos != std::string::npos) {
385        ProcessHTTPRequest(message.substr(0, request_end_pos));
386        return request_end_pos + strlen(kHttpRequestTerminator);
387      }
388      return 0;
389    }
390    if (size >= kAdbMessageHeaderSize) {
391      std::string message_header(data, kAdbMessageHeaderSize);
392      int message_size;
393
394      EXPECT_TRUE(base::HexStringToInt(message_header, &message_size));
395
396      if (size >= message_size + kAdbMessageHeaderSize) {
397        std::string message_body(data + kAdbMessageHeaderSize, message_size);
398        ProcessCommand(message_body);
399        return kAdbMessageHeaderSize + message_size;
400      }
401    }
402    return 0;
403  }
404
405  void ProcessHTTPRequest(const std::string& request) {
406    CHECK(CalledOnValidThread());
407    std::vector<std::string> tokens;
408    Tokenize(request, " ", &tokens);
409    CHECK_EQ(3U, tokens.size());
410    CHECK_EQ("GET", tokens[0]);
411    CHECK_EQ("HTTP/1.1", tokens[2]);
412
413    std::string path(tokens[1]);
414    if (path == kJsonPath)
415      path = kJsonListPath;
416
417    if (selected_socket_ == "chrome_devtools_remote") {
418      if (path == kJsonVersionPath)
419        SendHTTPResponse(kSampleChromeVersion);
420      else if (path == kJsonListPath)
421        SendHTTPResponse(kSampleChromePages);
422      else
423        NOTREACHED() << "Unknown command " << request;
424    } else if (selected_socket_ == "chrome_devtools_remote_1002") {
425      if (path == kJsonVersionPath)
426        SendHTTPResponse(kSampleChromeBetaVersion);
427      else if (path == kJsonListPath)
428        SendHTTPResponse(kSampleChromeBetaPages);
429      else
430        NOTREACHED() << "Unknown command " << request;
431    } else if (selected_socket_.find("noprocess_devtools_remote") == 0) {
432      if (path == kJsonVersionPath)
433        SendHTTPResponse("{}");
434      else if (path == kJsonListPath)
435        SendHTTPResponse("[]");
436      else
437        NOTREACHED() << "Unknown command " << request;
438    } else if (selected_socket_ == "webview_devtools_remote_2425") {
439      if (path == kJsonVersionPath)
440        SendHTTPResponse(kSampleWebViewVersion);
441      else if (path == kJsonListPath)
442        SendHTTPResponse(kSampleWebViewPages);
443      else
444        NOTREACHED() << "Unknown command " << request;
445    } else {
446      NOTREACHED() << "Unknown socket " << selected_socket_;
447    }
448  }
449
450  void ProcessCommand(const std::string& command) {
451    CHECK(CalledOnValidThread());
452    if (command == "host:devices") {
453      SendResponse(base::StringPrintf("%s\tdevice\n%s\toffline",
454                                      kSerialOnline,
455                                      kSerialOffline));
456    } else if (command.find(kHostTransportPrefix) == 0) {
457      selected_device_ = command.substr(strlen(kHostTransportPrefix));
458      SendResponse("");
459    } else if (selected_device_ != kSerialOnline) {
460      Send("FAIL", "device offline (x)");
461    } else if (command == kDeviceModelCommand) {
462      SendResponse(kDeviceModel);
463    } else if (command == kOpenedUnixSocketsCommand) {
464      SendResponse(kSampleOpenedUnixSockets);
465    } else if (command == kDumpsysCommand) {
466      SendResponse(kSampleDumpsys);
467    } else if (command == kListProcessesCommand) {
468      SendResponse(kSampleListProcesses);
469    } else if (command.find(kLocalAbstractPrefix) == 0) {
470      selected_socket_ = command.substr(strlen(kLocalAbstractPrefix));
471      SendResponse("");
472    } else {
473      NOTREACHED() << "Unknown command - " << command;
474    }
475  }
476
477  void SendResponse(const std::string& response) {
478    Send("OKAY", response);
479  }
480
481  void Send(const std::string& status, const std::string& response) {
482    CHECK(CalledOnValidThread());
483    CHECK_EQ(4U, status.size());
484
485    std::stringstream response_stream;
486    response_stream << status;
487
488    int size = response.size();
489    if (size > 0) {
490      static const char kHexChars[] = "0123456789ABCDEF";
491      for (int i = 3; i >= 0; i--)
492        response_stream << kHexChars[ (size >> 4*i) & 0x0f ];
493      response_stream << response;
494    }
495    callback_.Run(response_stream.str());
496  }
497
498  void SendHTTPResponse(const std::string& body) {
499    CHECK(CalledOnValidThread());
500    std::string response_data(base::StringPrintf(kHttpResponse,
501                                                 static_cast<int>(body.size()),
502                                                 body.c_str()));
503    callback_.Run(response_data);
504  }
505
506  std::string selected_device_;
507  std::string selected_socket_;
508  SimpleHttpServer::SendCallback callback_;
509};
510
511static SimpleHttpServer* mock_adb_server_ = NULL;
512
513void StartMockAdbServerOnIOThread() {
514  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
515  CHECK(mock_adb_server_ == NULL);
516  net::IPAddressNumber address;
517  net::ParseIPLiteralToNumber("127.0.0.1", &address);
518  net::IPEndPoint endpoint(address, kAdbPort);
519  mock_adb_server_ =
520      new SimpleHttpServer(base::Bind(&AdbParser::Create), endpoint);
521}
522
523void StopMockAdbServerOnIOThread() {
524  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
525  CHECK(mock_adb_server_ != NULL);
526  delete mock_adb_server_;
527  mock_adb_server_ = NULL;
528}
529
530} // namespace
531
532void StartMockAdbServer() {
533  BrowserThread::PostTaskAndReply(
534      BrowserThread::IO,
535      FROM_HERE,
536      base::Bind(&StartMockAdbServerOnIOThread),
537      base::MessageLoop::QuitClosure());
538  content::RunMessageLoop();
539}
540
541void StopMockAdbServer() {
542  BrowserThread::PostTaskAndReply(
543      BrowserThread::IO,
544      FROM_HERE,
545      base::Bind(&StopMockAdbServerOnIOThread),
546      base::MessageLoop::QuitClosure());
547  content::RunMessageLoop();
548}
549
550