diff options
Diffstat (limited to 'net/test/base_test_server.cc')
-rw-r--r-- | net/test/base_test_server.cc | 358 |
1 files changed, 358 insertions, 0 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 + |