1 // Copyright (c) 2013 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 <stdio.h> 6 #include <locale> 7 #include <string> 8 #include <vector> 9 10 #include "base/at_exit.h" 11 #include "base/bind.h" 12 #include "base/callback.h" 13 #include "base/command_line.h" 14 #include "base/files/file_path.h" 15 #include "base/lazy_instance.h" 16 #include "base/logging.h" 17 #include "base/memory/scoped_ptr.h" 18 #include "base/message_loop/message_loop.h" 19 #include "base/run_loop.h" 20 #include "base/strings/string_number_conversions.h" 21 #include "base/strings/string_util.h" 22 #include "base/strings/stringprintf.h" 23 #include "base/synchronization/waitable_event.h" 24 #include "base/threading/thread.h" 25 #include "base/threading/thread_local.h" 26 #include "chrome/test/chromedriver/logging.h" 27 #include "chrome/test/chromedriver/net/port_server.h" 28 #include "chrome/test/chromedriver/server/http_handler.h" 29 #include "chrome/test/chromedriver/version.h" 30 #include "net/base/ip_endpoint.h" 31 #include "net/base/net_errors.h" 32 #include "net/server/http_server.h" 33 #include "net/server/http_server_request_info.h" 34 #include "net/server/http_server_response_info.h" 35 #include "net/socket/tcp_listen_socket.h" 36 37 namespace { 38 39 typedef base::Callback< 40 void(const net::HttpServerRequestInfo&, const HttpResponseSenderFunc&)> 41 HttpRequestHandlerFunc; 42 43 class HttpServer : public net::HttpServer::Delegate { 44 public: 45 explicit HttpServer(const HttpRequestHandlerFunc& handle_request_func) 46 : handle_request_func_(handle_request_func), 47 weak_factory_(this) {} 48 49 virtual ~HttpServer() {} 50 51 bool Start(int port) { 52 server_ = new net::HttpServer( 53 net::TCPListenSocketFactory("0.0.0.0", port), this); 54 net::IPEndPoint address; 55 return server_->GetLocalAddress(&address) == net::OK; 56 } 57 58 // Overridden from net::HttpServer::Delegate: 59 virtual void OnHttpRequest(int connection_id, 60 const net::HttpServerRequestInfo& info) OVERRIDE { 61 handle_request_func_.Run( 62 info, 63 base::Bind(&HttpServer::OnResponse, 64 weak_factory_.GetWeakPtr(), 65 connection_id)); 66 } 67 virtual void OnWebSocketRequest( 68 int connection_id, 69 const net::HttpServerRequestInfo& info) OVERRIDE {} 70 virtual void OnWebSocketMessage(int connection_id, 71 const std::string& data) OVERRIDE {} 72 virtual void OnClose(int connection_id) OVERRIDE {} 73 74 private: 75 void OnResponse(int connection_id, 76 scoped_ptr<net::HttpServerResponseInfo> response) { 77 // Don't support keep-alive, since there's no way to detect if the 78 // client is HTTP/1.0. In such cases, the client may hang waiting for 79 // the connection to close (e.g., python 2.7 urllib). 80 response->AddHeader("Connection", "close"); 81 server_->SendResponse(connection_id, *response); 82 server_->Close(connection_id); 83 } 84 85 HttpRequestHandlerFunc handle_request_func_; 86 scoped_refptr<net::HttpServer> server_; 87 base::WeakPtrFactory<HttpServer> weak_factory_; // Should be last. 88 }; 89 90 void SendResponseOnCmdThread( 91 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, 92 const HttpResponseSenderFunc& send_response_on_io_func, 93 scoped_ptr<net::HttpServerResponseInfo> response) { 94 io_task_runner->PostTask( 95 FROM_HERE, base::Bind(send_response_on_io_func, base::Passed(&response))); 96 } 97 98 void HandleRequestOnCmdThread( 99 HttpHandler* handler, 100 const net::HttpServerRequestInfo& request, 101 const HttpResponseSenderFunc& send_response_func) { 102 handler->Handle(request, send_response_func); 103 } 104 105 void HandleRequestOnIOThread( 106 const scoped_refptr<base::SingleThreadTaskRunner>& cmd_task_runner, 107 const HttpRequestHandlerFunc& handle_request_on_cmd_func, 108 const net::HttpServerRequestInfo& request, 109 const HttpResponseSenderFunc& send_response_func) { 110 cmd_task_runner->PostTask( 111 FROM_HERE, 112 base::Bind(handle_request_on_cmd_func, 113 request, 114 base::Bind(&SendResponseOnCmdThread, 115 base::MessageLoopProxy::current(), 116 send_response_func))); 117 } 118 119 base::LazyInstance<base::ThreadLocalPointer<HttpServer> > 120 lazy_tls_server = LAZY_INSTANCE_INITIALIZER; 121 122 void StopServerOnIOThread() { 123 // Note, |server| may be NULL. 124 HttpServer* server = lazy_tls_server.Pointer()->Get(); 125 lazy_tls_server.Pointer()->Set(NULL); 126 delete server; 127 } 128 129 void StartServerOnIOThread(int port, 130 const HttpRequestHandlerFunc& handle_request_func) { 131 scoped_ptr<HttpServer> temp_server(new HttpServer(handle_request_func)); 132 if (!temp_server->Start(port)) { 133 printf("Port not available. Exiting...\n"); 134 exit(1); 135 } 136 lazy_tls_server.Pointer()->Set(temp_server.release()); 137 } 138 139 void RunServer(int port, 140 const std::string& url_base, 141 int adb_port, 142 scoped_ptr<PortServer> port_server) { 143 base::Thread io_thread("ChromeDriver IO"); 144 CHECK(io_thread.StartWithOptions( 145 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); 146 147 base::MessageLoop cmd_loop; 148 base::RunLoop cmd_run_loop; 149 HttpHandler handler(cmd_run_loop.QuitClosure(), 150 io_thread.message_loop_proxy(), 151 url_base, 152 adb_port, 153 port_server.Pass()); 154 HttpRequestHandlerFunc handle_request_func = 155 base::Bind(&HandleRequestOnCmdThread, &handler); 156 157 io_thread.message_loop() 158 ->PostTask(FROM_HERE, 159 base::Bind(&StartServerOnIOThread, 160 port, 161 base::Bind(&HandleRequestOnIOThread, 162 cmd_loop.message_loop_proxy(), 163 handle_request_func))); 164 // Run the command loop. This loop is quit after the response for a shutdown 165 // request is posted to the IO loop. After the command loop quits, a task 166 // is posted to the IO loop to stop the server. Lastly, the IO thread is 167 // destroyed, which waits until all pending tasks have been completed. 168 // This assumes the response is sent synchronously as part of the IO task. 169 cmd_run_loop.Run(); 170 io_thread.message_loop() 171 ->PostTask(FROM_HERE, base::Bind(&StopServerOnIOThread)); 172 } 173 174 } // namespace 175 176 int main(int argc, char *argv[]) { 177 CommandLine::Init(argc, argv); 178 179 base::AtExitManager at_exit; 180 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 181 182 #if defined(OS_LINUX) 183 // Select the locale from the environment by passing an empty string instead 184 // of the default "C" locale. This is particularly needed for the keycode 185 // conversion code to work. 186 std::setlocale(LC_ALL, ""); 187 #endif 188 189 // Parse command line flags. 190 int port = 9515; 191 int adb_port = 5037; 192 std::string url_base; 193 scoped_ptr<PortServer> port_server; 194 if (cmd_line->HasSwitch("h") || cmd_line->HasSwitch("help")) { 195 std::string options; 196 const char* kOptionAndDescriptions[] = { 197 "port=PORT", "port to listen on", 198 "adb-port=PORT", "adb server port", 199 "log-path=FILE", "write server log to file instead of stderr, " 200 "increases log level to INFO", 201 "verbose", "log verbosely", 202 "silent", "log nothing", 203 "url-base", "base URL path prefix for commands, e.g. wd/url", 204 "port-server", "address of server to contact for reserving a port", 205 }; 206 for (size_t i = 0; i < arraysize(kOptionAndDescriptions) - 1; i += 2) { 207 options += base::StringPrintf( 208 " --%-30s%s\n", 209 kOptionAndDescriptions[i], kOptionAndDescriptions[i + 1]); 210 } 211 printf("Usage: %s [OPTIONS]\n\nOptions\n%s", argv[0], options.c_str()); 212 return 0; 213 } 214 if (cmd_line->HasSwitch("port")) { 215 if (!base::StringToInt(cmd_line->GetSwitchValueASCII("port"), &port)) { 216 printf("Invalid port. Exiting...\n"); 217 return 1; 218 } 219 } 220 if (cmd_line->HasSwitch("adb-port")) { 221 if (!base::StringToInt(cmd_line->GetSwitchValueASCII("adb-port"), 222 &adb_port)) { 223 printf("Invalid adb-port. Exiting...\n"); 224 return 1; 225 } 226 } 227 if (cmd_line->HasSwitch("port-server")) { 228 #if defined(OS_LINUX) 229 std::string address = cmd_line->GetSwitchValueASCII("port-server"); 230 if (address.empty() || address[0] != '@') { 231 printf("Invalid port-server. Exiting...\n"); 232 return 1; 233 } 234 std::string path; 235 // First character of path is \0 to use Linux's abstract namespace. 236 path.push_back(0); 237 path += address.substr(1); 238 port_server.reset(new PortServer(path)); 239 #else 240 printf("Warning: port-server not implemented for this platform.\n"); 241 #endif 242 } 243 if (cmd_line->HasSwitch("url-base")) 244 url_base = cmd_line->GetSwitchValueASCII("url-base"); 245 if (url_base.empty() || url_base[0] != '/') 246 url_base = "/" + url_base; 247 if (url_base[url_base.length() - 1] != '/') 248 url_base = url_base + "/"; 249 if (!cmd_line->HasSwitch("silent")) { 250 printf( 251 "Starting ChromeDriver (v%s) on port %d\n", kChromeDriverVersion, port); 252 fflush(stdout); 253 } 254 255 if (!InitLogging()) { 256 printf("Unable to initialize logging. Exiting...\n"); 257 return 1; 258 } 259 RunServer(port, url_base, adb_port, port_server.Pass()); 260 return 0; 261 } 262