Home | History | Annotate | Download | only in server
      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