1 // Copyright 2013 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/spawned_test_server/base_test_server.h"
10 #include "base/base64.h"
11 #include "base/files/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 "net/base/address_list.h"
17 #include "net/base/host_port_pair.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/net_log.h"
20 #include "net/base/net_util.h"
21 #include "net/base/test_completion_callback.h"
22 #include "net/cert/test_root_certs.h"
23 #include "net/dns/host_resolver.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 std::string
GetClientCertType(SSLClientCertType type
) {
45 case CLIENT_CERT_RSA_SIGN
:
47 case CLIENT_CERT_DSS_SIGN
:
49 case CLIENT_CERT_ECDSA_SIGN
:
57 void GetKeyExchangesList(int key_exchange
, base::ListValue
* values
) {
58 if (key_exchange
& BaseTestServer::SSLOptions::KEY_EXCHANGE_RSA
)
59 values
->Append(new base::StringValue("rsa"));
60 if (key_exchange
& BaseTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA
)
61 values
->Append(new base::StringValue("dhe_rsa"));
64 void GetCiphersList(int cipher
, base::ListValue
* values
) {
65 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_RC4
)
66 values
->Append(new base::StringValue("rc4"));
67 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_AES128
)
68 values
->Append(new base::StringValue("aes128"));
69 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_AES256
)
70 values
->Append(new base::StringValue("aes256"));
71 if (cipher
& BaseTestServer::SSLOptions::BULK_CIPHER_3DES
)
72 values
->Append(new base::StringValue("3des"));
75 base::StringValue
* GetTLSIntoleranceType(
76 BaseTestServer::SSLOptions::TLSIntoleranceType type
) {
78 case BaseTestServer::SSLOptions::TLS_INTOLERANCE_ALERT
:
79 return new base::StringValue("alert");
80 case BaseTestServer::SSLOptions::TLS_INTOLERANCE_CLOSE
:
81 return new base::StringValue("close");
82 case BaseTestServer::SSLOptions::TLS_INTOLERANCE_RESET
:
83 return new base::StringValue("reset");
86 return new base::StringValue("");
92 BaseTestServer::SSLOptions::SSLOptions()
93 : server_certificate(CERT_OK
),
96 request_client_certificate(false),
97 key_exchanges(SSLOptions::KEY_EXCHANGE_ANY
),
98 bulk_ciphers(SSLOptions::BULK_CIPHER_ANY
),
100 tls_intolerant(TLS_INTOLERANT_NONE
),
101 tls_intolerance_type(TLS_INTOLERANCE_ALERT
),
102 fallback_scsv_enabled(false),
103 staple_ocsp_response(false),
105 disable_session_cache(false) {
108 BaseTestServer::SSLOptions::SSLOptions(
109 BaseTestServer::SSLOptions::ServerCertificate cert
)
110 : server_certificate(cert
),
111 ocsp_status(OCSP_OK
),
113 request_client_certificate(false),
114 key_exchanges(SSLOptions::KEY_EXCHANGE_ANY
),
115 bulk_ciphers(SSLOptions::BULK_CIPHER_ANY
),
116 record_resume(false),
117 tls_intolerant(TLS_INTOLERANT_NONE
),
118 tls_intolerance_type(TLS_INTOLERANCE_ALERT
),
119 fallback_scsv_enabled(false),
120 staple_ocsp_response(false),
122 disable_session_cache(false) {
125 BaseTestServer::SSLOptions::~SSLOptions() {}
127 base::FilePath
BaseTestServer::SSLOptions::GetCertificateFile() const {
128 switch (server_certificate
) {
130 case CERT_MISMATCHED_NAME
:
131 return base::FilePath(FILE_PATH_LITERAL("ok_cert.pem"));
133 return base::FilePath(FILE_PATH_LITERAL("expired_cert.pem"));
134 case CERT_CHAIN_WRONG_ROOT
:
135 // This chain uses its own dedicated test root certificate to avoid
136 // side-effects that may affect testing.
137 return base::FilePath(FILE_PATH_LITERAL("redundant-server-chain.pem"));
139 return base::FilePath();
143 return base::FilePath();
146 std::string
BaseTestServer::SSLOptions::GetOCSPArgument() const {
147 if (server_certificate
!= CERT_AUTO
)
148 return std::string();
150 switch (ocsp_status
) {
157 case OCSP_UNAUTHORIZED
:
158 return "unauthorized";
163 return std::string();
167 const char BaseTestServer::kLocalhost
[] = "127.0.0.1";
169 BaseTestServer::BaseTestServer(Type type
, const std::string
& host
)
172 log_to_console_(false),
173 ws_basic_auth_(false) {
177 BaseTestServer::BaseTestServer(Type type
, const SSLOptions
& ssl_options
)
178 : ssl_options_(ssl_options
),
181 log_to_console_(false),
182 ws_basic_auth_(false) {
183 DCHECK(UsingSSL(type
));
184 Init(GetHostname(type
, ssl_options
));
187 BaseTestServer::~BaseTestServer() {}
189 const HostPortPair
& BaseTestServer::host_port_pair() const {
191 return host_port_pair_
;
194 const base::DictionaryValue
& BaseTestServer::server_data() const {
196 DCHECK(server_data_
.get());
197 return *server_data_
;
200 std::string
BaseTestServer::GetScheme() const {
217 return std::string();
220 bool BaseTestServer::GetAddressList(AddressList
* address_list
) const {
221 DCHECK(address_list
);
223 scoped_ptr
<HostResolver
> resolver(HostResolver::CreateDefaultResolver(NULL
));
224 HostResolver::RequestInfo
info(host_port_pair_
);
225 TestCompletionCallback callback
;
226 int rv
= resolver
->Resolve(info
,
232 if (rv
== ERR_IO_PENDING
)
233 rv
= callback
.WaitForResult();
235 LOG(ERROR
) << "Failed to resolve hostname: " << host_port_pair_
.host();
241 uint16
BaseTestServer::GetPort() {
242 return host_port_pair_
.port();
245 void BaseTestServer::SetPort(uint16 port
) {
246 host_port_pair_
.set_port(port
);
249 GURL
BaseTestServer::GetURL(const std::string
& path
) const {
250 return GURL(GetScheme() + "://" + host_port_pair_
.ToString() + "/" + path
);
253 GURL
BaseTestServer::GetURLWithUser(const std::string
& path
,
254 const std::string
& user
) const {
255 return GURL(GetScheme() + "://" + user
+ "@" + host_port_pair_
.ToString() +
259 GURL
BaseTestServer::GetURLWithUserAndPassword(const std::string
& path
,
260 const std::string
& user
,
261 const std::string
& password
) const {
262 return GURL(GetScheme() + "://" + user
+ ":" + password
+ "@" +
263 host_port_pair_
.ToString() + "/" + path
);
267 bool BaseTestServer::GetFilePathWithReplacements(
268 const std::string
& original_file_path
,
269 const std::vector
<StringPair
>& text_to_replace
,
270 std::string
* replacement_path
) {
271 std::string new_file_path
= original_file_path
;
272 bool first_query_parameter
= true;
273 const std::vector
<StringPair
>::const_iterator end
= text_to_replace
.end();
274 for (std::vector
<StringPair
>::const_iterator it
= text_to_replace
.begin();
277 const std::string
& old_text
= it
->first
;
278 const std::string
& new_text
= it
->second
;
279 std::string base64_old
;
280 std::string base64_new
;
281 base::Base64Encode(old_text
, &base64_old
);
282 base::Base64Encode(new_text
, &base64_new
);
283 if (first_query_parameter
) {
284 new_file_path
+= "?";
285 first_query_parameter
= false;
287 new_file_path
+= "&";
289 new_file_path
+= "replace_text=";
290 new_file_path
+= base64_old
;
291 new_file_path
+= ":";
292 new_file_path
+= base64_new
;
295 *replacement_path
= new_file_path
;
299 void BaseTestServer::Init(const std::string
& host
) {
300 host_port_pair_
= HostPortPair(host
, 0);
302 // TODO(battre) Remove this after figuring out why the TestServer is flaky.
303 // http://crbug.com/96594
304 log_to_console_
= true;
307 void BaseTestServer::SetResourcePath(const base::FilePath
& document_root
,
308 const base::FilePath
& certificates_dir
) {
309 // This method shouldn't get called twice.
310 DCHECK(certificates_dir_
.empty());
311 document_root_
= document_root
;
312 certificates_dir_
= certificates_dir
;
313 DCHECK(!certificates_dir_
.empty());
316 bool BaseTestServer::ParseServerData(const std::string
& server_data
) {
317 VLOG(1) << "Server data: " << server_data
;
318 base::JSONReader json_reader
;
319 scoped_ptr
<base::Value
> value(json_reader
.ReadToValue(server_data
));
320 if (!value
.get() || !value
->IsType(base::Value::TYPE_DICTIONARY
)) {
321 LOG(ERROR
) << "Could not parse server data: "
322 << json_reader
.GetErrorMessage();
326 server_data_
.reset(static_cast<base::DictionaryValue
*>(value
.release()));
328 if (!server_data_
->GetInteger("port", &port
)) {
329 LOG(ERROR
) << "Could not find port value";
332 if ((port
<= 0) || (port
> kuint16max
)) {
333 LOG(ERROR
) << "Invalid port value: " << port
;
336 host_port_pair_
.set_port(port
);
341 bool BaseTestServer::LoadTestRootCert() const {
342 TestRootCerts
* root_certs
= TestRootCerts::GetInstance();
346 // Should always use absolute path to load the root certificate.
347 base::FilePath root_certificate_path
= certificates_dir_
;
348 if (!certificates_dir_
.IsAbsolute()) {
349 base::FilePath src_dir
;
350 if (!PathService::Get(base::DIR_SOURCE_ROOT
, &src_dir
))
352 root_certificate_path
= src_dir
.Append(certificates_dir_
);
355 return root_certs
->AddFromFile(
356 root_certificate_path
.AppendASCII("root_ca_cert.pem"));
359 bool BaseTestServer::SetupWhenServerStarted() {
360 DCHECK(host_port_pair_
.port());
362 if (UsingSSL(type_
) && !LoadTestRootCert())
366 allowed_port_
.reset(new ScopedPortException(host_port_pair_
.port()));
370 void BaseTestServer::CleanUpWhenStoppingServer() {
371 TestRootCerts
* root_certs
= TestRootCerts::GetInstance();
374 host_port_pair_
.set_port(0);
375 allowed_port_
.reset();
379 // Generates a dictionary of arguments to pass to the Python test server via
380 // the test server spawner, in the form of
381 // { argument-name: argument-value, ... }
382 // Returns false if an invalid configuration is specified.
383 bool BaseTestServer::GenerateArguments(base::DictionaryValue
* arguments
) const {
386 arguments
->SetString("host", host_port_pair_
.host());
387 arguments
->SetInteger("port", host_port_pair_
.port());
388 arguments
->SetString("data-dir", document_root_
.value());
390 if (VLOG_IS_ON(1) || log_to_console_
)
391 arguments
->Set("log-to-console", base::Value::CreateNullValue());
393 if (ws_basic_auth_
) {
394 DCHECK(type_
== TYPE_WS
|| type_
== TYPE_WSS
);
395 arguments
->Set("ws-basic-auth", base::Value::CreateNullValue());
398 if (UsingSSL(type_
)) {
399 // Check the certificate arguments of the HTTPS server.
400 base::FilePath
certificate_path(certificates_dir_
);
401 base::FilePath
certificate_file(ssl_options_
.GetCertificateFile());
402 if (!certificate_file
.value().empty()) {
403 certificate_path
= certificate_path
.Append(certificate_file
);
404 if (certificate_path
.IsAbsolute() &&
405 !base::PathExists(certificate_path
)) {
406 LOG(ERROR
) << "Certificate path " << certificate_path
.value()
407 << " doesn't exist. Can't launch https server.";
410 arguments
->SetString("cert-and-key-file", certificate_path
.value());
413 // Check the client certificate related arguments.
414 if (ssl_options_
.request_client_certificate
)
415 arguments
->Set("ssl-client-auth", base::Value::CreateNullValue());
416 scoped_ptr
<base::ListValue
> ssl_client_certs(new base::ListValue());
418 std::vector
<base::FilePath
>::const_iterator it
;
419 for (it
= ssl_options_
.client_authorities
.begin();
420 it
!= ssl_options_
.client_authorities
.end(); ++it
) {
421 if (it
->IsAbsolute() && !base::PathExists(*it
)) {
422 LOG(ERROR
) << "Client authority path " << it
->value()
423 << " doesn't exist. Can't launch https server.";
426 ssl_client_certs
->Append(new base::StringValue(it
->value()));
429 if (ssl_client_certs
->GetSize())
430 arguments
->Set("ssl-client-ca", ssl_client_certs
.release());
432 scoped_ptr
<base::ListValue
> client_cert_types(new base::ListValue());
433 for (size_t i
= 0; i
< ssl_options_
.client_cert_types
.size(); i
++) {
434 client_cert_types
->Append(new base::StringValue(
435 GetClientCertType(ssl_options_
.client_cert_types
[i
])));
437 if (client_cert_types
->GetSize())
438 arguments
->Set("ssl-client-cert-type", client_cert_types
.release());
441 if (type_
== TYPE_HTTPS
) {
442 arguments
->Set("https", base::Value::CreateNullValue());
444 std::string ocsp_arg
= ssl_options_
.GetOCSPArgument();
445 if (!ocsp_arg
.empty())
446 arguments
->SetString("ocsp", ocsp_arg
);
448 if (ssl_options_
.cert_serial
!= 0) {
449 arguments
->SetInteger("cert-serial", ssl_options_
.cert_serial
);
452 // Check key exchange argument.
453 scoped_ptr
<base::ListValue
> key_exchange_values(new base::ListValue());
454 GetKeyExchangesList(ssl_options_
.key_exchanges
, key_exchange_values
.get());
455 if (key_exchange_values
->GetSize())
456 arguments
->Set("ssl-key-exchange", key_exchange_values
.release());
457 // Check bulk cipher argument.
458 scoped_ptr
<base::ListValue
> bulk_cipher_values(new base::ListValue());
459 GetCiphersList(ssl_options_
.bulk_ciphers
, bulk_cipher_values
.get());
460 if (bulk_cipher_values
->GetSize())
461 arguments
->Set("ssl-bulk-cipher", bulk_cipher_values
.release());
462 if (ssl_options_
.record_resume
)
463 arguments
->Set("https-record-resume", base::Value::CreateNullValue());
464 if (ssl_options_
.tls_intolerant
!= SSLOptions::TLS_INTOLERANT_NONE
) {
465 arguments
->SetInteger("tls-intolerant", ssl_options_
.tls_intolerant
);
466 arguments
->Set("tls-intolerance-type", GetTLSIntoleranceType(
467 ssl_options_
.tls_intolerance_type
));
469 if (ssl_options_
.fallback_scsv_enabled
)
470 arguments
->Set("fallback-scsv", base::Value::CreateNullValue());
471 if (!ssl_options_
.signed_cert_timestamps_tls_ext
.empty()) {
472 std::string b64_scts_tls_ext
;
473 base::Base64Encode(ssl_options_
.signed_cert_timestamps_tls_ext
,
475 arguments
->SetString("signed-cert-timestamps-tls-ext", b64_scts_tls_ext
);
477 if (ssl_options_
.staple_ocsp_response
)
478 arguments
->Set("staple-ocsp-response", base::Value::CreateNullValue());
479 if (ssl_options_
.enable_npn
)
480 arguments
->Set("enable-npn", base::Value::CreateNullValue());
481 if (ssl_options_
.disable_session_cache
)
482 arguments
->Set("disable-session-cache", base::Value::CreateNullValue());
485 return GenerateAdditionalArguments(arguments
);
488 bool BaseTestServer::GenerateAdditionalArguments(
489 base::DictionaryValue
* arguments
) const {