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/remote_test_server.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/base_paths.h"
     10 #include "base/file_util.h"
     11 #include "base/files/file_path.h"
     12 #include "base/json/json_writer.h"
     13 #include "base/logging.h"
     14 #include "base/path_service.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/strings/string_split.h"
     17 #include "base/values.h"
     18 #include "net/base/host_port_pair.h"
     19 #include "net/base/net_errors.h"
     20 #include "net/test/spawned_test_server/spawner_communicator.h"
     21 #include "url/gurl.h"
     22 
     23 namespace net {
     24 
     25 namespace {
     26 
     27 // To reduce the running time of tests, tests may be sharded across several
     28 // devices. This means that it may be necessary to support multiple instances
     29 // of the test server spawner and the Python test server simultaneously on the
     30 // same host. Each pair of (test server spawner, Python test server) correspond
     31 // to a single testing device.
     32 // The mapping between the test server spawner and the individual Python test
     33 // servers is written to a file on the device prior to executing any tests.
     34 base::FilePath GetTestServerPortInfoFile() {
     35 #if !defined(OS_ANDROID)
     36   return base::FilePath("/tmp/net-test-server-ports");
     37 #else
     38   base::FilePath test_data_dir;
     39   PathService::Get(base::DIR_ANDROID_EXTERNAL_STORAGE, &test_data_dir);
     40   return test_data_dir.Append("net-test-server-ports");
     41 #endif
     42 }
     43 
     44 // Please keep it sync with dictionary SERVER_TYPES in testserver.py
     45 std::string GetServerTypeString(BaseTestServer::Type type) {
     46   switch (type) {
     47     case BaseTestServer::TYPE_FTP:
     48       return "ftp";
     49     case BaseTestServer::TYPE_HTTP:
     50     case BaseTestServer::TYPE_HTTPS:
     51       return "http";
     52     case BaseTestServer::TYPE_WS:
     53     case BaseTestServer::TYPE_WSS:
     54       return "ws";
     55     case BaseTestServer::TYPE_TCP_ECHO:
     56       return "tcpecho";
     57     case BaseTestServer::TYPE_UDP_ECHO:
     58       return "udpecho";
     59     default:
     60       NOTREACHED();
     61   }
     62   return std::string();
     63 }
     64 
     65 }  // namespace
     66 
     67 RemoteTestServer::RemoteTestServer(Type type,
     68                                    const std::string& host,
     69                                    const base::FilePath& document_root)
     70     : BaseTestServer(type, host),
     71       spawner_server_port_(0) {
     72   if (!Init(document_root))
     73     NOTREACHED();
     74 }
     75 
     76 RemoteTestServer::RemoteTestServer(Type type,
     77                                    const SSLOptions& ssl_options,
     78                                    const base::FilePath& document_root)
     79     : BaseTestServer(type, ssl_options),
     80       spawner_server_port_(0) {
     81   if (!Init(document_root))
     82     NOTREACHED();
     83 }
     84 
     85 RemoteTestServer::~RemoteTestServer() {
     86   Stop();
     87 }
     88 
     89 bool RemoteTestServer::Start() {
     90   if (spawner_communicator_.get())
     91     return true;
     92   spawner_communicator_.reset(new SpawnerCommunicator(spawner_server_port_));
     93 
     94   base::DictionaryValue arguments_dict;
     95   if (!GenerateArguments(&arguments_dict))
     96     return false;
     97 
     98   // Append the 'server-type' argument which is used by spawner server to
     99   // pass right server type to Python test server.
    100   arguments_dict.SetString("server-type", GetServerTypeString(type()));
    101 
    102   // Generate JSON-formatted argument string.
    103   std::string arguments_string;
    104   base::JSONWriter::Write(&arguments_dict, &arguments_string);
    105   if (arguments_string.empty())
    106     return false;
    107 
    108   // Start the Python test server on the remote machine.
    109   uint16 test_server_port;
    110   if (!spawner_communicator_->StartServer(arguments_string,
    111                                           &test_server_port)) {
    112     return false;
    113   }
    114   if (0 == test_server_port)
    115     return false;
    116 
    117   // Construct server data to initialize BaseTestServer::server_data_.
    118   base::DictionaryValue server_data_dict;
    119   // At this point, the test server should be spawned on the host. Update the
    120   // local port to real port of Python test server, which will be forwarded to
    121   // the remote server.
    122   server_data_dict.SetInteger("port", test_server_port);
    123   std::string server_data;
    124   base::JSONWriter::Write(&server_data_dict, &server_data);
    125   if (server_data.empty() || !ParseServerData(server_data)) {
    126     LOG(ERROR) << "Could not parse server_data: " << server_data;
    127     return false;
    128   }
    129 
    130   return SetupWhenServerStarted();
    131 }
    132 
    133 bool RemoteTestServer::StartInBackground() {
    134   NOTIMPLEMENTED();
    135   return false;
    136 }
    137 
    138 bool RemoteTestServer::BlockUntilStarted() {
    139   NOTIMPLEMENTED();
    140   return false;
    141 }
    142 
    143 bool RemoteTestServer::Stop() {
    144   if (!spawner_communicator_.get())
    145     return true;
    146   CleanUpWhenStoppingServer();
    147   bool stopped = spawner_communicator_->StopServer();
    148   // Explicitly reset |spawner_communicator_| to avoid reusing the stopped one.
    149   spawner_communicator_.reset(NULL);
    150   return stopped;
    151 }
    152 
    153 // On Android, the document root in the device is not the same as the document
    154 // root in the host machine where the test server is launched. So prepend
    155 // DIR_SOURCE_ROOT here to get the actual path of document root on the Android
    156 // device.
    157 base::FilePath RemoteTestServer::GetDocumentRoot() const {
    158   base::FilePath src_dir;
    159   PathService::Get(base::DIR_SOURCE_ROOT, &src_dir);
    160   return src_dir.Append(document_root());
    161 }
    162 
    163 bool RemoteTestServer::Init(const base::FilePath& document_root) {
    164   if (document_root.IsAbsolute())
    165     return false;
    166 
    167   // Gets ports information used by test server spawner and Python test server.
    168   int test_server_port = 0;
    169 
    170   // Parse file to extract the ports information.
    171   std::string port_info;
    172   if (!base::ReadFileToString(GetTestServerPortInfoFile(), &port_info) ||
    173       port_info.empty()) {
    174     return false;
    175   }
    176 
    177   std::vector<std::string> ports;
    178   base::SplitString(port_info, ':', &ports);
    179   if (ports.size() != 2u)
    180     return false;
    181 
    182   // Verify the ports information.
    183   base::StringToInt(ports[0], &spawner_server_port_);
    184   if (!spawner_server_port_ ||
    185       static_cast<uint32>(spawner_server_port_) >= kuint16max)
    186     return false;
    187 
    188   // Allow the test_server_port to be 0, which means the test server spawner
    189   // will pick up a random port to run the test server.
    190   base::StringToInt(ports[1], &test_server_port);
    191   if (static_cast<uint32>(test_server_port) >= kuint16max)
    192     return false;
    193   SetPort(test_server_port);
    194 
    195   // Unlike LocalTestServer, RemoteTestServer passes relative paths to the test
    196   // server. The test server fails on empty strings in some configurations.
    197   base::FilePath fixed_root = document_root;
    198   if (fixed_root.empty())
    199     fixed_root = base::FilePath(base::FilePath::kCurrentDirectory);
    200   SetResourcePath(fixed_root, base::FilePath().AppendASCII("net")
    201                                            .AppendASCII("data")
    202                                            .AppendASCII("ssl")
    203                                            .AppendASCII("certificates"));
    204   return true;
    205 }
    206 
    207 }  // namespace net
    208 
    209