Index: net/socket/ssl_client_socket_openssl_unittest.cc |
diff --git a/net/socket/ssl_client_socket_openssl_unittest.cc b/net/socket/ssl_client_socket_openssl_unittest.cc |
index 00cf9f35cb4b612841207513f112780a405880df..1dfc48a24646d11d9c597c5b4dd5429feda6cab9 100644 |
--- a/net/socket/ssl_client_socket_openssl_unittest.cc |
+++ b/net/socket/ssl_client_socket_openssl_unittest.cc |
@@ -18,6 +18,7 @@ |
#include "base/memory/ref_counted.h" |
#include "base/memory/scoped_handle.h" |
#include "base/message_loop/message_loop_proxy.h" |
+#include "base/run_loop.h" |
wtc
2014/07/31 02:02:24
Do we need to include this?
|
#include "base/values.h" |
#include "crypto/openssl_util.h" |
#include "net/base/address_list.h" |
@@ -38,6 +39,7 @@ |
#include "net/ssl/openssl_client_key_store.h" |
#include "net/ssl/ssl_cert_request_info.h" |
#include "net/ssl/ssl_config_service.h" |
+#include "net/socket/ssl_client_socket_test_util.cc" |
wtc
2014/07/31 02:02:24
Delete this line. Note that you are including a .c
|
#include "net/test/cert_test_util.h" |
#include "net/test/spawned_test_server/spawned_test_server.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -98,7 +100,8 @@ class SSLClientSocketOpenSSLClientAuthTest : public PlatformTest { |
SSLClientSocketOpenSSLClientAuthTest() |
: socket_factory_(net::ClientSocketFactory::GetDefaultFactory()), |
cert_verifier_(new net::MockCertVerifier), |
- transport_security_state_(new net::TransportSecurityState) { |
+ transport_security_state_(new net::TransportSecurityState), |
+ ran_handshake_completion_callback_(false) { |
cert_verifier_->set_default_result(net::OK); |
context_.cert_verifier = cert_verifier_.get(); |
context_.transport_security_state = transport_security_state_.get(); |
@@ -109,6 +112,11 @@ class SSLClientSocketOpenSSLClientAuthTest : public PlatformTest { |
key_store_->Flush(); |
} |
+ void RecordCompletedHandshake() { |
+ ran_handshake_completion_callback_ = true; |
+ printf("%s\n", "hi"); |
wtc
2014/07/31 02:02:24
Delete this debug printf statement.
(By the way,
|
+ } |
+ |
protected: |
scoped_ptr<SSLClientSocket> CreateSSLClientSocket( |
scoped_ptr<StreamSocket> transport_socket, |
@@ -173,6 +181,10 @@ class SSLClientSocketOpenSSLClientAuthTest : public PlatformTest { |
test_server_->host_port_pair(), |
ssl_config); |
+ sock_->SetHandshakeCompletionCallback(base::Bind( |
+ &SSLClientSocketOpenSSLClientAuthTest::RecordCompletedHandshake, |
+ base::Unretained(this))); |
+ |
if (sock_->IsConnected()) { |
LOG(ERROR) << "SSL Socket prematurely connected"; |
return false; |
@@ -202,6 +214,7 @@ class SSLClientSocketOpenSSLClientAuthTest : public PlatformTest { |
CapturingNetLog log_; |
scoped_ptr<StreamSocket> transport_; |
scoped_ptr<SSLClientSocket> sock_; |
+ bool ran_handshake_completion_callback_; |
}; |
// Connect to a server requesting client authentication, do not send |
@@ -220,6 +233,7 @@ TEST_F(SSLClientSocketOpenSSLClientAuthTest, NoCert) { |
EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); |
EXPECT_FALSE(sock_->IsConnected()); |
+ EXPECT_TRUE(ran_handshake_completion_callback_); |
} |
// Connect to a server requesting client authentication, and send it |
@@ -277,7 +291,175 @@ TEST_F(SSLClientSocketOpenSSLClientAuthTest, SendGoodCert) { |
sock_->Disconnect(); |
EXPECT_FALSE(sock_->IsConnected()); |
} |
+ |
+TEST_F(SSLClientSocketOpenSSLClientAuthTest, |
+ CompletionCallbackIsRun_WithFailure) { |
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
+ SpawnedTestServer::kLocalhost, |
+ base::FilePath()); |
+ ASSERT_TRUE(test_server.Start()); |
+ |
+ AddressList addr; |
+ ASSERT_TRUE(test_server.GetAddressList(&addr)); |
+ |
+ TestCompletionCallback callback; |
+ scoped_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr, NULL, NetLog::Source())); |
+ scoped_ptr<SynchronousErrorStreamSocket> transport( |
+ new SynchronousErrorStreamSocket(real_transport.Pass())); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ EXPECT_EQ(OK, rv); |
+ |
+ // Disable TLS False Start to avoid handshake non-determinism. |
+ SSLConfig ssl_config; |
+ ssl_config.false_start_enabled = false; |
+ |
+ SynchronousErrorStreamSocket* raw_transport = transport.get(); |
+ scoped_ptr<SSLClientSocket> sock( |
+ CreateSSLClientSocket(transport.PassAs<StreamSocket>(), |
+ test_server.host_port_pair(), |
+ ssl_config)); |
+ |
+ sock->SetHandshakeCompletionCallback(base::Bind( |
+ &SSLClientSocketOpenSSLClientAuthTest::RecordCompletedHandshake, |
+ base::Unretained(this))); |
+ |
+ rv = callback.GetResult(sock->Connect(callback.callback())); |
+ EXPECT_EQ(OK, rv); |
+ EXPECT_TRUE(sock->IsConnected()); |
+ |
+ const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; |
+ static const int kRequestTextSize = |
+ static_cast<int>(arraysize(request_text) - 1); |
+ scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kRequestTextSize)); |
+ memcpy(request_buffer->data(), request_text, kRequestTextSize); |
+ |
+ rv = callback.GetResult( |
+ sock->Write(request_buffer.get(), kRequestTextSize, callback.callback())); |
+ EXPECT_EQ(kRequestTextSize, rv); |
+ |
+ // Simulate an unclean/forcible shutdown. |
+ raw_transport->SetNextReadError(ERR_CONNECTION_RESET); |
+ |
+ scoped_refptr<IOBuffer> buf(new IOBuffer(4096)); |
+ |
+ // Note: This test will hang if this bug has regressed. Simply checking that |
+ // rv != ERR_IO_PENDING is insufficient, as ERR_IO_PENDING is a legitimate |
+ // result when using a dedicated task runner for NSS. |
+ rv = callback.GetResult(sock->Read(buf.get(), 4096, callback.callback())); |
+ |
+ EXPECT_TRUE(ran_handshake_completion_callback_); |
+} |
+ |
+TEST_F(SSLClientSocketOpenSSLClientAuthTest, |
+ CompletionCallbackIsRun_WithFalseStartFailure) { |
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
+ SpawnedTestServer::kLocalhost, |
+ base::FilePath()); |
+ ASSERT_TRUE(test_server.Start()); |
+ |
+ AddressList addr; |
+ ASSERT_TRUE(test_server.GetAddressList(&addr)); |
+ |
+ TestCompletionCallback callback; |
+ scoped_ptr<StreamSocket> real_transport( |
+ new TCPClientSocket(addr, NULL, NetLog::Source())); |
+ // Note: |error_socket|'s ownership is handed to |transport|, but a pointer |
+ // is retained in order to configure additional errors. |
+ scoped_ptr<SynchronousErrorStreamSocket> error_socket( |
+ new SynchronousErrorStreamSocket(real_transport.Pass())); |
+ SynchronousErrorStreamSocket* raw_error_socket = error_socket.get(); |
+ scoped_ptr<FakeBlockingStreamSocket> transport( |
+ new FakeBlockingStreamSocket(error_socket.PassAs<StreamSocket>())); |
+ FakeBlockingStreamSocket* raw_transport = transport.get(); |
+ int rv = callback.GetResult(transport->Connect(callback.callback())); |
+ EXPECT_EQ(OK, rv); |
+ |
+ SSLConfig ssl_config; |
+ ssl_config.false_start_enabled = true; |
+ |
+ scoped_ptr<SSLClientSocket> sock( |
+ CreateSSLClientSocket(transport.PassAs<StreamSocket>(), |
+ test_server.host_port_pair(), |
+ ssl_config)); |
+ |
+ sock->SetHandshakeCompletionCallback(base::Bind( |
+ &SSLClientSocketOpenSSLClientAuthTest::RecordCompletedHandshake, |
+ base::Unretained(this))); |
+ |
+ rv = callback.GetResult(sock->Connect(callback.callback())); |
+ EXPECT_EQ(OK, rv); |
+ EXPECT_TRUE(sock->IsConnected()); |
+ |
+ const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; |
+ static const int kRequestTextSize = |
+ static_cast<int>(arraysize(request_text) - 1); |
+ scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kRequestTextSize)); |
+ memcpy(request_buffer->data(), request_text, kRequestTextSize); |
+ |
+ // Simulate an unclean/forcible shutdown on the underlying socket. |
+ // However, simulate this error asynchronously. |
+ raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET); |
+ raw_transport->BlockWrite(); |
+ |
+ // This write should complete synchronously, because the TLS ciphertext |
+ // can be created and placed into the outgoing buffers independent of the |
+ // underlying transport. |
+ rv = callback.GetResult( |
+ sock->Write(request_buffer.get(), kRequestTextSize, callback.callback())); |
+ EXPECT_EQ(kRequestTextSize, rv); |
+ |
+ scoped_refptr<IOBuffer> buf(new IOBuffer(4096)); |
+ |
+ rv = sock->Read(buf.get(), 4096, callback.callback()); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ // Now unblock the outgoing request, having it fail with the connection |
+ // being reset. |
+ raw_transport->UnblockWrite(); |
+ |
+ // Note: This will cause an inifite loop if this bug has regressed. Simply |
+ // checking that rv != ERR_IO_PENDING is insufficient, as ERR_IO_PENDING |
+ // is a legitimate result when using a dedicated task runner for NSS. |
+ rv = callback.GetResult(rv); |
+ |
+ EXPECT_TRUE(ran_handshake_completion_callback_); |
+} |
+ |
+// Connect to a server requesting client authentication. Send it a |
+// matching certificate. It should allow the connection. |
+TEST_F(SSLClientSocketOpenSSLClientAuthTest, |
+ CompletionCallbackIsRun_WithSuccess) { |
+ SpawnedTestServer::SSLOptions ssl_options; |
+ ssl_options.request_client_certificate = true; |
+ ssl_options.client_authorities.push_back( |
+ GetTestClientCertsDirectory().AppendASCII("client_1_ca.pem")); |
+ |
+ ASSERT_TRUE(ConnectToTestServer(ssl_options)); |
+ |
+ base::FilePath certs_dir = GetTestCertsDirectory(); |
+ SSLConfig ssl_config = kDefaultSSLConfig; |
+ ssl_config.send_client_cert = true; |
+ ssl_config.client_cert = ImportCertFromFile(certs_dir, "client_1.pem"); |
+ |
+ // This is required to ensure that signing works with the client |
+ // certificate's private key. |
+ OpenSSLClientKeyStore::ScopedEVP_PKEY client_private_key; |
+ ASSERT_TRUE(LoadPrivateKeyOpenSSL(certs_dir.AppendASCII("client_1.key"), |
+ &client_private_key)); |
+ EXPECT_TRUE(RecordPrivateKey(ssl_config, client_private_key.get())); |
+ |
+ int rv; |
+ ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv)); |
+ EXPECT_EQ(OK, rv); |
+ |
+ EXPECT_TRUE(sock_->IsConnected()); |
+ |
+ EXPECT_TRUE(ran_handshake_completion_callback_); |
+} |
+ |
#endif // defined(USE_OPENSSL_CERTS) |
} // namespace |
+ |
} // namespace net |