// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/test/spawned_test_server/remote_test_server.h" #include #include "base/base_paths.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/json/json_writer.h" #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/values.h" #include "net/base/host_port_pair.h" #include "net/base/net_errors.h" #include "net/test/spawned_test_server/spawner_communicator.h" #include "url/gurl.h" namespace net { namespace { // To reduce the running time of tests, tests may be sharded across several // devices. This means that it may be necessary to support multiple instances // of the test server spawner and the Python test server simultaneously on the // same host. Each pair of (test server spawner, Python test server) correspond // to a single testing device. // The mapping between the test server spawner and the individual Python test // servers is written to a file on the device prior to executing any tests. base::FilePath GetTestServerPortInfoFile() { #if !defined(OS_ANDROID) return base::FilePath("/tmp/net-test-server-ports"); #else base::FilePath test_data_dir; PathService::Get(base::DIR_ANDROID_EXTERNAL_STORAGE, &test_data_dir); return test_data_dir.Append("net-test-server-ports"); #endif } // Please keep it sync with dictionary SERVER_TYPES in testserver.py std::string GetServerTypeString(BaseTestServer::Type type) { switch (type) { case BaseTestServer::TYPE_FTP: return "ftp"; case BaseTestServer::TYPE_HTTP: case BaseTestServer::TYPE_HTTPS: return "http"; case BaseTestServer::TYPE_WS: case BaseTestServer::TYPE_WSS: return "ws"; case BaseTestServer::TYPE_TCP_ECHO: return "tcpecho"; case BaseTestServer::TYPE_UDP_ECHO: return "udpecho"; default: NOTREACHED(); } return std::string(); } } // namespace RemoteTestServer::RemoteTestServer(Type type, const std::string& host, const base::FilePath& document_root) : BaseTestServer(type, host), spawner_server_port_(0) { if (!Init(document_root)) NOTREACHED(); } RemoteTestServer::RemoteTestServer(Type type, const SSLOptions& ssl_options, const base::FilePath& document_root) : BaseTestServer(type, ssl_options), spawner_server_port_(0) { if (!Init(document_root)) NOTREACHED(); } RemoteTestServer::~RemoteTestServer() { Stop(); } bool RemoteTestServer::Start() { if (spawner_communicator_.get()) return true; spawner_communicator_.reset(new SpawnerCommunicator(spawner_server_port_)); base::DictionaryValue arguments_dict; if (!GenerateArguments(&arguments_dict)) return false; // Append the 'server-type' argument which is used by spawner server to // pass right server type to Python test server. arguments_dict.SetString("server-type", GetServerTypeString(type())); // Generate JSON-formatted argument string. std::string arguments_string; base::JSONWriter::Write(&arguments_dict, &arguments_string); if (arguments_string.empty()) return false; // Start the Python test server on the remote machine. uint16 test_server_port; if (!spawner_communicator_->StartServer(arguments_string, &test_server_port)) { return false; } if (0 == test_server_port) return false; // Construct server data to initialize BaseTestServer::server_data_. base::DictionaryValue server_data_dict; // At this point, the test server should be spawned on the host. Update the // local port to real port of Python test server, which will be forwarded to // the remote server. server_data_dict.SetInteger("port", test_server_port); std::string server_data; base::JSONWriter::Write(&server_data_dict, &server_data); if (server_data.empty() || !ParseServerData(server_data)) { LOG(ERROR) << "Could not parse server_data: " << server_data; return false; } return SetupWhenServerStarted(); } bool RemoteTestServer::StartInBackground() { NOTIMPLEMENTED(); return false; } bool RemoteTestServer::BlockUntilStarted() { NOTIMPLEMENTED(); return false; } bool RemoteTestServer::Stop() { if (!spawner_communicator_.get()) return true; CleanUpWhenStoppingServer(); bool stopped = spawner_communicator_->StopServer(); // Explicitly reset |spawner_communicator_| to avoid reusing the stopped one. spawner_communicator_.reset(NULL); return stopped; } // On Android, the document root in the device is not the same as the document // root in the host machine where the test server is launched. So prepend // DIR_SOURCE_ROOT here to get the actual path of document root on the Android // device. base::FilePath RemoteTestServer::GetDocumentRoot() const { base::FilePath src_dir; PathService::Get(base::DIR_SOURCE_ROOT, &src_dir); return src_dir.Append(document_root()); } bool RemoteTestServer::Init(const base::FilePath& document_root) { if (document_root.IsAbsolute()) return false; // Gets ports information used by test server spawner and Python test server. int test_server_port = 0; // Parse file to extract the ports information. std::string port_info; if (!base::ReadFileToString(GetTestServerPortInfoFile(), &port_info) || port_info.empty()) { return false; } std::vector ports; base::SplitString(port_info, ':', &ports); if (ports.size() != 2u) return false; // Verify the ports information. base::StringToInt(ports[0], &spawner_server_port_); if (!spawner_server_port_ || static_cast(spawner_server_port_) >= kuint16max) return false; // Allow the test_server_port to be 0, which means the test server spawner // will pick up a random port to run the test server. base::StringToInt(ports[1], &test_server_port); if (static_cast(test_server_port) >= kuint16max) return false; SetPort(test_server_port); // Unlike LocalTestServer, RemoteTestServer passes relative paths to the test // server. The test server fails on empty strings in some configurations. base::FilePath fixed_root = document_root; if (fixed_root.empty()) fixed_root = base::FilePath(base::FilePath::kCurrentDirectory); SetResourcePath(fixed_root, base::FilePath().AppendASCII("net") .AppendASCII("data") .AppendASCII("ssl") .AppendASCII("certificates")); return true; } } // namespace net