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 2639870e81f36f4fefef5ad4626ced9bc629da13..7748f07106b2c73f1eeab10bd6ede8e832b5a5dd 100644 |
--- a/net/socket/ssl_client_socket_unittest.cc |
+++ b/net/socket/ssl_client_socket_unittest.cc |
@@ -599,9 +599,15 @@ class SSLClientSocketTest : public PlatformTest { |
} |
protected: |
- // Sets up a TCP connection to a HTTPS server. To actually do the SSL |
- // handshake, follow up with call to CreateAndConnectSSLClientSocket() below. |
- bool ConnectToTestServer(SpawnedTestServer::SSLOptions& ssl_options) { |
+ // The address of the spawned test server, after calling StartTestServer(). |
+ const AddressList& addr() const { return addr_; } |
+ |
+ // The SpawnedTestServer object, after calling StartTestServer(). |
+ const SpawnedTestServer* test_server() const { return test_server_.get(); } |
+ |
+ // Starts the test server with SSL configuration |ssl_options|. Returns true |
+ // on success. |
+ bool StartTestServer(const SpawnedTestServer::SSLOptions& ssl_options) { |
test_server_.reset(new SpawnedTestServer( |
SpawnedTestServer::TYPE_HTTPS, ssl_options, base::FilePath())); |
if (!test_server_->Start()) { |
@@ -613,6 +619,14 @@ class SSLClientSocketTest : public PlatformTest { |
LOG(ERROR) << "Could not get SpawnedTestServer address list"; |
return false; |
} |
+ return true; |
+ } |
+ |
+ // Sets up a TCP connection to a HTTPS server. To actually do the SSL |
+ // handshake, follow up with call to CreateAndConnectSSLClientSocket() below. |
+ bool ConnectToTestServer(const SpawnedTestServer::SSLOptions& ssl_options) { |
+ if (!StartTestServer(ssl_options)) |
+ return false; |
transport_.reset(new TCPClientSocket(addr_, &log_, NetLog::Source())); |
int rv = callback_.GetResult(transport_->Connect(callback_.callback())); |
@@ -713,58 +727,82 @@ class SSLClientSocketCertRequestInfoTest : public SSLClientSocketTest { |
class SSLClientSocketFalseStartTest : public SSLClientSocketTest { |
protected: |
- void TestFalseStart(const SpawnedTestServer::SSLOptions& server_options, |
- const SSLConfig& client_config, |
- bool expect_false_start) { |
- SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
- server_options, |
- base::FilePath()); |
- ASSERT_TRUE(test_server.Start()); |
- |
- AddressList addr; |
- ASSERT_TRUE(test_server.GetAddressList(&addr)); |
+ // 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. |
+ // |
+ // 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 |
+ // in |*out_raw_transport|. To complete the handshake and successfully read |
+ // data, the caller must unblock reads on |*out_raw_transport|. (Note that, if |
+ // the client successfully false started, |callback.WaitForResult()| will |
+ // return OK without unblocking transport reads. But Read() will still block.) |
+ // |
+ // Must be called after StartTestServer is called. |
+ void CreateAndConnectUntilServerFinishedReceived( |
+ const SSLConfig& client_config, |
+ TestCompletionCallback* callback, |
+ FakeBlockingStreamSocket** out_raw_transport, |
+ scoped_ptr<SSLClientSocket>* out_sock) { |
+ CHECK(test_server()); |
- TestCompletionCallback callback; |
scoped_ptr<StreamSocket> real_transport( |
- new TCPClientSocket(addr, NULL, NetLog::Source())); |
+ new TCPClientSocket(addr(), NULL, NetLog::Source())); |
scoped_ptr<FakeBlockingStreamSocket> transport( |
new FakeBlockingStreamSocket(real_transport.Pass())); |
- int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ int rv = callback->GetResult(transport->Connect(callback->callback())); |
EXPECT_EQ(OK, rv); |
FakeBlockingStreamSocket* raw_transport = transport.get(); |
- scoped_ptr<SSLClientSocket> sock( |
+ scoped_ptr<SSLClientSocket> sock = |
CreateSSLClientSocket(transport.PassAs<StreamSocket>(), |
- test_server.host_port_pair(), |
- client_config)); |
+ test_server()->host_port_pair(), |
+ client_config); |
// Connect. Stop before the client processes the first server leg |
// (ServerHello, etc.) |
raw_transport->BlockReadResult(); |
- rv = sock->Connect(callback.callback()); |
+ rv = sock->Connect(callback->callback()); |
EXPECT_EQ(ERR_IO_PENDING, rv); |
raw_transport->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()); |
+ EXPECT_FALSE(callback->have_result()); |
raw_transport->BlockWrite(); |
raw_transport->UnblockReadResult(); |
raw_transport->WaitForWrite(); |
// And, finally, release that and block the next server leg |
- // (ChangeCipherSpec, Finished). Note: callback.have_result() may or may not |
- // be true at this point depending on whether the SSL implementation waits |
- // for the client second leg to clear the internal write buffer and hit the |
- // network. |
+ // (ChangeCipherSpec, Finished). |
raw_transport->BlockReadResult(); |
raw_transport->UnblockWrite(); |
+ *out_raw_transport = raw_transport; |
+ *out_sock = sock.Pass(); |
+ } |
+ |
+ void TestFalseStart(const SpawnedTestServer::SSLOptions& server_options, |
+ const SSLConfig& client_config, |
+ bool expect_false_start) { |
+ ASSERT_TRUE(StartTestServer(server_options)); |
+ |
+ TestCompletionCallback callback; |
+ FakeBlockingStreamSocket* raw_transport = NULL; |
+ scoped_ptr<SSLClientSocket> sock; |
+ ASSERT_NO_FATAL_FAILURE(CreateAndConnectUntilServerFinishedReceived( |
+ client_config, &callback, &raw_transport, &sock)); |
+ |
if (expect_false_start) { |
// When False Starting, the handshake should complete before receiving the |
// Change Cipher Spec and Finished messages. |
- rv = callback.GetResult(rv); |
+ // |
+ // Note: callback.have_result() may not be true without waiting. The NSS |
+ // state machine sometimes lives on a separate thread, so this thread may |
+ // not yet have processed the signal that the handshake has completed. |
+ int rv = callback.WaitForResult(); |
EXPECT_EQ(OK, rv); |
EXPECT_TRUE(sock->IsConnected()); |
@@ -2458,7 +2496,8 @@ TEST_F(SSLClientSocketFalseStartTest, FalseStartEnabled) { |
server_options.enable_npn = true; |
SSLConfig client_config; |
client_config.next_protos.push_back("http/1.1"); |
- TestFalseStart(server_options, client_config, true); |
+ ASSERT_NO_FATAL_FAILURE( |
+ TestFalseStart(server_options, client_config, true)); |
} |
// Test that False Start is disabled without NPN. |
@@ -2468,7 +2507,8 @@ TEST_F(SSLClientSocketFalseStartTest, NoNPN) { |
SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA; |
SSLConfig client_config; |
client_config.next_protos.clear(); |
- TestFalseStart(server_options, client_config, false); |
+ ASSERT_NO_FATAL_FAILURE( |
+ TestFalseStart(server_options, client_config, false)); |
} |
// Test that False Start is disabled without a forward-secret cipher suite. |
@@ -2479,7 +2519,80 @@ TEST_F(SSLClientSocketFalseStartTest, NoForwardSecrecy) { |
server_options.enable_npn = true; |
SSLConfig client_config; |
client_config.next_protos.push_back("http/1.1"); |
- TestFalseStart(server_options, client_config, false); |
+ ASSERT_NO_FATAL_FAILURE( |
+ TestFalseStart(server_options, client_config, false)); |
+} |
+ |
+// Test that sessions are resumable after receiving the server Finished message. |
+TEST_F(SSLClientSocketFalseStartTest, SessionResumption) { |
+ // Start a server. |
+ SpawnedTestServer::SSLOptions server_options; |
+ server_options.key_exchanges = |
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA; |
+ server_options.enable_npn = true; |
+ SSLConfig client_config; |
+ client_config.next_protos.push_back("http/1.1"); |
+ |
+ // Let a full handshake complete with False Start. |
+ ASSERT_NO_FATAL_FAILURE( |
+ TestFalseStart(server_options, client_config, true)); |
+ |
+ // Make a second connection. |
+ TestCompletionCallback callback; |
+ 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); |
+ ASSERT_TRUE(sock2.get()); |
+ EXPECT_EQ(OK, callback.GetResult(sock2->Connect(callback.callback()))); |
+ |
+ // It should resume the session. |
+ SSLInfo ssl_info; |
+ EXPECT_TRUE(sock2->GetSSLInfo(&ssl_info)); |
+ EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type); |
+} |
+ |
+// Test that sessions are not resumable before receiving the server Finished |
+// message. |
+TEST_F(SSLClientSocketFalseStartTest, NoSessionResumptionBeforeFinish) { |
+ // Start a server. |
+ SpawnedTestServer::SSLOptions server_options; |
+ server_options.key_exchanges = |
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA; |
+ server_options.enable_npn = true; |
+ ASSERT_TRUE(StartTestServer(server_options)); |
+ |
+ SSLConfig client_config; |
+ client_config.next_protos.push_back("http/1.1"); |
+ |
+ // Start a handshake up to the server Finished message. |
+ TestCompletionCallback callback; |
+ FakeBlockingStreamSocket* raw_transport1; |
+ 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. |
+ EXPECT_EQ(OK, callback.WaitForResult()); |
+ |
+ // 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); |
} |
// Connect to a server using channel id. It should allow the connection. |