summaryrefslogtreecommitdiffstats
path: root/net/test
diff options
context:
space:
mode:
Diffstat (limited to 'net/test')
-rw-r--r--net/test/test_server.cc446
-rw-r--r--net/test/test_server.h349
-rw-r--r--net/test/test_server_posix.cc64
-rw-r--r--net/test/test_server_win.cc165
4 files changed, 451 insertions, 573 deletions
diff --git a/net/test/test_server.cc b/net/test/test_server.cc
index 7a2df0b..e133345 100644
--- a/net/test/test_server.cc
+++ b/net/test/test_server.cc
@@ -10,10 +10,7 @@
#include "build/build_config.h"
-#if defined(OS_WIN)
-#include <windows.h>
-#include <wincrypt.h>
-#elif defined(OS_MACOSX)
+#if defined(OS_MACOSX)
#include "net/base/x509_certificate.h"
#endif
@@ -23,17 +20,15 @@
#include "base/path_service.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
+#include "googleurl/src/gurl.h"
#include "net/base/cert_test_util.h"
+#include "net/base/host_port_pair.h"
#include "net/base/host_resolver.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/tcp_client_socket.h"
#include "net/socket/tcp_pinger.h"
#include "testing/platform_test.h"
-#if defined(OS_WIN)
-#pragma comment(lib, "crypt32.lib")
-#endif
-
namespace {
// Number of connection attempts for tests.
@@ -42,6 +37,33 @@ const int kServerConnectionAttempts = 10;
// Connection timeout in milliseconds for tests.
const int kServerConnectionTimeoutMs = 1000;
+int GetPort(net::TestServer::Type type) {
+ switch (type) {
+ case net::TestServer::TYPE_FTP:
+ return 1338;
+ case net::TestServer::TYPE_HTTP:
+ return 1337;
+ case net::TestServer::TYPE_HTTPS:
+ case net::TestServer::TYPE_HTTPS_CLIENT_AUTH:
+ case net::TestServer::TYPE_HTTPS_EXPIRED_CERTIFICATE:
+ return 9443;
+ case net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME:
+ return 9666;
+ default:
+ NOTREACHED();
+ }
+ return -1;
+}
+
+std::string GetHostname(net::TestServer::Type type) {
+ if (type == net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME) {
+ // Return a different hostname string that resolves to the same hostname.
+ return "localhost";
+ }
+
+ return "127.0.0.1";
+}
+
} // namespace
namespace net {
@@ -50,258 +72,192 @@ namespace net {
void SetMacTestCertificate(X509Certificate* cert);
#endif
-// static
-const char TestServerLauncher::kHostName[] = "127.0.0.1";
-const char TestServerLauncher::kMismatchedHostName[] = "localhost";
-const int TestServerLauncher::kOKHTTPSPort = 9443;
-const int TestServerLauncher::kBadHTTPSPort = 9666;
+TestServer::TestServer(Type type, const FilePath& document_root)
+ : host_port_pair_(GetHostname(type), GetPort(type)),
+ process_handle_(base::kNullProcessHandle),
+ type_(type) {
+ FilePath src_dir;
+ PathService::Get(base::DIR_SOURCE_ROOT, &src_dir);
-// The issuer name of the cert that should be trusted for the test to work.
-const wchar_t TestServerLauncher::kCertIssuerName[] = L"Test CA";
-
-TestServerLauncher::TestServerLauncher()
- : process_handle_(base::kNullProcessHandle),
- ssl_client_auth_(false) {
- InitCertPath();
-}
+ document_root_ = src_dir.Append(document_root);
-void TestServerLauncher::InitCertPath() {
- PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
- cert_dir_ = cert_dir_.Append(FILE_PATH_LITERAL("net"))
+ certificates_dir_ = src_dir.Append(FILE_PATH_LITERAL("net"))
.Append(FILE_PATH_LITERAL("data"))
.Append(FILE_PATH_LITERAL("ssl"))
.Append(FILE_PATH_LITERAL("certificates"));
}
-namespace {
-
-void AppendToPythonPath(const FilePath& dir) {
- // Do nothing if dir already on path.
-
-#if defined(OS_WIN)
- const wchar_t kPythonPath[] = L"PYTHONPATH";
- // TODO(dkegel): handle longer PYTHONPATH variables
- wchar_t oldpath[4096];
- if (GetEnvironmentVariable(kPythonPath, oldpath, arraysize(oldpath)) == 0) {
- SetEnvironmentVariableW(kPythonPath, dir.value().c_str());
- } else if (!wcsstr(oldpath, dir.value().c_str())) {
- std::wstring newpath(oldpath);
- newpath.append(L";");
- newpath.append(dir.value());
- SetEnvironmentVariableW(kPythonPath, newpath.c_str());
- }
-#elif defined(OS_POSIX)
- const char kPythonPath[] = "PYTHONPATH";
- const char* oldpath = getenv(kPythonPath);
- // setenv() leaks memory intentionally on Mac
- if (!oldpath) {
- setenv(kPythonPath, dir.value().c_str(), 1);
- } else if (!strstr(oldpath, dir.value().c_str())) {
- std::string newpath(oldpath);
- newpath.append(":");
- newpath.append(dir.value());
- setenv(kPythonPath, newpath.c_str(), 1);
- }
+TestServer::~TestServer() {
+#if defined(OS_MACOSX)
+ SetMacTestCertificate(NULL);
#endif
+ Stop();
}
-} // end namespace
-
-void TestServerLauncher::SetPythonPath() {
- FilePath third_party_dir;
- CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir));
- third_party_dir = third_party_dir.Append(FILE_PATH_LITERAL("third_party"));
-
- AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("tlslite")));
- AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("pyftpdlib")));
-
- // Locate the Python code generated by the protocol buffers compiler.
- FilePath generated_code_dir;
- CHECK(PathService::Get(base::DIR_EXE, &generated_code_dir));
- generated_code_dir = generated_code_dir.Append(FILE_PATH_LITERAL("pyproto"));
- AppendToPythonPath(generated_code_dir);
- AppendToPythonPath(generated_code_dir.Append(FILE_PATH_LITERAL("sync_pb")));
-}
-
-bool TestServerLauncher::Start(Protocol protocol,
- const std::string& host_name, int port,
- const FilePath& document_root,
- const FilePath& cert_path,
- const std::wstring& file_root_url) {
- if (!cert_path.value().empty()) {
+bool TestServer::Start() {
+ if (GetScheme() == "https") {
if (!LoadTestRootCert())
return false;
if (!CheckCATrusted())
return false;
}
- std::string port_str = base::IntToString(port);
-
// Get path to python server script
FilePath testserver_path;
- if (!PathService::Get(base::DIR_SOURCE_ROOT, &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"));
- PathService::Get(base::DIR_SOURCE_ROOT, &document_root_dir_);
- document_root_dir_ = document_root_dir_.Append(document_root);
-
- SetPythonPath();
-
-#if defined(OS_WIN)
- // Get path to python interpreter
- FilePath python_exe;
- if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_exe))
+ if (!SetPythonPath())
return false;
- python_exe = python_exe
- .Append(FILE_PATH_LITERAL("third_party"))
- .Append(FILE_PATH_LITERAL("python_24"))
- .Append(FILE_PATH_LITERAL("python.exe"));
-
- std::wstring command_line =
- L"\"" + python_exe.ToWStringHack() + L"\" " +
- L"\"" + testserver_path.ToWStringHack() +
- L"\" --port=" + UTF8ToWide(port_str) +
- L" --data-dir=\"" + document_root_dir_.ToWStringHack() + L"\"";
- if (protocol == ProtoFTP)
- command_line.append(L" -f");
- if (!cert_path.value().empty()) {
- command_line.append(L" --https=\"");
- command_line.append(cert_path.ToWStringHack());
- command_line.append(L"\"");
- }
- if (!file_root_url.empty()) {
- command_line.append(L" --file-root-url=\"");
- command_line.append(file_root_url);
- command_line.append(L"\"");
- }
- if (ssl_client_auth_)
- command_line.append(L" --ssl-client-auth");
-
- if (!LaunchTestServerAsJob(command_line,
- true,
- &process_handle_,
- &job_handle_)) {
- LOG(ERROR) << "Failed to launch " << command_line;
- return false;
- }
-#elif defined(OS_POSIX)
- std::vector<std::string> command_line;
- command_line.push_back("python");
- command_line.push_back(testserver_path.value());
- command_line.push_back("--port=" + port_str);
- command_line.push_back("--data-dir=" + document_root_dir_.value());
- if (protocol == ProtoFTP)
- command_line.push_back("-f");
- if (!cert_path.value().empty())
- command_line.push_back("--https=" + cert_path.value());
- if (ssl_client_auth_)
- command_line.push_back("--ssl-client-auth");
-
- base::file_handle_mapping_vector no_mappings;
- LOG(INFO) << "Trying to launch " << command_line[0] << " ...";
- if (!base::LaunchApp(command_line, no_mappings, false, &process_handle_)) {
- LOG(ERROR) << "Failed to launch " << command_line[0] << " ...";
+
+ if (!LaunchPython(testserver_path))
return false;
- }
-#endif
- // Let the server start, then verify that it's up.
- // Our server is Python, and takes about 500ms to start
- // up the first time, and about 200ms after that.
- if (!WaitToStart(host_name, port)) {
- LOG(ERROR) << "Failed to connect to server";
+ if (!WaitToStart()) {
Stop();
return false;
}
- LOG(INFO) << "Started on port " << port_str;
return true;
}
-bool TestServerLauncher::WaitToStart(const std::string& host_name, int port) {
- // Verify that the webserver is actually started.
- // Otherwise tests can fail if they run faster than Python can start.
- net::AddressList addr;
- scoped_refptr<net::HostResolver> resolver(
- net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism));
- net::HostResolver::RequestInfo info(host_name, port);
- int rv = resolver->Resolve(info, &addr, NULL, NULL, BoundNetLog());
- if (rv != net::OK)
- return false;
-
- net::TCPPinger pinger(addr);
- rv = pinger.Ping(
- base::TimeDelta::FromMilliseconds(kServerConnectionTimeoutMs),
- kServerConnectionAttempts);
- return rv == net::OK;
-}
-
-bool TestServerLauncher::WaitToFinish(int timeout_ms) {
+bool TestServer::Stop() {
if (!process_handle_)
return true;
- bool ret = base::WaitForSingleProcess(process_handle_, timeout_ms);
+ // 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;
- LOG(INFO) << "Finished.";
} else {
- LOG(INFO) << "Timed out.";
+ LOG(INFO) << "Kill failed?";
}
+
return ret;
}
-bool TestServerLauncher::Stop() {
+bool TestServer::WaitToFinish(int timeout_ms) {
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);
-
+ bool ret = base::WaitForSingleProcess(process_handle_, timeout_ms);
if (ret) {
base::CloseProcessHandle(process_handle_);
process_handle_ = base::kNullProcessHandle;
- LOG(INFO) << "Stopped.";
} else {
- LOG(INFO) << "Kill failed?";
+ LOG(ERROR) << "Timed out.";
}
-
return ret;
}
-TestServerLauncher::~TestServerLauncher() {
-#if defined(OS_MACOSX)
- SetMacTestCertificate(NULL);
-#endif
- Stop();
+std::string TestServer::GetScheme() const {
+ switch (type_) {
+ case TYPE_FTP:
+ return "ftp";
+ case TYPE_HTTP:
+ return "http";
+ case TYPE_HTTPS:
+ case TYPE_HTTPS_CLIENT_AUTH:
+ case TYPE_HTTPS_MISMATCHED_HOSTNAME:
+ case TYPE_HTTPS_EXPIRED_CERTIFICATE:
+ return "https";
+ default:
+ NOTREACHED();
+ }
+ return std::string();
+}
+
+bool TestServer::GetAddressList(AddressList* address_list) const {
+ DCHECK(address_list);
+
+ scoped_refptr<HostResolver> resolver(
+ CreateSystemHostResolver(HostResolver::kDefaultParallelism));
+ HostResolver::RequestInfo info(host_port_pair_.host(),
+ host_port_pair_.port());
+ int rv = resolver->Resolve(info, address_list, NULL, NULL, BoundNetLog());
+ 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) {
+ return GURL(GetScheme() + "://" + host_port_pair_.ToString() +
+ "/" + path);
}
-FilePath TestServerLauncher::GetRootCertPath() {
- FilePath path(cert_dir_);
- path = path.AppendASCII("root_ca_cert.crt");
- return path;
+GURL TestServer::GetURLWithUser(const std::string& path,
+ const std::string& user) {
+ return GURL(GetScheme() + "://" + user + "@" +
+ host_port_pair_.ToString() +
+ "/" + path);
+}
+
+GURL TestServer::GetURLWithUserAndPassword(const std::string& path,
+ const std::string& user,
+ const std::string& password) {
+ return GURL(GetScheme() + "://" + user + ":" + password +
+ "@" + host_port_pair_.ToString() +
+ "/" + path);
+}
+
+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.Append(FILE_PATH_LITERAL("third_party"));
+
+ AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("tlslite")));
+ AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("pyftpdlib")));
+
+ // Locate the Python code generated by the protocol buffers compiler.
+ FilePath generated_code_dir;
+ if (!PathService::Get(base::DIR_EXE, &generated_code_dir)) {
+ LOG(ERROR) << "Failed to get DIR_EXE";
+ return false;
+ }
+ generated_code_dir = generated_code_dir.Append(FILE_PATH_LITERAL("pyproto"));
+ AppendToPythonPath(generated_code_dir);
+ AppendToPythonPath(generated_code_dir.Append(FILE_PATH_LITERAL("sync_pb")));
+
+ return true;
}
-FilePath TestServerLauncher::GetOKCertPath() {
- FilePath path(cert_dir_);
- path = path.AppendASCII("ok_cert.pem");
- return path;
+bool TestServer::WaitToStart() {
+ net::AddressList addr;
+ if (!GetAddressList(&addr))
+ return false;
+
+ net::TCPPinger pinger(addr);
+ int rv = pinger.Ping(
+ base::TimeDelta::FromMilliseconds(kServerConnectionTimeoutMs),
+ kServerConnectionAttempts);
+ bool result = (rv == net::OK);
+ if (!result) {
+ LOG(ERROR) << "Failed to connect to server";
+ }
+ return result;
}
-FilePath TestServerLauncher::GetExpiredCertPath() {
- FilePath path(cert_dir_);
- path = path.AppendASCII("expired_cert.pem");
- return path;
+FilePath TestServer::GetRootCertificatePath() {
+ return certificates_dir_.AppendASCII("root_ca_cert.crt");
}
-bool TestServerLauncher::LoadTestRootCert() {
+bool TestServer::LoadTestRootCert() {
#if defined(USE_NSS)
if (cert_)
return true;
@@ -312,11 +268,10 @@ bool TestServerLauncher::LoadTestRootCert() {
// TODO(dkegel): fix the leak and remove the entry in
// tools/valgrind/memcheck/suppressions.txt
ANNOTATE_SCOPED_MEMORY_LEAK; // Tell heap checker about the leak.
- cert_ = LoadTemporaryRootCert(GetRootCertPath());
- DCHECK(cert_);
+ cert_ = LoadTemporaryRootCert(GetRootCertificatePath());
return (cert_ != NULL);
#elif defined(OS_MACOSX)
- X509Certificate* cert = LoadTemporaryRootCert(GetRootCertPath());
+ X509Certificate* cert = LoadTemporaryRootCert(GetRootCertificatePath());
if (!cert)
return false;
SetMacTestCertificate(cert);
@@ -326,88 +281,21 @@ bool TestServerLauncher::LoadTestRootCert() {
#endif
}
-bool TestServerLauncher::CheckCATrusted() {
-#if defined(OS_WIN)
- HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT");
- if (!cert_store) {
- LOG(ERROR) << " could not open trusted root CA store";
- return false;
- }
- PCCERT_CONTEXT cert =
- CertFindCertificateInStore(cert_store,
- X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
- 0,
- CERT_FIND_ISSUER_STR,
- kCertIssuerName,
- NULL);
- if (cert)
- CertFreeCertificateContext(cert);
- CertCloseStore(cert_store, 0);
-
- if (!cert) {
- LOG(ERROR) << " TEST CONFIGURATION ERROR: you need to import the test ca "
- "certificate to your trusted roots for this test to work. "
- "For more info visit:\n"
- "http://dev.chromium.org/developers/testing\n";
- return false;
+FilePath TestServer::GetCertificatePath() {
+ switch (type_) {
+ case TYPE_FTP:
+ case TYPE_HTTP:
+ return FilePath();
+ case TYPE_HTTPS:
+ case TYPE_HTTPS_CLIENT_AUTH:
+ case TYPE_HTTPS_MISMATCHED_HOSTNAME:
+ return certificates_dir_.AppendASCII("ok_cert.pem");
+ case TYPE_HTTPS_EXPIRED_CERTIFICATE:
+ return certificates_dir_.AppendASCII("expired_cert.pem");
+ default:
+ NOTREACHED();
}
-#endif
- return true;
-}
-
-#if defined(OS_WIN)
-bool LaunchTestServerAsJob(const std::wstring& cmdline,
- bool start_hidden,
- base::ProcessHandle* process_handle,
- ScopedHandle* job_handle) {
- // Launch test server process.
- STARTUPINFO startup_info = {0};
- startup_info.cb = sizeof(startup_info);
- startup_info.dwFlags = STARTF_USESHOWWINDOW;
- startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
- PROCESS_INFORMATION process_info;
-
- // If this code is run under a debugger, the test server process is
- // automatically associated with a job object created by the debugger.
- // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
- if (!CreateProcess(NULL,
- const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
- FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL,
- &startup_info, &process_info)) {
- LOG(ERROR) << "Could not create process.";
- return false;
- }
- CloseHandle(process_info.hThread);
-
- // If the caller wants the process handle, we won't close it.
- if (process_handle) {
- *process_handle = process_info.hProcess;
- } else {
- CloseHandle(process_info.hProcess);
- }
-
- // Create a JobObject and associate the test server process with it.
- job_handle->Set(CreateJobObject(NULL, NULL));
- if (!job_handle->IsValid()) {
- LOG(ERROR) << "Could not create JobObject.";
- return false;
- } else {
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
- limit_info.BasicLimitInformation.LimitFlags =
- JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
- if (0 == SetInformationJobObject(job_handle->Get(),
- JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info))) {
- LOG(ERROR) << "Could not SetInformationJobObject.";
- return false;
- }
- if (0 == AssignProcessToJobObject(job_handle->Get(),
- process_info.hProcess)) {
- LOG(ERROR) << "Could not AssignProcessToObject.";
- return false;
- }
- }
- return true;
+ return FilePath();
}
-#endif
} // namespace net
diff --git a/net/test/test_server.h b/net/test/test_server.h
index 989e38d..5f51571 100644
--- a/net/test/test_server.h
+++ b/net/test/test_server.h
@@ -13,52 +13,40 @@
#include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/process_util.h"
-#include "base/ref_counted.h"
-#include "base/string_number_conversions.h"
-//#include "base/string_util.h"
-#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
#if defined(OS_WIN)
#include "base/scoped_handle_win.h"
#endif
#if defined(USE_NSS)
+#include "base/ref_counted.h"
#include "net/base/x509_certificate.h"
#endif
-namespace net {
+class GURL;
-const int kHTTPDefaultPort = 1337;
-const int kFTPDefaultPort = 1338;
+namespace net {
-const char kDefaultHostName[] = "localhost";
+class AddressList;
-// This object bounds the lifetime of an external python-based HTTP/HTTPS/FTP
-// server that can provide various responses useful for testing.
-class TestServerLauncher {
+// This object bounds the lifetime of an external python-based HTTP/FTP server
+// that can provide various responses useful for testing.
+class TestServer {
public:
- TestServerLauncher();
- virtual ~TestServerLauncher();
-
- enum Protocol {
- ProtoHTTP, ProtoFTP
+ enum Type {
+ TYPE_FTP,
+ TYPE_HTTP,
+ TYPE_HTTPS,
+ TYPE_HTTPS_CLIENT_AUTH,
+ TYPE_HTTPS_MISMATCHED_HOSTNAME,
+ TYPE_HTTPS_EXPIRED_CERTIFICATE,
};
- // Load the test root cert, if it hasn't been loaded yet.
- bool LoadTestRootCert() WARN_UNUSED_RESULT;
+ TestServer(Type type, const FilePath& document_root);
+ ~TestServer();
- // Start src/net/tools/testserver/testserver.py and
- // ask it to serve the given protocol.
- // If protocol is HTTP, and cert_path is not empty, serves HTTPS.
- // file_root_url specifies the root url on the server that documents will be
- // served out of. This is /files/ by default.
- // Returns true on success, false if files not found or root cert
- // not trusted.
- bool Start(net::TestServerLauncher::Protocol protocol,
- const std::string& host_name, int port,
- const FilePath& document_root,
- const FilePath& cert_path,
- const std::wstring& file_root_url) WARN_UNUSED_RESULT;
+ bool Start() WARN_UNUSED_RESULT;
// Stop the server started by Start().
bool Stop();
@@ -69,56 +57,56 @@ class TestServerLauncher {
// It returns true if the server exited cleanly.
bool WaitToFinish(int milliseconds) WARN_UNUSED_RESULT;
- // Paths to a good, an expired, and an invalid server certificate
- // (use as arguments to Start()).
- FilePath GetOKCertPath();
- FilePath GetExpiredCertPath();
-
- FilePath GetDocumentRootPath() { return document_root_dir_; }
-
- // When Start is called, if protocol is HTTPS and ssl_client_auth_ is true,
- // the server will request a client certificate on each connection. Must be
- // called before Start to take effect.
- void set_ssl_client_auth(bool ssl_client_auth) {
- ssl_client_auth_ = ssl_client_auth;
- }
+ const FilePath& document_root() const { return document_root_; }
+ const HostPortPair& host_port_pair() const { return host_port_pair_; }
+ std::string GetScheme() const;
+ bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT;
- // Issuer name of the root cert that should be trusted for the test to work.
- static const wchar_t kCertIssuerName[];
+ GURL GetURL(const std::string& path);
- // Hostname to use for test server
- static const char kHostName[];
+ GURL GetURLWithUser(const std::string& path,
+ const std::string& user);
- // Different hostname to use for test server (that still resolves to same IP)
- static const char kMismatchedHostName[];
+ GURL GetURLWithUserAndPassword(const std::string& path,
+ const std::string& user,
+ const std::string& password);
- // Port to use for test server
- static const int kOKHTTPSPort;
+ private:
+ // Appends |dir| to PYTHONPATH.
+ static void AppendToPythonPath(const FilePath& dir);
- // Port to use for bad test server
- static const int kBadHTTPSPort;
+ // Modify PYTHONPATH to contain libraries we need.
+ bool SetPythonPath() WARN_UNUSED_RESULT;
- private:
- // Wait a while for the server to start, return whether
- // we were able to make a connection to it.
- bool WaitToStart(const std::string& host_name, int port) WARN_UNUSED_RESULT;
+ // Launches the Python test server. Returns true on success.
+ bool LaunchPython(const FilePath& testserver_path) WARN_UNUSED_RESULT;
- // Append to PYTHONPATH so Python can find pyftpdlib and tlslite.
- void SetPythonPath();
+ // Waits for the server to start. Returns true on success.
+ bool WaitToStart() WARN_UNUSED_RESULT;
- // Path to our test root certificate.
- FilePath GetRootCertPath();
+ // Returns path to the root certificate.
+ FilePath GetRootCertificatePath();
// Returns false if our test root certificate is not trusted.
bool CheckCATrusted() WARN_UNUSED_RESULT;
- // Initilize the certificate path.
- void InitCertPath();
+ // Load the test root cert, if it hasn't been loaded yet.
+ bool LoadTestRootCert() WARN_UNUSED_RESULT;
+
+ // Returns path to the SSL certificate we should use, or empty path
+ // if not applicable.
+ FilePath GetCertificatePath();
- FilePath document_root_dir_;
+ // Document root of the test server.
+ FilePath document_root_;
- FilePath cert_dir_;
+ // Directory that contains the SSL certificates.
+ FilePath certificates_dir_;
+ // Address the test server listens on.
+ HostPortPair host_port_pair_;
+
+ // Handle of the Python process running the test server.
base::ProcessHandle process_handle_;
#if defined(OS_WIN)
@@ -130,238 +118,11 @@ class TestServerLauncher {
scoped_refptr<X509Certificate> cert_;
#endif
- bool ssl_client_auth_;
-
- DISALLOW_COPY_AND_ASSIGN(TestServerLauncher);
-};
-
-#if defined(OS_WIN)
-// Launch test server as a job so that it is not orphaned if the test case is
-// abnormally terminated.
-bool LaunchTestServerAsJob(const std::wstring& cmdline,
- bool start_hidden,
- base::ProcessHandle* process_handle,
- ScopedHandle* job_handle);
-#endif
-
-// This object bounds the lifetime of an external python-based HTTP/FTP server
-// that can provide various responses useful for testing.
-class BaseTestServer : public base::RefCounted<BaseTestServer> {
- protected:
- BaseTestServer() {}
+ Type type_;
- public:
- bool WaitToFinish(int milliseconds) {
- return launcher_.WaitToFinish(milliseconds);
- }
-
- bool Stop() {
- return launcher_.Stop();
- }
-
- GURL TestServerPage(const std::string& base_address,
- const std::string& path) {
- return GURL(base_address + path);
- }
-
- GURL TestServerPage(const std::string& path) {
- // TODO(phajdan.jr): Check for problems with IPv6.
- return GURL(scheme_ + "://" + host_name_ + ":" + port_str_ + "/" + path);
- }
-
- GURL TestServerPage(const std::string& path,
- const std::string& user,
- const std::string& password) {
- // TODO(phajdan.jr): Check for problems with IPv6.
-
- if (password.empty())
- return GURL(scheme_ + "://" + user + "@" +
- host_name_ + ":" + port_str_ + "/" + path);
-
- return GURL(scheme_ + "://" + user + ":" + password +
- "@" + host_name_ + ":" + port_str_ + "/" + path);
- }
-
- FilePath GetDataDirectory() {
- return launcher_.GetDocumentRootPath();
- }
-
- protected:
- friend class base::RefCounted<BaseTestServer>;
- virtual ~BaseTestServer() { }
-
- bool Start(net::TestServerLauncher::Protocol protocol,
- const std::string& host_name, int port,
- const FilePath& document_root,
- const FilePath& cert_path,
- const std::wstring& file_root_url) {
- if (!launcher_.Start(protocol,
- host_name, port, document_root, cert_path, file_root_url))
- return false;
-
- if (protocol == net::TestServerLauncher::ProtoFTP)
- scheme_ = "ftp";
- else
- scheme_ = "http";
- if (!cert_path.empty())
- scheme_.push_back('s');
-
- host_name_ = host_name;
- port_str_ = base::IntToString(port);
- return true;
- }
-
- net::TestServerLauncher launcher_;
- std::string scheme_;
- std::string host_name_;
- std::string port_str_;
+ DISALLOW_COPY_AND_ASSIGN(TestServer);
};
-class HTTPTestServer : public BaseTestServer {
- protected:
- HTTPTestServer() {}
-
- public:
- // Creates and returns a new HTTPTestServer.
- static scoped_refptr<HTTPTestServer> CreateServer(
- const std::wstring& document_root) {
- return CreateServerWithFileRootURL(document_root, std::wstring());
- }
-
- static scoped_refptr<HTTPTestServer> CreateServerWithFileRootURL(
- const std::wstring& document_root,
- const std::wstring& file_root_url) {
- scoped_refptr<HTTPTestServer> test_server(new HTTPTestServer());
- FilePath no_cert;
- FilePath docroot = FilePath::FromWStringHack(document_root);
- if (!StartTestServer(test_server.get(), docroot, no_cert, file_root_url))
- return NULL;
- return test_server;
- }
-
- static bool StartTestServer(HTTPTestServer* server,
- const FilePath& document_root,
- const FilePath& cert_path,
- const std::wstring& file_root_url) {
- return server->Start(net::TestServerLauncher::ProtoHTTP, kDefaultHostName,
- kHTTPDefaultPort, document_root, cert_path,
- file_root_url);
- }
-};
-
-class HTTPSTestServer : public HTTPTestServer {
- protected:
- HTTPSTestServer() {}
-
- public:
- // Create a server with a valid certificate
- // TODO(dkegel): HTTPSTestServer should not require an instance to specify
- // stock test certificates
- static scoped_refptr<HTTPSTestServer> CreateGoodServer(
- const std::wstring& document_root) {
- scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
- FilePath docroot = FilePath::FromWStringHack(document_root);
- FilePath certpath = test_server->launcher_.GetOKCertPath();
- if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
- net::TestServerLauncher::kHostName,
- net::TestServerLauncher::kOKHTTPSPort,
- docroot, certpath, std::wstring())) {
- return NULL;
- }
- return test_server;
- }
-
- // Create a server which requests SSL client auth
- static scoped_refptr<HTTPSTestServer> CreateClientAuthServer(
- const std::wstring& document_root) {
- scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
- FilePath docroot = FilePath::FromWStringHack(document_root);
- FilePath certpath = test_server->launcher_.GetOKCertPath();
- test_server->launcher_.set_ssl_client_auth(true);
- if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
- net::TestServerLauncher::kHostName,
- net::TestServerLauncher::kOKHTTPSPort,
- docroot, certpath, std::wstring())) {
- return NULL;
- }
- return test_server;
- }
-
- // Create a server with an up to date certificate for the wrong hostname
- // for this host
- static scoped_refptr<HTTPSTestServer> CreateMismatchedServer(
- const std::wstring& document_root) {
- scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
- FilePath docroot = FilePath::FromWStringHack(document_root);
- FilePath certpath = test_server->launcher_.GetOKCertPath();
- if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
- net::TestServerLauncher::kMismatchedHostName,
- net::TestServerLauncher::kOKHTTPSPort,
- docroot, certpath, std::wstring())) {
- return NULL;
- }
- return test_server;
- }
-
- // Create a server with an expired certificate
- static scoped_refptr<HTTPSTestServer> CreateExpiredServer(
- const std::wstring& document_root) {
- scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
- FilePath docroot = FilePath::FromWStringHack(document_root);
- FilePath certpath = test_server->launcher_.GetExpiredCertPath();
- if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
- net::TestServerLauncher::kHostName,
- net::TestServerLauncher::kBadHTTPSPort,
- docroot, certpath, std::wstring())) {
- return NULL;
- }
- return test_server;
- }
-
- // Create a server with an arbitrary certificate
- static scoped_refptr<HTTPSTestServer> CreateServer(
- const std::string& host_name, int port,
- const std::wstring& document_root,
- const std::wstring& cert_path) {
- scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
- FilePath docroot = FilePath::FromWStringHack(document_root);
- FilePath certpath = FilePath::FromWStringHack(cert_path);
- if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
- host_name, port, docroot, certpath, std::wstring())) {
- return NULL;
- }
- return test_server;
- }
-
- protected:
- std::wstring cert_path_;
-
- private:
- virtual ~HTTPSTestServer() {}
-};
-
-class FTPTestServer : public BaseTestServer {
- public:
- FTPTestServer() {
- }
-
- static scoped_refptr<FTPTestServer> CreateServer(
- const std::wstring& document_root) {
- scoped_refptr<FTPTestServer> test_server = new FTPTestServer();
- FilePath docroot = FilePath::FromWStringHack(document_root);
- FilePath no_cert;
- if (!test_server->Start(net::TestServerLauncher::ProtoFTP,
- kDefaultHostName, kFTPDefaultPort, docroot, no_cert, std::wstring())) {
- return NULL;
- }
- return test_server;
- }
-
- private:
- ~FTPTestServer() {}
-};
-
-
} // namespace net
#endif // NET_TEST_TEST_SERVER_H_
diff --git a/net/test/test_server_posix.cc b/net/test/test_server_posix.cc
new file mode 100644
index 0000000..783bdc3
--- /dev/null
+++ b/net/test/test_server_posix.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2010 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 "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+
+namespace net {
+
+// static
+void TestServer::AppendToPythonPath(const FilePath& dir) {
+ const char kPythonPath[] = "PYTHONPATH";
+ const char* oldpath = getenv(kPythonPath);
+ // setenv() leaks memory intentionally on Mac
+ if (!oldpath) {
+ setenv(kPythonPath, dir.value().c_str(), 1);
+ } else if (!strstr(oldpath, dir.value().c_str())) {
+ std::string newpath(oldpath);
+ newpath.append(":");
+ newpath.append(dir.value());
+ setenv(kPythonPath, newpath.c_str(), 1);
+ }
+}
+
+bool TestServer::LaunchPython(const FilePath& testserver_path) {
+ std::vector<std::string> command_line;
+ command_line.push_back("python");
+ command_line.push_back(testserver_path.value());
+ command_line.push_back("--port=" + base::IntToString(host_port_pair_.port()));
+ command_line.push_back("--data-dir=" + document_root_.value());
+
+ if (type_ == TYPE_FTP)
+ command_line.push_back("-f");
+
+ FilePath certificate_path(GetCertificatePath());
+ if (!certificate_path.value().empty()) {
+ 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.push_back("--https=" + certificate_path.value());
+ }
+
+ if (type_ == TYPE_HTTPS_CLIENT_AUTH)
+ command_line.push_back("--ssl-client-auth");
+
+ base::file_handle_mapping_vector no_mappings;
+ if (!base::LaunchApp(command_line, no_mappings, false, &process_handle_)) {
+ LOG(ERROR) << "Failed to launch " << command_line[0] << " ...";
+ return false;
+ }
+
+ return true;
+}
+
+bool TestServer::CheckCATrusted() {
+ return true;
+}
+
+} // namespace net
diff --git a/net/test/test_server_win.cc b/net/test/test_server_win.cc
new file mode 100644
index 0000000..b806cba
--- /dev/null
+++ b/net/test/test_server_win.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2010 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 <windows.h>
+#include <wincrypt.h>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+namespace {
+
+bool LaunchTestServerAsJob(const std::wstring& cmdline,
+ bool start_hidden,
+ base::ProcessHandle* process_handle,
+ ScopedHandle* job_handle) {
+ // Launch test server process.
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
+ PROCESS_INFORMATION process_info;
+
+ // If this code is run under a debugger, the test server process is
+ // automatically associated with a job object created by the debugger.
+ // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
+ if (!CreateProcess(NULL,
+ const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+ FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL,
+ &startup_info, &process_info)) {
+ LOG(ERROR) << "Could not create process.";
+ return false;
+ }
+ CloseHandle(process_info.hThread);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle) {
+ *process_handle = process_info.hProcess;
+ } else {
+ CloseHandle(process_info.hProcess);
+ }
+
+ // Create a JobObject and associate the test server process with it.
+ job_handle->Set(CreateJobObject(NULL, NULL));
+ if (!job_handle->IsValid()) {
+ LOG(ERROR) << "Could not create JobObject.";
+ return false;
+ } else {
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
+ limit_info.BasicLimitInformation.LimitFlags =
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ if (0 == SetInformationJobObject(job_handle->Get(),
+ JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info))) {
+ LOG(ERROR) << "Could not SetInformationJobObject.";
+ return false;
+ }
+ if (0 == AssignProcessToJobObject(job_handle->Get(),
+ process_info.hProcess)) {
+ LOG(ERROR) << "Could not AssignProcessToObject.";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+namespace net {
+
+// static
+void TestServer::AppendToPythonPath(const FilePath& dir) {
+ const wchar_t kPythonPath[] = L"PYTHONPATH";
+ // TODO(dkegel): handle longer PYTHONPATH variables
+ wchar_t oldpath[4096];
+ if (GetEnvironmentVariable(kPythonPath, oldpath, arraysize(oldpath)) == 0) {
+ SetEnvironmentVariableW(kPythonPath, dir.value().c_str());
+ } else if (!wcsstr(oldpath, dir.value().c_str())) {
+ std::wstring newpath(oldpath);
+ newpath.append(L";");
+ newpath.append(dir.value());
+ SetEnvironmentVariableW(kPythonPath, newpath.c_str());
+ }
+}
+
+bool TestServer::LaunchPython(const FilePath& testserver_path) {
+ FilePath python_exe;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_exe))
+ return false;
+ python_exe = python_exe
+ .Append(FILE_PATH_LITERAL("third_party"))
+ .Append(FILE_PATH_LITERAL("python_24"))
+ .Append(FILE_PATH_LITERAL("python.exe"));
+
+ std::wstring command_line =
+ L"\"" + python_exe.value() + L"\" " +
+ L"\"" + testserver_path.value() +
+ L"\" --port=" + ASCIIToWide(base::IntToString(host_port_pair_.port())) +
+ L" --data-dir=\"" + document_root_.value() + L"\"";
+
+ if (type_ == TYPE_FTP)
+ command_line.append(L" -f");
+
+ FilePath certificate_path(GetCertificatePath());
+ if (!certificate_path.value().empty()) {
+ 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.append(L" --https=\"");
+ command_line.append(certificate_path.value());
+ command_line.append(L"\"");
+ }
+
+ if (type_ == TYPE_HTTPS_CLIENT_AUTH)
+ command_line.append(L" --ssl-client-auth");
+
+ if (!LaunchTestServerAsJob(command_line,
+ true,
+ &process_handle_,
+ &job_handle_)) {
+ LOG(ERROR) << "Failed to launch " << command_line;
+ return false;
+ }
+
+ return true;
+}
+
+bool TestServer::CheckCATrusted() {
+ HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT");
+ if (!cert_store) {
+ LOG(ERROR) << " could not open trusted root CA store";
+ return false;
+ }
+ PCCERT_CONTEXT cert =
+ CertFindCertificateInStore(cert_store,
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0,
+ CERT_FIND_ISSUER_STR,
+ L"Test CA",
+ NULL);
+ if (cert)
+ CertFreeCertificateContext(cert);
+ CertCloseStore(cert_store, 0);
+
+ if (!cert) {
+ LOG(ERROR) << " TEST CONFIGURATION ERROR: you need to import the test ca "
+ "certificate to your trusted roots for this test to work. "
+ "For more info visit:\n"
+ "http://dev.chromium.org/developers/testing\n";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace net