summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authordkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-18 23:42:36 +0000
committerdkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-18 23:42:36 +0000
commitc8816c164145d55d46635aface02072461d4824f (patch)
tree554fbf9f5ee4d91f443b15f15ccd0ca1a307ca68 /net/base
parent27866a0b950ded8afb87120628af72dfd0b3582d (diff)
downloadchromium_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.cc53
-rw-r--r--net/base/ssl_client_socket_nss.h18
-rw-r--r--net/base/ssl_client_socket_unittest.cc158
-rw-r--r--net/base/ssl_test_util.cc240
-rw-r--r--net/base/ssl_test_util.h85
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_
+