| // 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 |