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 <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:
     45   explicit HttpServer(const HttpRequestHandlerFunc& handle_request_func)
     46       : handle_request_func_(handle_request_func),
     47         weak_factory_(this) {}
     48 
     49   virtual ~HttpServer() {}
     50 
     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:
     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   }
     67   virtual void OnWebSocketRequest(
     68       int connection_id,
     69       const net::HttpServerRequestInfo& info) OVERRIDE {}
     70   virtual void OnWebSocketMessage(int connection_id,
     71                                   const std::string& data) OVERRIDE {}
     72   virtual void OnClose(int connection_id) OVERRIDE {}
     73 
     74  private:
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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