Chromium Code Reviews| 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. |