diff options
author | dkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-18 23:42:36 +0000 |
---|---|---|
committer | dkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-18 23:42:36 +0000 |
commit | c8816c164145d55d46635aface02072461d4824f (patch) | |
tree | 554fbf9f5ee4d91f443b15f15ccd0ca1a307ca68 /net/base | |
parent | 27866a0b950ded8afb87120628af72dfd0b3582d (diff) | |
download | chromium_src-c8816c164145d55d46635aface02072461d4824f.zip chromium_src-c8816c164145d55d46635aface02072461d4824f.tar.gz chromium_src-c8816c164145d55d46635aface02072461d4824f.tar.bz2 |
Undo revert http://codereview.chromium.org/23028
Changes since original http://codereview.chromium.org/16207 :
- use Release rather than delete on reference counted objects
- Preserve bool WaitToFinish(int milliseconds)
and std::wstring GetDataDirectory() at Tommi's request,
and add unit test for WaitToFinish().
Review URL: http://codereview.chromium.org/20444
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9992 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/ssl_client_socket_nss.cc | 53 | ||||
-rw-r--r-- | net/base/ssl_client_socket_nss.h | 18 | ||||
-rw-r--r-- | net/base/ssl_client_socket_unittest.cc | 158 | ||||
-rw-r--r-- | net/base/ssl_test_util.cc | 240 | ||||
-rw-r--r-- | net/base/ssl_test_util.h | 85 |
5 files changed, 455 insertions, 99 deletions
diff --git a/net/base/ssl_client_socket_nss.cc b/net/base/ssl_client_socket_nss.cc index 5c33dc8..f67c246 100644 --- a/net/base/ssl_client_socket_nss.cc +++ b/net/base/ssl_client_socket_nss.cc @@ -23,21 +23,6 @@ static const int kRecvBufferSize = 4096; -namespace { - -// NSS calls this if an incoming certificate is invalid. -SECStatus OwnBadCertHandler(void* arg, PRFileDesc* socket) { - PRErrorCode err = PR_GetError(); - LOG(INFO) << "server certificate is invalid; NSS error code " << err; - // Return SECSuccess to override the problem, - // or SECFailure to let the original function fail - // Chromium wants it to fail here, and may retry it later. - LOG(WARNING) << "TODO(dkegel): return SECFailure here"; - return SECSuccess; -} - -} // anonymous namespace - namespace net { // State machines are easier to debug if you log state transitions. @@ -79,6 +64,8 @@ int NetErrorFromNSPRError(PRErrorCode err) { case SEC_ERROR_REVOKED_KEY: return ERR_CERT_REVOKED; case SEC_ERROR_UNKNOWN_ISSUER: + case SEC_ERROR_UNTRUSTED_CERT: + case SEC_ERROR_UNTRUSTED_ISSUER: return ERR_CERT_AUTHORITY_INVALID; default: { @@ -119,7 +106,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket, user_callback_(NULL), user_buf_(NULL), user_buf_len_(0), - server_cert_status_(0), + server_cert_error_(0), completed_handshake_(false), next_state_(STATE_NONE), nss_fd_(NULL), @@ -242,9 +229,12 @@ void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { << " for cipherSuite " << channel_info.cipherSuite; } } - ssl_info->cert_status = server_cert_status_; - // TODO(port): implement X509Certificate so we can set the cert field! - // CERTCertificate *nssCert = SSL_PeerCertificate(nss_fd_); + if (server_cert_error_ != net::OK) + ssl_info->SetCertError(server_cert_error_); + X509Certificate::OSCertHandle nss_cert = SSL_PeerCertificate(nss_fd_); + if (nss_cert) + ssl_info->cert = X509Certificate::CreateFromHandle(nss_cert, + X509Certificate::SOURCE_FROM_NETWORK); LeaveFunction(""); } @@ -401,6 +391,19 @@ int SSLClientSocketNSS::DoConnect() { return transport_->Connect(&io_callback_); } +// static +// NSS calls this if an incoming certificate is invalid. +SECStatus SSLClientSocketNSS::OwnBadCertHandler(void* arg, PRFileDesc* socket) { + SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); + PRErrorCode prerr = PR_GetError(); + that->server_cert_error_ = NetErrorFromNSPRError(prerr); + LOG(INFO) << "server certificate is invalid; NSS error code " << prerr + << ", net error " << that->server_cert_error_; + // Return SECSuccess to override the problem. + // Chromium wants it to succeed here, and may abort the connection later. + return SECSuccess; +} + int SSLClientSocketNSS::DoConnectComplete(int result) { EnterFunction(result); if (result < 0) @@ -479,7 +482,7 @@ int SSLClientSocketNSS::DoConnectComplete(int result) { if (rv != SECSuccess) return ERR_UNEXPECTED; - rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, NULL); + rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, this); if (rv != SECSuccess) return ERR_UNEXPECTED; @@ -500,11 +503,10 @@ int SSLClientSocketNSS::DoHandshakeRead() { int rv = SSL_ForceHandshake(nss_fd_); if (rv == SECSuccess) { - net_error = OK; + net_error = server_cert_error_; // there's a callback for this, too completed_handshake_ = true; - // Indicate we're ready to handle I/O. Badly named? - GotoState(STATE_NONE); + // Done! } else { PRErrorCode prerr = PR_GetError(); net_error = NetErrorFromNSPRError(prerr); @@ -513,10 +515,9 @@ int SSLClientSocketNSS::DoHandshakeRead() { if (net_error == ERR_IO_PENDING) { GotoState(STATE_HANDSHAKE_READ); } else { - server_cert_status_ = MapNetErrorToCertStatus(net_error); + server_cert_error_ = net_error; LOG(ERROR) << "handshake failed; NSS error code " << prerr - << ", net_error " << net_error << ", server_cert_status " - << server_cert_status_; + << ", net_error " << net_error; } } diff --git a/net/base/ssl_client_socket_nss.h b/net/base/ssl_client_socket_nss.h index 5015e1e..b16557c 100644 --- a/net/base/ssl_client_socket_nss.h +++ b/net/base/ssl_client_socket_nss.h @@ -5,15 +5,13 @@ #ifndef NET_BASE_SSL_CLIENT_SOCKET_NSS_H_ #define NET_BASE_SSL_CLIENT_SOCKET_NSS_H_ -#include "build/build_config.h" - -#include <prio.h> -#include "net/base/nss_memio.h" - +#include <nspr.h> +#include <nss.h> #include <string> #include "base/scoped_ptr.h" #include "net/base/completion_callback.h" +#include "net/base/nss_memio.h" #include "net/base/ssl_client_socket.h" #include "net/base/ssl_config_service.h" @@ -60,6 +58,9 @@ class SSLClientSocketNSS : public SSLClientSocket { void BufferSendComplete(int result); void BufferRecvComplete(int result); + // nss calls this on error. We pass 'this' as the first argument. + static SECStatus OwnBadCertHandler(void* arg, PRFileDesc* socket); + CompletionCallbackImpl<SSLClientSocketNSS> buffer_send_callback_; CompletionCallbackImpl<SSLClientSocketNSS> buffer_recv_callback_; bool transport_send_busy_; @@ -76,7 +77,8 @@ class SSLClientSocketNSS : public SSLClientSocket { char* user_buf_; int user_buf_len_; - int server_cert_status_; + // Set when handshake finishes. Value is net error code, see net_errors.h + int server_cert_error_; bool completed_handshake_; @@ -91,10 +93,10 @@ class SSLClientSocketNSS : public SSLClientSocket { }; State next_state_; - /* The NSS SSL state machine */ + // The NSS SSL state machine PRFileDesc* nss_fd_; - /* Buffers for the network end of the SSL state machine */ + // Buffers for the network end of the SSL state machine memio_Private* nss_bufs_; static bool nss_options_initialized_; diff --git a/net/base/ssl_client_socket_unittest.cc b/net/base/ssl_client_socket_unittest.cc index faf5a4a..4710151 100644 --- a/net/base/ssl_client_socket_unittest.cc +++ b/net/base/ssl_client_socket_unittest.cc @@ -10,6 +10,7 @@ #include "net/base/net_errors.h" #include "net/base/ssl_client_socket.h" #include "net/base/ssl_config_service.h" +#include "net/base/ssl_test_util.h" #include "net/base/tcp_client_socket.h" #include "net/base/test_completion_callback.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,41 +23,74 @@ const net::SSLConfig kDefaultSSLConfig; class SSLClientSocketTest : public PlatformTest { public: SSLClientSocketTest() - : host_mapper_(new net::RuleBasedHostMapper()), - scoped_host_mapper_(host_mapper_.get()), - socket_factory_(net::ClientSocketFactory::GetDefaultFactory()) { - // TODO(darin): kill this exception once we have a way to test out the - // TCPClientSocket class using loopback connections. - host_mapper_->AddRule("bugs.webkit.org", "bugs.webkit.org"); + : socket_factory_(net::ClientSocketFactory::GetDefaultFactory()) { + } + + void StartOKServer() { + bool success = server_.Start(net::TestServerLauncher::ProtoHTTP, + server_.kHostName, server_.kOKHTTPSPort, + FilePath(), server_.GetOKCertPath()); + ASSERT_TRUE(success); + } + + void StartMismatchedServer() { + bool success = server_.Start(net::TestServerLauncher::ProtoHTTP, + server_.kMismatchedHostName, server_.kOKHTTPSPort, + FilePath(), server_.GetOKCertPath()); + ASSERT_TRUE(success); + } + + void StartExpiredServer() { + bool success = server_.Start(net::TestServerLauncher::ProtoHTTP, + server_.kHostName, server_.kBadHTTPSPort, + FilePath(), server_.GetExpiredCertPath()); + ASSERT_TRUE(success); } protected: - scoped_refptr<net::RuleBasedHostMapper> host_mapper_; - net::ScopedHostMapper scoped_host_mapper_; net::ClientSocketFactory* socket_factory_; + net::TestServerLauncher server_; }; //----------------------------------------------------------------------------- -// bug 1354783 -TEST_F(SSLClientSocketTest, DISABLED_Connect) { +#if defined(OS_MACOSX) +#define MAYBE_Connect DISABLED_Connect +#define MAYBE_ConnectExpired DISABLED_ConnectExpired +#define MAYBE_ConnectMismatched DISABLED_ConnectMismatched +#define MAYBE_Read DISABLED_Read +#define MAYBE_Read_SmallChunks DISABLED_Read_SmallChunks +#define MAYBE_Read_Interrupted DISABLED_Read_Interrupted +#else +#define MAYBE_Connect Connect +#define MAYBE_ConnectExpired ConnectExpired +#define MAYBE_ConnectMismatched ConnectMismatched +#define MAYBE_Read Read +#define MAYBE_Read_SmallChunks Read_SmallChunks +#define MAYBE_Read_Interrupted Read_Interrupted +#endif + +TEST_F(SSLClientSocketTest, MAYBE_Connect) { + StartOKServer(); + net::AddressList addr; net::HostResolver resolver; TestCompletionCallback callback; - std::string hostname = "bugs.webkit.org"; - int rv = resolver.Resolve(hostname, 443, &addr, NULL); + int rv = resolver.Resolve(server_.kHostName, server_.kOKHTTPSPort, + &addr, NULL); EXPECT_EQ(net::OK, rv); scoped_ptr<net::SSLClientSocket> sock( socket_factory_->CreateSSLClientSocket(new net::TCPClientSocket(addr), - hostname, kDefaultSSLConfig)); + server_.kHostName, kDefaultSSLConfig)); EXPECT_FALSE(sock->IsConnected()); rv = sock->Connect(&callback); if (rv != net::OK) { ASSERT_EQ(net::ERR_IO_PENDING, rv); + EXPECT_FALSE(sock->IsConnected()); rv = callback.WaitForResult(); EXPECT_EQ(net::OK, rv); @@ -68,14 +102,79 @@ TEST_F(SSLClientSocketTest, DISABLED_Connect) { EXPECT_FALSE(sock->IsConnected()); } -// bug 1354783 -TEST_F(SSLClientSocketTest, DISABLED_Read) { +TEST_F(SSLClientSocketTest, MAYBE_ConnectExpired) { + StartExpiredServer(); + net::AddressList addr; net::HostResolver resolver; TestCompletionCallback callback; - std::string hostname = "bugs.webkit.org"; - int rv = resolver.Resolve(hostname, 443, &addr, &callback); + int rv = resolver.Resolve(server_.kHostName, server_.kBadHTTPSPort, + &addr, NULL); + EXPECT_EQ(net::OK, rv); + + scoped_ptr<net::SSLClientSocket> sock( + socket_factory_->CreateSSLClientSocket(new net::TCPClientSocket(addr), + server_.kHostName, kDefaultSSLConfig)); + + EXPECT_FALSE(sock->IsConnected()); + + rv = sock->Connect(&callback); + if (rv != net::OK) { + ASSERT_EQ(net::ERR_IO_PENDING, rv); + EXPECT_FALSE(sock->IsConnected()); + + rv = callback.WaitForResult(); + EXPECT_EQ(net::ERR_CERT_DATE_INVALID, rv); + } + + EXPECT_TRUE(sock->IsConnected()); +} + +TEST_F(SSLClientSocketTest, MAYBE_ConnectMismatched) { + StartMismatchedServer(); + + net::AddressList addr; + net::HostResolver resolver; + TestCompletionCallback callback; + + int rv = resolver.Resolve(server_.kMismatchedHostName, server_.kOKHTTPSPort, + &addr, NULL); + EXPECT_EQ(net::OK, rv); + + scoped_ptr<net::SSLClientSocket> sock( + socket_factory_->CreateSSLClientSocket(new net::TCPClientSocket(addr), + server_.kMismatchedHostName, kDefaultSSLConfig)); + + EXPECT_FALSE(sock->IsConnected()); + + rv = sock->Connect(&callback); + if (rv != net::ERR_CERT_COMMON_NAME_INVALID) { + ASSERT_EQ(net::ERR_IO_PENDING, rv); + EXPECT_FALSE(sock->IsConnected()); + + rv = callback.WaitForResult(); + EXPECT_EQ(net::ERR_CERT_COMMON_NAME_INVALID, rv); + } + + // The Windows code happens to keep the connection + // open now in spite of an error. The designers of + // this API intended to also allow the connection + // to be closed on error, in which case the caller + // should call ReconnectIgnoringLastError, but + // that's currently unimplemented. + EXPECT_TRUE(sock->IsConnected()); +} + +TEST_F(SSLClientSocketTest, MAYBE_Read) { + StartOKServer(); + + net::AddressList addr; + net::HostResolver resolver; + TestCompletionCallback callback; + + int rv = resolver.Resolve(server_.kHostName, server_.kOKHTTPSPort, + &addr, &callback); EXPECT_EQ(net::ERR_IO_PENDING, rv); rv = callback.WaitForResult(); @@ -83,7 +182,8 @@ TEST_F(SSLClientSocketTest, DISABLED_Read) { scoped_ptr<net::SSLClientSocket> sock( socket_factory_->CreateSSLClientSocket(new net::TCPClientSocket(addr), - hostname, kDefaultSSLConfig)); + server_.kHostName, + kDefaultSSLConfig)); rv = sock->Connect(&callback); if (rv != net::OK) { @@ -117,19 +217,20 @@ TEST_F(SSLClientSocketTest, DISABLED_Read) { } } -// bug 1354783 -TEST_F(SSLClientSocketTest, DISABLED_Read_SmallChunks) { +TEST_F(SSLClientSocketTest, MAYBE_Read_SmallChunks) { + StartOKServer(); + net::AddressList addr; net::HostResolver resolver; TestCompletionCallback callback; - std::string hostname = "bugs.webkit.org"; - int rv = resolver.Resolve(hostname, 443, &addr, NULL); + int rv = resolver.Resolve(server_.kHostName, server_.kOKHTTPSPort, + &addr, NULL); EXPECT_EQ(net::OK, rv); scoped_ptr<net::SSLClientSocket> sock( socket_factory_->CreateSSLClientSocket(new net::TCPClientSocket(addr), - hostname, kDefaultSSLConfig)); + server_.kHostName, kDefaultSSLConfig)); rv = sock->Connect(&callback); if (rv != net::OK) { @@ -162,19 +263,20 @@ TEST_F(SSLClientSocketTest, DISABLED_Read_SmallChunks) { } } -// bug 1354783 -TEST_F(SSLClientSocketTest, DISABLED_Read_Interrupted) { +TEST_F(SSLClientSocketTest, MAYBE_Read_Interrupted) { + StartOKServer(); + net::AddressList addr; net::HostResolver resolver; TestCompletionCallback callback; - std::string hostname = "bugs.webkit.org"; - int rv = resolver.Resolve(hostname, 443, &addr, NULL); + int rv = resolver.Resolve(server_.kHostName, server_.kOKHTTPSPort, + &addr, NULL); EXPECT_EQ(net::OK, rv); scoped_ptr<net::SSLClientSocket> sock( socket_factory_->CreateSSLClientSocket(new net::TCPClientSocket(addr), - hostname, kDefaultSSLConfig)); + server_.kHostName, kDefaultSSLConfig)); rv = sock->Connect(&callback); if (rv != net::OK) { diff --git a/net/base/ssl_test_util.cc b/net/base/ssl_test_util.cc index d22e4fd..46b241f 100644 --- a/net/base/ssl_test_util.cc +++ b/net/base/ssl_test_util.cc @@ -5,6 +5,8 @@ #include <string> #include <algorithm> +#include "net/base/ssl_test_util.h" + #include "build/build_config.h" #if defined(OS_WIN) @@ -27,16 +29,15 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/path_service.h" +#include "base/platform_thread.h" +#include "base/string_util.h" +#include "net/base/tcp_pinger.h" +#include "net/base/host_resolver.h" +#include "net/base/tcp_client_socket.h" +#include "net/base/test_completion_callback.h" +#include "testing/platform_test.h" -#include "net/base/ssl_test_util.h" - -// static -const char SSLTestUtil::kHostName[] = "127.0.0.1"; -const int SSLTestUtil::kOKHTTPSPort = 9443; -const int SSLTestUtil::kBadHTTPSPort = 9666; - -// The issuer name of the cert that should be trusted for the test to work. -const wchar_t SSLTestUtil::kCertIssuerName[] = L"Test CA"; +namespace { #if defined(OS_LINUX) static CERTCertificate* LoadTemporaryCert(const FilePath& filename) { @@ -67,57 +68,247 @@ static CERTCertificate* LoadTemporaryCert(const FilePath& filename) { rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust); if (rv != SECSuccess) { - LOG(ERROR) << "Can't change trust for certificate " + LOG(ERROR) << "Can't change trust for certificate " << filename.ToWStringHack(); CERT_DestroyCertificate(cert); return NULL; } - LOG(INFO) << "Loaded temporary certificate " << filename.ToWStringHack(); + // TODO(dkegel): figure out how to get this to only happen once? return cert; } #endif -SSLTestUtil::SSLTestUtil() { +} // namespace + +namespace net { + +// static +const char TestServerLauncher::kHostName[] = "127.0.0.1"; +const char TestServerLauncher::kMismatchedHostName[] = "localhost"; +const int TestServerLauncher::kOKHTTPSPort = 9443; +const int TestServerLauncher::kBadHTTPSPort = 9666; + +// 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_(NULL) +#if defined(OS_LINUX) +, cert_(NULL) +#endif +{ PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_); - cert_dir_ = cert_dir_.AppendASCII("net"); - cert_dir_ = cert_dir_.AppendASCII("data"); - cert_dir_ = cert_dir_.AppendASCII("ssl"); - cert_dir_ = cert_dir_.AppendASCII("certificates"); + cert_dir_ = cert_dir_.Append(FILE_PATH_LITERAL("net")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("ssl")) + .Append(FILE_PATH_LITERAL("certificates")); +} + +namespace { + +void AppendToPythonPath(FilePath dir) { + // Do nothing if dir already on path. + +#if defined(OS_WIN) + const wchar_t kPythonPath[] = L"PYTHONPATH"; + // FIXME(dkegel): handle longer PYTHONPATH variables + wchar_t oldpath[4096]; + if (GetEnvironmentVariable(kPythonPath, oldpath, sizeof(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); + 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); + } +#endif +} + +} // end namespace + +void TestServerLauncher::SetPythonPath() { + FilePath third_party_dir; + ASSERT_TRUE(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"))); +} + +bool TestServerLauncher::Start(Protocol protocol, + const std::string& host_name, int port, + const FilePath& document_root, + const FilePath& cert_path) { + if (!TestServerLauncher::CheckCATrusted()) + return false; + + std::string port_str = IntToString(port); + + // Get path to python server script + FilePath testserver_path; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path)) + 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); #if defined(OS_LINUX) - cert_ = reinterpret_cast<PrivateCERTCertificate*>( + if (!cert_ && !cert_path.value().empty()) { + cert_ = reinterpret_cast<PrivateCERTCertificate*>( LoadTemporaryCert(GetRootCertPath())); - DCHECK(cert_); + DCHECK(cert_); + } #endif + + SetPythonPath(); + +#if defined(OS_WIN) + // Get path to python interpreter + if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_runtime_)) + return false; + python_runtime_ = python_runtime_ + .Append(FILE_PATH_LITERAL("third_party")) + .Append(FILE_PATH_LITERAL("python_24")) + .Append(FILE_PATH_LITERAL("python.exe")); + + std::wstring command_line = + L"\"" + python_runtime_.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 (!base::LaunchApp(command_line, false, true, &process_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(WideToUTF8(testserver_path.ToWStringHack())); + command_line.push_back("--port=" + port_str); + command_line.push_back("--data-dir=" + + WideToUTF8(document_root_dir_.ToWStringHack())); + if (protocol == ProtoFTP) + command_line.push_back("-f"); + if (!cert_path.value().empty()) + command_line.push_back("--https=" + WideToUTF8(cert_path.ToWStringHack())); + + 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] << " ..."; + 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"; + Stop(); + return false; + } + + LOG(INFO) << "Started on port " << port_str; + return true; } -SSLTestUtil::~SSLTestUtil() { +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; + net::HostResolver resolver; + int rv = resolver.Resolve(host_name, port, &addr, NULL); + if (rv != net::OK) + return false; + + net::TCPPinger pinger(addr); + rv = pinger.Ping(); + return rv == net::OK; +} + +bool TestServerLauncher::WaitToFinish(int timeout_ms) { + if (!process_handle_) + return true; + + bool ret = base::WaitForSingleProcess(process_handle_, timeout_ms); + if (ret) { + base::CloseProcessHandle(process_handle_); + process_handle_ = NULL; + LOG(INFO) << "Finished."; + } else { + LOG(INFO) << "Timed out."; + } + return ret; +} + +bool TestServerLauncher::Stop() { + if (!process_handle_) + return true; + + bool ret = base::KillProcess(process_handle_, 1, true); + if (ret) { + base::CloseProcessHandle(process_handle_); + process_handle_ = NULL; + LOG(INFO) << "Stopped."; + } else { + LOG(INFO) << "Kill failed?"; + } + + return ret; +} + +TestServerLauncher::~TestServerLauncher() { #if defined(OS_LINUX) if (cert_) CERT_DestroyCertificate(reinterpret_cast<CERTCertificate*>(cert_)); #endif + Stop(); } -FilePath SSLTestUtil::GetRootCertPath() { +FilePath TestServerLauncher::GetRootCertPath() { FilePath path(cert_dir_); path = path.AppendASCII("root_ca_cert.crt"); return path; } -FilePath SSLTestUtil::GetOKCertPath() { +FilePath TestServerLauncher::GetOKCertPath() { FilePath path(cert_dir_); path = path.AppendASCII("ok_cert.pem"); return path; } -FilePath SSLTestUtil::GetExpiredCertPath() { +FilePath TestServerLauncher::GetExpiredCertPath() { FilePath path(cert_dir_); path = path.AppendASCII("expired_cert.pem"); return path; } -bool SSLTestUtil::CheckCATrusted() { +bool TestServerLauncher::CheckCATrusted() { // TODO(port): Port either this or LoadTemporaryCert to MacOSX. #if defined(OS_WIN) HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT"); @@ -146,3 +337,6 @@ bool SSLTestUtil::CheckCATrusted() { #endif return true; } + +} // namespace net + diff --git a/net/base/ssl_test_util.h b/net/base/ssl_test_util.h index 9daa4cc..9340126 100644 --- a/net/base/ssl_test_util.h +++ b/net/base/ssl_test_util.h @@ -5,49 +5,106 @@ #ifndef NET_BASE_SSL_TEST_UTIL_H_ #define NET_BASE_SSL_TEST_UTIL_H_ -#include "build/build_config.h" +#include <string> #include "base/file_path.h" +#include "base/process_util.h" +#include "base/ref_counted.h" +#include "build/build_config.h" -// TODO(dkegel): share this between net/base and +// TODO(dkegel): share this between net/base and // chrome/browser without putting it in net.lib -class SSLTestUtil { - public: - SSLTestUtil(); - - ~SSLTestUtil(); +namespace net { - FilePath GetRootCertPath(); +// This object bounds the lifetime of an external python-based HTTP/HTTPS/FTP +// server that can provide various responses useful for testing. +// A few basic convenience methods are provided, but no +// URL handling methods (those belong at a higher layer, e.g. in +// url_request_unittest.h). +class TestServerLauncher { + public: + TestServerLauncher(); + + virtual ~TestServerLauncher(); + + enum Protocol { + ProtoHTTP, ProtoFTP + }; + + // 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. + // Returns true on success, false if files not found or root cert + // not trusted. + bool Start(Protocol protocol, + const std::string& host_name, int port, + const FilePath& document_root, + const FilePath& cert_path); + + // Stop the server started by Start(). + bool Stop(); + + // If you access the server's Kill url, it will exit by itself + // without a call to Stop(). + // WaitToFinish is handy in that case. + // It returns true if the server exited cleanly. + bool WaitToFinish(int milliseconds); + + // 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_; } + + // Issuer name of the root cert that should be trusted for the test to work. + static const wchar_t kCertIssuerName[]; + // Hostname to use for test server static const char kHostName[]; + // Different hostname to use for test server (that still resolves to same IP) + static const char kMismatchedHostName[]; + // Port to use for test server static const int kOKHTTPSPort; // Port to use for bad test server static const int kBadHTTPSPort; - // Issuer name of the cert that should be trusted for the test to work. - static const wchar_t kCertIssuerName[]; + 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); + + // Append to PYTHONPATH so Python can find pyftpdlib and tlslite. + void SetPythonPath(); + + // Path to our test root certificate. + FilePath GetRootCertPath(); // Returns false if our test root certificate is not trusted. bool CheckCATrusted(); - private: + FilePath document_root_dir_; + FilePath cert_dir_; + FilePath python_runtime_; + + base::ProcessHandle process_handle_; + #if defined(OS_LINUX) struct PrivateCERTCertificate; PrivateCERTCertificate *cert_; #endif - DISALLOW_COPY_AND_ASSIGN(SSLTestUtil); + DISALLOW_COPY_AND_ASSIGN(TestServerLauncher); }; -#endif // NET_BASE_SSL_TEST_UTIL_H_ +} + +#endif // NET_BASE_SSL_TEST_UTIL_H_ + |