1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/test/base_test_server.h"
10 #include "base/base64.h"
11 #include "base/file_util.h"
12 #include "base/json/json_reader.h"
13 #include "base/logging.h"
14 #include "base/path_service.h"
15 #include "base/values.h"
16 #include "googleurl/src/gurl.h"
17 #include "net/base/address_list.h"
18 #include "net/base/host_port_pair.h"
19 #include "net/base/host_resolver.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/net_log.h"
22 #include "net/base/net_util.h"
23 #include "net/base/test_completion_callback.h"
24 #include "net/base/test_root_certs.h"
30 std::string
GetHostname(BaseTestServer::Type type
,
31 const BaseTestServer::SSLOptions
& options
) {
32 if (BaseTestServer::UsingSSL(type
) &&
33 options
.server_certificate
==
34 BaseTestServer::SSLOptions::CERT_MISMATCHED_NAME
) {
35 // Return a different hostname string that resolves to the same hostname.
39 // Use the 127.0.0.1 as default.
40 return BaseTestServer::kLocalhost
;
43 void GetCiphersList(int cipher
, base::ListValue
* values
) {
44 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_RC4
)
45 values
->Append(base::Value::CreateStringValue("rc4"));
46 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_AES128
)
47 values
->Append(base::Value::CreateStringValue("aes128"));
48 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_AES256
)
49 values
->Append(base::Value::CreateStringValue("aes256"));
50 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_3DES
)
51 values
->Append(base::Value::CreateStringValue("3des"));
56 BaseTestServer::SSLOptions::SSLOptions()
57 : server_certificate(CERT_OK
),
59 request_client_certificate(false),
60 bulk_ciphers(SSLOptions::BULK_CIPHER_ANY
),
62 tls_intolerant(TLS_INTOLERANT_NONE
) {}
64 BaseTestServer::SSLOptions::SSLOptions(
65 BaseTestServer::SSLOptions::ServerCertificate cert
)
66 : server_certificate(cert
),
67 request_client_certificate(false),
68 bulk_ciphers(SSLOptions::BULK_CIPHER_ANY
),
70 tls_intolerant(TLS_INTOLERANT_NONE
) {}
72 BaseTestServer::SSLOptions::~SSLOptions() {}
74 FilePath
BaseTestServer::SSLOptions::GetCertificateFile() const {
75 switch (server_certificate
) {
77 case CERT_MISMATCHED_NAME
:
78 return FilePath(FILE_PATH_LITERAL("ok_cert.pem"));
80 return FilePath(FILE_PATH_LITERAL("expired_cert.pem"));
81 case CERT_CHAIN_WRONG_ROOT
:
82 // This chain uses its own dedicated test root certificate to avoid
83 // side-effects that may affect testing.
84 return FilePath(FILE_PATH_LITERAL("redundant-server-chain.pem"));
93 std::string
BaseTestServer::SSLOptions::GetOCSPArgument() const {
94 if (server_certificate
!= CERT_AUTO
)
97 switch (ocsp_status
) {
104 case OCSP_UNAUTHORIZED
:
105 return "unauthorized";
114 const char BaseTestServer::kLocalhost
[] = "127.0.0.1";
116 BaseTestServer::BaseTestServer(Type type
, const std::string
& host
)
119 log_to_console_(false) {
123 BaseTestServer::BaseTestServer(Type type
, const SSLOptions
& ssl_options
)
124 : ssl_options_(ssl_options
),
127 log_to_console_(false) {
128 DCHECK(UsingSSL(type
));
129 Init(GetHostname(type
, ssl_options
));
132 BaseTestServer::~BaseTestServer() {}
134 const HostPortPair
& BaseTestServer::host_port_pair() const {
136 return host_port_pair_
;
139 const DictionaryValue
& BaseTestServer::server_data() const {
141 DCHECK(server_data_
.get());
142 return *server_data_
;
145 std::string
BaseTestServer::GetScheme() const {
163 return std::string();
166 bool BaseTestServer::GetAddressList(AddressList
* address_list
) const {
167 DCHECK(address_list
);
169 scoped_ptr
<HostResolver
> resolver(HostResolver::CreateDefaultResolver(NULL
));
170 HostResolver::RequestInfo
info(host_port_pair_
);
171 TestCompletionCallback callback
;
172 int rv
= resolver
->Resolve(info
, address_list
, callback
.callback(), NULL
,
174 if (rv
== ERR_IO_PENDING
)
175 rv
= callback
.WaitForResult();
177 LOG(ERROR
) << "Failed to resolve hostname: " << host_port_pair_
.host();
183 uint16
BaseTestServer::GetPort() {
184 return host_port_pair_
.port();
187 void BaseTestServer::SetPort(uint16 port
) {
188 host_port_pair_
.set_port(port
);
191 GURL
BaseTestServer::GetURL(const std::string
& path
) const {
192 return GURL(GetScheme() + "://" + host_port_pair_
.ToString() + "/" + path
);
195 GURL
BaseTestServer::GetURLWithUser(const std::string
& path
,
196 const std::string
& user
) const {
197 return GURL(GetScheme() + "://" + user
+ "@" + host_port_pair_
.ToString() +
201 GURL
BaseTestServer::GetURLWithUserAndPassword(const std::string
& path
,
202 const std::string
& user
,
203 const std::string
& password
) const {
204 return GURL(GetScheme() + "://" + user
+ ":" + password
+ "@" +
205 host_port_pair_
.ToString() + "/" + path
);
209 bool BaseTestServer::GetFilePathWithReplacements(
210 const std::string
& original_file_path
,
211 const std::vector
<StringPair
>& text_to_replace
,
212 std::string
* replacement_path
) {
213 std::string new_file_path
= original_file_path
;
214 bool first_query_parameter
= true;
215 const std::vector
<StringPair
>::const_iterator end
= text_to_replace
.end();
216 for (std::vector
<StringPair
>::const_iterator it
= text_to_replace
.begin();
219 const std::string
& old_text
= it
->first
;
220 const std::string
& new_text
= it
->second
;
221 std::string base64_old
;
222 std::string base64_new
;
223 if (!base::Base64Encode(old_text
, &base64_old
))
225 if (!base::Base64Encode(new_text
, &base64_new
))
227 if (first_query_parameter
) {
228 new_file_path
+= "?";
229 first_query_parameter
= false;
231 new_file_path
+= "&";
233 new_file_path
+= "replace_text=";
234 new_file_path
+= base64_old
;
235 new_file_path
+= ":";
236 new_file_path
+= base64_new
;
239 *replacement_path
= new_file_path
;
243 void BaseTestServer::Init(const std::string
& host
) {
244 host_port_pair_
= HostPortPair(host
, 0);
246 // TODO(battre) Remove this after figuring out why the TestServer is flaky.
247 // http://crbug.com/96594
248 log_to_console_
= true;
251 void BaseTestServer::SetResourcePath(const FilePath
& document_root
,
252 const FilePath
& certificates_dir
) {
253 // This method shouldn't get called twice.
254 DCHECK(certificates_dir_
.empty());
255 document_root_
= document_root
;
256 certificates_dir_
= certificates_dir
;
257 DCHECK(!certificates_dir_
.empty());
260 bool BaseTestServer::ParseServerData(const std::string
& server_data
) {
261 VLOG(1) << "Server data: " << server_data
;
262 base::JSONReader json_reader
;
263 scoped_ptr
<Value
> value(json_reader
.ReadToValue(server_data
));
264 if (!value
.get() || !value
->IsType(Value::TYPE_DICTIONARY
)) {
265 LOG(ERROR
) << "Could not parse server data: "
266 << json_reader
.GetErrorMessage();
270 server_data_
.reset(static_cast<DictionaryValue
*>(value
.release()));
272 if (!server_data_
->GetInteger("port", &port
)) {
273 LOG(ERROR
) << "Could not find port value";
276 if ((port
<= 0) || (port
> kuint16max
)) {
277 LOG(ERROR
) << "Invalid port value: " << port
;
280 host_port_pair_
.set_port(port
);
285 bool BaseTestServer::LoadTestRootCert() const {
286 TestRootCerts
* root_certs
= TestRootCerts::GetInstance();
290 // Should always use absolute path to load the root certificate.
291 FilePath root_certificate_path
= certificates_dir_
;
292 if (!certificates_dir_
.IsAbsolute()) {
294 if (!PathService::Get(base::DIR_SOURCE_ROOT
, &src_dir
))
296 root_certificate_path
= src_dir
.Append(certificates_dir_
);
299 return root_certs
->AddFromFile(
300 root_certificate_path
.AppendASCII("root_ca_cert.crt"));
303 bool BaseTestServer::SetupWhenServerStarted() {
304 DCHECK(host_port_pair_
.port());
306 if (UsingSSL(type_
) && !LoadTestRootCert())
310 allowed_port_
.reset(new ScopedPortException(host_port_pair_
.port()));
314 void BaseTestServer::CleanUpWhenStoppingServer() {
315 TestRootCerts
* root_certs
= TestRootCerts::GetInstance();
318 host_port_pair_
.set_port(0);
319 allowed_port_
.reset();
323 // Generates a dictionary of arguments to pass to the Python test server via
324 // the test server spawner, in the form of
325 // { argument-name: argument-value, ... }
326 // Returns false if an invalid configuration is specified.
327 bool BaseTestServer::GenerateArguments(base::DictionaryValue
* arguments
) const {
330 arguments
->SetString("host", host_port_pair_
.host());
331 arguments
->SetInteger("port", host_port_pair_
.port());
332 arguments
->SetString("data-dir", document_root_
.value());
334 if (VLOG_IS_ON(1) || log_to_console_
)
335 arguments
->Set("log-to-console", base::Value::CreateNullValue());
337 if (UsingSSL(type_
)) {
338 // Check the certificate arguments of the HTTPS server.
339 FilePath
certificate_path(certificates_dir_
);
340 FilePath
certificate_file(ssl_options_
.GetCertificateFile());
341 if (!certificate_file
.value().empty()) {
342 certificate_path
= certificate_path
.Append(certificate_file
);
343 if (certificate_path
.IsAbsolute() &&
344 !file_util::PathExists(certificate_path
)) {
345 LOG(ERROR
) << "Certificate path " << certificate_path
.value()
346 << " doesn't exist. Can't launch https server.";
349 arguments
->SetString("cert-and-key-file", certificate_path
.value());
352 // Check the client certificate related arguments.
353 if (ssl_options_
.request_client_certificate
)
354 arguments
->Set("ssl-client-auth", base::Value::CreateNullValue());
355 scoped_ptr
<base::ListValue
> ssl_client_certs(new base::ListValue());
357 std::vector
<FilePath
>::const_iterator it
;
358 for (it
= ssl_options_
.client_authorities
.begin();
359 it
!= ssl_options_
.client_authorities
.end(); ++it
) {
360 if (it
->IsAbsolute() && !file_util::PathExists(*it
)) {
361 LOG(ERROR
) << "Client authority path " << it
->value()
362 << " doesn't exist. Can't launch https server.";
365 ssl_client_certs
->Append(base::Value::CreateStringValue(it
->value()));
368 if (ssl_client_certs
->GetSize())
369 arguments
->Set("ssl-client-ca", ssl_client_certs
.release());
372 if (type_
== TYPE_HTTPS
) {
373 arguments
->Set("https", base::Value::CreateNullValue());
375 std::string ocsp_arg
= ssl_options_
.GetOCSPArgument();
376 if (!ocsp_arg
.empty())
377 arguments
->SetString("ocsp", ocsp_arg
);
379 // Check bulk cipher argument.
380 scoped_ptr
<base::ListValue
> bulk_cipher_values(new base::ListValue());
381 GetCiphersList(ssl_options_
.bulk_ciphers
, bulk_cipher_values
.get());
382 if (bulk_cipher_values
->GetSize())
383 arguments
->Set("ssl-bulk-cipher", bulk_cipher_values
.release());
384 if (ssl_options_
.record_resume
)
385 arguments
->Set("https-record-resume", base::Value::CreateNullValue());
386 if (ssl_options_
.tls_intolerant
!= SSLOptions::TLS_INTOLERANT_NONE
) {
387 arguments
->Set("tls-intolerant",
388 base::Value::CreateIntegerValue(ssl_options_
.tls_intolerant
));
392 return GenerateAdditionalArguments(arguments
);
395 bool BaseTestServer::GenerateAdditionalArguments(
396 base::DictionaryValue
* arguments
) const {