Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1694)

Unified Diff: net/socket/ssl_client_socket_unittest.cc

Issue 994263002: Rewrite session cache in OpenSSL ports. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sleevi comments Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/socket/ssl_client_socket_unittest.cc
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 4b74ca0b93dd6caa20495c7efd8614df05c70f16..eca63596f1f39e129cdff48cdfb0f12e6f663143 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -816,10 +816,12 @@ class SSLClientSocketCertRequestInfoTest : public SSLClientSocketTest {
class SSLClientSocketFalseStartTest : public SSLClientSocketTest {
protected:
- // Creates an SSLClientSocket with |client_config| attached to a
- // FakeBlockingStreamSocket, returning both in |*out_raw_transport| and
- // |*out_sock|. The FakeBlockingStreamSocket is owned by the SSLClientSocket,
- // so |*out_raw_transport| is a raw pointer.
+ // Creates an SSLClientSocket with |client_config| to a series of modifier
+ // sockets: a SynchronousErrorStreamSocket which wraps a
+ // FakeBlockingStreamSocket which wraps the real connection to the test
+ // server. These are returned in |*out_sock|, |*out_raw_error_socket|, and
+ // |*out_raw_blocking_socket|, respectively. Note that the last two are raw
+ // pointers owned by |*out_sock|.
//
// The client socket will begin a connect using |callback| but stop before the
// server's finished message is received. The finished message will be blocked
@@ -828,46 +830,53 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest {
// the client successfully false started, |callback.WaitForResult()| will
// return OK without unblocking transport reads. But Read() will still block.)
//
+ // |*out_raw_error_socket| may be used to cause reading the Finished to fail.
+ //
// Must be called after StartTestServer is called.
void CreateAndConnectUntilServerFinishedReceived(
const SSLConfig& client_config,
TestCompletionCallback* callback,
- FakeBlockingStreamSocket** out_raw_transport,
+ FakeBlockingStreamSocket** out_raw_blocking_socket,
+ SynchronousErrorStreamSocket** out_raw_error_socket,
scoped_ptr<SSLClientSocket>* out_sock) {
CHECK(test_server());
scoped_ptr<StreamSocket> real_transport(
new TCPClientSocket(addr(), NULL, NetLog::Source()));
- scoped_ptr<FakeBlockingStreamSocket> transport(
+ scoped_ptr<FakeBlockingStreamSocket> blocking_socket(
new FakeBlockingStreamSocket(real_transport.Pass()));
- int rv = callback->GetResult(transport->Connect(callback->callback()));
+ FakeBlockingStreamSocket* raw_blocking_socket = blocking_socket.get();
+ scoped_ptr<SynchronousErrorStreamSocket> error_socket(
+ new SynchronousErrorStreamSocket(blocking_socket.Pass()));
+ SynchronousErrorStreamSocket* raw_error_socket = error_socket.get();
+ int rv = callback->GetResult(error_socket->Connect(callback->callback()));
EXPECT_EQ(OK, rv);
- FakeBlockingStreamSocket* raw_transport = transport.get();
scoped_ptr<SSLClientSocket> sock = CreateSSLClientSocket(
- transport.Pass(), test_server()->host_port_pair(), client_config);
+ error_socket.Pass(), test_server()->host_port_pair(), client_config);
// Connect. Stop before the client processes the first server leg
// (ServerHello, etc.)
- raw_transport->BlockReadResult();
+ raw_blocking_socket->BlockReadResult();
rv = sock->Connect(callback->callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
- raw_transport->WaitForReadResult();
+ raw_blocking_socket->WaitForReadResult();
// Release the ServerHello and wait for the client to write
// ClientKeyExchange, etc. (A proxy for waiting for the entirety of the
// server's leg to complete, since it may span multiple reads.)
EXPECT_FALSE(callback->have_result());
- raw_transport->BlockWrite();
- raw_transport->UnblockReadResult();
- raw_transport->WaitForWrite();
+ raw_blocking_socket->BlockWrite();
+ raw_blocking_socket->UnblockReadResult();
+ raw_blocking_socket->WaitForWrite();
// And, finally, release that and block the next server leg
// (ChangeCipherSpec, Finished).
- raw_transport->BlockReadResult();
- raw_transport->UnblockWrite();
+ raw_blocking_socket->BlockReadResult();
+ raw_blocking_socket->UnblockWrite();
- *out_raw_transport = raw_transport;
+ *out_raw_error_socket = raw_error_socket;
+ *out_raw_blocking_socket = raw_blocking_socket;
*out_sock = sock.Pass();
}
@@ -877,10 +886,12 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest {
ASSERT_TRUE(StartTestServer(server_options));
TestCompletionCallback callback;
- FakeBlockingStreamSocket* raw_transport = NULL;
+ FakeBlockingStreamSocket* raw_blocking_socket = nullptr;
+ SynchronousErrorStreamSocket* raw_error_socket = nullptr;
scoped_ptr<SSLClientSocket> sock;
ASSERT_NO_FATAL_FAILURE(CreateAndConnectUntilServerFinishedReceived(
- client_config, &callback, &raw_transport, &sock));
+ client_config, &callback, &raw_blocking_socket, &raw_error_socket,
+ &sock));
if (expect_false_start) {
// When False Starting, the handshake should complete before receiving the
@@ -911,7 +922,7 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest {
rv = sock->Read(buf.get(), 4096, callback.callback());
// After releasing reads, the connection proceeds.
- raw_transport->UnblockReadResult();
+ raw_blocking_socket->UnblockReadResult();
rv = callback.GetResult(rv);
EXPECT_LT(0, rv);
} else {
@@ -2804,6 +2815,85 @@ TEST_F(SSLClientSocketTest, ReuseStates) {
// attempt to read one byte extra.
}
+// Tests that basic session resumption works.
+TEST_F(SSLClientSocketTest, SessionResumption) {
+ SpawnedTestServer::SSLOptions ssl_options;
+ ASSERT_TRUE(StartTestServer(ssl_options));
+
+ // First, perform a full handshake.
+ SSLConfig ssl_config;
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> transport(
+ new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ ASSERT_EQ(OK, callback.GetResult(transport->Connect(callback.callback())));
+ scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
+ transport.Pass(), test_server()->host_port_pair(), ssl_config));
+ ASSERT_EQ(OK, callback.GetResult(sock->Connect(callback.callback())));
+ SSLInfo ssl_info;
+ ASSERT_TRUE(sock->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+
+ // The next connection should resume.
+ transport.reset(new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ ASSERT_EQ(OK, callback.GetResult(transport->Connect(callback.callback())));
+ sock = CreateSSLClientSocket(transport.Pass(),
+ test_server()->host_port_pair(), ssl_config);
+ ASSERT_EQ(OK, callback.GetResult(sock->Connect(callback.callback())));
+ ASSERT_TRUE(sock->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+
+ // Using a different HostPortPair uses a different session cache key.
+ transport.reset(new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ ASSERT_EQ(OK, callback.GetResult(transport->Connect(callback.callback())));
+ sock = CreateSSLClientSocket(transport.Pass(),
+ HostPortPair("example.com", 443), ssl_config);
+ ASSERT_EQ(OK, callback.GetResult(sock->Connect(callback.callback())));
+ ASSERT_TRUE(sock->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+
+ SSLClientSocket::ClearSessionCache();
+
+ // After clearing the session cache, the next handshake doesn't resume.
+ transport.reset(new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ ASSERT_EQ(OK, callback.GetResult(transport->Connect(callback.callback())));
+ sock = CreateSSLClientSocket(transport.Pass(),
+ test_server()->host_port_pair(), ssl_config);
+ ASSERT_EQ(OK, callback.GetResult(sock->Connect(callback.callback())));
+ ASSERT_TRUE(sock->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+}
+
+// Tests that connections with certificate errors do not add entries to the
+// session cache.
+TEST_F(SSLClientSocketTest, CertificateErrorNoResume) {
+ SpawnedTestServer::SSLOptions ssl_options;
+ ASSERT_TRUE(StartTestServer(ssl_options));
+
+ cert_verifier_->set_default_result(ERR_CERT_COMMON_NAME_INVALID);
+
+ SSLConfig ssl_config;
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> transport(
+ new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ ASSERT_EQ(OK, callback.GetResult(transport->Connect(callback.callback())));
+ scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
+ transport.Pass(), test_server()->host_port_pair(), ssl_config));
+ EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID,
+ callback.GetResult(sock->Connect(callback.callback())));
+
+ cert_verifier_->set_default_result(OK);
+
+ // The next connection should perform a full handshake.
+ transport.reset(new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ ASSERT_EQ(OK, callback.GetResult(transport->Connect(callback.callback())));
+ sock = CreateSSLClientSocket(transport.Pass(),
+ test_server()->host_port_pair(), ssl_config);
+ ASSERT_EQ(OK, callback.GetResult(sock->Connect(callback.callback())));
+ SSLInfo ssl_info;
+ ASSERT_TRUE(sock->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+}
+
// Tests that session caches are sharded by max_version.
TEST_F(SSLClientSocketTest, FallbackShardSessionCache) {
SpawnedTestServer::SSLOptions ssl_options;
@@ -2981,8 +3071,8 @@ TEST_F(SSLClientSocketFalseStartTest, SessionResumption) {
EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
}
-// Test that sessions are not resumable before receiving the server Finished
-// message.
+// Test that False Started sessions are not resumable before receiving the
+// server Finished message.
TEST_F(SSLClientSocketFalseStartTest, NoSessionResumptionBeforeFinish) {
if (!SupportsAESGCM()) {
LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
@@ -3003,12 +3093,14 @@ TEST_F(SSLClientSocketFalseStartTest, NoSessionResumptionBeforeFinish) {
// Start a handshake up to the server Finished message.
TestCompletionCallback callback;
- FakeBlockingStreamSocket* raw_transport1 = NULL;
+ FakeBlockingStreamSocket* raw_blocking_socket1 = nullptr;
+ SynchronousErrorStreamSocket* raw_error_socket1 = nullptr;
scoped_ptr<SSLClientSocket> sock1;
ASSERT_NO_FATAL_FAILURE(CreateAndConnectUntilServerFinishedReceived(
- client_config, &callback, &raw_transport1, &sock1));
- // Although raw_transport1 has the server Finished blocked, the handshake
- // still completes.
+ client_config, &callback, &raw_blocking_socket1, &raw_error_socket1,
+ &sock1));
+ // Although |raw_blocking_socket1| has the server Finished blocked, the
+ // handshake still completes.
EXPECT_EQ(OK, callback.WaitForResult());
// Continue to block the client (|sock1|) from processing the Finished
@@ -3023,7 +3115,64 @@ TEST_F(SSLClientSocketFalseStartTest, NoSessionResumptionBeforeFinish) {
scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
int rv = sock1->Read(buf.get(), 4096, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
- raw_transport1->WaitForReadResult();
+ raw_blocking_socket1->WaitForReadResult();
+
+ // Drop the old socket. This is needed because the Python test server can't
+ // service two sockets in parallel.
+ sock1.reset();
+
+ // Start a second connection.
+ scoped_ptr<StreamSocket> transport2(
+ new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ EXPECT_EQ(OK, callback.GetResult(transport2->Connect(callback.callback())));
+ scoped_ptr<SSLClientSocket> sock2 = CreateSSLClientSocket(
+ transport2.Pass(), test_server()->host_port_pair(), client_config);
+ EXPECT_EQ(OK, callback.GetResult(sock2->Connect(callback.callback())));
+
+ // No session resumption because the first connection never received a server
+ // Finished message.
+ SSLInfo ssl_info;
+ EXPECT_TRUE(sock2->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+}
+
+// Test that False Started sessions are not resumable if reading the server
+// Finished message failed.
Ryan Sleevi 2015/04/02 20:00:14 I didn't mean reading the server finished failed -
davidben 2015/04/03 00:37:11 Switched it to that. That was actually somewhat le
+TEST_F(SSLClientSocketFalseStartTest, NoSessionResumptionBadFinish) {
+ if (!SupportsAESGCM()) {
+ LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+ return;
+ }
+
+ // Start a server.
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+ server_options.bulk_ciphers =
+ SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
+ server_options.enable_npn = true;
+ ASSERT_TRUE(StartTestServer(server_options));
+
+ SSLConfig client_config;
+ client_config.next_protos.push_back(kProtoHTTP11);
+
+ // Start a handshake up to the server Finished message.
+ TestCompletionCallback callback;
+ FakeBlockingStreamSocket* raw_blocking_socket1 = nullptr;
+ SynchronousErrorStreamSocket* raw_error_socket1 = nullptr;
+ scoped_ptr<SSLClientSocket> sock1;
+ ASSERT_NO_FATAL_FAILURE(CreateAndConnectUntilServerFinishedReceived(
+ client_config, &callback, &raw_blocking_socket1, &raw_error_socket1,
+ &sock1));
+ // Although |raw_blocking_socket1| has the server Finished blocked, the
+ // handshake still completes.
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ // Fail to read the Finished.
+ raw_error_socket1->SetNextReadError(ERR_CONNECTION_RESET);
+ scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
+ EXPECT_EQ(ERR_CONNECTION_RESET, callback.GetResult(sock1->Read(
+ buf.get(), 4096, callback.callback())));
// Drop the old socket. This is needed because the Python test server can't
// service two sockets in parallel.

Powered by Google App Engine
This is Rietveld 408576698