summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/extension_apitest.cc1
-rw-r--r--chrome/browser/printing/printing_layout_uitest.cc1
-rw-r--r--chrome/browser/ui/webui/web_ui_browsertest.cc1
-rw-r--r--chrome/test/perf/rendering/latency_tests.cc1
-rw-r--r--content/common/net/url_fetcher_impl_unittest.cc3
-rw-r--r--net/net.gyp23
-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
18 files changed, 1680 insertions, 658 deletions
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index e03b1eb..708c3f3 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -14,6 +14,7 @@
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
+#include "net/base/net_util.h"
namespace {
diff --git a/chrome/browser/printing/printing_layout_uitest.cc b/chrome/browser/printing/printing_layout_uitest.cc
index 10cee7e..29c7d15 100644
--- a/chrome/browser/printing/printing_layout_uitest.cc
+++ b/chrome/browser/printing/printing_layout_uitest.cc
@@ -5,6 +5,7 @@
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
+#include "base/process_util.h"
#include "base/string_util.h"
#include "base/test/test_file_util.h"
#include "base/threading/simple_thread.h"
diff --git a/chrome/browser/ui/webui/web_ui_browsertest.cc b/chrome/browser/ui/webui/web_ui_browsertest.cc
index 114ffd6..ca7483c 100644
--- a/chrome/browser/ui/webui/web_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/web_ui_browsertest.cc
@@ -28,6 +28,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_message_handler.h"
+#include "net/base/net_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest-spi.h"
#include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/test/perf/rendering/latency_tests.cc b/chrome/test/perf/rendering/latency_tests.cc
index b299bc6..b275a6e 100644
--- a/chrome/test/perf/rendering/latency_tests.cc
+++ b/chrome/test/perf/rendering/latency_tests.cc
@@ -24,6 +24,7 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
+#include "net/base/net_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
diff --git a/content/common/net/url_fetcher_impl_unittest.cc b/content/common/net/url_fetcher_impl_unittest.cc
index 43439a0..354d9c0 100644
--- a/content/common/net/url_fetcher_impl_unittest.cc
+++ b/content/common/net/url_fetcher_impl_unittest.cc
@@ -4,7 +4,10 @@
#include "content/common/net/url_fetcher_impl.h"
+#include <string>
+
#include "base/bind.h"
+#include "base/file_util.h"
#include "base/message_loop_proxy.h"
#include "base/scoped_temp_dir.h"
#include "base/synchronization/waitable_event.h"
diff --git a/net/net.gyp b/net/net.gyp
index e791245..2260ae8 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -1531,11 +1531,18 @@
'proxy/proxy_config_service_common_unittest.h',
'socket/socket_test_util.cc',
'socket/socket_test_util.h',
+ 'test/base_test_server.cc',
+ 'test/base_test_server.h',
+ 'test/local_test_server_posix.cc',
+ 'test/local_test_server_win.cc',
+ 'test/local_test_server.cc',
+ 'test/local_test_server.h',
'test/python_utils.cc',
- 'test/python_utils.h',
- 'test/test_server.cc',
- 'test/test_server_posix.cc',
- 'test/test_server_win.cc',
+ 'test/python_utils.h',
+ 'test/remote_test_server.cc',
+ 'test/remote_test_server.h',
+ 'test/spawner_communicator.cc',
+ 'test/spawner_communicator.h',
'test/test_server.h',
'url_request/url_request_test_util.cc',
'url_request/url_request_test_util.h',
@@ -1571,6 +1578,14 @@
}],
],
}],
+ ['OS != "android"', {
+ 'sources!': [
+ 'test/remote_test_server.cc',
+ 'test/remote_test_server.h',
+ 'test/spawner_communicator.cc',
+ 'test/spawner_communicator.h',
+ ],
+ }],
],
},
{
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_
+