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 <string> 7 #include <vector> 8 9 #include "base/at_exit.h" 10 #include "base/bind.h" 11 #include "base/callback.h" 12 #include "base/command_line.h" 13 #include "base/files/file_path.h" 14 #include "base/lazy_instance.h" 15 #include "base/logging.h" 16 #include "base/memory/scoped_ptr.h" 17 #include "base/message_loop/message_loop.h" 18 #include "base/run_loop.h" 19 #include "base/strings/string_number_conversions.h" 20 #include "base/strings/string_util.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/synchronization/waitable_event.h" 23 #include "base/threading/thread.h" 24 #include "base/threading/thread_local.h" 25 #include "chrome/test/chromedriver/chrome/log.h" 26 #include "chrome/test/chromedriver/chrome/version.h" 27 #include "chrome/test/chromedriver/server/http_handler.h" 28 #include "net/base/ip_endpoint.h" 29 #include "net/base/net_errors.h" 30 #include "net/server/http_server.h" 31 #include "net/server/http_server_request_info.h" 32 #include "net/server/http_server_response_info.h" 33 #include "net/socket/tcp_listen_socket.h" 34 35 #if defined(OS_POSIX) 36 #include <fcntl.h> 37 #include <unistd.h> 38 #endif 39 40 namespace { 41 42 typedef base::Callback< 43 void(const net::HttpServerRequestInfo&, const HttpResponseSenderFunc&)> 44 HttpRequestHandlerFunc; 45 46 class HttpServer : public net::HttpServer::Delegate { 47 public: 48 explicit HttpServer(const HttpRequestHandlerFunc& handle_request_func) 49 : handle_request_func_(handle_request_func), 50 weak_factory_(this) {} 51 52 virtual ~HttpServer() {} 53 54 bool Start(int port) { 55 server_ = new net::HttpServer( 56 net::TCPListenSocketFactory("0.0.0.0", port), this); 57 net::IPEndPoint address; 58 return server_->GetLocalAddress(&address) == net::OK; 59 } 60 61 // Overridden from net::HttpServer::Delegate: 62 virtual void OnHttpRequest(int connection_id, 63 const net::HttpServerRequestInfo& info) OVERRIDE { 64 handle_request_func_.Run( 65 info, 66 base::Bind(&HttpServer::OnResponse, 67 weak_factory_.GetWeakPtr(), 68 connection_id)); 69 } 70 virtual void OnWebSocketRequest( 71 int connection_id, 72 const net::HttpServerRequestInfo& info) OVERRIDE {} 73 virtual void OnWebSocketMessage(int connection_id, 74 const std::string& data) OVERRIDE {} 75 virtual void OnClose(int connection_id) OVERRIDE {} 76 77 private: 78 void OnResponse(int connection_id, 79 scoped_ptr<net::HttpServerResponseInfo> response) { 80 // Don't support keep-alive, since there's no way to detect if the 81 // client is HTTP/1.0. In such cases, the client may hang waiting for 82 // the connection to close (e.g., python 2.7 urllib). 83 response->AddHeader("Connection", "close"); 84 server_->SendResponse(connection_id, *response); 85 server_->Close(connection_id); 86 } 87 88 HttpRequestHandlerFunc handle_request_func_; 89 scoped_refptr<net::HttpServer> server_; 90 base::WeakPtrFactory<HttpServer> weak_factory_; // Should be last. 91 }; 92 93 void SendResponseOnCmdThread( 94 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, 95 const HttpResponseSenderFunc& send_response_on_io_func, 96 scoped_ptr<net::HttpServerResponseInfo> response) { 97 io_task_runner->PostTask( 98 FROM_HERE, base::Bind(send_response_on_io_func, base::Passed(&response))); 99 } 100 101 void HandleRequestOnCmdThread( 102 HttpHandler* handler, 103 const net::HttpServerRequestInfo& request, 104 const HttpResponseSenderFunc& send_response_func) { 105 handler->Handle(request, send_response_func); 106 } 107 108 void HandleRequestOnIOThread( 109 const scoped_refptr<base::SingleThreadTaskRunner>& cmd_task_runner, 110 const HttpRequestHandlerFunc& handle_request_on_cmd_func, 111 const net::HttpServerRequestInfo& request, 112 const HttpResponseSenderFunc& send_response_func) { 113 cmd_task_runner->PostTask( 114 FROM_HERE, 115 base::Bind(handle_request_on_cmd_func, 116 request, 117 base::Bind(&SendResponseOnCmdThread, 118 base::MessageLoopProxy::current(), 119 send_response_func))); 120 } 121 122 base::LazyInstance<base::ThreadLocalPointer<HttpServer> > 123 lazy_tls_server = LAZY_INSTANCE_INITIALIZER; 124 125 void StopServerOnIOThread() { 126 // Note, |server| may be NULL. 127 HttpServer* server = lazy_tls_server.Pointer()->Get(); 128 lazy_tls_server.Pointer()->Set(NULL); 129 delete server; 130 } 131 132 void StartServerOnIOThread(int port, 133 const HttpRequestHandlerFunc& handle_request_func) { 134 scoped_ptr<HttpServer> temp_server(new HttpServer(handle_request_func)); 135 if (!temp_server->Start(port)) { 136 printf("Port not available. Exiting...\n"); 137 exit(1); 138 } 139 lazy_tls_server.Pointer()->Set(temp_server.release()); 140 } 141 142 void RunServer(Log::Level log_level, int port, const std::string& url_base) { 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 Logger log(log_level); 150 HttpHandler handler(cmd_run_loop.QuitClosure(), 151 io_thread.message_loop_proxy(), 152 &log, 153 url_base); 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 // Parse command line flags. 183 int port = 9515; 184 std::string url_base; 185 base::FilePath log_path; 186 Log::Level log_level = Log::kError; 187 if (cmd_line->HasSwitch("h") || cmd_line->HasSwitch("help")) { 188 std::string options; 189 const char* kOptionAndDescriptions[] = { 190 "port=PORT", "port to listen on", 191 "log-path=FILE", "write server log to file instead of stderr, " 192 "increases log level to INFO", 193 "verbose", "log verbosely", 194 "silent", "log nothing", 195 "url-base", "base URL path prefix for commands, e.g. wd/url", 196 }; 197 for (size_t i = 0; i < arraysize(kOptionAndDescriptions) - 1; i += 2) { 198 options += base::StringPrintf( 199 " --%-30s%s\n", 200 kOptionAndDescriptions[i], kOptionAndDescriptions[i + 1]); 201 } 202 printf("Usage: %s [OPTIONS]\n\nOptions\n%s", argv[0], options.c_str()); 203 return 0; 204 } 205 if (cmd_line->HasSwitch("port")) { 206 if (!base::StringToInt(cmd_line->GetSwitchValueASCII("port"), &port)) { 207 printf("Invalid port. Exiting...\n"); 208 return 1; 209 } 210 } 211 if (cmd_line->HasSwitch("url-base")) 212 url_base = cmd_line->GetSwitchValueASCII("url-base"); 213 if (url_base.empty() || url_base[0] != '/') 214 url_base = "/" + url_base; 215 if (url_base[url_base.length() - 1] != '/') 216 url_base = url_base + "/"; 217 if (cmd_line->HasSwitch("log-path")) { 218 log_level = Log::kLog; 219 log_path = cmd_line->GetSwitchValuePath("log-path"); 220 #if defined(OS_WIN) 221 FILE* redir_stderr = _wfreopen(log_path.value().c_str(), L"w", stderr); 222 #else 223 FILE* redir_stderr = freopen(log_path.value().c_str(), "w", stderr); 224 #endif 225 if (!redir_stderr) { 226 printf("Failed to redirect stderr to log file. Exiting...\n"); 227 return 1; 228 } 229 } 230 if (cmd_line->HasSwitch("verbose")) { 231 log_level = Log::kDebug; 232 } else { 233 #if defined(OS_POSIX) 234 // Close stderr on exec, so that Chrome log spew doesn't confuse users. 235 fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC); 236 #endif 237 } 238 if (!cmd_line->HasSwitch("silent")) { 239 printf( 240 "Starting ChromeDriver (v%s) on port %d\n", kChromeDriverVersion, port); 241 fflush(stdout); 242 } 243 244 245 logging::LoggingSettings settings; 246 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; 247 bool success = logging::InitLogging(settings); 248 if (!success) { 249 PLOG(ERROR) << "Unable to initialize logging"; 250 } 251 logging::SetLogItems(false, // enable_process_id 252 false, // enable_thread_id 253 false, // enable_timestamp 254 false); // enable_tickcount 255 if (!cmd_line->HasSwitch("verbose")) 256 logging::SetMinLogLevel(logging::LOG_FATAL); 257 258 RunServer(log_level, port, url_base); 259 return 0; 260 } 261