remote_test_server.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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
23namespace net {
24
25namespace {
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.
34base::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
45std::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
67RemoteTestServer::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
76RemoteTestServer::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
85RemoteTestServer::~RemoteTestServer() {
86  Stop();
87}
88
89bool 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
133bool RemoteTestServer::StartInBackground() {
134  NOTIMPLEMENTED();
135  return false;
136}
137
138bool RemoteTestServer::BlockUntilStarted() {
139  NOTIMPLEMENTED();
140  return false;
141}
142
143bool 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.
157base::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
163bool 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