Home | History | Annotate | Download | only in spawned_test_server
      1 // Copyright 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 "net/test/spawned_test_server/local_test_server.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/json/json_reader.h"
      9 #include "base/logging.h"
     10 #include "base/path_service.h"
     11 #include "base/process/kill.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/values.h"
     14 #include "net/base/host_port_pair.h"
     15 #include "net/base/net_errors.h"
     16 #include "net/test/python_utils.h"
     17 #include "url/gurl.h"
     18 
     19 namespace net {
     20 
     21 namespace {
     22 
     23 bool AppendArgumentFromJSONValue(const std::string& key,
     24                                  const base::Value& value_node,
     25                                  CommandLine* command_line) {
     26   std::string argument_name = "--" + key;
     27   switch (value_node.GetType()) {
     28     case base::Value::TYPE_NULL:
     29       command_line->AppendArg(argument_name);
     30       break;
     31     case base::Value::TYPE_INTEGER: {
     32       int value;
     33       bool result = value_node.GetAsInteger(&value);
     34       DCHECK(result);
     35       command_line->AppendArg(argument_name + "=" + base::IntToString(value));
     36       break;
     37     }
     38     case Value::TYPE_STRING: {
     39       std::string value;
     40       bool result = value_node.GetAsString(&value);
     41       if (!result || value.empty())
     42         return false;
     43       command_line->AppendArg(argument_name + "=" + value);
     44       break;
     45     }
     46     case base::Value::TYPE_BOOLEAN:
     47     case base::Value::TYPE_DOUBLE:
     48     case base::Value::TYPE_LIST:
     49     case base::Value::TYPE_DICTIONARY:
     50     case base::Value::TYPE_BINARY:
     51     default:
     52       NOTREACHED() << "improper json type";
     53       return false;
     54   }
     55   return true;
     56 }
     57 
     58 }  // namespace
     59 
     60 LocalTestServer::LocalTestServer(Type type,
     61                                  const std::string& host,
     62                                  const base::FilePath& document_root)
     63     : BaseTestServer(type, host) {
     64   if (!Init(document_root))
     65     NOTREACHED();
     66 }
     67 
     68 LocalTestServer::LocalTestServer(Type type,
     69                                  const SSLOptions& ssl_options,
     70                                  const base::FilePath& document_root)
     71     : BaseTestServer(type, ssl_options) {
     72   if (!Init(document_root))
     73     NOTREACHED();
     74 }
     75 
     76 LocalTestServer::~LocalTestServer() {
     77   Stop();
     78 }
     79 
     80 bool LocalTestServer::GetTestServerPath(base::FilePath* testserver_path) const {
     81   base::FilePath testserver_dir;
     82   if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_dir)) {
     83     LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
     84     return false;
     85   }
     86   testserver_dir = testserver_dir.Append(FILE_PATH_LITERAL("net"))
     87                                  .Append(FILE_PATH_LITERAL("tools"))
     88                                  .Append(FILE_PATH_LITERAL("testserver"));
     89   *testserver_path = testserver_dir.Append(FILE_PATH_LITERAL("testserver.py"));
     90   return true;
     91 }
     92 
     93 bool LocalTestServer::Start() {
     94   return StartInBackground() && BlockUntilStarted();
     95 }
     96 
     97 bool LocalTestServer::StartInBackground() {
     98   // Get path to Python server script.
     99   base::FilePath testserver_path;
    100   if (!GetTestServerPath(&testserver_path))
    101     return false;
    102 
    103   if (!SetPythonPath())
    104     return false;
    105 
    106   if (!LaunchPython(testserver_path))
    107     return false;
    108 
    109   return true;
    110 }
    111 
    112 bool LocalTestServer::BlockUntilStarted() {
    113   if (!WaitToStart()) {
    114     Stop();
    115     return false;
    116   }
    117 
    118   return SetupWhenServerStarted();
    119 }
    120 
    121 bool LocalTestServer::Stop() {
    122   CleanUpWhenStoppingServer();
    123 
    124   if (!process_handle_)
    125     return true;
    126 
    127   // First check if the process has already terminated.
    128   bool ret = base::WaitForSingleProcess(process_handle_, base::TimeDelta());
    129   if (!ret)
    130     ret = base::KillProcess(process_handle_, 1, true);
    131 
    132   if (ret) {
    133     base::CloseProcessHandle(process_handle_);
    134     process_handle_ = base::kNullProcessHandle;
    135   } else {
    136     VLOG(1) << "Kill failed?";
    137   }
    138 
    139   return ret;
    140 }
    141 
    142 bool LocalTestServer::Init(const base::FilePath& document_root) {
    143   if (document_root.IsAbsolute())
    144     return false;
    145 
    146   // At this point, the port that the test server will listen on is unknown.
    147   // The test server will listen on an ephemeral port, and write the port
    148   // number out over a pipe that this TestServer object will read from. Once
    149   // that is complete, the host port pair will contain the actual port.
    150   DCHECK(!GetPort());
    151   process_handle_ = base::kNullProcessHandle;
    152 
    153   base::FilePath src_dir;
    154   if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir))
    155     return false;
    156   SetResourcePath(src_dir.Append(document_root),
    157                   src_dir.AppendASCII("net")
    158                          .AppendASCII("data")
    159                          .AppendASCII("ssl")
    160                          .AppendASCII("certificates"));
    161   return true;
    162 }
    163 
    164 bool LocalTestServer::SetPythonPath() const {
    165   base::FilePath third_party_dir;
    166   if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
    167     LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
    168     return false;
    169   }
    170   third_party_dir = third_party_dir.AppendASCII("third_party");
    171 
    172   // For simplejson. (simplejson, unlike all the other Python modules
    173   // we include, doesn't have an extra 'simplejson' directory, so we
    174   // need to include its parent directory, i.e. third_party_dir).
    175   AppendToPythonPath(third_party_dir);
    176 
    177   AppendToPythonPath(third_party_dir.AppendASCII("tlslite"));
    178   AppendToPythonPath(
    179       third_party_dir.AppendASCII("pyftpdlib").AppendASCII("src"));
    180   AppendToPythonPath(
    181       third_party_dir.AppendASCII("pywebsocket").AppendASCII("src"));
    182 
    183   // Locate the Python code generated by the protocol buffers compiler.
    184   base::FilePath pyproto_dir;
    185   if (!GetPyProtoPath(&pyproto_dir)) {
    186     LOG(WARNING) << "Cannot find pyproto dir for generated code. "
    187                  << "Testserver features that rely on it will not work";
    188     return true;
    189   }
    190   AppendToPythonPath(pyproto_dir);
    191 
    192   return true;
    193 }
    194 
    195 bool LocalTestServer::AddCommandLineArguments(CommandLine* command_line) const {
    196   base::DictionaryValue arguments_dict;
    197   if (!GenerateArguments(&arguments_dict))
    198     return false;
    199 
    200   // Serialize the argument dictionary into CommandLine.
    201   for (base::DictionaryValue::Iterator it(arguments_dict); !it.IsAtEnd();
    202        it.Advance()) {
    203     const base::Value& value = it.value();
    204     const std::string& key = it.key();
    205 
    206     // Add arguments from a list.
    207     if (value.IsType(Value::TYPE_LIST)) {
    208       const base::ListValue* list = NULL;
    209       if (!value.GetAsList(&list) || !list || list->empty())
    210         return false;
    211       for (base::ListValue::const_iterator list_it = list->begin();
    212            list_it != list->end(); ++list_it) {
    213         if (!AppendArgumentFromJSONValue(key, *(*list_it), command_line))
    214           return false;
    215       }
    216     } else if (!AppendArgumentFromJSONValue(key, value, command_line)) {
    217         return false;
    218     }
    219   }
    220 
    221   // Append the appropriate server type argument.
    222   switch (type()) {
    223     case TYPE_HTTP:  // The default type is HTTP, no argument required.
    224       break;
    225     case TYPE_HTTPS:
    226       command_line->AppendArg("--https");
    227       break;
    228     case TYPE_WS:
    229     case TYPE_WSS:
    230       command_line->AppendArg("--websocket");
    231       break;
    232     case TYPE_FTP:
    233       command_line->AppendArg("--ftp");
    234       break;
    235     case TYPE_TCP_ECHO:
    236       command_line->AppendArg("--tcp-echo");
    237       break;
    238     case TYPE_UDP_ECHO:
    239       command_line->AppendArg("--udp-echo");
    240       break;
    241     case TYPE_BASIC_AUTH_PROXY:
    242       command_line->AppendArg("--basic-auth-proxy");
    243       break;
    244     default:
    245       NOTREACHED();
    246       return false;
    247   }
    248 
    249   return true;
    250 }
    251 
    252 }  // namespace net
    253