• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
HttpServer(const HttpRequestHandlerFunc & handle_request_func)45   explicit HttpServer(const HttpRequestHandlerFunc& handle_request_func)
46       : handle_request_func_(handle_request_func),
47         weak_factory_(this) {}
48 
~HttpServer()49   virtual ~HttpServer() {}
50 
Start(int port)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:
OnHttpRequest(int connection_id,const net::HttpServerRequestInfo & info)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   }
OnWebSocketRequest(int connection_id,const net::HttpServerRequestInfo & info)67   virtual void OnWebSocketRequest(
68       int connection_id,
69       const net::HttpServerRequestInfo& info) OVERRIDE {}
OnWebSocketMessage(int connection_id,const std::string & data)70   virtual void OnWebSocketMessage(int connection_id,
71                                   const std::string& data) OVERRIDE {}
OnClose(int connection_id)72   virtual void OnClose(int connection_id) OVERRIDE {}
73 
74  private:
OnResponse(int connection_id,scoped_ptr<net::HttpServerResponseInfo> response)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 
SendResponseOnCmdThread(const scoped_refptr<base::SingleThreadTaskRunner> & io_task_runner,const HttpResponseSenderFunc & send_response_on_io_func,scoped_ptr<net::HttpServerResponseInfo> response)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 
HandleRequestOnCmdThread(HttpHandler * handler,const net::HttpServerRequestInfo & request,const HttpResponseSenderFunc & send_response_func)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 
HandleRequestOnIOThread(const scoped_refptr<base::SingleThreadTaskRunner> & cmd_task_runner,const HttpRequestHandlerFunc & handle_request_on_cmd_func,const net::HttpServerRequestInfo & request,const HttpResponseSenderFunc & send_response_func)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 
StopServerOnIOThread()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 
StartServerOnIOThread(int port,const HttpRequestHandlerFunc & handle_request_func)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 
RunServer(int port,const std::string & url_base,int adb_port,scoped_ptr<PortServer> port_server)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 
main(int argc,char * argv[])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