| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stdio.h> | 5 #include <stdio.h> |
| 6 #include <string> | 6 #include <string> |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/at_exit.h" | 9 #include "base/at_exit.h" |
| 10 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 11 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
| 12 #include "base/logging.h" | 13 #include "base/logging.h" |
| 13 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 15 #include "base/message_loop/message_loop.h" |
| 16 #include "base/run_loop.h" |
| 14 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 15 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 16 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 17 #include "base/synchronization/waitable_event.h" | 20 #include "base/synchronization/waitable_event.h" |
| 21 #include "base/threading/thread.h" |
| 18 #include "chrome/test/chromedriver/chrome/log.h" | 22 #include "chrome/test/chromedriver/chrome/log.h" |
| 19 #include "chrome/test/chromedriver/chrome/version.h" | 23 #include "chrome/test/chromedriver/chrome/version.h" |
| 20 #include "chrome/test/chromedriver/server/http_handler.h" | 24 #include "chrome/test/chromedriver/server/http_handler.h" |
| 21 #include "chrome/test/chromedriver/server/http_response.h" | 25 #include "chrome/test/chromedriver/server/http_response.h" |
| 22 #include "net/server/http_server_request_info.h" | 26 #include "net/server/http_server_request_info.h" |
| 23 #include "third_party/mongoose/mongoose.h" | 27 #include "third_party/mongoose/mongoose.h" |
| 24 | 28 |
| 25 #if defined(OS_POSIX) | 29 #if defined(OS_POSIX) |
| 26 #include <fcntl.h> | 30 #include <fcntl.h> |
| 27 #include <unistd.h> | 31 #include <unistd.h> |
| 28 #endif | 32 #endif |
| 29 | 33 |
| 30 namespace { | 34 namespace { |
| 31 | 35 |
| 36 void SendHttpResponse(bool shutdown, |
| 37 const HttpResponseSenderFunc& send_response_func, |
| 38 scoped_ptr<HttpResponse> response) { |
| 39 send_response_func.Run(response.Pass()); |
| 40 if (shutdown) |
| 41 base::MessageLoop::current()->QuitWhenIdle(); |
| 42 } |
| 43 |
| 44 void HandleHttpRequest(HttpHandler* handler, |
| 45 const net::HttpServerRequestInfo& request, |
| 46 const HttpResponseSenderFunc& send_response_func) { |
| 47 handler->Handle(request, |
| 48 base::Bind(&SendHttpResponse, |
| 49 handler->ShouldShutdown(request), |
| 50 send_response_func)); |
| 51 } |
| 52 |
| 32 void ReadRequestBody(const struct mg_request_info* const request_info, | 53 void ReadRequestBody(const struct mg_request_info* const request_info, |
| 33 struct mg_connection* const connection, | 54 struct mg_connection* const connection, |
| 34 std::string* request_body) { | 55 std::string* request_body) { |
| 35 int content_length = 0; | 56 int content_length = 0; |
| 36 // 64 maximum header count hard-coded in mongoose.h | 57 // 64 maximum header count hard-coded in mongoose.h |
| 37 for (int header_index = 0; header_index < 64; ++header_index) { | 58 for (int header_index = 0; header_index < 64; ++header_index) { |
| 38 if (request_info->http_headers[header_index].name == NULL) { | 59 if (request_info->http_headers[header_index].name == NULL) { |
| 39 break; | 60 break; |
| 40 } | 61 } |
| 41 if (LowerCaseEqualsASCII(request_info->http_headers[header_index].name, | 62 if (LowerCaseEqualsASCII(request_info->http_headers[header_index].name, |
| 42 "content-length")) { | 63 "content-length")) { |
| 43 base::StringToInt( | 64 base::StringToInt( |
| 44 request_info->http_headers[header_index].value, &content_length); | 65 request_info->http_headers[header_index].value, &content_length); |
| 45 break; | 66 break; |
| 46 } | 67 } |
| 47 } | 68 } |
| 48 if (content_length > 0) { | 69 if (content_length > 0) { |
| 49 request_body->resize(content_length); | 70 request_body->resize(content_length); |
| 50 int bytes_read = 0; | 71 int bytes_read = 0; |
| 51 while (bytes_read < content_length) { | 72 while (bytes_read < content_length) { |
| 52 bytes_read += mg_read(connection, | 73 bytes_read += mg_read(connection, |
| 53 &(*request_body)[bytes_read], | 74 &(*request_body)[bytes_read], |
| 54 content_length - bytes_read); | 75 content_length - bytes_read); |
| 55 } | 76 } |
| 56 } | 77 } |
| 57 } | 78 } |
| 58 | 79 |
| 80 typedef base::Callback< |
| 81 void(const net::HttpServerRequestInfo&, const HttpResponseSenderFunc&)> |
| 82 HttpRequestHandlerFunc; |
| 83 |
| 59 struct MongooseUserData { | 84 struct MongooseUserData { |
| 60 HttpHandler* handler; | 85 base::SingleThreadTaskRunner* cmd_task_runner; |
| 61 base::WaitableEvent* shutdown_event; | 86 HttpRequestHandlerFunc* handler_func; |
| 62 }; | 87 }; |
| 63 | 88 |
| 89 void DoneProcessing(base::WaitableEvent* event, |
| 90 scoped_ptr<HttpResponse>* response_to_set, |
| 91 scoped_ptr<HttpResponse> response) { |
| 92 *response_to_set = response.Pass(); |
| 93 event->Signal(); |
| 94 } |
| 95 |
| 64 void* ProcessHttpRequest(mg_event event_raised, | 96 void* ProcessHttpRequest(mg_event event_raised, |
| 65 struct mg_connection* connection, | 97 struct mg_connection* connection, |
| 66 const struct mg_request_info* request_info) { | 98 const struct mg_request_info* request_info) { |
| 67 if (event_raised != MG_NEW_REQUEST) | 99 if (event_raised != MG_NEW_REQUEST) |
| 68 return reinterpret_cast<void*>(false); | 100 return reinterpret_cast<void*>(false); |
| 69 MongooseUserData* user_data = | 101 MongooseUserData* user_data = |
| 70 reinterpret_cast<MongooseUserData*>(request_info->user_data); | 102 reinterpret_cast<MongooseUserData*>(request_info->user_data); |
| 71 | 103 |
| 72 net::HttpServerRequestInfo request; | 104 net::HttpServerRequestInfo request; |
| 73 request.method = request_info->request_method; | 105 request.method = request_info->request_method; |
| 74 request.path = request_info->uri; | 106 request.path = request_info->uri; |
| 75 ReadRequestBody(request_info, connection, &request.data); | 107 ReadRequestBody(request_info, connection, &request.data); |
| 76 | 108 |
| 77 HttpResponse response; | 109 base::WaitableEvent event(false, false); |
| 78 user_data->handler->Handle(request, &response); | 110 scoped_ptr<HttpResponse> response; |
| 111 user_data->cmd_task_runner |
| 112 ->PostTask(FROM_HERE, |
| 113 base::Bind(*user_data->handler_func, |
| 114 request, |
| 115 base::Bind(&DoneProcessing, &event, &response))); |
| 116 event.Wait(); |
| 79 | 117 |
| 80 // Don't allow HTTP keep alive. | 118 // Don't allow HTTP keep alive. |
| 81 response.AddHeader("connection", "close"); | 119 response->AddHeader("connection", "close"); |
| 82 std::string data; | 120 std::string data; |
| 83 response.GetData(&data); | 121 response->GetData(&data); |
| 84 mg_write(connection, data.data(), data.length()); | 122 mg_write(connection, data.data(), data.length()); |
| 85 if (user_data->handler->ShouldShutdown(request)) | |
| 86 user_data->shutdown_event->Signal(); | |
| 87 return reinterpret_cast<void*>(true); | 123 return reinterpret_cast<void*>(true); |
| 88 } | 124 } |
| 89 | 125 |
| 90 void MakeMongooseOptions(const std::string& port, | 126 void MakeMongooseOptions(const std::string& port, |
| 91 int http_threads, | 127 int http_threads, |
| 92 std::vector<std::string>* out_options) { | 128 std::vector<std::string>* out_options) { |
| 93 out_options->push_back("listening_ports"); | 129 out_options->push_back("listening_ports"); |
| 94 out_options->push_back(port); | 130 out_options->push_back(port); |
| 95 out_options->push_back("enable_keep_alive"); | 131 out_options->push_back("enable_keep_alive"); |
| 96 out_options->push_back("no"); | 132 out_options->push_back("no"); |
| 97 out_options->push_back("num_threads"); | 133 out_options->push_back("num_threads"); |
| 98 out_options->push_back(base::IntToString(http_threads)); | 134 out_options->push_back(base::IntToString(http_threads)); |
| 99 } | 135 } |
| 100 | 136 |
| 101 } // namespace | 137 } // namespace |
| 102 | 138 |
| 103 int main(int argc, char *argv[]) { | 139 int main(int argc, char *argv[]) { |
| 104 CommandLine::Init(argc, argv); | 140 CommandLine::Init(argc, argv); |
| 105 | 141 |
| 106 base::AtExitManager exit; | 142 base::AtExitManager at_exit; |
| 107 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 143 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| 108 | 144 |
| 109 // Parse command line flags. | 145 // Parse command line flags. |
| 110 std::string port = "9515"; | 146 std::string port = "9515"; |
| 111 std::string url_base; | 147 std::string url_base; |
| 112 int http_threads = 4; | 148 int http_threads = 4; |
| 113 base::FilePath log_path; | 149 base::FilePath log_path; |
| 114 Log::Level log_level = Log::kError; | 150 Log::Level log_level = Log::kError; |
| 115 if (cmd_line->HasSwitch("h") || cmd_line->HasSwitch("help")) { | 151 if (cmd_line->HasSwitch("h") || cmd_line->HasSwitch("help")) { |
| 116 std::string options; | 152 std::string options; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 if (!success) { | 204 if (!success) { |
| 169 PLOG(ERROR) << "Unable to initialize logging"; | 205 PLOG(ERROR) << "Unable to initialize logging"; |
| 170 } | 206 } |
| 171 logging::SetLogItems(false, // enable_process_id | 207 logging::SetLogItems(false, // enable_process_id |
| 172 false, // enable_thread_id | 208 false, // enable_thread_id |
| 173 false, // enable_timestamp | 209 false, // enable_timestamp |
| 174 false); // enable_tickcount | 210 false); // enable_tickcount |
| 175 if (!cmd_line->HasSwitch("verbose")) | 211 if (!cmd_line->HasSwitch("verbose")) |
| 176 logging::SetMinLogLevel(logging::LOG_FATAL); | 212 logging::SetMinLogLevel(logging::LOG_FATAL); |
| 177 | 213 |
| 214 base::Thread io_thread("ChromeDriver IO"); |
| 215 CHECK(io_thread.StartWithOptions( |
| 216 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); |
| 217 |
| 178 scoped_ptr<Log> log(new Logger(log_level)); | 218 scoped_ptr<Log> log(new Logger(log_level)); |
| 179 HttpHandler handler(log.get(), url_base); | 219 HttpHandler handler(io_thread.message_loop_proxy(), log.get(), url_base); |
| 180 base::WaitableEvent shutdown_event(false, false); | 220 base::MessageLoop cmd_loop; |
| 181 MongooseUserData user_data = { &handler, &shutdown_event }; | 221 HttpRequestHandlerFunc handler_func = |
| 222 base::Bind(&HandleHttpRequest, &handler); |
| 223 MongooseUserData user_data = { cmd_loop.message_loop_proxy(), &handler_func }; |
| 182 | 224 |
| 183 std::vector<std::string> args; | 225 std::vector<std::string> args; |
| 184 MakeMongooseOptions(port, http_threads, &args); | 226 MakeMongooseOptions(port, http_threads, &args); |
| 185 scoped_ptr<const char*[]> options(new const char*[args.size() + 1]); | 227 scoped_ptr<const char*[]> options(new const char*[args.size() + 1]); |
| 186 for (size_t i = 0; i < args.size(); ++i) { | 228 for (size_t i = 0; i < args.size(); ++i) { |
| 187 options[i] = args[i].c_str(); | 229 options[i] = args[i].c_str(); |
| 188 } | 230 } |
| 189 options[args.size()] = NULL; | 231 options[args.size()] = NULL; |
| 190 | 232 |
| 191 struct mg_context* ctx = mg_start(&ProcessHttpRequest, | 233 struct mg_context* ctx = mg_start(&ProcessHttpRequest, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 203 fflush(stdout); | 245 fflush(stdout); |
| 204 } | 246 } |
| 205 | 247 |
| 206 #if defined(OS_POSIX) | 248 #if defined(OS_POSIX) |
| 207 if (!cmd_line->HasSwitch("verbose")) { | 249 if (!cmd_line->HasSwitch("verbose")) { |
| 208 // Close stderr on exec, so that Chrome log spew doesn't confuse users. | 250 // Close stderr on exec, so that Chrome log spew doesn't confuse users. |
| 209 fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC); | 251 fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC); |
| 210 } | 252 } |
| 211 #endif | 253 #endif |
| 212 | 254 |
| 213 // Run until we receive command to shutdown. | 255 base::RunLoop cmd_run_loop; |
| 214 shutdown_event.Wait(); | 256 cmd_run_loop.Run(); |
| 215 | 257 // Don't run destructors for objects passed via MongooseUserData, |
| 216 return 0; | 258 // because ProcessHttpRequest may be accessing them. |
| 259 // TODO(kkania): Fix when switching to net::HttpServer. |
| 260 exit(0); |
| 217 } | 261 } |
| OLD | NEW |