| // 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 <limits> | |
| #include <vector> | |
| #include "base/base_paths.h" | |
| #include "base/files/file_path.h" | |
| #include "base/files/file_util.h" | |
| #include "base/json/json_writer.h" | |
| #include "base/logging.h" | |
| #include "base/message_loop/message_loop.h" | |
| #include "base/path_service.h" | |
| #include "base/strings/string_number_conversions.h" | |
| #include "base/strings/string_split.h" | |
| #include "base/strings/stringprintf.h" | |
| #include "base/values.h" | |
| #include "net/base/host_port_pair.h" | |
| #include "net/base/ip_endpoint.h" | |
| #include "net/base/net_errors.h" | |
| #include "net/test/spawned_test_server/remote_test_server_spawner_request.h" | |
| #include "net/test/tcp_socket_proxy.h" | |
| #include "starboard/types.h" | |
| #include "url/gurl.h" | |
| namespace net { | |
| namespace { | |
| // Please keep in 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 base::FilePath& document_root) | |
| : BaseTestServer(type), io_thread_("RemoteTestServer IO Thread") { | |
| if (!Init(document_root)) | |
| NOTREACHED(); | |
| } | |
| RemoteTestServer::RemoteTestServer(Type type, | |
| const SSLOptions& ssl_options, | |
| const base::FilePath& document_root) | |
| : BaseTestServer(type, ssl_options), | |
| io_thread_("RemoteTestServer IO Thread") { | |
| if (!Init(document_root)) | |
| NOTREACHED(); | |
| } | |
| RemoteTestServer::~RemoteTestServer() { | |
| Stop(); | |
| } | |
| bool RemoteTestServer::StartInBackground() { | |
| DCHECK(!started()); | |
| DCHECK(!start_request_); | |
| base::DictionaryValue arguments_dict; | |
| if (!GenerateArguments(&arguments_dict)) | |
| return false; | |
| arguments_dict.Set("on-remote-server", std::make_unique<base::Value>()); | |
| // 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())); | |
| // If the server is expected to handle OCSP, it needs to know what port | |
| // number to write into the AIA urls. Initialize the ocsp proxy to | |
| // reserve a port, and pass it to the testserver so it can generate | |
| // certificates for the OCSP server valid for the proxied port. Note that | |
| // the test spawer may forward OCSP a second time, from the device to the | |
| // host. | |
| bool ocsp_server_enabled = | |
| type() == TYPE_HTTPS && (ssl_options().server_certificate == | |
| SSLOptions::CERT_AUTO_AIA_INTERMEDIATE || | |
| !ssl_options().GetOCSPArgument().empty()); | |
| if (ocsp_server_enabled) { | |
| ocsp_proxy_ = std::make_unique<TcpSocketProxy>(io_thread_.task_runner()); | |
| bool initialized = ocsp_proxy_->Initialize(); | |
| CHECK(initialized); | |
| arguments_dict.SetKey("ocsp-proxy-port-number", | |
| base::Value(ocsp_proxy_->local_port())); | |
| } | |
| // Generate JSON-formatted argument string. | |
| std::string arguments_string; | |
| base::JSONWriter::Write(arguments_dict, &arguments_string); | |
| if (arguments_string.empty()) | |
| return false; | |
| start_request_ = std::make_unique<RemoteTestServerSpawnerRequest>( | |
| io_thread_.task_runner(), config_.GetSpawnerUrl("start"), | |
| arguments_string); | |
| return true; | |
| } | |
| bool RemoteTestServer::BlockUntilStarted() { | |
| DCHECK(start_request_); | |
| std::string server_data_json; | |
| bool request_result = start_request_->WaitForCompletion(&server_data_json); | |
| start_request_.reset(); | |
| if (!request_result) | |
| return false; | |
| // Parse server_data_json. | |
| if (server_data_json.empty() || | |
| !SetAndParseServerData(server_data_json, &remote_port_)) { | |
| LOG(ERROR) << "Could not parse server_data: " << server_data_json; | |
| return false; | |
| } | |
| // If the server is not on localhost then start a proxy on localhost to | |
| // forward connections to the server. | |
| if (config_.address() != IPAddress::IPv4Localhost()) { | |
| test_server_proxy_ = | |
| std::make_unique<TcpSocketProxy>(io_thread_.task_runner()); | |
| bool initialized = test_server_proxy_->Initialize(); | |
| CHECK(initialized); | |
| test_server_proxy_->Start(IPEndPoint(config_.address(), remote_port_)); | |
| SetPort(test_server_proxy_->local_port()); | |
| } else { | |
| SetPort(remote_port_); | |
| } | |
| if (ocsp_proxy_) { | |
| const base::Value* ocsp_port_value = server_data().FindKey("ocsp_port"); | |
| if (ocsp_port_value && ocsp_port_value->is_int()) { | |
| ocsp_proxy_->Start( | |
| IPEndPoint(config_.address(), ocsp_port_value->GetInt())); | |
| } else { | |
| LOG(WARNING) << "testserver.py didn't return ocsp_port."; | |
| } | |
| } | |
| return SetupWhenServerStarted(); | |
| } | |
| bool RemoteTestServer::Stop() { | |
| DCHECK(!start_request_); | |
| if (remote_port_) { | |
| std::unique_ptr<RemoteTestServerSpawnerRequest> kill_request = | |
| std::make_unique<RemoteTestServerSpawnerRequest>( | |
| io_thread_.task_runner(), | |
| config_.GetSpawnerUrl( | |
| base::StringPrintf("kill?port=%d", remote_port_)), | |
| std::string()); | |
| if (!kill_request->WaitForCompletion(nullptr)) | |
| LOG(ERROR) << "Failed stopping RemoteTestServer"; | |
| remote_port_ = 0; | |
| } | |
| CleanUpWhenStoppingServer(); | |
| return true; | |
| } | |
| // 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_FOR_TESTING here to get the actual path of document root on | |
| // the Android device. | |
| base::FilePath RemoteTestServer::GetDocumentRoot() const { | |
| base::FilePath src_dir; | |
| base::PathService::Get(base::DIR_TEST_DATA, &src_dir); | |
| return src_dir.Append(document_root()); | |
| } | |
| bool RemoteTestServer::Init(const base::FilePath& document_root) { | |
| if (document_root.IsAbsolute()) | |
| return false; | |
| config_ = RemoteTestServerConfig::Load(); | |
| bool thread_started = io_thread_.StartWithOptions( | |
| base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| CHECK(thread_started); | |
| // 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 |