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.
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_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/synchronization/waitable_event.h"
25 #include "base/thread_task_runner_handle.h"
26 #include "base/threading/thread.h"
27 #include "base/threading/thread_local.h"
28 #include "chrome/test/chromedriver/logging.h"
29 #include "chrome/test/chromedriver/net/port_server.h"
30 #include "chrome/test/chromedriver/server/http_handler.h"
31 #include "chrome/test/chromedriver/version.h"
32 #include "net/base/ip_endpoint.h"
33 #include "net/base/net_errors.h"
34 #include "net/server/http_server.h"
35 #include "net/server/http_server_request_info.h"
36 #include "net/server/http_server_response_info.h"
37 #include "net/socket/tcp_server_socket.h"
41 const char kLocalHostAddress
[] = "127.0.0.1";
42 const int kBufferSize
= 100 * 1024 * 1024; // 100 MB
44 typedef base::Callback
<
45 void(const net::HttpServerRequestInfo
&, const HttpResponseSenderFunc
&)>
46 HttpRequestHandlerFunc
;
48 class HttpServer
: public net::HttpServer::Delegate
{
50 explicit HttpServer(const HttpRequestHandlerFunc
& handle_request_func
)
51 : handle_request_func_(handle_request_func
),
52 weak_factory_(this) {}
54 ~HttpServer() override
{}
56 bool Start(uint16 port
, bool allow_remote
) {
57 std::string binding_ip
= kLocalHostAddress
;
59 binding_ip
= "0.0.0.0";
60 scoped_ptr
<net::ServerSocket
> server_socket(
61 new net::TCPServerSocket(NULL
, net::NetLog::Source()));
62 server_socket
->ListenWithAddressAndPort(binding_ip
, port
, 1);
63 server_
.reset(new net::HttpServer(server_socket
.Pass(), this));
64 net::IPEndPoint address
;
65 return server_
->GetLocalAddress(&address
) == net::OK
;
68 // Overridden from net::HttpServer::Delegate:
69 void OnConnect(int connection_id
) override
{
70 server_
->SetSendBufferSize(connection_id
, kBufferSize
);
71 server_
->SetReceiveBufferSize(connection_id
, kBufferSize
);
73 void OnHttpRequest(int connection_id
,
74 const net::HttpServerRequestInfo
& info
) override
{
75 handle_request_func_
.Run(
77 base::Bind(&HttpServer::OnResponse
,
78 weak_factory_
.GetWeakPtr(),
81 void OnWebSocketRequest(int connection_id
,
82 const net::HttpServerRequestInfo
& info
) override
{}
83 void OnWebSocketMessage(int connection_id
, const std::string
& data
) override
{
85 void OnClose(int connection_id
) override
{}
88 void OnResponse(int connection_id
,
89 scoped_ptr
<net::HttpServerResponseInfo
> response
) {
90 // Don't support keep-alive, since there's no way to detect if the
91 // client is HTTP/1.0. In such cases, the client may hang waiting for
92 // the connection to close (e.g., python 2.7 urllib).
93 response
->AddHeader("Connection", "close");
94 server_
->SendResponse(connection_id
, *response
);
95 // Don't need to call server_->Close(), since SendResponse() will handle
99 HttpRequestHandlerFunc handle_request_func_
;
100 scoped_ptr
<net::HttpServer
> server_
;
101 base::WeakPtrFactory
<HttpServer
> weak_factory_
; // Should be last.
104 void SendResponseOnCmdThread(
105 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
,
106 const HttpResponseSenderFunc
& send_response_on_io_func
,
107 scoped_ptr
<net::HttpServerResponseInfo
> response
) {
108 io_task_runner
->PostTask(
109 FROM_HERE
, base::Bind(send_response_on_io_func
, base::Passed(&response
)));
112 void HandleRequestOnCmdThread(
113 HttpHandler
* handler
,
114 const std::vector
<std::string
>& whitelisted_ips
,
115 const net::HttpServerRequestInfo
& request
,
116 const HttpResponseSenderFunc
& send_response_func
) {
117 if (!whitelisted_ips
.empty()) {
118 std::string peer_address
= request
.peer
.ToStringWithoutPort();
119 if (peer_address
!= kLocalHostAddress
&&
120 std::find(whitelisted_ips
.begin(), whitelisted_ips
.end(),
121 peer_address
) == whitelisted_ips
.end()) {
122 LOG(WARNING
) << "unauthorized access from " << request
.peer
.ToString();
123 scoped_ptr
<net::HttpServerResponseInfo
> response(
124 new net::HttpServerResponseInfo(net::HTTP_UNAUTHORIZED
));
125 response
->SetBody("Unauthorized access", "text/plain");
126 send_response_func
.Run(response
.Pass());
131 handler
->Handle(request
, send_response_func
);
134 void HandleRequestOnIOThread(
135 const scoped_refptr
<base::SingleThreadTaskRunner
>& cmd_task_runner
,
136 const HttpRequestHandlerFunc
& handle_request_on_cmd_func
,
137 const net::HttpServerRequestInfo
& request
,
138 const HttpResponseSenderFunc
& send_response_func
) {
139 cmd_task_runner
->PostTask(
140 FROM_HERE
, base::Bind(handle_request_on_cmd_func
, request
,
141 base::Bind(&SendResponseOnCmdThread
,
142 base::ThreadTaskRunnerHandle::Get(),
143 send_response_func
)));
146 base::LazyInstance
<base::ThreadLocalPointer
<HttpServer
> >
147 lazy_tls_server
= LAZY_INSTANCE_INITIALIZER
;
149 void StopServerOnIOThread() {
150 // Note, |server| may be NULL.
151 HttpServer
* server
= lazy_tls_server
.Pointer()->Get();
152 lazy_tls_server
.Pointer()->Set(NULL
);
156 void StartServerOnIOThread(uint16 port
,
158 const HttpRequestHandlerFunc
& handle_request_func
) {
159 scoped_ptr
<HttpServer
> temp_server(new HttpServer(handle_request_func
));
160 if (!temp_server
->Start(port
, allow_remote
)) {
161 printf("Port not available. Exiting...\n");
164 lazy_tls_server
.Pointer()->Set(temp_server
.release());
167 void RunServer(uint16 port
,
169 const std::vector
<std::string
>& whitelisted_ips
,
170 const std::string
& url_base
,
172 scoped_ptr
<PortServer
> port_server
) {
173 base::Thread
io_thread("ChromeDriver IO");
174 CHECK(io_thread
.StartWithOptions(
175 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0)));
177 base::MessageLoop cmd_loop
;
178 base::RunLoop cmd_run_loop
;
179 HttpHandler
handler(cmd_run_loop
.QuitClosure(), io_thread
.task_runner(),
180 url_base
, adb_port
, port_server
.Pass());
181 HttpRequestHandlerFunc handle_request_func
=
182 base::Bind(&HandleRequestOnCmdThread
, &handler
, whitelisted_ips
);
184 io_thread
.message_loop()->PostTask(
186 base::Bind(&StartServerOnIOThread
, port
, allow_remote
,
187 base::Bind(&HandleRequestOnIOThread
, cmd_loop
.task_runner(),
188 handle_request_func
)));
189 // Run the command loop. This loop is quit after the response for a shutdown
190 // request is posted to the IO loop. After the command loop quits, a task
191 // is posted to the IO loop to stop the server. Lastly, the IO thread is
192 // destroyed, which waits until all pending tasks have been completed.
193 // This assumes the response is sent synchronously as part of the IO task.
195 io_thread
.message_loop()
196 ->PostTask(FROM_HERE
, base::Bind(&StopServerOnIOThread
));
201 int main(int argc
, char *argv
[]) {
202 base::CommandLine::Init(argc
, argv
);
204 base::AtExitManager at_exit
;
205 base::CommandLine
* cmd_line
= base::CommandLine::ForCurrentProcess();
207 #if defined(OS_LINUX)
208 // Select the locale from the environment by passing an empty string instead
209 // of the default "C" locale. This is particularly needed for the keycode
210 // conversion code to work.
211 setlocale(LC_ALL
, "");
214 // Parse command line flags.
217 bool allow_remote
= false;
218 std::vector
<std::string
> whitelisted_ips
;
219 std::string url_base
;
220 scoped_ptr
<PortServer
> port_server
;
221 if (cmd_line
->HasSwitch("h") || cmd_line
->HasSwitch("help")) {
223 const char* const kOptionAndDescriptions
[] = {
224 "port=PORT", "port to listen on",
225 "adb-port=PORT", "adb server port",
226 "log-path=FILE", "write server log to file instead of stderr, "
227 "increases log level to INFO",
228 "verbose", "log verbosely",
229 "version", "print the version number and exit",
230 "silent", "log nothing",
231 "url-base", "base URL path prefix for commands, e.g. wd/url",
232 "port-server", "address of server to contact for reserving a port",
233 "whitelisted-ips", "comma-separated whitelist of remote IPv4 addresses "
234 "which are allowed to connect to ChromeDriver",
236 for (size_t i
= 0; i
< arraysize(kOptionAndDescriptions
) - 1; i
+= 2) {
237 options
+= base::StringPrintf(
239 kOptionAndDescriptions
[i
], kOptionAndDescriptions
[i
+ 1]);
241 printf("Usage: %s [OPTIONS]\n\nOptions\n%s", argv
[0], options
.c_str());
244 if (cmd_line
->HasSwitch("v") || cmd_line
->HasSwitch("version")) {
245 printf("ChromeDriver %s\n", kChromeDriverVersion
);
248 if (cmd_line
->HasSwitch("port")) {
250 if (!base::StringToInt(cmd_line
->GetSwitchValueASCII("port"),
252 cmd_line_port
< 0 || cmd_line_port
> 65535) {
253 printf("Invalid port. Exiting...\n");
256 port
= static_cast<uint16
>(cmd_line_port
);
258 if (cmd_line
->HasSwitch("adb-port")) {
259 if (!base::StringToInt(cmd_line
->GetSwitchValueASCII("adb-port"),
261 printf("Invalid adb-port. Exiting...\n");
265 if (cmd_line
->HasSwitch("port-server")) {
266 #if defined(OS_LINUX)
267 std::string address
= cmd_line
->GetSwitchValueASCII("port-server");
268 if (address
.empty() || address
[0] != '@') {
269 printf("Invalid port-server. Exiting...\n");
273 // First character of path is \0 to use Linux's abstract namespace.
275 path
+= address
.substr(1);
276 port_server
.reset(new PortServer(path
));
278 printf("Warning: port-server not implemented for this platform.\n");
281 if (cmd_line
->HasSwitch("url-base"))
282 url_base
= cmd_line
->GetSwitchValueASCII("url-base");
283 if (url_base
.empty() || url_base
[0] != '/')
284 url_base
= "/" + url_base
;
285 if (url_base
[url_base
.length() - 1] != '/')
286 url_base
= url_base
+ "/";
287 if (cmd_line
->HasSwitch("whitelisted-ips")) {
289 std::string whitelist
= cmd_line
->GetSwitchValueASCII("whitelisted-ips");
290 whitelisted_ips
= base::SplitString(
291 whitelist
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
293 if (!cmd_line
->HasSwitch("silent")) {
294 printf("Starting ChromeDriver %s on port %u\n", kChromeDriverVersion
, port
);
296 printf("Only local connections are allowed.\n");
297 } else if (!whitelisted_ips
.empty()) {
298 printf("Remote connections are allowed by a whitelist (%s).\n",
299 cmd_line
->GetSwitchValueASCII("whitelisted-ips").c_str());
301 printf("All remote connections are allowed. Use a whitelist instead!\n");
306 if (!InitLogging()) {
307 printf("Unable to initialize logging. Exiting...\n");
310 RunServer(port
, allow_remote
, whitelisted_ips
,
311 url_base
, adb_port
, port_server
.Pass());