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