summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/base/ssl_client_socket_nss.cc196
-rw-r--r--net/base/ssl_client_socket_nss.h2
-rw-r--r--net/base/ssl_test_util.cc148
-rw-r--r--net/base/ssl_test_util.h53
-rw-r--r--net/build/net.vcproj4
-rw-r--r--net/data/ssl/certificates/expired_cert.pem41
-rw-r--r--net/data/ssl/certificates/invalid_cert.pem41
-rw-r--r--net/data/ssl/certificates/ok_cert.pem41
-rw-r--r--net/data/ssl/certificates/root_ca_cert.crt35
-rw-r--r--net/net.xcodeproj/project.pbxproj16
-rw-r--r--net/net_unittests.scons1
-rw-r--r--net/url_request/url_request_unittest.cc47
12 files changed, 579 insertions, 46 deletions
diff --git a/net/base/ssl_client_socket_nss.cc b/net/base/ssl_client_socket_nss.cc
index c954ec9..ffd1023 100644
--- a/net/base/ssl_client_socket_nss.cc
+++ b/net/base/ssl_client_socket_nss.cc
@@ -6,10 +6,12 @@
#include <nspr.h>
#include <nss.h>
+#include <secerr.h>
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
// until NSS 3.12.2 comes out and we update to it.
#define Lock FOO_NSS_Lock
#include <ssl.h>
+#include <sslerr.h>
#include <pk11pub.h>
#undef Lock
@@ -21,18 +23,14 @@
static const int kRecvBufferSize = 4096;
-/*
- * nss calls this if an incoming certificate is invalid.
- * TODO(port): expose to app via GetSSLInfo so it can put up
- * the appropriate GUI and retry with override if desired
- */
-static SECStatus
-ownBadCertHandler(void * arg, PRFileDesc * socket)
-{
- PRErrorCode err = PR_GetError();
- LOG(ERROR) << "server certificate is invalid; NSS error code " << err;
- // Return SECSuccess to override the problem, SECFailure to let the original function fail
- return SECSuccess; /* override, say it's OK. */
+// nss calls this if an incoming certificate is invalid.
+static 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.
+ return SECFailure;
}
@@ -44,6 +42,7 @@ namespace net {
#define EnterFunction(x)
#define LeaveFunction(x)
#define GotoState(s) next_state_ = s
+#define LogData(s, len)
#else
#define EnterFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
" enter " << x << "; next_state " << next_state_
@@ -51,14 +50,85 @@ namespace net {
" leave " << x << "; next_state " << next_state_
#define GotoState(s) do { LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
" jump to state " << s; next_state_ = s; } while (0)
+#define LogData(s, len) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
+ " data [" << std::string(s, len) << "]";
+
#endif
+namespace {
+
+int NetErrorFromNSPRError(PRErrorCode err) {
+ // TODO(port): fill this out as we learn what's important
+ switch (err) {
+ case PR_WOULD_BLOCK_ERROR:
+ return ERR_IO_PENDING;
+ case SSL_ERROR_NO_CYPHER_OVERLAP:
+ return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
+ case SSL_ERROR_BAD_CERT_DOMAIN:
+ return ERR_CERT_COMMON_NAME_INVALID;
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ return ERR_CERT_DATE_INVALID;
+ case SEC_ERROR_BAD_SIGNATURE:
+ return ERR_CERT_INVALID;
+ case SSL_ERROR_REVOKED_CERT_ALERT:
+ case SEC_ERROR_REVOKED_CERTIFICATE:
+ case SEC_ERROR_REVOKED_KEY:
+ return ERR_CERT_REVOKED;
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ return ERR_CERT_AUTHORITY_INVALID;
+
+ default: {
+ if (IS_SSL_ERROR(err)) {
+ LOG(WARNING) << "Unknown SSL error " << err <<
+ " mapped to net::ERR_SSL_PROTOCOL_ERROR";
+ return ERR_SSL_PROTOCOL_ERROR;
+ }
+ if (IS_SEC_ERROR(err)) {
+ // TODO(port): Probably not the best mapping
+ LOG(WARNING) << "Unknown SEC error " << err <<
+ " mapped to net::ERR_CERT_INVALID";
+ return ERR_CERT_INVALID;
+ }
+ LOG(WARNING) << "Unknown error " << err <<
+ " mapped to net::ERR_FAILED";
+ return ERR_FAILED;
+ }
+ }
+}
+
+// Shared with the Windows code. TODO(avi): merge to a common place
+int CertStatusFromNetError(int error) {
+ switch (error) {
+ case ERR_CERT_COMMON_NAME_INVALID:
+ return CERT_STATUS_COMMON_NAME_INVALID;
+ case ERR_CERT_DATE_INVALID:
+ return CERT_STATUS_DATE_INVALID;
+ case ERR_CERT_AUTHORITY_INVALID:
+ return CERT_STATUS_AUTHORITY_INVALID;
+ case ERR_CERT_NO_REVOCATION_MECHANISM:
+ return CERT_STATUS_NO_REVOCATION_MECHANISM;
+ case ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
+ return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+ case ERR_CERT_REVOKED:
+ return CERT_STATUS_REVOKED;
+ case ERR_CERT_CONTAINS_ERRORS:
+ NOTREACHED();
+ // Falls through.
+ case ERR_CERT_INVALID:
+ return CERT_STATUS_INVALID;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
bool SSLClientSocketNSS::nss_options_initialized_ = false;
SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
const std::string& hostname,
const SSLConfig& ssl_config)
- :
+ :
buffer_send_callback_(this, &SSLClientSocketNSS::BufferSendComplete),
buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete),
transport_send_busy_(false),
@@ -70,6 +140,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
user_callback_(NULL),
user_buf_(NULL),
user_buf_len_(0),
+ server_cert_status_(0),
completed_handshake_(false),
next_state_(STATE_NONE),
nss_fd_(NULL),
@@ -107,7 +178,8 @@ int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
return rv;
}
-int SSLClientSocketNSS::ReconnectIgnoringLastError(CompletionCallback* callback) {
+int SSLClientSocketNSS::ReconnectIgnoringLastError(
+ CompletionCallback* callback) {
EnterFunction("");
// TODO(darin): implement me!
LeaveFunction("");
@@ -134,7 +206,7 @@ bool SSLClientSocketNSS::IsConnected() const {
}
int SSLClientSocketNSS::Read(char* buf, int buf_len,
- CompletionCallback* callback) {
+ CompletionCallback* callback) {
EnterFunction(buf_len);
DCHECK(completed_handshake_);
DCHECK(next_state_ == STATE_NONE);
@@ -148,7 +220,7 @@ int SSLClientSocketNSS::Read(char* buf, int buf_len,
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
- LeaveFunction("");
+ LeaveFunction(rv);
return rv;
}
@@ -167,14 +239,30 @@ int SSLClientSocketNSS::Write(const char* buf, int buf_len,
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
- LeaveFunction("");
+ LeaveFunction(rv);
return rv;
}
void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
EnterFunction("");
- // TODO(port): implement!
ssl_info->Reset();
+ SSLChannelInfo channel_info;
+ SECStatus ok = SSL_GetChannelInfo(nss_fd_,
+ &channel_info, sizeof(channel_info));
+ if (ok == SECSuccess) {
+ SSLCipherSuiteInfo cipher_info;
+ ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite,
+ &cipher_info, sizeof(cipher_info));
+ if (ok == SECSuccess) {
+ ssl_info->security_bits = cipher_info.effectiveKeyBits;
+ } else {
+ ssl_info->security_bits = -1;
+ NOTREACHED();
+ }
+ }
+ ssl_info->cert_status = server_cert_status_;
+ // TODO(port): implement X509Certificate so we can set the cert field!
+ // CERTCertificate *nssCert = SSL_PeerCertificate(nss_fd_);
LeaveFunction("");
}
@@ -209,9 +297,9 @@ static PRErrorCode MapErrorToNSS(int result) {
return PR_UNKNOWN_ERROR;
}
-/*
+/*
* Do network I/O between the given buffer and the given socket.
- * Return 0 for EOF,
+ * Return 0 for EOF,
* > 0 for bytes transferred immediately,
* < 0 for error (or the non-error ERR_IO_PENDING).
*/
@@ -283,9 +371,9 @@ int SSLClientSocketNSS::DoLoop(int last_io_result) {
do {
network_moved = false;
// Default to STATE_NONE for next state.
- // (This is a quirk carried over from the windows
+ // (This is a quirk carried over from the windows
// implementation. It makes reading the logs a bit harder.)
- // State handlers can and often do call GotoState just
+ // State handlers can and often do call GotoState just
// to stay in the current state.
State state = next_state_;
GotoState(STATE_NONE);
@@ -357,7 +445,7 @@ int SSLClientSocketNSS::DoConnectComplete(int result) {
}
memio_SetPeerName(nss_fd_, &peername);
- // Grab pointer to buffers
+ // Grab pointer to buffers
nss_bufs_ = memio_GetSecret(nss_fd_);
/* Create SSL state machine */
@@ -378,18 +466,37 @@ int SSLClientSocketNSS::DoConnectComplete(int result) {
if (rv != SECSuccess)
return ERR_UNEXPECTED;
+ // SNI is enabled automatically if TLS is enabled -- as long as
+ // SSL_V2_COMPATIBLE_HELLO isn't.
+ // So don't do V2 compatible hellos unless we're really using SSL2,
+ // to avoid errors like
+ // "common name `mail.google.com' != requested host name `gmail.com'"
+ rv = SSL_OptionSet(nss_fd_, SSL_V2_COMPATIBLE_HELLO,
+ ssl_config_.ssl2_enabled);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled);
if (rv != SECSuccess)
return ERR_UNEXPECTED;
- rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.tls1_enabled);
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled);
if (rv != SECSuccess)
return ERR_UNEXPECTED;
+#ifdef SSL_ENABLE_SESSION_TICKETS
+ // Support RFC 5077
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
+ if (rv != SECSuccess)
+ LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?";
+#else
+ #error "You need to install NSS-3.12 or later to build chromium"
+#endif
+
rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
if (rv != SECSuccess)
return ERR_UNEXPECTED;
-
+
rv = SSL_BadCertHook(nss_fd_, ownBadCertHandler, NULL);
if (rv != SECSuccess)
return ERR_UNEXPECTED;
@@ -407,31 +514,39 @@ int SSLClientSocketNSS::DoConnectComplete(int result) {
int SSLClientSocketNSS::DoHandshakeRead() {
EnterFunction("");
+ int net_error;
int rv = SSL_ForceHandshake(nss_fd_);
+
if (rv == SECSuccess) {
+ net_error = OK;
// there's a callback for this, too
completed_handshake_ = true;
// Indicate we're ready to handle I/O. Badly named?
GotoState(STATE_NONE);
- LeaveFunction("");
- return OK;
- }
- PRErrorCode prerr = PR_GetError();
- if (prerr == PR_WOULD_BLOCK_ERROR) {
- // at this point, it should have tried to send some bytes
- GotoState(STATE_HANDSHAKE_READ);
- LeaveFunction("");
- return ERR_IO_PENDING;
+ } else {
+ PRErrorCode prerr = PR_GetError();
+ net_error = NetErrorFromNSPRError(prerr);
+
+ // If not done, stay in this state
+ if (net_error == ERR_IO_PENDING) {
+ GotoState(STATE_HANDSHAKE_READ);
+ } else {
+ server_cert_status_ = CertStatusFromNetError(net_error);
+ LOG(ERROR) << "handshake failed; NSS error code " << prerr
+ << ", net_error " << net_error << ", server_cert_status "
+ << server_cert_status_;
+ }
}
- // TODO: map rv to net error code properly
+
LeaveFunction("");
- return ERR_SSL_PROTOCOL_ERROR;
+ return net_error;
}
-
+
int SSLClientSocketNSS::DoPayloadRead() {
EnterFunction(user_buf_len_);
int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_);
if (rv >= 0) {
+ LogData(user_buf_, rv);
user_buf_ = NULL;
LeaveFunction("");
return rv;
@@ -444,14 +559,14 @@ int SSLClientSocketNSS::DoPayloadRead() {
}
user_buf_ = NULL;
LeaveFunction("");
- // TODO: map rv to net error code properly
- return ERR_SSL_PROTOCOL_ERROR;
+ return NetErrorFromNSPRError(prerr);
}
int SSLClientSocketNSS::DoPayloadWrite() {
EnterFunction(user_buf_len_);
int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_);
if (rv >= 0) {
+ LogData(user_buf_, rv);
user_buf_ = NULL;
LeaveFunction("");
return rv;
@@ -463,8 +578,7 @@ int SSLClientSocketNSS::DoPayloadWrite() {
}
user_buf_ = NULL;
LeaveFunction("");
- // TODO: map rv to net error code properly
- return ERR_SSL_PROTOCOL_ERROR;
+ return NetErrorFromNSPRError(prerr);
}
} // namespace net
diff --git a/net/base/ssl_client_socket_nss.h b/net/base/ssl_client_socket_nss.h
index 41098f3..5015e1e 100644
--- a/net/base/ssl_client_socket_nss.h
+++ b/net/base/ssl_client_socket_nss.h
@@ -76,6 +76,8 @@ class SSLClientSocketNSS : public SSLClientSocket {
char* user_buf_;
int user_buf_len_;
+ int server_cert_status_;
+
bool completed_handshake_;
enum State {
diff --git a/net/base/ssl_test_util.cc b/net/base/ssl_test_util.cc
index e69de29..18e3905 100644
--- a/net/base/ssl_test_util.cc
+++ b/net/base/ssl_test_util.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2006-2008 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 <string>
+#include <algorithm>
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+#elif defined(OS_LINUX)
+#include <nspr.h>
+#include <nss.h>
+#include <secerr.h>
+// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
+// until NSS 3.12.2 comes out and we update to it.
+#define Lock FOO_NSS_Lock
+#include <ssl.h>
+#include <sslerr.h>
+#include <pk11pub.h>
+#undef Lock
+#include "base/nss_init.h"
+#endif
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.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";
+
+#if defined(OS_LINUX)
+static CERTCertificate* LoadTemporaryCert(const FilePath& filename) {
+ base::EnsureNSSInit();
+
+ std::string rawcert;
+ if (!file_util::ReadFileToString(filename.ToWStringHack(), &rawcert)) {
+ LOG(ERROR) << "Can't load certificate " << filename.ToWStringHack();
+ return NULL;
+ }
+
+ CERTCertificate *cert;
+ cert = CERT_DecodeCertFromPackage(const_cast<char *>(rawcert.c_str()),
+ rawcert.length());
+ if (!cert) {
+ LOG(ERROR) << "Can't convert certificate " << filename.ToWStringHack();
+ return NULL;
+ }
+
+ // TODO(port): remove this const_cast after NSS 3.12.3 is released
+ CERTCertTrust trust;
+ int rv = CERT_DecodeTrustString(&trust, const_cast<char *>("TCu,Cu,Tu"));
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "Can't decode trust string";
+ CERT_DestroyCertificate(cert);
+ return NULL;
+ }
+
+ rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust);
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "Can't change trust for certificate "
+ << filename.ToWStringHack();
+ CERT_DestroyCertificate(cert);
+ return NULL;
+ }
+
+ LOG(INFO) << "Loaded temporary certificate " << filename.ToWStringHack();
+ return cert;
+}
+#endif
+
+SSLTestUtil::SSLTestUtil() {
+ PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
+ cert_dir_ = cert_dir_.Append(FILE_PATH_LITERAL("net"));
+ cert_dir_ = cert_dir_.Append(FILE_PATH_LITERAL("data"));
+ cert_dir_ = cert_dir_.Append(FILE_PATH_LITERAL("ssl"));
+ cert_dir_ = cert_dir_.Append(FILE_PATH_LITERAL("certificates"));
+
+#if defined(OS_LINUX)
+ cert_ = reinterpret_cast<PrivateCERTCertificate*>(
+ LoadTemporaryCert(GetRootCertPath()));
+ DCHECK(cert_);
+#endif
+}
+
+SSLTestUtil::~SSLTestUtil() {
+#if defined(OS_LINUX)
+ if (cert_)
+ CERT_DestroyCertificate(reinterpret_cast<CERTCertificate*>(cert_));
+#endif
+}
+
+FilePath SSLTestUtil::GetRootCertPath() {
+ FilePath path(cert_dir_);
+ path = path.Append(FILE_PATH_LITERAL("root_ca_cert.crt"));
+ return path;
+}
+
+FilePath SSLTestUtil::GetOKCertPath() {
+ FilePath path(cert_dir_);
+ path = path.Append(FILE_PATH_LITERAL("ok_cert.pem"));
+ return path;
+}
+
+FilePath SSLTestUtil::GetExpiredCertPath() {
+ FilePath path(cert_dir_);
+ path = path.Append(FILE_PATH_LITERAL("expired_cert.pem"));
+ return path;
+}
+
+bool SSLTestUtil::CheckCATrusted() {
+// TODO(port): Port either this or LoadTemporaryCert to MacOSX.
+#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;
+ }
+#endif
+ return true;
+}
diff --git a/net/base/ssl_test_util.h b/net/base/ssl_test_util.h
index e69de29..9daa4cc 100644
--- a/net/base/ssl_test_util.h
+++ b/net/base/ssl_test_util.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef NET_BASE_SSL_TEST_UTIL_H_
+#define NET_BASE_SSL_TEST_UTIL_H_
+
+#include "build/build_config.h"
+
+#include "base/file_path.h"
+
+// TODO(dkegel): share this between net/base and
+// chrome/browser without putting it in net.lib
+
+class SSLTestUtil {
+ public:
+ SSLTestUtil();
+
+ ~SSLTestUtil();
+
+ FilePath GetRootCertPath();
+
+ FilePath GetOKCertPath();
+
+ FilePath GetExpiredCertPath();
+
+ // Hostname to use for test server
+ static const char kHostName[];
+
+ // 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[];
+
+ // Returns false if our test root certificate is not trusted.
+ bool CheckCATrusted();
+
+ private:
+ FilePath cert_dir_;
+
+#if defined(OS_LINUX)
+ struct PrivateCERTCertificate;
+ PrivateCERTCertificate *cert_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(SSLTestUtil);
+};
+
+#endif // NET_BASE_SSL_TEST_UTIL_H_
diff --git a/net/build/net.vcproj b/net/build/net.vcproj
index fed4d4f..4530a27 100644
--- a/net/build/net.vcproj
+++ b/net/build/net.vcproj
@@ -461,6 +461,10 @@
>
</File>
<File
+ RelativePath="..\base\ssl_test_util.cc"
+ >
+ </File>
+ <File
RelativePath="..\base\tcp_client_socket.h"
>
</File>
diff --git a/net/data/ssl/certificates/expired_cert.pem b/net/data/ssl/certificates/expired_cert.pem
new file mode 100644
index 0000000..370330d
--- /dev/null
+++ b/net/data/ssl/certificates/expired_cert.pem
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIEZDCCAkygAwIBAgIBAzANBgkqhkiG9w0BAQUFADBgMRAwDgYDVQQDEwdUZXN0
+IENBMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
+TW91bnRhaW4gVmlldzESMBAGA1UEChMJQ2VydCBUZXN0MB4XDTA2MDEwMTAwMDAw
+MFoXDTA3MDEwMTAwMDAwMFowSjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
+b3JuaWExEjAQBgNVBAoTCUNlcnQgVGVzdDESMBAGA1UEAxMJMTI3LjAuMC4xMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUjUPzXBw61Xsh1RGSyJowrqC+TYv
+pf44P2c5sF/Y/KX0LoJEA72in+2d7N8sf3IsJvy6X1uPnVdX7wKzgtHI6FFjOcbR
+zs9MiyEJkeFKADI+ZMV0httNOy93ajESsjZMwgoHMKOv5ikNCdwE/nefSfu6Zuap
+CsadArkHe3d5dwIDAQABo4HCMIG/MAkGA1UdEwQCMAAwHQYDVR0OBBYEFNr8t46H
+i0oOuzYzmjE24g25B9fKMIGSBgNVHSMEgYowgYeAFF3Of5nj1BlBMU/Gz7El9Vqv
+45cxoWSkYjBgMRAwDgYDVQQDEwdUZXN0IENBMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzESMBAGA1UEChMJ
+Q2VydCBUZXN0ggkA1FGT1D/e2U4wDQYJKoZIhvcNAQEFBQADggIBABdhpWwsDDAn
+eQKSk70sigbUk1eeGzE0AR2nNfs1N/Lx+EYBmWdOEZTi5ngwJoDavb6okaOWRYV5
+nIyIWGuJQfJ6OPZycNn9HMC2vL9er2gu6y8/6Sob4POThAdf519oHXFWR7S5fnPK
+rvbspRHm/gycMA3Lvkzm2tHEQI4d+3pKAPtGtDUEnHEuo13tbaA3N8RJXStJXp4y
+foqYEPJpG5SD3hDBTlVbg6J3li6B4r2LfCMgRXjrsNJSBamPrXX8n+K2SzCPLDli
+9s387TMKLxGI7v5y5BcLcxuvBrAGS5rUw1q6+wNAvAkOSai6SfBmhUSn4OwQl0yX
+y/1IOj2iCEwUJDMHQRp0K03xvN+nAaqFbnbBxVcL42lgMkLt2cL+X4tkBPzx2/Ys
+4tuEk2/BftaHX0rby7OWCCh8q9QM+CmXzOipbwsjViTBpdR99RP7lrhojO3Tkx58
+bxHBj9EpOjxCQyv1x3xZ5GpyAIgSduVusHwk7FJBNetg5Csa0GBEr2d6Lh+zPSm/
+ZPgWqaVH6YoY+2HP2Mi2YxNYWJOR3ycNOxTuTp3M/glDBFYJaB5/PXTzFKrzo/vE
+za7vs4El0Ei7YZYC0vxqpUbiS6l7Ul+TPfk6q/OAkYjaJ76uYlcKVmN060wK83z5
+Vtoy6YuY/8hrMRl3jrMaiVsMSMg+nOsM
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDuUjUPzXBw61Xsh1RGSyJowrqC+TYvpf44P2c5sF/Y/KX0LoJE
+A72in+2d7N8sf3IsJvy6X1uPnVdX7wKzgtHI6FFjOcbRzs9MiyEJkeFKADI+ZMV0
+httNOy93ajESsjZMwgoHMKOv5ikNCdwE/nefSfu6ZuapCsadArkHe3d5dwIDAQAB
+AoGARF1wu0fDqaDY1TE9C60iActalZfH44NKmfgWNIZT+HsBYDq3Q6uPCse18kew
+/aLRrbjBZvVT3q7UD3WQ4M32P44rEAAhdYGhMndF53xrtX5SQ7LPb381mlPoJ2AE
+Wv9R+0X45aRtiSC/Y7E9ebN5p4RY9NjRUjcaI67d1NgNAwECQQD6k83u6R73tsJt
+k2OCTnUxvQi9Oo5GwDfiubHOQx3tG3ZDIjsyg1GCJa/PVRiP6PXkgIoAWmiyGFIR
++gIO86kPAkEA83qA4L4HxLQLapDvBDNgiz9eHA/FJvsk5yH0jupR+CDLRqM6Dfzj
+iLeztyJV3n96lMDIc4lEzplysDw/TD6ZGQJABzYFsSBoUbyir8CUFoXnfXzxKXDr
+80FI2m95nHP2AoLNznTHu1ZoHBS1kIPkZw/PM7o3Pndbl/R3OCk3sfJ1JQJABSB7
+PkBCIZ4471GF9uyuB8CZ1rVuTVfUexl38Np2R2dJYH9nZYwBrotPuXd7n2fHyQIW
+NniZflA03gkmvBBFYQJADftQ5+jabFxXkqwZsrsvtG+Ji53BZrLrczFxgPyGVfUF
+A1pyQE3M6SykYjp+S3Zg6eS+yKE06guecEVhTQtwBA==
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/ssl/certificates/invalid_cert.pem b/net/data/ssl/certificates/invalid_cert.pem
new file mode 100644
index 0000000..8507299
--- /dev/null
+++ b/net/data/ssl/certificates/invalid_cert.pem
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIEZDCCAkygAwIBAgIBAjANBgkqhkiG9w0BAQUFADBgMRAwDgYDVQQDEwdUZXN0
+IENBMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
+TW91bnRhaW4gVmlldzESMBAGA1UEChMJQ2VydCBUZXN0MB4XDTA4MDcyODIyMzMx
+M1oXDTEzMDcyNzIyMzMxM1owSjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
+b3JuaWExEjAQBgNVBAoTCUNlcnQgVGVzdDESMBAGA1UEAxMJMTI3LjAuMC4xMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyzyyQzKTllTQnbitgRp+AdEPfmmed
+6FdlXidUMJYI4bUHxbEdqsnzkdParf6iVsRcghaqIHAaGhPaG/68zYKgIvfGwiEM
+6+ddTiVFpw8gdF5XfXLotE7VjE/DoDfTmXqiNg9IzcUb4Gm5rWgVHkPZvmge5GtI
+Odbtdm2GaA2wiQIDAQABo4HCMIG/MAkGA1UdEwQCMAAwHQYDVR0OBBYEFGRByitN
+R6WoVGWe1L9HgR4uc64AMIGSBgNVHSMEgYowgYeAFF3Of5nj1BlBMU/Gz7El9Vqv
+45cxoWSkYjBgMRAwDgYDVQQDEwdUZXN0IENBMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzESMBAGA1UEChMJ
+Q2VydCBUZXN0ggkA1FGT1D/e2U4wDQYJKoZIhvcNAQEFBQADggIBACypwHlR1RtX
+HvbjTh45Zb7RqzYDHEUVPfau9aImq+6T2mhwIGyE4FpkIr5HOuA+fvcyDJVE8d0E
+1SI6IVgBIYh9npI2EvsgC4ifCh/yxzHa4ouz/w4+BslemhMFt8iNlCL6Rqh1NlNA
+NXJL2UiqwBd5m9Qhg2JKdWu59bCusgVSPEmMx2qE4EzUlUPBzCIKPtjuF7BFl+6R
+zFNZ9l25CHa3CRPomHZyKNzBN8y+SiGMUXH0psndnAXvO/OWzGyxcW8xntUqjTE1
+Tw/MjzNPOZPE5S8JR7QZ1zsL+3hhXaf/9CLGG6bPCGgGsF55I6wyOhnxmP2s9jTB
+oA7GrjqZZzOs1MZZxfuzw5xopElf3PEpjCARxhBklFVFTK3upg5mMBFLxU+X7RLm
+vZJo3Ml5Gj6TtoisXRjcYyw8+6s3M0kkCBstZygIC8WWSCCkq6yFID8Vn1T82Z0z
+xRlAQSVDns6S/Yl0n1eV6RY/jG5+EcXynxV4WCN0I6KbArv98vekY5GjqjiLgFjp
+73BdX0S/A2ke+JcAh0FAr+VHWx0njv30fjW4x/nkzUApUDRxJeW3Eo7VX53Jd9S5
+Kle5Xn88FHDrKGw5U7QBiF8qJUNLFo6VHtpXYZuUEXzRwzVzRJhnqgW+VsGYxRAc
+Fzn33aqQpans9ui4986+/xBo7XHhPVm9
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCyzyyQzKTllTQnbitgRp+AdEPfmmed6FdlXidUMJYI4bUHxbEd
+qsnzkdParf6iVsRcghaqIHAaGhPaG/68zYKgIvfGwiEM6+ddTiVFpw8gdF5XfXLo
+tE7VjE/DoDfTmXqiNg9IzcUb4Gm5rWgVHkPZvmge5GtIOdbtdm2GaA2wiQIDAQAB
+AoGAUgdKyY6oIw5ko6Ydzgf4CiTMMlsSJ9W7qiwcXLoBSkfJFayyiQqdpNiEWPCz
+POQTJuEi5p8E7mSCMO/g19prjqjORbZUORepkyi30/m9zeB+ofKnKbJLZtEeXqtE
+bUOgYSwG6Ez5HzEH0vpDXvewbzSi4KDgAHc+0Xho//FoVgUCQQDWocEQi5gC39Fo
+W6EXoay2m+awghpsFEWBcY6Ylhvl1Qj2JFdc8lO3oFULFZ09Xv1f84YlchonmzzQ
+I84HMk6bAkEA1UXh0ulgM/r2huo5Lsbne0Vex9bbo1quC2LEx/ttBpfojcki4RAf
+J5PTbdMtY8gHwp2GhInydwV8mJvrf4z9qwJBALPJQQDL/eSCeVYyUumJqzQ3uXCn
+OtzHykUYyzqzmuDVp7EPVgLslgXeDv6+9zy+Qf3K7taVEqWUmsc7BszubfsCQDBq
+ou+cp4hlat4KAl1KLnbfsrqvXkFhbq2LCJ1VyNR8XAEELnTlkg0MFC2UiXNjj89w
+r38bSf4XMsf6baYRPe0CQBwK/MKoHT8Qo8ztKlRxXBqKmEqC0Ou09mGefgRjfkqA
+tzDhSXaZwLYrAe2AXIquudndBvdtkoouSu414toGMcg=
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/ssl/certificates/ok_cert.pem b/net/data/ssl/certificates/ok_cert.pem
new file mode 100644
index 0000000..6349b78
--- /dev/null
+++ b/net/data/ssl/certificates/ok_cert.pem
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIEZDCCAkygAwIBAgIBATANBgkqhkiG9w0BAQUFADBgMRAwDgYDVQQDEwdUZXN0
+IENBMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
+TW91bnRhaW4gVmlldzESMBAGA1UEChMJQ2VydCBUZXN0MB4XDTA4MDcyODIyMzIy
+OFoXDTEzMDcyNzIyMzIyOFowSjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
+b3JuaWExEjAQBgNVBAoTCUNlcnQgVGVzdDESMBAGA1UEAxMJMTI3LjAuMC4xMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQj2tPWPUgbuI4H3/3dnttqVbndwU3
+3BdRCd67DFM44GRrsjDSH4bY/EbFyX9D52d/iy6ZaAmDePcCz5k/fgP3DMujykYG
+qgNiV2ywxTlMj7NlN2C7SRt68fQMZr5iI7rypdxuaZt9lSMD3ENBffYtuLTyZd9a
+3JPJe1TaIab5GwIDAQABo4HCMIG/MAkGA1UdEwQCMAAwHQYDVR0OBBYEFCYLBv5K
+x5sLNVlpLh5FwTwhdDl7MIGSBgNVHSMEgYowgYeAFF3Of5nj1BlBMU/Gz7El9Vqv
+45cxoWSkYjBgMRAwDgYDVQQDEwdUZXN0IENBMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzESMBAGA1UEChMJ
+Q2VydCBUZXN0ggkA1FGT1D/e2U4wDQYJKoZIhvcNAQEFBQADggIBAEtkVmLObUgk
+b2cIA2S+QDtifq1UgVfBbytvR2lFmnADOR55mo0gHQG3HHqq4g034LmoVXDHhUk8
+Gb6aFiv4QubmVhLXcUelTRXwiNvGzkW7pC6Jrq105hdPjzXMKTcmiLaopm5Fqfc7
+hj5Cn1Sjspc8pdeQjrbeMdvca7KlFrGP8YkwCU2xOOX9PiN9G0966BWfjnr/fZZp
++OQVuUFHdiAZwthEMuDpAAXHqYXIsermgdOpgJaA53cf8NqBV2QGhtFgtsJCRoiu
+7DKqhyRWBGyz19VIH2b7y+6qvQVxuHk19kKRM0nftw/yNcJnm7gtttespMUPsOMa
+a2SD1G0hm0TND6vxaBhgR3cVqpl/qIpAdFi00Tm7hTyYE7I43zPW03t+/DpCt3Um
+EMRZsQ90co5q+bcx/vQ7YAtwUh30uMb0wpibeyCwDp8cqNmSiRkEuc/FjTYes5t8
+5gR//WX1l0+qjrjusO9NmoLnq2Yk6UcioX+z+q6Z/dudGfqhLfeWD2Q0LWYA242C
+d7km5Y3KAt1PJdVsof/aiVhVdddY/OIEKTRQhWEdDbosy2eh16BCKXT2FFvhNDg1
+AYFvn6I8nj9IldMJiIc3DdhacEAEzRMeRgPdzAa1griKUGknxsyTyRii8ru0WS6w
+DCNrlDOVXdzYGEZooBI76BDVY0W0akjV
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDQj2tPWPUgbuI4H3/3dnttqVbndwU33BdRCd67DFM44GRrsjDS
+H4bY/EbFyX9D52d/iy6ZaAmDePcCz5k/fgP3DMujykYGqgNiV2ywxTlMj7NlN2C7
+SRt68fQMZr5iI7rypdxuaZt9lSMD3ENBffYtuLTyZd9a3JPJe1TaIab5GwIDAQAB
+AoGANHXu8z2YIzlhE+bwhGm8MGBpKL3qhRuKjeriqMA36tWezOw8lY4ymEAU+Ulv
+BsCdaxqydQoTYou57m4TyUHEcxq9pq3H0zB0qL709DdHi/t4zbV9XIoAzC5v0/hG
+9+Ca29TwC02FCw+qLkNrtwCpwOcQmc+bPxqvFu1iMiahURECQQD2I/Hi2413CMZz
+TBjl8fMiVO9GhA2J0sc8Qi+YcgJakaLD9xcbaiLkTzPZDlA389C1b6Ia+poAr4YA
+Ve0FFbxpAkEA2OobayyHE/QtPEqoy6NLR57jirmVBNmSWWd4lAyL5UIHIYVttJZg
+8CLvbzaU/iDGwR+wKsM664rKPHEmtlyo4wJBAMeSqYO5ZOCJGu9NWjrHjM3fdAsG
+8zs2zhiLya+fcU0iHIksBW5TBmt71Jw/wMc9R5J1K0kYvFml98653O5si1ECQBCk
+RV4/mE1rmlzZzYFyEcB47DQkcM5ictvxGEsje0gnfKyRtAz6zI0f4QbDRUMJ+LWw
+XK+rMsYHa+SfOb0b9skCQQCLdeonsIpFDv/Uv+flHISy0WA+AFkLXrRkBKh6G/OD
+dMHaNevkJgUnpceVEnkrdenp5CcEoFTI17pd+nBgDm/B
+-----END RSA PRIVATE KEY-----
diff --git a/net/data/ssl/certificates/root_ca_cert.crt b/net/data/ssl/certificates/root_ca_cert.crt
new file mode 100644
index 0000000..873b4f7
--- /dev/null
+++ b/net/data/ssl/certificates/root_ca_cert.crt
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGCTCCA/GgAwIBAgIJANRRk9Q/3tlOMA0GCSqGSIb3DQEBBQUAMGAxEDAOBgNV
+BAMTB1Rlc3QgQ0ExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw
+FAYDVQQHEw1Nb3VudGFpbiBWaWV3MRIwEAYDVQQKEwlDZXJ0IFRlc3QwHhcNMDgw
+NzI4MjIzMjAxWhcNMTMwNzI3MjIzMjAxWjBgMRAwDgYDVQQDEwdUZXN0IENBMQsw
+CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRh
+aW4gVmlldzESMBAGA1UEChMJQ2VydCBUZXN0MIICIjANBgkqhkiG9w0BAQEFAAOC
+Ag8AMIICCgKCAgEAuyR/rlxmPi+L48rXdlACgz+Cq06jf8Ns0H/eaofQCB7odsKu
+CXzyngqGetohqAmW8XNzGKAlHzjJcnySzhxG8V3V22aILTI7lfSxrNpLKGaJ69MO
+lg6zFty/zkZ94oRX6Q1t2yRrWBN4isXMOxwRg7cGzuN8ejG0/vbgIVQulF6cqDge
+5BXtXhD4GnKEbvLh5TRxK8x3VBNlVIlRx1gKVz0q8MFgyFRcMS3PEiaFarSWwq8M
+I7QaeifUnXwA5BquXdwf6+8CWdanPv2wG9LylWHK/YbnBz4g/9ppYCAz2V++em+K
+/CADHRNIyuEXw6LWmwmYzMd05J+46dbEEua0tRiK3yVl5Q60Bo4NZcUZA3KZ7A9l
+JS13/tzFKLqCn+SLzQnZfDAp8ys/Eu9arxYq70dAPUv0eJDSuXNQx2HQBxF3phQ+
+9AvKH5Vu4UgD3lSCqXiLwtOjeCxXZJ3830jIkFOupSZI7H3f3NmMxDba5OzLXXb+
+GfLgR9ulI1Qk+UmISL6/Tsu5453jn6opv/kapD9g7Jc6AFTak1SlHWZU4JH7c1x6
+THSB9FLcjbHt5heK8qcFMU5U6OiJmKW2WKn5k2Rwv0hteLhfN1lchqZkwobzu74z
+QG/Z2Q/ClU89X95U5qF1ZxuriP668SxETzPR8D9uBU/5VzkrC3c8SbcTFUECAwEA
+AaOBxTCBwjAdBgNVHQ4EFgQUXc5/mePUGUExT8bPsSX1Wq/jlzEwgZIGA1UdIwSB
+ijCBh4AUXc5/mePUGUExT8bPsSX1Wq/jlzGhZKRiMGAxEDAOBgNVBAMTB1Rlc3Qg
+Q0ExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
+b3VudGFpbiBWaWV3MRIwEAYDVQQKEwlDZXJ0IFRlc3SCCQDUUZPUP97ZTjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQA2FoW/5tyDSkEfEQFVfLcfNvmt
+s2XK7SbPZtoIYx0lnqXZ8fUxCg0chmkiCypng6msvhEwGs3z7qZWd04bY8RAZYbQ
+9bhjvB0LH4lVfsK4pCAKYQDAHC271XZPd3RmdHaekH70T0w2avFxG9M91CaVRaLh
+SheeCVm6aJcFnaBfSd78vDz8Ly+Jtod737JZrkmP7Oko78SrB/CfiMcf6jIdekxP
+9bwi75Y0+rfaK9x/Pyjz4U23ooZLX2f/21c3JSTwR19qGZOHvQircP7scQzgtlJl
+zGxQKEZwYHAPxNHbPoScLOP2LXqWUnheC5LQcsm3BtRKM5AniEqW8HqKAxrGOPNO
+cj8E3hVf6lZaAeFH5CTN/rudb869k/ibJkaeo6pjeytssMr+PvpstWmWqhfEJTZn
+om7w+xMsYKe1cZv6c2WIVFuvl/g8CLYuu6SXjtDqj7HVbhC1+IupXl5N4pBXIXCf
+Q33D4JsmM51ySMk5SxAl6HaxiB9hU3dqJqWjiKBJztLnZUDWUpCb3g09yygXeTmY
+fNGaY+mZX3HBohrAy0kj06OgT12ovmWsmQRIvpTBybTbX37vt11xOLN/WNnifkH9
+HcEOc/WW68x2dJbnF4sBfV/0edA8ectGdyqzn1gvYjdMDPSSbAZ4B/EIijfHqILb
+R3901wi9fJwhBxFIuQ==
+-----END CERTIFICATE-----
diff --git a/net/net.xcodeproj/project.pbxproj b/net/net.xcodeproj/project.pbxproj
index 11a8199..bb16121 100644
--- a/net/net.xcodeproj/project.pbxproj
+++ b/net/net.xcodeproj/project.pbxproj
@@ -151,6 +151,7 @@
8220FABD0E914ACA008170A9 /* ssl_client_socket_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32950E5A181C00A747DB /* ssl_client_socket_unittest.cc */; };
8220FAFC0E915561008170A9 /* ssl_client_socket_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32970E5A181C00A747DB /* ssl_client_socket_mac.cc */; };
825C2FCC0E5C968B00FDEAB7 /* ev_root_ca_metadata.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32BE0E5A181C00A747DB /* ev_root_ca_metadata.cc */; };
+ 826F15770EE48CEA00D973C7 /* ssl_test_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 826F15760EE48CEA00D973C7 /* ssl_test_util.cc */; };
827E139D0E81611D00183614 /* x509_certificate_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32800E5A181C00A747DB /* x509_certificate_mac.cc */; };
82ECB3090E5B651D00A913E3 /* mime_sniffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32AD0E5A181C00A747DB /* mime_sniffer.cc */; };
93D11DCE0E91463000C36437 /* file_stream_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93D11DCD0E91463000C36437 /* file_stream_posix.cc */; };
@@ -673,6 +674,8 @@
82113A270E84360200E3848F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = "<group>"; };
82113BBC0E892E5800E3848F /* x509_certificate.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x509_certificate.cc; sourceTree = "<group>"; };
8249C4920EA786B100A4A54B /* ssl_client_socket_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ssl_client_socket_mac.h; sourceTree = "<group>"; };
+ 826F15750EE48CEA00D973C7 /* ssl_test_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ssl_test_util.h; sourceTree = "<group>"; };
+ 826F15760EE48CEA00D973C7 /* ssl_test_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ssl_test_util.cc; sourceTree = "<group>"; };
936882DC0E9154E200043405 /* file_stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_stream.h; sourceTree = "<group>"; };
93D11DCD0E91463000C36437 /* file_stream_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_stream_posix.cc; sourceTree = "<group>"; };
A5AB7BFB0EB7DBA10070A7D3 /* file_stream_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_stream_unittest.cc; sourceTree = "<group>"; };
@@ -967,6 +970,8 @@
7BED32930E5A181C00A747DB /* ssl_config_service.h */,
7BED32920E5A181C00A747DB /* ssl_config_service_unittest.cc */,
7BED32910E5A181C00A747DB /* ssl_info.h */,
+ 826F15760EE48CEA00D973C7 /* ssl_test_util.cc */,
+ 826F15750EE48CEA00D973C7 /* ssl_test_util.h */,
7BED328F0E5A181C00A747DB /* tcp_client_socket.h */,
E47E933E0E8924DC00CA613E /* tcp_client_socket_libevent.cc */,
7BED328E0E5A181C00A747DB /* tcp_client_socket_unittest.cc */,
@@ -1548,8 +1553,10 @@
7BD8F70E0E65DCE500034DE9 /* disk_cache_test_util.cc in Sources */,
7BD8F70F0E65DCEB00034DE9 /* entry_unittest.cc in Sources */,
7B4DF6B10E5B98ED004D7619 /* escape_unittest.cc in Sources */,
+ A5AB7BFC0EB7DBA10070A7D3 /* file_stream_unittest.cc in Sources */,
7BA0151F0E5A1B9200044150 /* gzip_filter_unittest.cc in Sources */,
7B82FF460E763620008F45CF /* host_resolver_unittest.cc in Sources */,
+ 042A4D480EC4F4500083281F /* http_auth_cache_unittest.cc in Sources */,
04C626D80E8DE3AA0067E92A /* http_auth_handler_basic_unittest.cc in Sources */,
04C626D60E8DE39E0067E92A /* http_auth_handler_digest_unittest.cc in Sources */,
04C626DA0E8DE3BA0067E92A /* http_auth_unittest.cc in Sources */,
@@ -1559,6 +1566,7 @@
821F21320E5CD756003C7E38 /* http_response_headers_unittest.cc in Sources */,
E4CE9C2E0E8C02ED00D5378C /* http_transaction_unittest.cc in Sources */,
821F21130E5CD662003C7E38 /* http_vary_data_unittest.cc in Sources */,
+ A50055C00EBF7CD6007B0A90 /* listen_socket_unittest.cc in Sources */,
7BD8F7100E65DCF000034DE9 /* mapped_file_unittest.cc in Sources */,
7B4DF9AC0E5C906A004D7619 /* mime_sniffer_unittest.cc in Sources */,
048268090E5B3B4800A30786 /* mime_util_unittest.cc in Sources */,
@@ -1568,15 +1576,13 @@
E4AFA6430E5241B400201347 /* run_all_unittests.cc in Sources */,
7BA362B70E8C3D040023C8B9 /* sdch_filter_unittest.cc in Sources */,
8220FABD0E914ACA008170A9 /* ssl_client_socket_unittest.cc in Sources */,
+ 826F15770EE48CEA00D973C7 /* ssl_test_util.cc in Sources */,
7BD8F7110E65DCF500034DE9 /* storage_block_unittest.cc in Sources */,
E47E93430E8924EE00CA613E /* tcp_client_socket_unittest.cc in Sources */,
- 7BA361450E8C341F0023C8B9 /* test_completion_callback_unittest.cc in Sources */,
- 82113A1D0E8434EE00E3848F /* x509_certificate_unittest.cc in Sources */,
- A5AB7BFC0EB7DBA10070A7D3 /* file_stream_unittest.cc in Sources */,
A50055BF0EBF7CB2007B0A90 /* telnet_server_unittest.cc in Sources */,
- A50055C00EBF7CD6007B0A90 /* listen_socket_unittest.cc in Sources */,
- 042A4D480EC4F4500083281F /* http_auth_cache_unittest.cc in Sources */,
+ 7BA361450E8C341F0023C8B9 /* test_completion_callback_unittest.cc in Sources */,
048133550ED27FEF005C5BBC /* url_request_unittest.cc in Sources */,
+ 82113A1D0E8434EE00E3848F /* x509_certificate_unittest.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/net/net_unittests.scons b/net/net_unittests.scons
index 0879dee..3535850 100644
--- a/net/net_unittests.scons
+++ b/net/net_unittests.scons
@@ -54,6 +54,7 @@ input_files = [
'base/net_util_unittest.cc',
'base/registry_controlled_domain_unittest.cc',
'base/run_all_unittests.cc',
+ 'base/ssl_test_util.cc',
'base/ssl_client_socket_unittest.cc',
'base/tcp_client_socket_unittest.cc',
'base/telnet_server_unittest.cc',
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index e2197f8..83d5744 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -4,9 +4,13 @@
#include "net/url_request/url_request_unittest.h"
+#include "build/build_config.h"
+
#if defined(OS_WIN)
#include <windows.h>
#include <shlobj.h>
+#elif defined(OS_LINUX)
+#include "base/nss_init.h"
#endif
#include <algorithm>
@@ -21,6 +25,7 @@
#include "net/base/net_errors.h"
#include "net/base/net_module.h"
#include "net/base/net_util.h"
+#include "net/base/ssl_test_util.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_layer.h"
@@ -116,6 +121,48 @@ TEST_F(URLRequestTest, GetTest) {
#endif
}
+class HTTPSRequestTest : public testing::Test {
+ protected:
+ HTTPSRequestTest() : util_() {};
+
+ SSLTestUtil util_;
+};
+
+#if defined(OS_MACOSX)
+// TODO(port): support temporary root cert on mac
+#define MAYBE_HTTPSGetTest DISABLED_HTTPSGetTest
+#else
+#define MAYBE_HTTPSGetTest HTTPSGetTest
+#endif
+
+TEST_F(HTTPSRequestTest, MAYBE_HTTPSGetTest) {
+ // Note: tools/testserver/testserver.py does not need
+ // a working document root to server the pages / and /hello.html,
+ // so this test doesn't really need to specify a document root.
+ // But if it did, a good one would be net/data/ssl.
+ HTTPSTestServer https_server(util_.kHostName, util_.kOKHTTPSPort,
+ L"net/data/ssl",
+ util_.GetOKCertPath().ToWStringHack());
+
+ EXPECT_TRUE(util_.CheckCATrusted());
+ TestDelegate d;
+ {
+ TestURLRequest r(https_server.TestServerPage(""), &d);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, d.response_started_count());
+ EXPECT_FALSE(d.received_data_before_response());
+ EXPECT_NE(0, d.bytes_received());
+ }
+#ifndef NDEBUG
+ DCHECK_EQ(url_request_metrics.object_count,0);
+#endif
+}
+
TEST_F(URLRequestTest, CancelTest) {
TestDelegate d;
{