summaryrefslogtreecommitdiffstats
path: root/net/test
diff options
context:
space:
mode:
authorjnd@chromium.org <jnd@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-12 06:04:43 +0000
committerjnd@chromium.org <jnd@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-12 06:04:43 +0000
commit31526823f1e7e7f1418f44cfb0f4e0283daf54ec (patch)
treeb751259ceec955513fcceaec3272316570e0d33c /net/test
parente4f849682312f4300e6447b7227650e38c8d8415 (diff)
downloadchromium_src-31526823f1e7e7f1418f44cfb0f4e0283daf54ec.zip
chromium_src-31526823f1e7e7f1418f44cfb0f4e0283daf54ec.tar.gz
chromium_src-31526823f1e7e7f1418f44cfb0f4e0283daf54ec.tar.bz2
Make test server to talk with the python test server which is running on remote machine.
For Chromium/Android platform, since there is no python env support on Android device, we have to run the test server on the remote host and let the test server to talk with it. Also add the net_util.h into some test files to fix compilation error. They used to implicitly rely on the net_util.h definition by including test_server.h, but the net_util.h is removed from test_server.h in this patch. Since the fix in those test files are trivial, Ryan and Paweł are OK with the patch, just TBR to the owners to skip the owner presubmit check for landing. TBR=owners BUG=None TEST=net_unittests should pass on all platforms Review URL: http://codereview.chromium.org/9359051 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126100 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/test')
-rw-r--r--net/test/base_test_server.cc358
-rw-r--r--net/test/base_test_server.h213
-rw-r--r--net/test/local_test_server.cc237
-rw-r--r--net/test/local_test_server.h87
-rw-r--r--net/test/local_test_server_posix.cc (renamed from net/test/test_server_posix.cc)10
-rw-r--r--net/test/local_test_server_win.cc (renamed from net/test/test_server_win.cc)13
-rw-r--r--net/test/remote_test_server.cc168
-rw-r--r--net/test/remote_test_server.h53
-rw-r--r--net/test/spawner_communicator.cc367
-rw-r--r--net/test/spawner_communicator.h150
-rw-r--r--net/test/test_server.cc427
-rw-r--r--net/test/test_server.h225
12 files changed, 1654 insertions, 654 deletions
diff --git a/net/test/base_test_server.cc b/net/test/base_test_server.cc
new file mode 100644
index 0000000..ce28980
--- /dev/null
+++ b/net/test/base_test_server.cc
@@ -0,0 +1,358 @@
+// Copyright (c) 2012 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/base_test_server.h"
+
+#include <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/values.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/address_list.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/test_root_certs.h"
+
+namespace net {
+
+namespace {
+
+std::string GetHostname(BaseTestServer::Type type,
+ const BaseTestServer::HTTPSOptions& options) {
+ if (type == BaseTestServer::TYPE_HTTPS &&
+ options.server_certificate ==
+ BaseTestServer::HTTPSOptions::CERT_MISMATCHED_NAME) {
+ // Return a different hostname string that resolves to the same hostname.
+ return "localhost";
+ }
+
+ // Use the 127.0.0.1 as default.
+ return BaseTestServer::kLocalhost;
+}
+
+void GetCiphersList(int cipher, base::ListValue* values) {
+ if (cipher & BaseTestServer::HTTPSOptions::BULK_CIPHER_RC4)
+ values->Append(base::Value::CreateStringValue("rc4"));
+ if (cipher & BaseTestServer::HTTPSOptions::BULK_CIPHER_AES128)
+ values->Append(base::Value::CreateStringValue("aes128"));
+ if (cipher & BaseTestServer::HTTPSOptions::BULK_CIPHER_AES256)
+ values->Append(base::Value::CreateStringValue("aes256"));
+ if (cipher & BaseTestServer::HTTPSOptions::BULK_CIPHER_3DES)
+ values->Append(base::Value::CreateStringValue("3des"));
+}
+
+} // namespace
+
+BaseTestServer::HTTPSOptions::HTTPSOptions()
+ : server_certificate(CERT_OK),
+ request_client_certificate(false),
+ bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY),
+ record_resume(false) {}
+
+BaseTestServer::HTTPSOptions::HTTPSOptions(
+ BaseTestServer::HTTPSOptions::ServerCertificate cert)
+ : server_certificate(cert),
+ request_client_certificate(false),
+ bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY),
+ record_resume(false) {}
+
+BaseTestServer::HTTPSOptions::~HTTPSOptions() {}
+
+FilePath BaseTestServer::HTTPSOptions::GetCertificateFile() const {
+ switch (server_certificate) {
+ case CERT_OK:
+ case CERT_MISMATCHED_NAME:
+ return FilePath(FILE_PATH_LITERAL("ok_cert.pem"));
+ case CERT_EXPIRED:
+ return FilePath(FILE_PATH_LITERAL("expired_cert.pem"));
+ case CERT_CHAIN_WRONG_ROOT:
+ // This chain uses its own dedicated test root certificate to avoid
+ // side-effects that may affect testing.
+ return FilePath(FILE_PATH_LITERAL("redundant-server-chain.pem"));
+ default:
+ NOTREACHED();
+ }
+ return FilePath();
+}
+
+const char BaseTestServer::kLocalhost[] = "127.0.0.1";
+const char BaseTestServer::kGDataAuthToken[] = "testtoken";
+
+BaseTestServer::BaseTestServer(Type type, const std::string& host)
+ : type_(type),
+ started_(false),
+ log_to_console_(false) {
+ Init(host);
+}
+
+BaseTestServer::BaseTestServer(const HTTPSOptions& https_options)
+ : https_options_(https_options),
+ type_(TYPE_HTTPS),
+ started_(false),
+ log_to_console_(false) {
+ Init(GetHostname(TYPE_HTTPS, https_options));
+}
+
+BaseTestServer::~BaseTestServer() {}
+
+const HostPortPair& BaseTestServer::host_port_pair() const {
+ DCHECK(started_);
+ return host_port_pair_;
+}
+
+const DictionaryValue& BaseTestServer::server_data() const {
+ DCHECK(started_);
+ DCHECK(server_data_.get());
+ return *server_data_;
+}
+
+std::string BaseTestServer::GetScheme() const {
+ switch (type_) {
+ case TYPE_FTP:
+ return "ftp";
+ case TYPE_GDATA:
+ case TYPE_HTTP:
+ case TYPE_SYNC:
+ return "http";
+ case TYPE_HTTPS:
+ return "https";
+ case TYPE_TCP_ECHO:
+ case TYPE_UDP_ECHO:
+ default:
+ NOTREACHED();
+ }
+ return std::string();
+}
+
+bool BaseTestServer::GetAddressList(AddressList* address_list) const {
+ DCHECK(address_list);
+
+ scoped_ptr<HostResolver> resolver(
+ CreateSystemHostResolver(HostResolver::kDefaultParallelism,
+ HostResolver::kDefaultRetryAttempts,
+ NULL));
+ HostResolver::RequestInfo info(host_port_pair_);
+ TestCompletionCallback callback;
+ int rv = resolver->Resolve(info, address_list, callback.callback(), NULL,
+ BoundNetLog());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ if (rv != net::OK) {
+ LOG(ERROR) << "Failed to resolve hostname: " << host_port_pair_.host();
+ return false;
+ }
+ return true;
+}
+
+uint16 BaseTestServer::GetPort() {
+ return host_port_pair_.port();
+}
+
+void BaseTestServer::SetPort(uint16 port) {
+ host_port_pair_.set_port(port);
+}
+
+GURL BaseTestServer::GetURL(const std::string& path) const {
+ return GURL(GetScheme() + "://" + host_port_pair_.ToString() + "/" + path);
+}
+
+GURL BaseTestServer::GetURLWithUser(const std::string& path,
+ const std::string& user) const {
+ return GURL(GetScheme() + "://" + user + "@" + host_port_pair_.ToString() +
+ "/" + path);
+}
+
+GURL BaseTestServer::GetURLWithUserAndPassword(const std::string& path,
+ const std::string& user,
+ const std::string& password) const {
+ return GURL(GetScheme() + "://" + user + ":" + password + "@" +
+ host_port_pair_.ToString() + "/" + path);
+}
+
+// static
+bool BaseTestServer::GetFilePathWithReplacements(
+ const std::string& original_file_path,
+ const std::vector<StringPair>& text_to_replace,
+ std::string* replacement_path) {
+ std::string new_file_path = original_file_path;
+ bool first_query_parameter = true;
+ const std::vector<StringPair>::const_iterator end = text_to_replace.end();
+ for (std::vector<StringPair>::const_iterator it = text_to_replace.begin();
+ it != end;
+ ++it) {
+ const std::string& old_text = it->first;
+ const std::string& new_text = it->second;
+ std::string base64_old;
+ std::string base64_new;
+ if (!base::Base64Encode(old_text, &base64_old))
+ return false;
+ if (!base::Base64Encode(new_text, &base64_new))
+ return false;
+ if (first_query_parameter) {
+ new_file_path += "?";
+ first_query_parameter = false;
+ } else {
+ new_file_path += "&";
+ }
+ new_file_path += "replace_text=";
+ new_file_path += base64_old;
+ new_file_path += ":";
+ new_file_path += base64_new;
+ }
+
+ *replacement_path = new_file_path;
+ return true;
+}
+
+void BaseTestServer::Init(const std::string& host) {
+ host_port_pair_ = HostPortPair(host, 0);
+
+ // TODO(battre) Remove this after figuring out why the TestServer is flaky.
+ // http://crbug.com/96594
+ log_to_console_ = true;
+}
+
+void BaseTestServer::SetResourcePath(const FilePath& document_root,
+ const FilePath& certificates_dir) {
+ // This method shouldn't get called twice.
+ DCHECK(certificates_dir_.empty());
+ document_root_ = document_root;
+ certificates_dir_ = certificates_dir;
+ DCHECK(!certificates_dir_.empty());
+}
+
+bool BaseTestServer::ParseServerData(const std::string& server_data) {
+ VLOG(1) << "Server data: " << server_data;
+ base::JSONReader json_reader;
+ scoped_ptr<Value> value(json_reader.JsonToValue(server_data, true, false));
+ if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) {
+ LOG(ERROR) << "Could not parse server data: "
+ << json_reader.GetErrorMessage();
+ return false;
+ }
+
+ server_data_.reset(static_cast<DictionaryValue*>(value.release()));
+ int port = 0;
+ if (!server_data_->GetInteger("port", &port)) {
+ LOG(ERROR) << "Could not find port value";
+ return false;
+ }
+ if ((port <= 0) || (port > kuint16max)) {
+ LOG(ERROR) << "Invalid port value: " << port;
+ return false;
+ }
+ host_port_pair_.set_port(port);
+
+ return true;
+}
+
+bool BaseTestServer::LoadTestRootCert() const {
+ TestRootCerts* root_certs = TestRootCerts::GetInstance();
+ if (!root_certs)
+ return false;
+
+ // Should always use absolute path to load the root certificate.
+ FilePath root_certificate_path = certificates_dir_;
+ if (!certificates_dir_.IsAbsolute()) {
+ FilePath src_dir;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir))
+ return false;
+ root_certificate_path = src_dir.Append(certificates_dir_);
+ }
+
+ return root_certs->AddFromFile(
+ root_certificate_path.AppendASCII("root_ca_cert.crt"));
+}
+
+bool BaseTestServer::SetupWhenServerStarted() {
+ DCHECK(host_port_pair_.port());
+
+ if (type_ == TYPE_HTTPS && !LoadTestRootCert())
+ return false;
+
+ started_ = true;
+ allowed_port_.reset(new ScopedPortException(host_port_pair_.port()));
+ return true;
+}
+
+void BaseTestServer::CleanUpWhenStoppingServer() {
+ TestRootCerts* root_certs = TestRootCerts::GetInstance();
+ root_certs->Clear();
+
+ host_port_pair_.set_port(0);
+ allowed_port_.reset();
+ started_ = false;
+}
+
+// Generates a dictionary of arguments to pass to the Python test server via
+// the test server spawner, in the form of
+// { argument-name: argument-value, ... }
+// Returns false if an invalid configuration is specified.
+bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const {
+ DCHECK(arguments);
+
+ arguments->SetString("host", host_port_pair_.host());
+ arguments->SetInteger("port", host_port_pair_.port());
+ arguments->SetString("data-dir", document_root_.value());
+
+ if (VLOG_IS_ON(1) || log_to_console_)
+ arguments->Set("log-to-console", base::Value::CreateNullValue());
+
+ if (type_ == TYPE_GDATA) {
+ // --auth-token will be used in tests for chrome/browser/chromeos/gdata.
+ arguments->SetString("auth-token", kGDataAuthToken);
+ } else if (type_ == TYPE_HTTPS) {
+ // Check the certificate arguments of the HTTPS server.
+ FilePath certificate_path(certificates_dir_);
+ certificate_path = certificate_path.Append(
+ https_options_.GetCertificateFile());
+ if (certificate_path.IsAbsolute() &&
+ !file_util::PathExists(certificate_path)) {
+ LOG(ERROR) << "Certificate path " << certificate_path.value()
+ << " doesn't exist. Can't launch https server.";
+ return false;
+ }
+ arguments->SetString("https", certificate_path.value());
+
+ // Check the client certificate related arguments.
+ if (https_options_.request_client_certificate)
+ arguments->Set("ssl-client-auth", base::Value::CreateNullValue());
+ scoped_ptr<base::ListValue> ssl_client_certs(new base::ListValue());
+
+ std::vector<FilePath>::const_iterator it;
+ for (it = https_options_.client_authorities.begin();
+ it != https_options_.client_authorities.end(); ++it) {
+ if (it->IsAbsolute() && !file_util::PathExists(*it)) {
+ LOG(ERROR) << "Client authority path " << it->value()
+ << " doesn't exist. Can't launch https server.";
+ return false;
+ }
+ ssl_client_certs->Append(base::Value::CreateStringValue(it->value()));
+ }
+
+ if (ssl_client_certs->GetSize())
+ arguments->Set("ssl-client-ca", ssl_client_certs.release());
+
+ // Check bulk cipher argument.
+ scoped_ptr<base::ListValue> bulk_cipher_values(new base::ListValue());
+ GetCiphersList(https_options_.bulk_ciphers, bulk_cipher_values.get());
+ if (bulk_cipher_values->GetSize())
+ arguments->Set("ssl-bulk-cipher", bulk_cipher_values.release());
+ if (https_options_.record_resume)
+ arguments->Set("https-record-resume", base::Value::CreateNullValue());
+ }
+ return true;
+}
+
+} // namespace net
+
diff --git a/net/test/base_test_server.h b/net/test/base_test_server.h
new file mode 100644
index 0000000..38f755d
--- /dev/null
+++ b/net/test/base_test_server.h
@@ -0,0 +1,213 @@
+// Copyright (c) 2012 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.
+
+#ifndef NET_TEST_BASE_TEST_SERVER_H_
+#define NET_TEST_BASE_TEST_SERVER_H_
+#pragma once
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/host_port_pair.h"
+
+class GURL;
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace net {
+
+class AddressList;
+class ScopedPortException;
+
+// The base class of Test server implementation.
+class BaseTestServer {
+ public:
+ typedef std::pair<std::string, std::string> StringPair;
+
+ enum Type {
+ TYPE_FTP,
+ TYPE_GDATA,
+ TYPE_HTTP,
+ TYPE_HTTPS,
+ TYPE_SYNC,
+ TYPE_TCP_ECHO,
+ TYPE_UDP_ECHO,
+ };
+
+ // Container for various options to control how the HTTPS server is
+ // initialized.
+ struct HTTPSOptions {
+ enum ServerCertificate {
+ CERT_OK,
+ CERT_MISMATCHED_NAME,
+ CERT_EXPIRED,
+ // Cross-signed certificate to test PKIX path building. Contains an
+ // intermediate cross-signed by an unknown root, while the client (via
+ // TestRootStore) is expected to have a self-signed version of the
+ // intermediate.
+ CERT_CHAIN_WRONG_ROOT,
+ };
+
+ // Bitmask of bulk encryption algorithms that the test server supports
+ // and that can be selectively enabled or disabled.
+ enum BulkCipher {
+ // Special value used to indicate that any algorithm the server supports
+ // is acceptable. Preferred over explicitly OR-ing all ciphers.
+ BULK_CIPHER_ANY = 0,
+
+ BULK_CIPHER_RC4 = (1 << 0),
+ BULK_CIPHER_AES128 = (1 << 1),
+ BULK_CIPHER_AES256 = (1 << 2),
+
+ // NOTE: 3DES support in the Python test server has external
+ // dependencies and not be available on all machines. Clients may not
+ // be able to connect if only 3DES is specified.
+ BULK_CIPHER_3DES = (1 << 3),
+ };
+
+ // Initialize a new HTTPSOptions using CERT_OK as the certificate.
+ HTTPSOptions();
+
+ // Initialize a new HTTPSOptions that will use the specified certificate.
+ explicit HTTPSOptions(ServerCertificate cert);
+ ~HTTPSOptions();
+
+ // Returns the relative filename of the file that contains the
+ // |server_certificate|.
+ FilePath GetCertificateFile() const;
+
+ // The certificate to use when serving requests.
+ ServerCertificate server_certificate;
+
+ // True if a CertificateRequest should be sent to the client during
+ // handshaking.
+ bool request_client_certificate;
+
+ // If |request_client_certificate| is true, an optional list of files,
+ // each containing a single, PEM-encoded X.509 certificates. The subject
+ // from each certificate will be added to the certificate_authorities
+ // field of the CertificateRequest.
+ std::vector<FilePath> client_authorities;
+
+ // A bitwise-OR of BulkCipher that should be used by the
+ // HTTPS server, or BULK_CIPHER_ANY to indicate that all implemented
+ // ciphers are acceptable.
+ int bulk_ciphers;
+
+ // If true, pass the --https-record-resume argument to testserver.py which
+ // causes it to log session cache actions and echo the log on
+ // /ssl-session-cache.
+ bool record_resume;
+ };
+
+ // Pass as the 'host' parameter during construction to server on 127.0.0.1
+ static const char kLocalhost[];
+
+ // The auth token to be used for TYPE_GDATA server.
+ static const char kGDataAuthToken[];
+
+ // Initialize a TestServer listening on a specific host (IP or hostname).
+ BaseTestServer(Type type, const std::string& host);
+
+ // Initialize a HTTPS TestServer with a specific set of HTTPSOptions.
+ explicit BaseTestServer(const HTTPSOptions& https_options);
+
+ ~BaseTestServer();
+
+ // Returns the host port pair used by current Python based test server only
+ // if the server is started.
+ const HostPortPair& host_port_pair() const;
+
+ const FilePath& document_root() const { return document_root_; }
+ const base::DictionaryValue& server_data() const;
+ std::string GetScheme() const;
+ bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT;
+
+ GURL GetURL(const std::string& path) const;
+
+ GURL GetURLWithUser(const std::string& path,
+ const std::string& user) const;
+
+ GURL GetURLWithUserAndPassword(const std::string& path,
+ const std::string& user,
+ const std::string& password) const;
+
+ static bool GetFilePathWithReplacements(
+ const std::string& original_path,
+ const std::vector<StringPair>& text_to_replace,
+ std::string* replacement_path);
+
+ protected:
+ Type type() const { return type_; }
+
+ // Gets port currently assigned to host_port_pair_ without checking
+ // whether it's available (server started) or not.
+ uint16 GetPort();
+
+ // Sets |port| as the actual port used by Python based test server.
+ void SetPort(uint16 port);
+
+ // Set up internal status when the server is started.
+ bool SetupWhenServerStarted() WARN_UNUSED_RESULT;
+
+ // Clean up internal status when starting to stop server.
+ void CleanUpWhenStoppingServer();
+
+ // Set path of test resources.
+ void SetResourcePath(const FilePath& document_root,
+ const FilePath& certificates_dir);
+
+ // Parses the server data read from the test server. Returns true
+ // on success.
+ bool ParseServerData(const std::string& server_data) WARN_UNUSED_RESULT;
+
+ // Generates a DictionaryValue with the arguments for launching the external
+ // Python test server.
+ bool GenerateArguments(base::DictionaryValue* arguments) const;
+
+ private:
+ void Init(const std::string& host);
+
+ // Marks the root certificate of an HTTPS test server as trusted for
+ // the duration of tests.
+ bool LoadTestRootCert() const WARN_UNUSED_RESULT;
+
+ // Document root of the test server.
+ FilePath document_root_;
+
+ // Directory that contains the SSL certificates.
+ FilePath certificates_dir_;
+
+ // Address the test server listens on.
+ HostPortPair host_port_pair_;
+
+ // Holds the data sent from the server (e.g., port number).
+ scoped_ptr<base::DictionaryValue> server_data_;
+
+ // If |type_| is TYPE_HTTPS, the TLS settings to use for the test server.
+ HTTPSOptions https_options_;
+
+ Type type_;
+
+ // Has the server been started?
+ bool started_;
+
+ // Enables logging of the server to the console.
+ bool log_to_console_;
+
+ scoped_ptr<ScopedPortException> allowed_port_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseTestServer);
+};
+
+} // namespace net
+
+#endif // NET_TEST_BASE_TEST_SERVER_H_
+
diff --git a/net/test/local_test_server.cc b/net/test/local_test_server.cc
new file mode 100644
index 0000000..c36d2ff
--- /dev/null
+++ b/net/test/local_test_server.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 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/local_test_server.h"
+
+#include "base/command_line.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/test/python_utils.h"
+
+namespace net {
+
+namespace {
+
+bool AppendArgumentFromJSONValue(const std::string& key,
+ const base::Value& value_node,
+ CommandLine* command_line) {
+ std::string argument_name = "--" + key;
+ switch (value_node.GetType()) {
+ case base::Value::TYPE_NULL:
+ command_line->AppendArg(argument_name);
+ break;
+ case base::Value::TYPE_INTEGER: {
+ int value;
+ bool result = value_node.GetAsInteger(&value);
+ DCHECK(result);
+ command_line->AppendArg(argument_name + "=" + base::IntToString(value));
+ break;
+ }
+ case Value::TYPE_STRING: {
+ std::string value;
+ bool result = value_node.GetAsString(&value);
+ if (!result || value.empty())
+ return false;
+ command_line->AppendArg(argument_name + "=" + value);
+ break;
+ }
+ case base::Value::TYPE_BOOLEAN:
+ case base::Value::TYPE_DOUBLE:
+ case base::Value::TYPE_LIST:
+ case base::Value::TYPE_DICTIONARY:
+ case base::Value::TYPE_BINARY:
+ default:
+ NOTREACHED() << "improper json type";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+LocalTestServer::LocalTestServer(Type type,
+ const std::string& host,
+ const FilePath& document_root)
+ : BaseTestServer(type, host) {
+ if (!Init(document_root))
+ NOTREACHED();
+}
+
+LocalTestServer::LocalTestServer(const HTTPSOptions& https_options,
+ const FilePath& document_root)
+ : BaseTestServer(https_options) {
+ if (!Init(document_root))
+ NOTREACHED();
+}
+
+LocalTestServer::~LocalTestServer() {
+ Stop();
+}
+
+bool LocalTestServer::Start() {
+ // Get path to Python server script.
+ FilePath testserver_path;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path)) {
+ LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
+ return false;
+ }
+ testserver_path = testserver_path
+ .Append(FILE_PATH_LITERAL("net"))
+ .Append(FILE_PATH_LITERAL("tools"))
+ .Append(FILE_PATH_LITERAL("testserver"))
+ .Append(FILE_PATH_LITERAL("testserver.py"));
+
+ if (!SetPythonPath())
+ return false;
+
+ if (!LaunchPython(testserver_path))
+ return false;
+
+ if (!WaitToStart()) {
+ Stop();
+ return false;
+ }
+
+ return SetupWhenServerStarted();
+}
+
+bool LocalTestServer::Stop() {
+ CleanUpWhenStoppingServer();
+
+ if (!process_handle_)
+ return true;
+
+ // First check if the process has already terminated.
+ bool ret = base::WaitForSingleProcess(process_handle_, 0);
+ if (!ret)
+ ret = base::KillProcess(process_handle_, 1, true);
+
+ if (ret) {
+ base::CloseProcessHandle(process_handle_);
+ process_handle_ = base::kNullProcessHandle;
+ } else {
+ VLOG(1) << "Kill failed?";
+ }
+
+ return ret;
+}
+
+bool LocalTestServer::Init(const FilePath& document_root) {
+ if (document_root.IsAbsolute())
+ return false;
+
+ // At this point, the port that the test server will listen on is unknown.
+ // The test server will listen on an ephemeral port, and write the port
+ // number out over a pipe that this TestServer object will read from. Once
+ // that is complete, the host port pair will contain the actual port.
+ DCHECK(!GetPort());
+ process_handle_ = base::kNullProcessHandle;
+
+ FilePath src_dir;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir))
+ return false;
+ SetResourcePath(src_dir.Append(document_root),
+ src_dir.AppendASCII("net")
+ .AppendASCII("data")
+ .AppendASCII("ssl")
+ .AppendASCII("certificates"));
+ return true;
+}
+
+bool LocalTestServer::SetPythonPath() const {
+ FilePath third_party_dir;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
+ LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
+ return false;
+ }
+ third_party_dir = third_party_dir.AppendASCII("third_party");
+
+ // For simplejson. (simplejson, unlike all the other Python modules
+ // we include, doesn't have an extra 'simplejson' directory, so we
+ // need to include its parent directory, i.e. third_party_dir).
+ AppendToPythonPath(third_party_dir);
+
+ AppendToPythonPath(third_party_dir.AppendASCII("tlslite"));
+ AppendToPythonPath(
+ third_party_dir.AppendASCII("pyftpdlib").AppendASCII("src"));
+
+ // Locate the Python code generated by the protocol buffers compiler.
+ FilePath pyproto_dir;
+ if (!GetPyProtoPath(&pyproto_dir)) {
+ LOG(WARNING) << "Cannot find pyproto dir for generated code. "
+ << "Testserver features that rely on it will not work";
+ return true;
+ }
+
+ AppendToPythonPath(pyproto_dir);
+ AppendToPythonPath(pyproto_dir.AppendASCII("sync").AppendASCII("protocol"));
+ AppendToPythonPath(pyproto_dir.AppendASCII("chrome")
+ .AppendASCII("browser")
+ .AppendASCII("policy")
+ .AppendASCII("proto"));
+
+ return true;
+}
+
+bool LocalTestServer::AddCommandLineArguments(CommandLine* command_line) const {
+ base::DictionaryValue arguments_dict;
+ if (!GenerateArguments(&arguments_dict))
+ return false;
+
+ // Serialize the argument dictionary into CommandLine.
+ for (DictionaryValue::Iterator it(arguments_dict); it.HasNext();
+ it.Advance()) {
+ const base::Value& value = it.value();
+ const std::string& key = it.key();
+
+ // Add arguments from a list.
+ if (value.IsType(Value::TYPE_LIST)) {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list) || !list || list->empty())
+ return false;
+ for (base::ListValue::const_iterator list_it = list->begin();
+ list_it != list->end(); ++list_it) {
+ if (!AppendArgumentFromJSONValue(key, *(*list_it), command_line))
+ return false;
+ }
+ } else if (!AppendArgumentFromJSONValue(key, value, command_line)) {
+ return false;
+ }
+ }
+
+ // Append the appropriate server type argument.
+ switch (type()) {
+ case TYPE_HTTP:
+ case TYPE_HTTPS:
+ // The default type is HTTP, no argument required.
+ break;
+ case TYPE_FTP:
+ command_line->AppendArg("-f");
+ break;
+ case TYPE_SYNC:
+ command_line->AppendArg("--sync");
+ break;
+ case TYPE_TCP_ECHO:
+ command_line->AppendArg("--tcp-echo");
+ break;
+ case TYPE_UDP_ECHO:
+ command_line->AppendArg("--udp-echo");
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace net
+
diff --git a/net/test/local_test_server.h b/net/test/local_test_server.h
new file mode 100644
index 0000000..48c2186
--- /dev/null
+++ b/net/test/local_test_server.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 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.
+
+#ifndef NET_TEST_LOCAL_TEST_SERVER_H_
+#define NET_TEST_LOCAL_TEST_SERVER_H_
+#pragma once
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/process.h"
+#include "net/test/base_test_server.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#endif
+
+class CommandLine;
+
+namespace net {
+
+// The LocalTestServer runs an external Python-based test server in the
+// same machine in which the LocalTestServer runs.
+class LocalTestServer : public BaseTestServer {
+ public:
+ // Initialize a TestServer listening on a specific host (IP or hostname).
+ // |document_root| must be a relative path under the root tree.
+ LocalTestServer(Type type,
+ const std::string& host,
+ const FilePath& document_root);
+
+ // Initialize a HTTPS TestServer with a specific set of HTTPSOptions.
+ // |document_root| must be a relative path under the root tree.
+ LocalTestServer(const HTTPSOptions& https_options,
+ const FilePath& document_root);
+
+ ~LocalTestServer();
+
+ bool Start() WARN_UNUSED_RESULT;
+
+ // Stop the server started by Start().
+ bool Stop();
+
+ private:
+ bool Init(const FilePath& document_root);
+
+ // Modify PYTHONPATH to contain libraries we need.
+ bool SetPythonPath() const WARN_UNUSED_RESULT;
+
+ // Launches the Python test server. Returns true on success.
+ bool LaunchPython(const FilePath& testserver_path) WARN_UNUSED_RESULT;
+
+ // Waits for the server to start. Returns true on success.
+ bool WaitToStart() WARN_UNUSED_RESULT;
+
+ // Add the command line arguments for the Python test server to
+ // |command_line|. Return true on success.
+ bool AddCommandLineArguments(CommandLine* command_line) const;
+
+ // Handle of the Python process running the test server.
+ base::ProcessHandle process_handle_;
+
+#if defined(OS_WIN)
+ // JobObject used to clean up orphaned child processes.
+ base::win::ScopedHandle job_handle_;
+
+ // The pipe file handle we read from.
+ base::win::ScopedHandle child_read_fd_;
+
+ // The pipe file handle the child and we write to.
+ base::win::ScopedHandle child_write_fd_;
+#endif
+
+#if defined(OS_POSIX)
+ // The file descriptor the child writes to when it starts.
+ int child_fd_;
+ file_util::ScopedFD child_fd_closer_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(LocalTestServer);
+};
+
+} // namespace net
+
+#endif // NET_TEST_LOCAL_TEST_SERVER_H_
+
diff --git a/net/test/test_server_posix.cc b/net/test/local_test_server_posix.cc
index f15acc3..0cb4857 100644
--- a/net/test/test_server_posix.cc
+++ b/net/test/local_test_server_posix.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/test/test_server.h"
+#include "net/test/local_test_server.h"
#include <poll.h>
@@ -76,7 +76,7 @@ bool ReadData(int fd, ssize_t bytes_max, uint8* buffer,
base::Time current_time = base::Time::Now();
base::TimeDelta elapsed_time_cycle = current_time - previous_time;
- DCHECK(elapsed_time_cycle.InMilliseconds() >= 0);
+ DCHECK_GE(elapsed_time_cycle.InMilliseconds(), 0);
*remaining_time -= elapsed_time_cycle;
previous_time = current_time;
@@ -93,7 +93,7 @@ bool ReadData(int fd, ssize_t bytes_max, uint8* buffer,
namespace net {
-bool TestServer::LaunchPython(const FilePath& testserver_path) {
+bool LocalTestServer::LaunchPython(const FilePath& testserver_path) {
CommandLine python_command(FilePath(FILE_PATH_LITERAL("python")));
python_command.AppendArgPath(testserver_path);
if (!AddCommandLineArguments(&python_command))
@@ -116,7 +116,7 @@ bool TestServer::LaunchPython(const FilePath& testserver_path) {
// Try to kill any orphaned testserver processes that may be running.
OrphanedTestServerFilter filter(testserver_path.value(),
- base::IntToString(host_port_pair_.port()));
+ base::IntToString(GetPort()));
if (!base::KillProcesses("python", -1, &filter)) {
LOG(WARNING) << "Failed to clean up older orphaned testserver instances.";
}
@@ -132,7 +132,7 @@ bool TestServer::LaunchPython(const FilePath& testserver_path) {
return true;
}
-bool TestServer::WaitToStart() {
+bool LocalTestServer::WaitToStart() {
file_util::ScopedFD child_fd_closer(child_fd_closer_.release());
base::TimeDelta remaining_time = base::TimeDelta::FromMilliseconds(
diff --git a/net/test/test_server_win.cc b/net/test/local_test_server_win.cc
index 07bcee5..b56e98a 100644
--- a/net/test/test_server_win.cc
+++ b/net/test/local_test_server_win.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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/test_server.h"
+#include "net/test/local_test_server.h"
#include <windows.h>
#include <wincrypt.h>
@@ -29,8 +29,8 @@ namespace {
// Used as a crude timeout mechanism by ReadData().
void UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) {
std::string unblock_data(size, '\0');
- // Unblock the ReadFile in TestServer::WaitToStart by writing to the pipe.
- // Make sure the call succeeded, otherwise we are very likely to hang.
+ // Unblock the ReadFile in LocalTestServer::WaitToStart by writing to the
+ // pipe. Make sure the call succeeded, otherwise we are very likely to hang.
DWORD bytes_written = 0;
LOG(WARNING) << "Timeout reached; unblocking pipe by writing "
<< size << " bytes";
@@ -84,7 +84,7 @@ bool ReadData(HANDLE read_fd, HANDLE write_fd,
namespace net {
-bool TestServer::LaunchPython(const FilePath& testserver_path) {
+bool LocalTestServer::LaunchPython(const FilePath& testserver_path) {
FilePath python_exe;
if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_exe))
return false;
@@ -149,7 +149,7 @@ bool TestServer::LaunchPython(const FilePath& testserver_path) {
return true;
}
-bool TestServer::WaitToStart() {
+bool LocalTestServer::WaitToStart() {
base::win::ScopedHandle read_fd(child_read_fd_.Take());
base::win::ScopedHandle write_fd(child_write_fd_.Take());
@@ -176,3 +176,4 @@ bool TestServer::WaitToStart() {
}
} // namespace net
+
diff --git a/net/test/remote_test_server.cc b/net/test/remote_test_server.cc
new file mode 100644
index 0000000..fc144f2
--- /dev/null
+++ b/net/test/remote_test_server.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2012 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/remote_test_server.h"
+
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/values.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/test/spawner_communicator.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.
+const char kTestServerPortInfoFile[] = "/data/local/tmp/net-test-server-ports";
+
+// 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_GDATA:
+ case BaseTestServer::TYPE_HTTP:
+ case BaseTestServer::TYPE_HTTPS:
+ return "http";
+ case BaseTestServer::TYPE_SYNC:
+ return "sync";
+ 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 FilePath& document_root)
+ : BaseTestServer(type, host) {
+ if (!Init(document_root))
+ NOTREACHED();
+}
+
+RemoteTestServer::RemoteTestServer(
+ const HTTPSOptions& https_options,
+ const FilePath& document_root)
+ : BaseTestServer(https_options) {
+ if (!Init(document_root))
+ NOTREACHED();
+}
+
+RemoteTestServer::~RemoteTestServer() {
+ Stop();
+}
+
+bool RemoteTestServer::Start() {
+ if (!spawner_communicator_.get())
+ return false;
+
+ 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, false, &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, false, &server_data);
+ if (server_data.empty() || !ParseServerData(server_data)) {
+ LOG(ERROR) << "Could not parse server_data: " << server_data;
+ return false;
+ }
+
+ return SetupWhenServerStarted();
+}
+
+bool RemoteTestServer::Stop() {
+ CleanUpWhenStoppingServer();
+ return spawner_communicator_->StopServer();
+}
+
+bool RemoteTestServer::Init(const 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;
+ int spawner_server_port = 0;
+
+ // Parse file to extract the ports information.
+ std::string port_info;
+ if (!file_util::ReadFileToString(FilePath(kTestServerPortInfoFile),
+ &port_info) ||
+ port_info.empty()) {
+ return false;
+ }
+
+ std::vector<std::string> 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<uint32>(spawner_server_port) >= kuint16max)
+ return false;
+ spawner_communicator_.reset(new SpawnerCommunicator(spawner_server_port));
+
+ // 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<uint32>(test_server_port) >= kuint16max)
+ return false;
+ SetPort(test_server_port);
+
+ SetResourcePath(document_root, FilePath().AppendASCII("net")
+ .AppendASCII("data")
+ .AppendASCII("ssl")
+ .AppendASCII("certificates"));
+ return true;
+}
+
+} // namespace net
+
diff --git a/net/test/remote_test_server.h b/net/test/remote_test_server.h
new file mode 100644
index 0000000..1f375cb
--- /dev/null
+++ b/net/test/remote_test_server.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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.
+
+#ifndef NET_TEST_REMOTE_TEST_SERVER_H_
+#define NET_TEST_REMOTE_TEST_SERVER_H_
+#pragma once
+
+#include <string>
+
+#include "net/test/base_test_server.h"
+
+namespace net {
+
+class SpawnerCommunicator;
+
+// The RemoteTestServer runs an external Python-based test server in another
+// machine that is different from the machine in which RemoteTestServer runs.
+class RemoteTestServer : public BaseTestServer {
+ public:
+ // Initialize a TestServer listening on a specific host (IP or hostname).
+ // |document_root| must be a relative path under the root tree.
+ RemoteTestServer(Type type,
+ const std::string& host,
+ const FilePath& document_root);
+
+ // Initialize a HTTPS TestServer with a specific set of HTTPSOptions.
+ // |document_root| must be a relative path under the root tree.
+ RemoteTestServer(const HTTPSOptions& https_options,
+ const FilePath& document_root);
+
+ ~RemoteTestServer();
+
+ // Starts the Python test server on the host, instead of on the device.
+ bool Start() WARN_UNUSED_RESULT;
+
+ // Stops the Python test server that is running on the host machine.
+ bool Stop();
+
+ private:
+ bool Init(const FilePath& document_root);
+
+ // Helper to start and stop instances of the Python test server that runs on
+ // the host machine.
+ scoped_ptr<SpawnerCommunicator> spawner_communicator_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteTestServer);
+};
+
+} // namespace net
+
+#endif // NET_TEST_REMOTE_TEST_SERVER_H_
+
diff --git a/net/test/spawner_communicator.cc b/net/test/spawner_communicator.cc
new file mode 100644
index 0000000..8b454f0
--- /dev/null
+++ b/net/test/spawner_communicator.cc
@@ -0,0 +1,367 @@
+// Copyright (c) 2012 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/spawner_communicator.h"
+
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/supports_user_data.h"
+#include "base/test/test_timeouts.h"
+#include "base/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_test_util.h"
+
+namespace net {
+
+namespace {
+
+GURL GenerateSpawnerCommandURL(const std::string& command, uint16 port) {
+ // Always performs HTTP request for sending command to the spawner server.
+ return GURL(base::StringPrintf("%s:%u/%s", "http://127.0.0.1", port,
+ command.c_str()));
+}
+
+int kBufferSize = 2048;
+
+// A class to hold all data needed to send a command to spawner server.
+class SpawnerRequestData : public base::SupportsUserData::Data {
+ public:
+ SpawnerRequestData(int id, int* result_code, std::string* data_received)
+ : request_id_(id),
+ buf_(new IOBuffer(kBufferSize)),
+ result_code_(result_code),
+ data_received_(data_received),
+ response_started_count_(0) {
+ DCHECK(result_code);
+ *result_code_ = OK;
+ DCHECK(data_received);
+ data_received_->clear();
+ }
+
+ virtual ~SpawnerRequestData() {}
+
+ bool DoesRequestIdMatch(int request_id) const {
+ return request_id_ == request_id;
+ }
+
+ IOBuffer* buf() const { return buf_.get(); }
+
+ bool IsResultOK() const { return *result_code_ == OK; }
+
+ void ClearReceivedData() { data_received_->clear(); }
+
+ void SetResultCode(int result_code) { *result_code_ = result_code; }
+
+ void IncreaseResponseStartedCount() { response_started_count_++; }
+
+ int response_started_count() const { return response_started_count_; }
+
+ // Write data read from URLRequest::Read() to |data_received_|. Returns true
+ // if |num_bytes| is great than 0. |num_bytes| is 0 for EOF, < 0 on errors.
+ bool ConsumeBytesRead(int num_bytes) {
+ // Error while reading, or EOF.
+ if (num_bytes <= 0)
+ return false;
+
+ data_received_->append(buf_->data(), num_bytes);
+ return true;
+ }
+
+ private:
+ // Unique ID for the current request.
+ int request_id_;
+
+ // Buffer that URLRequest writes into.
+ scoped_refptr<IOBuffer> buf_;
+
+ // Holds the error condition that was hit on the current request, or OK.
+ int* result_code_;
+
+ // Data received from server;
+ std::string* data_received_;
+
+ // Used to track how many times the OnResponseStarted get called after
+ // sending a command to spawner server.
+ int response_started_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpawnerRequestData);
+};
+
+} // namespace
+
+SpawnerCommunicator::SpawnerCommunicator(uint16 port)
+ : io_thread_("spawner_communicator"),
+ event_(false, false),
+ port_(port),
+ next_id_(0),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
+ is_running_(false) {}
+
+SpawnerCommunicator::~SpawnerCommunicator() {}
+
+void SpawnerCommunicator::WaitForResponse() {
+ DCHECK_NE(MessageLoop::current(), io_thread_.message_loop());
+ event_.Wait();
+ event_.Reset();
+}
+
+void SpawnerCommunicator::StartIOThread() {
+ DCHECK_NE(MessageLoop::current(), io_thread_.message_loop());
+ if (is_running_)
+ return;
+
+ allowed_port_.reset(new ScopedPortException(port_));
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ is_running_ = io_thread_.StartWithOptions(options);
+ DCHECK(is_running_);
+}
+
+void SpawnerCommunicator::Shutdown() {
+ DCHECK_NE(MessageLoop::current(), io_thread_.message_loop());
+ DCHECK(is_running_);
+ DCHECK(!cur_request_.get());
+ io_thread_.Stop();
+ allowed_port_.reset();
+}
+
+void SpawnerCommunicator::SendCommandAndWaitForResult(
+ const std::string& command,
+ const std::string& post_data,
+ int* result_code,
+ std::string* data_received) {
+ if (!result_code || !data_received)
+ return;
+ // Start the communicator thread to talk to test server spawner.
+ StartIOThread();
+ DCHECK(io_thread_.message_loop());
+
+ // Since the method will be blocked until SpawnerCommunicator gets result
+ // from the spawner server or timed-out. It's safe to use base::Unretained
+ // when using base::Bind.
+ io_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
+ &SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread,
+ base::Unretained(this), command, post_data, result_code, data_received));
+ WaitForResponse();
+}
+
+void SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread(
+ const std::string& command,
+ const std::string& post_data,
+ int* result_code,
+ std::string* data_received) {
+ MessageLoop* loop = io_thread_.message_loop();
+ DCHECK(loop);
+ DCHECK_EQ(MessageLoop::current(), loop);
+
+ // Prepare the URLRequest for sending the command.
+ DCHECK(!cur_request_.get());
+ cur_request_.reset(new URLRequest(GenerateSpawnerCommandURL(command, port_),
+ this));
+ DCHECK(cur_request_.get());
+ int current_request_id = ++next_id_;
+ SpawnerRequestData* data = new SpawnerRequestData(current_request_id,
+ result_code,
+ data_received);
+ DCHECK(data);
+ cur_request_->SetUserData(this, data);
+
+ // Build the URLRequest.
+ scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext());
+ cur_request_->set_context(context);
+ if (post_data.empty()) {
+ cur_request_->set_method("GET");
+ } else {
+ cur_request_->set_method("POST");
+ cur_request_->AppendBytesToUpload(post_data.c_str(), post_data.size());
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(net::HttpRequestHeaders::kContentType,
+ "application/json");
+ cur_request_->SetExtraRequestHeaders(headers);
+ }
+
+ // Post a task to timeout this request if it takes too long.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&SpawnerCommunicator::OnTimeout, weak_factory_.GetWeakPtr(),
+ current_request_id),
+ TestTimeouts::action_max_timeout());
+
+ // Start the request.
+ cur_request_->Start();
+}
+
+void SpawnerCommunicator::OnTimeout(int id) {
+ // Timeout tasks may outlive the URLRequest they reference. Make sure it
+ // is still applicable.
+ if (!cur_request_.get())
+ return;
+ SpawnerRequestData* data =
+ static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this));
+ DCHECK(data);
+
+ if (!data->DoesRequestIdMatch(id))
+ return;
+ // Set the result code and cancel the timed-out task.
+ data->SetResultCode(ERR_TIMED_OUT);
+ cur_request_->Cancel();
+ OnSpawnerCommandCompleted(cur_request_.get());
+}
+
+void SpawnerCommunicator::OnSpawnerCommandCompleted(URLRequest* request) {
+ if (!cur_request_.get())
+ return;
+ DCHECK_EQ(request, cur_request_.get());
+ SpawnerRequestData* data =
+ static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this));
+ DCHECK(data);
+
+ // If request is faild,return the error code.
+ if (!cur_request_->status().is_success())
+ data->SetResultCode(cur_request_->status().error());
+
+ if (!data->IsResultOK()) {
+ LOG(ERROR) << "request failed, status: "
+ << static_cast<int>(request->status().status())
+ << ", error: " << request->status().error();
+ // Clear the buffer of received data if any net error happened.
+ data->ClearReceivedData();
+ } else {
+ DCHECK_EQ(1, data->response_started_count());
+ }
+
+ // Clear current request to indicate the completion of sending a command
+ // to spawner server and getting the result.
+ cur_request_.reset();
+ // Wakeup the caller in user thread.
+ event_.Signal();
+}
+
+void SpawnerCommunicator::ReadResult(URLRequest* request) {
+ DCHECK_EQ(request, cur_request_.get());
+ SpawnerRequestData* data =
+ static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this));
+ DCHECK(data);
+
+ IOBuffer* buf = data->buf();
+ // Read as many bytes as are available synchronously.
+ while (true) {
+ int num_bytes;
+ if (!request->Read(buf, kBufferSize, &num_bytes)) {
+ // Check whether the read failed synchronously.
+ if (!request->status().is_io_pending())
+ OnSpawnerCommandCompleted(request);
+ return;
+ }
+ if (!data->ConsumeBytesRead(num_bytes)) {
+ OnSpawnerCommandCompleted(request);
+ return;
+ }
+ }
+}
+
+void SpawnerCommunicator::OnResponseStarted(URLRequest* request) {
+ DCHECK_EQ(request, cur_request_.get());
+ SpawnerRequestData* data =
+ static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this));
+ DCHECK(data);
+
+ data->IncreaseResponseStartedCount();
+
+ if (!request->status().is_success()) {
+ OnSpawnerCommandCompleted(request);
+ return;
+ }
+
+ // Require HTTP responses to have a success status code.
+ if (request->GetResponseCode() != 200) {
+ LOG(ERROR) << "Spawner server returned bad status: "
+ << request->response_headers()->GetStatusLine();
+ data->SetResultCode(ERR_FAILED);
+ request->Cancel();
+ OnSpawnerCommandCompleted(request);
+ return;
+ }
+
+ ReadResult(request);
+}
+
+void SpawnerCommunicator::OnReadCompleted(URLRequest* request, int num_bytes) {
+ if (!cur_request_.get())
+ return;
+ DCHECK_EQ(request, cur_request_.get());
+ SpawnerRequestData* data =
+ static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this));
+ DCHECK(data);
+
+ if (data->ConsumeBytesRead(num_bytes)) {
+ // Keep reading.
+ ReadResult(request);
+ } else {
+ OnSpawnerCommandCompleted(request);
+ }
+}
+
+bool SpawnerCommunicator::StartServer(const std::string& arguments,
+ uint16* port) {
+ *port = 0;
+ // Send the start command to spawner server to start the Python test server
+ // on remote machine.
+ std::string server_return_data;
+ int result_code;
+ SendCommandAndWaitForResult("start", arguments, &result_code,
+ &server_return_data);
+ if (OK != result_code || server_return_data.empty())
+ return false;
+
+ // Check whether the data returned from spawner server is JSON-formatted.
+ base::JSONReader json_reader;
+ scoped_ptr<base::Value> value(json_reader.JsonToValue(server_return_data,
+ true, false));
+ if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+ LOG(ERROR) << "Invalid server data: " << server_return_data.c_str();
+ return false;
+ }
+
+ // Check whether spawner server returns valid data.
+ DictionaryValue* server_data = static_cast<DictionaryValue*>(value.get());
+ std::string message;
+ if (!server_data->GetString("message", &message) || message != "started") {
+ LOG(ERROR) << "Invalid message in server data: ";
+ return false;
+ }
+ int int_port;
+ if (!server_data->GetInteger("port", &int_port) || int_port <= 0 ||
+ int_port > kuint16max) {
+ LOG(ERROR) << "Invalid port value: " << int_port;
+ return false;
+ }
+ *port = static_cast<uint16>(int_port);
+ return true;
+}
+
+bool SpawnerCommunicator::StopServer() {
+ // It's OK to stop the SpawnerCommunicator without starting it. Some tests
+ // have test server on their test fixture but do not actually use it.
+ if (!is_running_)
+ return true;
+
+ // When the test is done, ask the test server spawner to kill the test server
+ // on the remote machine.
+ std::string server_return_data;
+ int result_code;
+ SendCommandAndWaitForResult("kill", "", &result_code, &server_return_data);
+ if (OK != result_code || server_return_data != "killed")
+ return false;
+ Shutdown();
+ return true;
+}
+
+} // namespace net
+
diff --git a/net/test/spawner_communicator.h b/net/test/spawner_communicator.h
new file mode 100644
index 0000000..43068f5
--- /dev/null
+++ b/net/test/spawner_communicator.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2012 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.
+
+#ifndef NET_TEST_SPAWNER_COMMUNICATOR_H_
+#define NET_TEST_SPAWNER_COMMUNICATOR_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "net/url_request/url_request.h"
+
+namespace net {
+
+class ScopedPortException;
+
+// SpawnerCommunicator communicates with a spawner server that runs on a
+// remote system.
+//
+// The test server used by unit tests is written in Python. However, Android
+// does not support running Python code, so the test server cannot run on the
+// same device running unit tests.
+//
+// The actual test server is executed on the host machine, while the unit tests
+// themselves continue running on the device. To control the test server on the
+// host machine, a second HTTP server is started, the spawner server, which
+// controls the life cycle of remote test servers. Calls to start/kill the
+// net::TestServer are then redirected to the spawner server via this spawner
+// communicator.
+//
+// Currently only three commands are supported by spawner.
+//
+// (1) Start Python test server, format is:
+// Path: "/start".
+// Method: "POST".
+// Data to server: all arguments needed to launch the Python test server, in
+// JSON format.
+// Data from server: a JSON dict includes the following two field if success,
+// "port": the port the Python test server actually listen on that.
+// "message": must be "started".
+//
+// (2) Kill Python test server, format is:
+// Path: "/kill".
+// Method: "GET".
+// Data to server: None.
+// Data from server: String "killed" returned if success.
+//
+// (3) Ping Python test server to see whether it is alive, format is:
+// Path: "/ping".
+// Method: "GET".
+// Data to server: None.
+// Data from server: String "ready" returned if success.
+//
+// The internal I/O thread is required by net stack to perform net I/O.
+// The Start/StopServer methods block the caller thread until result is
+// fetched from spawner server or timed-out.
+class SpawnerCommunicator : public net::URLRequest::Delegate {
+ public:
+ explicit SpawnerCommunicator(uint16 port);
+ ~SpawnerCommunicator();
+
+ // Starts an instance of the Python test server on the host/ machine.
+ // If successfully started, returns true, setting |*port| to the port
+ // on the local machine that can be used to communicate with the remote
+ // test server.
+ bool StartServer(const std::string& arguments,
+ uint16* port) WARN_UNUSED_RESULT;
+
+ bool StopServer() WARN_UNUSED_RESULT;
+
+ private:
+ // Starts the IO thread. Called on the user thread.
+ void StartIOThread();
+
+ // Shuts down the remote test server spawner. Called on the user thread.
+ void Shutdown();
+
+ // Waits for the server response on IO thread. Called on the user thread.
+ void WaitForResponse();
+
+ // Sends a command to the test server over HTTP, returning the result code
+ // |*result_code| and response data in |*data_received|, those two arguments
+ // must be not NULL, otherwise the method returns immediately without sending
+ // the |command|. If |post_data| is empty, HTTP GET will be used to send
+ // |command|. If |post_data| is non-empty, performs an HTTP POST.
+ // This method is called on the user thread.
+ void SendCommandAndWaitForResult(const std::string& command,
+ const std::string& post_data,
+ int* result_code,
+ std::string* data_received);
+
+ // Performs the command sending on the IO thread. Called on the IO thread.
+ void SendCommandAndWaitForResultOnIOThread(const std::string& command,
+ const std::string& post_data,
+ int* result_code,
+ std::string* data_received);
+
+ // URLRequest::Delegate methods. Called on the IO thread.
+ virtual void OnResponseStarted(URLRequest* request) OVERRIDE;
+ virtual void OnReadCompleted(URLRequest* request, int num_bytes) OVERRIDE;
+
+ // Reads Result from the response. Called on the IO thread.
+ void ReadResult(URLRequest* request);
+
+ // Called on the IO thread upon completion of the spawner command.
+ void OnSpawnerCommandCompleted(URLRequest* request);
+
+ // Callback on the IO thread for time-out task of request with id |id|.
+ void OnTimeout(int id);
+
+ // A thread to communicate with test_spawner server.
+ base::Thread io_thread_;
+
+ // WaitableEvent to notify whether the communication is done.
+ base::WaitableEvent event_;
+
+ // The local port used to communicate with the TestServer spawner. This is
+ // used to control the startup and shutdown of the Python TestServer running
+ // on the remote machine. On Android, this port will be redirected to the
+ // same port on the host machine.
+ uint16 port_;
+
+ // Helper to add |port_| to the list of the globally explicitly allowed ports.
+ scoped_ptr<ScopedPortException> allowed_port_;
+
+ // The next ID to use for |cur_request_| (monotonically increasing).
+ int next_id_;
+
+ // Factory for creating the time-out task. This takes care of revoking
+ // outstanding tasks when |this| is deleted.
+ base::WeakPtrFactory<SpawnerCommunicator> weak_factory_;
+
+ // The current (in progress) request, or NULL.
+ scoped_ptr<URLRequest> cur_request_;
+
+ // Only gets/sets |is_running_| on user's thread to avoid race-condition.
+ bool is_running_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpawnerCommunicator);
+};
+
+} // namespace net
+
+#endif // NET_TEST_SPAWNER_COMMUNICATOR_H_
+
diff --git a/net/test/test_server.cc b/net/test/test_server.cc
deleted file mode 100644
index 4aee7d6..0000000
--- a/net/test/test_server.cc
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright (c) 2012 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/test_server.h"
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include "build/build_config.h"
-
-#if defined(OS_MACOSX)
-#include "net/base/x509_certificate.h"
-#endif
-
-#include "base/base64.h"
-#include "base/command_line.h"
-#include "base/debug/leak_annotations.h"
-#include "base/file_util.h"
-#include "base/json/json_reader.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/path_service.h"
-#include "base/string_number_conversions.h"
-#include "base/utf_string_conversions.h"
-#include "base/values.h"
-#include "googleurl/src/gurl.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/host_resolver.h"
-#include "net/base/net_errors.h"
-#include "net/base/test_completion_callback.h"
-#include "net/base/test_root_certs.h"
-#include "net/socket/tcp_client_socket.h"
-#include "net/test/python_utils.h"
-#include "testing/platform_test.h"
-
-namespace net {
-
-TestServer::HTTPSOptions::HTTPSOptions()
- : server_certificate(CERT_OK),
- request_client_certificate(false),
- bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY),
- record_resume(false) {}
-
-TestServer::HTTPSOptions::HTTPSOptions(
- TestServer::HTTPSOptions::ServerCertificate cert)
- : server_certificate(cert),
- request_client_certificate(false),
- bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY),
- record_resume(false) {}
-
-TestServer::HTTPSOptions::~HTTPSOptions() {}
-
-FilePath TestServer::HTTPSOptions::GetCertificateFile() const {
- switch (server_certificate) {
- case CERT_OK:
- case CERT_MISMATCHED_NAME:
- return FilePath(FILE_PATH_LITERAL("ok_cert.pem"));
- case CERT_EXPIRED:
- return FilePath(FILE_PATH_LITERAL("expired_cert.pem"));
- case CERT_CHAIN_WRONG_ROOT:
- // This chain uses its own dedicated test root certificate to avoid
- // side-effects that may affect testing.
- return FilePath(FILE_PATH_LITERAL("redundant-server-chain.pem"));
- default:
- NOTREACHED();
- }
- return FilePath();
-}
-
-const char TestServer::kLocalhost[] = "127.0.0.1";
-const char TestServer::kGDataAuthToken[] = "testtoken";
-
-TestServer::TestServer(Type type,
- const std::string& host,
- const FilePath& document_root)
- : type_(type),
- started_(false),
- log_to_console_(false) {
- Init(host, document_root);
-}
-
-TestServer::TestServer(const HTTPSOptions& https_options,
- const FilePath& document_root)
- : https_options_(https_options),
- type_(TYPE_HTTPS),
- started_(false),
- log_to_console_(false) {
- const char* host = "127.0.0.1";
- if (https_options_.server_certificate ==
- TestServer::HTTPSOptions::CERT_MISMATCHED_NAME) {
- // Use a different hostname string that resolves to the same address.
- host = "localhost";
- }
-
- Init(host, document_root);
-}
-
-TestServer::~TestServer() {
- TestRootCerts* root_certs = TestRootCerts::GetInstance();
- root_certs->Clear();
- Stop();
-}
-
-bool TestServer::Start() {
- if (type_ == TYPE_HTTPS) {
- if (!LoadTestRootCert())
- return false;
- }
-
- // Get path to python server script
- FilePath testserver_path;
- if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path)) {
- LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
- return false;
- }
- testserver_path = testserver_path
- .Append(FILE_PATH_LITERAL("net"))
- .Append(FILE_PATH_LITERAL("tools"))
- .Append(FILE_PATH_LITERAL("testserver"))
- .Append(FILE_PATH_LITERAL("testserver.py"));
-
- if (!SetPythonPath())
- return false;
-
- if (!LaunchPython(testserver_path))
- return false;
-
- if (!WaitToStart()) {
- Stop();
- return false;
- }
-
- allowed_port_.reset(new ScopedPortException(host_port_pair_.port()));
-
- started_ = true;
- return true;
-}
-
-bool TestServer::Stop() {
- if (!process_handle_)
- return true;
-
- started_ = false;
-
- // First check if the process has already terminated.
- bool ret = base::WaitForSingleProcess(process_handle_, 0);
- if (!ret)
- ret = base::KillProcess(process_handle_, 1, true);
-
- if (ret) {
- base::CloseProcessHandle(process_handle_);
- process_handle_ = base::kNullProcessHandle;
- } else {
- VLOG(1) << "Kill failed?";
- }
-
- allowed_port_.reset();
-
- return ret;
-}
-
-const HostPortPair& TestServer::host_port_pair() const {
- DCHECK(started_);
- return host_port_pair_;
-}
-
-const DictionaryValue& TestServer::server_data() const {
- DCHECK(started_);
- return *server_data_;
-}
-
-std::string TestServer::GetScheme() const {
- switch (type_) {
- case TYPE_FTP:
- return "ftp";
- case TYPE_GDATA:
- case TYPE_HTTP:
- case TYPE_SYNC:
- return "http";
- case TYPE_HTTPS:
- return "https";
- case TYPE_TCP_ECHO:
- NOTREACHED();
- case TYPE_UDP_ECHO:
- NOTREACHED();
- default:
- NOTREACHED();
- }
- return std::string();
-}
-
-bool TestServer::GetAddressList(AddressList* address_list) const {
- DCHECK(address_list);
-
- scoped_ptr<HostResolver> resolver(
- CreateSystemHostResolver(HostResolver::kDefaultParallelism,
- HostResolver::kDefaultRetryAttempts,
- NULL));
- HostResolver::RequestInfo info(host_port_pair_);
- TestCompletionCallback callback;
- int rv = resolver->Resolve(info, address_list, callback.callback(), NULL,
- BoundNetLog());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- if (rv != net::OK) {
- LOG(ERROR) << "Failed to resolve hostname: " << host_port_pair_.host();
- return false;
- }
- return true;
-}
-
-GURL TestServer::GetURL(const std::string& path) const {
- return GURL(GetScheme() + "://" + host_port_pair_.ToString() +
- "/" + path);
-}
-
-GURL TestServer::GetURLWithUser(const std::string& path,
- const std::string& user) const {
- return GURL(GetScheme() + "://" + user + "@" +
- host_port_pair_.ToString() +
- "/" + path);
-}
-
-GURL TestServer::GetURLWithUserAndPassword(const std::string& path,
- const std::string& user,
- const std::string& password) const {
- return GURL(GetScheme() + "://" + user + ":" + password +
- "@" + host_port_pair_.ToString() +
- "/" + path);
-}
-
-// static
-bool TestServer::GetFilePathWithReplacements(
- const std::string& original_file_path,
- const std::vector<StringPair>& text_to_replace,
- std::string* replacement_path) {
- std::string new_file_path = original_file_path;
- bool first_query_parameter = true;
- const std::vector<StringPair>::const_iterator end = text_to_replace.end();
- for (std::vector<StringPair>::const_iterator it = text_to_replace.begin();
- it != end;
- ++it) {
- const std::string& old_text = it->first;
- const std::string& new_text = it->second;
- std::string base64_old;
- std::string base64_new;
- if (!base::Base64Encode(old_text, &base64_old))
- return false;
- if (!base::Base64Encode(new_text, &base64_new))
- return false;
- if (first_query_parameter) {
- new_file_path += "?";
- first_query_parameter = false;
- } else {
- new_file_path += "&";
- }
- new_file_path += "replace_text=";
- new_file_path += base64_old;
- new_file_path += ":";
- new_file_path += base64_new;
- }
-
- *replacement_path = new_file_path;
- return true;
-}
-
-void TestServer::Init(const std::string& host, const FilePath& document_root) {
- // At this point, the port that the testserver will listen on is unknown.
- // The testserver will listen on an ephemeral port, and write the port
- // number out over a pipe that this TestServer object will read from. Once
- // that is complete, the host_port_pair_ will contain the actual port.
- host_port_pair_ = HostPortPair(host, 0);
- process_handle_ = base::kNullProcessHandle;
-
- FilePath src_dir;
- PathService::Get(base::DIR_SOURCE_ROOT, &src_dir);
-
- document_root_ = src_dir.Append(document_root);
-
- certificates_dir_ = src_dir.Append(FILE_PATH_LITERAL("net"))
- .Append(FILE_PATH_LITERAL("data"))
- .Append(FILE_PATH_LITERAL("ssl"))
- .Append(FILE_PATH_LITERAL("certificates"));
-
- // TODO(battre) Remove this after figuring out why the TestServer is flaky.
- // http://crbug.com/96594
- log_to_console_ = true;
-}
-
-bool TestServer::SetPythonPath() {
- FilePath third_party_dir;
- if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
- LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
- return false;
- }
- third_party_dir = third_party_dir.AppendASCII("third_party");
-
- // For simplejson. (simplejson, unlike all the other python modules
- // we include, doesn't have an extra 'simplejson' directory, so we
- // need to include its parent directory, i.e. third_party_dir).
- AppendToPythonPath(third_party_dir);
-
- AppendToPythonPath(third_party_dir.AppendASCII("tlslite"));
- AppendToPythonPath(
- third_party_dir.AppendASCII("pyftpdlib").AppendASCII("src"));
-
- // Locate the Python code generated by the protocol buffers compiler.
- FilePath pyproto_dir;
- if (!GetPyProtoPath(&pyproto_dir)) {
- LOG(WARNING) << "Cannot find pyproto dir for generated code. "
- << "Testserver features that rely on it will not work";
- return true;
- }
-
- AppendToPythonPath(pyproto_dir);
- AppendToPythonPath(pyproto_dir.AppendASCII("sync").AppendASCII("protocol"));
- AppendToPythonPath(pyproto_dir.AppendASCII("chrome")
- .AppendASCII("browser")
- .AppendASCII("policy")
- .AppendASCII("proto"));
-
- return true;
-}
-
-bool TestServer::ParseServerData(const std::string& server_data) {
- VLOG(1) << "Server data: " << server_data;
- base::JSONReader json_reader;
- scoped_ptr<Value> value(json_reader.JsonToValue(server_data, true, false));
- if (!value.get() ||
- !value->IsType(Value::TYPE_DICTIONARY)) {
- LOG(ERROR) << "Could not parse server data: "
- << json_reader.GetErrorMessage();
- return false;
- }
- server_data_.reset(static_cast<DictionaryValue*>(value.release()));
- int port = 0;
- if (!server_data_->GetInteger("port", &port)) {
- LOG(ERROR) << "Could not find port value";
- return false;
- }
- if ((port <= 0) || (port > kuint16max)) {
- LOG(ERROR) << "Invalid port value: " << port;
- return false;
- }
- host_port_pair_.set_port(port);
- return true;
-}
-
-FilePath TestServer::GetRootCertificatePath() const {
- return certificates_dir_.AppendASCII("root_ca_cert.crt");
-}
-
-bool TestServer::LoadTestRootCert() {
- TestRootCerts* root_certs = TestRootCerts::GetInstance();
- return root_certs->AddFromFile(GetRootCertificatePath());
-}
-
-bool TestServer::AddCommandLineArguments(CommandLine* command_line) const {
- command_line->AppendArg("--host=" + host_port_pair_.host());
- command_line->AppendArg("--port=" +
- base::IntToString(host_port_pair_.port()));
- command_line->AppendArgNative(FILE_PATH_LITERAL("--data-dir=") +
- document_root_.value());
-
- if (VLOG_IS_ON(1) || log_to_console_) {
- command_line->AppendArg("--log-to-console");
- }
-
- if (type_ == TYPE_FTP) {
- command_line->AppendArg("-f");
- } else if (type_ == TYPE_SYNC) {
- command_line->AppendArg("--sync");
- } else if (type_ == TYPE_TCP_ECHO) {
- command_line->AppendArg("--tcp-echo");
- } else if (type_ == TYPE_UDP_ECHO) {
- command_line->AppendArg("--udp-echo");
- } else if (type_ == TYPE_GDATA) {
- // --auth-token will be used in tests for chrome/browser/chromeos/gdata.
- command_line->AppendArg(std::string("--auth-token=") + kGDataAuthToken);
- } else if (type_ == TYPE_HTTPS) {
- FilePath certificate_path(certificates_dir_);
- certificate_path = certificate_path.Append(
- https_options_.GetCertificateFile());
- if (!file_util::PathExists(certificate_path)) {
- LOG(ERROR) << "Certificate path " << certificate_path.value()
- << " doesn't exist. Can't launch https server.";
- return false;
- }
- command_line->AppendArgNative(FILE_PATH_LITERAL("--https=") +
- certificate_path.value());
-
- if (https_options_.request_client_certificate)
- command_line->AppendArg("--ssl-client-auth");
-
- for (std::vector<FilePath>::const_iterator it =
- https_options_.client_authorities.begin();
- it != https_options_.client_authorities.end(); ++it) {
- if (!file_util::PathExists(*it)) {
- LOG(ERROR) << "Client authority path " << it->value()
- << " doesn't exist. Can't launch https server.";
- return false;
- }
-
- command_line->AppendArgNative(FILE_PATH_LITERAL("--ssl-client-ca=") +
- it->value());
- }
-
- const std::string kBulkCipherSwitch("--ssl-bulk-cipher");
- if (https_options_.bulk_ciphers & HTTPSOptions::BULK_CIPHER_RC4)
- command_line->AppendArg(kBulkCipherSwitch + "=rc4");
- if (https_options_.bulk_ciphers & HTTPSOptions::BULK_CIPHER_AES128)
- command_line->AppendArg(kBulkCipherSwitch + "=aes128");
- if (https_options_.bulk_ciphers & HTTPSOptions::BULK_CIPHER_AES256)
- command_line->AppendArg(kBulkCipherSwitch + "=aes256");
- if (https_options_.bulk_ciphers & HTTPSOptions::BULK_CIPHER_3DES)
- command_line->AppendArg(kBulkCipherSwitch + "=3des");
-
- if (https_options_.record_resume)
- command_line->AppendArg("--https-record-resume");
- }
-
- return true;
-}
-
-} // namespace net
diff --git a/net/test/test_server.h b/net/test/test_server.h
index 5c04a40..a3e0da0 100644
--- a/net/test/test_server.h
+++ b/net/test/test_server.h
@@ -6,230 +6,23 @@
#define NET_TEST_TEST_SERVER_H_
#pragma once
-#include <string>
-#include <utility>
-#include <vector>
-
#include "build/build_config.h"
-#include "base/compiler_specific.h"
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/process_util.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/net_util.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_handle.h"
+#if defined(OS_ANDROID)
+#include "net/test/remote_test_server.h"
+#else
+#include "net/test/local_test_server.h"
#endif
-class CommandLine;
-class GURL;
-
-namespace base {
-class DictionaryValue;
-}
-
namespace net {
-class AddressList;
-
-// This object bounds the lifetime of an external python-based HTTP/FTP server
-// that can provide various responses useful for testing.
-class TestServer {
- public:
- typedef std::pair<std::string, std::string> StringPair;
-
- enum Type {
- TYPE_FTP,
- TYPE_GDATA,
- TYPE_HTTP,
- TYPE_HTTPS,
- TYPE_SYNC,
- TYPE_TCP_ECHO,
- TYPE_UDP_ECHO,
- };
-
- // Container for various options to control how the HTTPS server is
- // initialized.
- struct HTTPSOptions {
- enum ServerCertificate {
- CERT_OK,
- CERT_MISMATCHED_NAME,
- CERT_EXPIRED,
- // Cross-signed certificate to test PKIX path building. Contains an
- // intermediate cross-signed by an unknown root, while the client (via
- // TestRootStore) is expected to have a self-signed version of the
- // intermediate.
- CERT_CHAIN_WRONG_ROOT,
- };
-
- // Bitmask of bulk encryption algorithms that the test server supports
- // and that can be selectively enabled or disabled.
- enum BulkCipher {
- // Special value used to indicate that any algorithm the server supports
- // is acceptable. Preferred over explicitly OR-ing all ciphers.
- BULK_CIPHER_ANY = 0,
-
- BULK_CIPHER_RC4 = (1 << 0),
- BULK_CIPHER_AES128 = (1 << 1),
- BULK_CIPHER_AES256 = (1 << 2),
-
- // NOTE: 3DES support in the Python test server has external
- // dependencies and not be available on all machines. Clients may not
- // be able to connect if only 3DES is specified.
- BULK_CIPHER_3DES = (1 << 3),
- };
-
- // Initialize a new HTTPSOptions using CERT_OK as the certificate.
- HTTPSOptions();
-
- // Initialize a new HTTPSOptions that will use the specified certificate.
- explicit HTTPSOptions(ServerCertificate cert);
- ~HTTPSOptions();
-
- // Returns the relative filename of the file that contains the
- // |server_certificate|.
- FilePath GetCertificateFile() const;
-
- // The certificate to use when serving requests.
- ServerCertificate server_certificate;
-
- // True if a CertificateRequest should be sent to the client during
- // handshaking.
- bool request_client_certificate;
-
- // If |request_client_certificate| is true, an optional list of files,
- // each containing a single, PEM-encoded X.509 certificates. The subject
- // from each certificate will be added to the certificate_authorities
- // field of the CertificateRequest.
- std::vector<FilePath> client_authorities;
-
- // A bitwise-OR of BulkCipher that should be used by the
- // HTTPS server, or BULK_CIPHER_ANY to indicate that all implemented
- // ciphers are acceptable.
- int bulk_ciphers;
-
- // If true, pass the --https-record-resume argument to testserver.py which
- // causes it to log session cache actions and echo the log on
- // /ssl-session-cache.
- bool record_resume;
- };
-
- // Pass as the 'host' parameter during construction to server on 127.0.0.1
- static const char kLocalhost[];
-
- // The auth token to be used for TYPE_GDATA server.
- static const char kGDataAuthToken[];
-
- // Initialize a TestServer listening on the specified host (IP or hostname).
- TestServer(Type type, const std::string& host, const FilePath& document_root);
-
- // Initialize a HTTPS TestServer with a specific set of HTTPSOptions.
- TestServer(const HTTPSOptions& https_options,
- const FilePath& document_root);
-
- ~TestServer();
-
- bool Start() WARN_UNUSED_RESULT;
-
- // Stop the server started by Start().
- bool Stop();
-
- const FilePath& document_root() const { return document_root_; }
- const HostPortPair& host_port_pair() const;
- const base::DictionaryValue& server_data() const;
- std::string GetScheme() const;
- bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT;
-
- GURL GetURL(const std::string& path) const;
-
- GURL GetURLWithUser(const std::string& path,
- const std::string& user) const;
-
- GURL GetURLWithUserAndPassword(const std::string& path,
- const std::string& user,
- const std::string& password) const;
-
- static bool GetFilePathWithReplacements(
- const std::string& original_path,
- const std::vector<StringPair>& text_to_replace,
- std::string* replacement_path);
-
- private:
- void Init(const std::string& host, const FilePath& document_root);
-
- // Modify PYTHONPATH to contain libraries we need.
- bool SetPythonPath() WARN_UNUSED_RESULT;
-
- // Launches the Python test server. Returns true on success.
- bool LaunchPython(const FilePath& testserver_path) WARN_UNUSED_RESULT;
-
- // Waits for the server to start. Returns true on success.
- bool WaitToStart() WARN_UNUSED_RESULT;
-
- // Parses the server data read from the test server. Returns true
- // on success.
- bool ParseServerData(const std::string& server_data) WARN_UNUSED_RESULT;
-
- // Returns path to the root certificate.
- FilePath GetRootCertificatePath() const;
-
- // Load the test root cert, if it hasn't been loaded yet.
- bool LoadTestRootCert() WARN_UNUSED_RESULT;
-
- // Add the command line arguments for the Python test server to
- // |command_line|. Return true on success.
- bool AddCommandLineArguments(CommandLine* command_line) const;
-
- // Document root of the test server.
- FilePath document_root_;
-
- // Directory that contains the SSL certificates.
- FilePath certificates_dir_;
-
- // Address the test server listens on.
- HostPortPair host_port_pair_;
-
- // Holds the data sent from the server (e.g., port number).
- scoped_ptr<base::DictionaryValue> server_data_;
-
- // Handle of the Python process running the test server.
- base::ProcessHandle process_handle_;
-
- scoped_ptr<net::ScopedPortException> allowed_port_;
-
-#if defined(OS_WIN)
- // JobObject used to clean up orphaned child processes.
- base::win::ScopedHandle job_handle_;
-
- // The pipe file handle we read from.
- base::win::ScopedHandle child_read_fd_;
-
- // The pipe file handle the child and we write to.
- base::win::ScopedHandle child_write_fd_;
-#endif
-
-#if defined(OS_POSIX)
- // The file descriptor the child writes to when it starts.
- int child_fd_;
- file_util::ScopedFD child_fd_closer_;
+#if defined(OS_ANDROID)
+typedef RemoteTestServer TestServer;
+#else
+typedef LocalTestServer TestServer;
#endif
- // If |type_| is TYPE_HTTPS, the TLS settings to use for the test server.
- HTTPSOptions https_options_;
-
- Type type_;
-
- // Has the server been started?
- bool started_;
-
- // Enables logging of the server to the console.
- bool log_to_console_;
-
- DISALLOW_COPY_AND_ASSIGN(TestServer);
-};
-
} // namespace net
#endif // NET_TEST_TEST_SERVER_H_
+