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.
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"
19 using content::BrowserThread;
23 const char kHostTransportPrefix[] = "host:transport:";
24 const char kLocalAbstractPrefix[] = "localabstract:";
26 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
27 const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
28 const char kDumpsysCommand[] = "shell:dumpsys window policy";
29 const char kListProcessesCommand[] = "shell:ps";
31 const char kSerialOnline[] = "01498B321301A00A";
32 const char kSerialOffline[] = "01498B2B0D01300E";
33 const char kDeviceModel[] = "Nexus 6";
35 const char kJsonVersionPath[] = "/json/version";
36 const char kJsonPath[] = "/json";
37 const char kJsonListPath[] = "/json/list";
39 const char kHttpRequestTerminator[] = "\r\n\r\n";
41 const char kHttpResponse[] =
43 "Content-Length:%d\r\n"
44 "Content-Type:application/json; charset=UTF-8\r\n\r\n%s";
46 const 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";
61 const 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";
70 const 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";
76 char 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"
84 char 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"
92 char 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"
100 char kSampleChromePages[] = "[ {\n"
101 " \"description\": \"\",\n"
102 " \"devtoolsFrontendUrl\": \"/devtools/devtools.html?"
103 "ws=/devtools/page/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"
112 char kSampleChromeBetaPages[] = "[ {\n"
113 " \"description\": \"\",\n"
114 " \"devtoolsFrontendUrl\": \"/devtools/devtools.html?"
115 "ws=/devtools/page/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"
124 char 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"
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"
155 static const int kBufferSize = 16*1024;
156 static const int kAdbPort = 5037;
158 static const int kAdbMessageHeaderSize = 4;
160 class SimpleHttpServer : base::NonThreadSafe {
164 virtual int Consume(const char* data, int size) = 0;
168 typedef base::Callback<void(const std::string&)> SendCallback;
169 typedef base::Callback<Parser*(const SendCallback&)> ParserFactory;
171 SimpleHttpServer(const ParserFactory& factory, net::IPEndPoint endpoint);
172 virtual ~SimpleHttpServer();
175 class Connection : base::NonThreadSafe {
177 Connection(net::StreamSocket* socket, const ParserFactory& factory);
178 virtual ~Connection();
181 void Send(const std::string& message);
183 void OnDataRead(int count);
185 void OnDataWritten(int count);
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_;
193 base::WeakPtrFactory<Connection> weak_factory_;
195 DISALLOW_COPY_AND_ASSIGN(Connection);
198 void AcceptConnection();
199 void OnAccepted(int result);
201 ParserFactory factory_;
202 scoped_ptr<net::TCPServerSocket> socket_;
203 scoped_ptr<net::StreamSocket> client_socket_;
204 base::WeakPtrFactory<SimpleHttpServer> weak_factory_;
206 DISALLOW_COPY_AND_ASSIGN(SimpleHttpServer);
209 SimpleHttpServer::SimpleHttpServer(const ParserFactory& factory,
210 net::IPEndPoint endpoint)
212 socket_(new net::TCPServerSocket(NULL, net::NetLog::Source())),
213 weak_factory_(this) {
214 socket_->Listen(endpoint, 5);
218 SimpleHttpServer::~SimpleHttpServer() {
221 SimpleHttpServer::Connection::Connection(net::StreamSocket* socket,
222 const ParserFactory& factory)
224 parser_(factory.Run(base::Bind(&Connection::Send,
225 base::Unretained(this)))),
226 input_buffer_(new net::GrowableIOBuffer()),
227 output_buffer_(new net::GrowableIOBuffer()),
230 weak_factory_(this) {
231 input_buffer_->SetCapacity(kBufferSize);
235 SimpleHttpServer::Connection::~Connection() {
238 void SimpleHttpServer::Connection::Send(const std::string& message) {
239 CHECK(CalledOnValidThread());
240 const char* data = message.c_str();
241 int size = message.size();
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);
251 memmove(output_buffer_->StartOfBuffer(),
252 output_buffer_->data(),
254 output_buffer_->set_offset(0);
257 memcpy(output_buffer_->data() + bytes_to_write_, data, size);
258 bytes_to_write_ += size;
260 if (bytes_to_write_ == size)
261 // If write loop wasn't yet started, then start it
265 void SimpleHttpServer::Connection::ReadData() {
266 CHECK(CalledOnValidThread());
268 if (input_buffer_->RemainingCapacity() == 0)
269 input_buffer_->SetCapacity(input_buffer_->capacity() * 2);
271 int read_result = socket_->Read(
273 input_buffer_->RemainingCapacity(),
274 base::Bind(&Connection::OnDataRead, base::Unretained(this)));
276 if (read_result != net::ERR_IO_PENDING)
277 OnDataRead(read_result);
280 void SimpleHttpServer::Connection::OnDataRead(int count) {
281 CHECK(CalledOnValidThread());
283 if (bytes_to_write_ == 0)
289 input_buffer_->set_offset(input_buffer_->offset() + count);
293 char* data = input_buffer_->StartOfBuffer();
294 int data_size = input_buffer_->offset();
295 bytes_processed = parser_->Consume(data, data_size);
297 if (bytes_processed) {
298 memmove(data, data + bytes_processed, data_size - bytes_processed);
299 input_buffer_->set_offset(data_size - bytes_processed);
301 } while (bytes_processed);
302 // Posting to avoid deep recursion in case of synchronous IO
303 base::MessageLoop::current()->PostTask(
305 base::Bind(&Connection::ReadData, weak_factory_.GetWeakPtr()));
308 void SimpleHttpServer::Connection::WriteData() {
309 CHECK(CalledOnValidThread());
310 CHECK_GE(output_buffer_->capacity(),
311 output_buffer_->offset() + bytes_to_write_) << "Overflow";
313 int write_result = socket_->Write(
314 output_buffer_.get(),
316 base::Bind(&Connection::OnDataWritten, base::Unretained(this)));
318 if (write_result != net::ERR_IO_PENDING)
319 OnDataWritten(write_result);
322 void SimpleHttpServer::Connection::OnDataWritten(int count) {
323 CHECK(CalledOnValidThread());
329 CHECK_GE(output_buffer_->capacity(),
330 output_buffer_->offset() + bytes_to_write_) << "Overflow";
332 bytes_to_write_ -= count;
333 output_buffer_->set_offset(output_buffer_->offset() + count);
335 if (bytes_to_write_ != 0)
336 // Posting to avoid deep recursion in case of synchronous IO
337 base::MessageLoop::current()->PostTask(
339 base::Bind(&Connection::WriteData, weak_factory_.GetWeakPtr()));
340 else if (read_closed_)
344 void SimpleHttpServer::AcceptConnection() {
345 CHECK(CalledOnValidThread());
347 int accept_result = socket_->Accept(&client_socket_,
348 base::Bind(&SimpleHttpServer::OnAccepted, base::Unretained(this)));
350 if (accept_result != net::ERR_IO_PENDING)
351 base::MessageLoop::current()->PostTask(
353 base::Bind(&SimpleHttpServer::OnAccepted,
354 weak_factory_.GetWeakPtr(),
358 void 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_);
365 class AdbParser : SimpleHttpServer::Parser, base::NonThreadSafe {
367 static Parser* Create(const SimpleHttpServer::SendCallback& callback) {
368 return new AdbParser(callback);
371 explicit AdbParser(const SimpleHttpServer::SendCallback& callback)
372 : callback_(callback) {
375 virtual ~AdbParser() {
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);
390 if (size >= kAdbMessageHeaderSize) {
391 std::string message_header(data, kAdbMessageHeaderSize);
394 EXPECT_TRUE(base::HexStringToInt(message_header, &message_size));
396 if (size >= message_size + kAdbMessageHeaderSize) {
397 std::string message_body(data + kAdbMessageHeaderSize, message_size);
398 ProcessCommand(message_body);
399 return kAdbMessageHeaderSize + message_size;
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]);
413 std::string path(tokens[1]);
414 if (path == kJsonPath)
415 path = kJsonListPath;
417 if (selected_socket_ == "chrome_devtools_remote") {
418 if (path == kJsonVersionPath)
419 SendHTTPResponse(kSampleChromeVersion);
420 else if (path == kJsonListPath)
421 SendHTTPResponse(kSampleChromePages);
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);
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("[]");
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);
444 NOTREACHED() << "Unknown command " << request;
446 NOTREACHED() << "Unknown socket " << selected_socket_;
450 void ProcessCommand(const std::string& command) {
451 CHECK(CalledOnValidThread());
452 if (command == "host:devices") {
453 SendResponse(base::StringPrintf("%s\tdevice\n%s\toffline",
456 } else if (command.find(kHostTransportPrefix) == 0) {
457 selected_device_ = command.substr(strlen(kHostTransportPrefix));
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));
473 NOTREACHED() << "Unknown command - " << command;
477 void SendResponse(const std::string& response) {
478 Send("OKAY", response);
481 void Send(const std::string& status, const std::string& response) {
482 CHECK(CalledOnValidThread());
483 CHECK_EQ(4U, status.size());
485 std::stringstream response_stream;
486 response_stream << status;
488 int size = response.size();
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;
495 callback_.Run(response_stream.str());
498 void SendHTTPResponse(const std::string& body) {
499 CHECK(CalledOnValidThread());
500 std::string response_data(base::StringPrintf(kHttpResponse,
501 static_cast<int>(body.size()),
503 callback_.Run(response_data);
506 std::string selected_device_;
507 std::string selected_socket_;
508 SimpleHttpServer::SendCallback callback_;
511 static SimpleHttpServer* mock_adb_server_ = NULL;
513 void 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);
520 new SimpleHttpServer(base::Bind(&AdbParser::Create), endpoint);
523 void StopMockAdbServerOnIOThread() {
524 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
525 CHECK(mock_adb_server_ != NULL);
526 delete mock_adb_server_;
527 mock_adb_server_ = NULL;
532 void StartMockAdbServer() {
533 BrowserThread::PostTaskAndReply(
536 base::Bind(&StartMockAdbServerOnIOThread),
537 base::MessageLoop::QuitClosure());
538 content::RunMessageLoop();
541 void StopMockAdbServer() {
542 BrowserThread::PostTaskAndReply(
545 base::Bind(&StopMockAdbServerOnIOThread),
546 base::MessageLoop::QuitClosure());
547 content::RunMessageLoop();