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/command_line.h"
12 #include "base/file_util.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "chrome/test/chromedriver/chrome/version.h"
18 #include "chrome/test/chromedriver/command_executor_impl.h"
19 #include "chrome/test/chromedriver/server/http_handler.h"
20 #include "chrome/test/chromedriver/server/http_response.h"
21 #include "third_party/mongoose/mongoose.h"
25 void ReadRequestBody(const struct mg_request_info
* const request_info
,
26 struct mg_connection
* const connection
,
27 std::string
* request_body
) {
28 int content_length
= 0;
29 // 64 maximum header count hard-coded in mongoose.h
30 for (int header_index
= 0; header_index
< 64; ++header_index
) {
31 if (request_info
->http_headers
[header_index
].name
== NULL
) {
34 if (strcmp(request_info
->http_headers
[header_index
].name
,
35 "Content-Length") == 0) {
37 request_info
->http_headers
[header_index
].value
, &content_length
);
41 if (content_length
> 0) {
42 request_body
->resize(content_length
);
44 while (bytes_read
< content_length
) {
45 bytes_read
+= mg_read(connection
,
46 &(*request_body
)[bytes_read
],
47 content_length
- bytes_read
);
52 struct MongooseUserData
{
54 base::WaitableEvent
* shutdown_event
;
57 void* ProcessHttpRequest(mg_event event_raised
,
58 struct mg_connection
* connection
,
59 const struct mg_request_info
* request_info
) {
60 if (event_raised
!= MG_NEW_REQUEST
)
61 return reinterpret_cast<void*>(false);
62 MongooseUserData
* user_data
=
63 reinterpret_cast<MongooseUserData
*>(request_info
->user_data
);
65 std::string method_str
= request_info
->request_method
;
66 HttpMethod method
= kGet
;
67 if (method_str
== "PUT" || method_str
== "POST")
69 else if (method_str
== "DELETE")
73 ReadRequestBody(request_info
, connection
, &body
);
75 HttpRequest
request(method
, request_info
->uri
, body
);
76 LOG(INFO
) << "Handling request: "
77 << std::string(request_info
->uri
) << " " << body
;
79 HttpResponse response
;
80 user_data
->handler
->Handle(request
, &response
);
81 LOG(INFO
) << "Done handling request: "
82 << response
.status() << " " << response
.body();
84 // Don't allow HTTP keep alive.
85 response
.AddHeader("connection", "close");
87 response
.GetData(&data
);
88 mg_write(connection
, data
.data(), data
.length());
89 if (user_data
->handler
->ShouldShutdown(request
))
90 user_data
->shutdown_event
->Signal();
91 return reinterpret_cast<void*>(true);
94 void MakeMongooseOptions(const std::string
& port
,
96 std::vector
<std::string
>* out_options
) {
97 out_options
->push_back("listening_ports");
98 out_options
->push_back(port
);
99 out_options
->push_back("enable_keep_alive");
100 out_options
->push_back("no");
101 out_options
->push_back("num_threads");
102 out_options
->push_back(base::IntToString(http_threads
));
107 int main(int argc
, char *argv
[]) {
108 CommandLine::Init(argc
, argv
);
110 base::AtExitManager exit
;
111 CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
113 // Parse command line flags.
114 std::string port
= "9515";
115 std::string url_base
;
116 int http_threads
= 4;
117 base::FilePath log_path
;
118 if (cmd_line
->HasSwitch("port"))
119 port
= cmd_line
->GetSwitchValueASCII("port");
120 if (cmd_line
->HasSwitch("url-base"))
121 url_base
= cmd_line
->GetSwitchValueASCII("url-base");
122 if (url_base
.empty() || url_base
[0] != '/')
123 url_base
= "/" + url_base
;
124 if (url_base
[url_base
.length() - 1] != '/')
125 url_base
= url_base
+ "/";
126 if (cmd_line
->HasSwitch("http-threads")) {
127 if (!base::StringToInt(cmd_line
->GetSwitchValueASCII("http-threads"),
129 printf("'http-threads' option must be an integer\n");
133 if (cmd_line
->HasSwitch("log-path")) {
134 log_path
= cmd_line
->GetSwitchValuePath("log-path");
136 base::FilePath::StringType
log_name(FILE_PATH_LITERAL("chromedriver.log"));
137 log_path
= base::FilePath(log_name
);
138 file_util::ScopedFILE
file(file_util::OpenFile(log_path
, "w"));
139 base::FilePath temp_dir
;
140 if (!file
.get() && file_util::GetTempDir(&temp_dir
))
141 log_path
= temp_dir
.Append(log_name
);
144 if (!log_path
.IsAbsolute()) {
146 if (file_util::GetCurrentDirectory(&cwd
))
147 log_path
= cwd
.Append(log_path
);
149 bool success
= InitLogging(
150 log_path
.value().c_str(),
151 logging::LOG_ONLY_TO_FILE
,
152 logging::DONT_LOCK_LOG_FILE
,
153 logging::DELETE_OLD_LOG_FILE
,
154 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS
);
156 PLOG(ERROR
) << "Unable to initialize logging";
158 logging::SetLogItems(false, // enable_process_id
159 false, // enable_thread_id
160 true, // enable_timestamp
161 false); // enable_tickcount
162 if (cmd_line
->HasSwitch("verbose")) {
163 logging::SetMinLogLevel(logging::LOG_VERBOSE
);
166 scoped_ptr
<CommandExecutor
> executor(new CommandExecutorImpl());
167 HttpHandler
handler(executor
.Pass(), HttpHandler::CreateCommandMap(),
169 base::WaitableEvent
shutdown_event(false, false);
170 MongooseUserData user_data
= { &handler
, &shutdown_event
};
172 std::vector
<std::string
> args
;
173 MakeMongooseOptions(port
, http_threads
, &args
);
174 scoped_ptr
<const char*[]> options(new const char*[args
.size() + 1]);
175 for (size_t i
= 0; i
< args
.size(); ++i
) {
176 options
[i
] = args
[i
].c_str();
178 options
[args
.size()] = NULL
;
180 struct mg_context
* ctx
= mg_start(&ProcessHttpRequest
,
184 printf("Port already in use. Exiting...\n");
188 if (!cmd_line
->HasSwitch("silent")) {
189 std::cout
<< "Started ChromeDriver" << std::endl
190 << "port=" << port
<< std::endl
191 << "version=" << std::string(kChromeDriverVersion
) << std::endl
192 << "log=" << log_path
.value() << std::endl
;
194 if (!cmd_line
->HasSwitch("verbose")) {
199 // Run until we receive command to shutdown.
200 shutdown_event
.Wait();