| 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 704211382cfacd554d24d096d7ac20c0ece152e2..f0e7120a1356eb6f3fec36100c9f08f7f9956c9a 100644
|
| --- a/net/socket/ssl_client_socket_unittest.cc
|
| +++ b/net/socket/ssl_client_socket_unittest.cc
|
| @@ -257,7 +257,7 @@ class SynchronousErrorStreamSocket : public WrappedStreamSocket {
|
| virtual int Write(net::IOBuffer* buf, int buf_len,
|
| const net::CompletionCallback& callback) OVERRIDE;
|
|
|
| - // Sets the the next Read() call to return |error|.
|
| + // Sets the next Read() call and all future calls to return |error|.
|
| // If there is already a pending asynchronous read, the configured error
|
| // will not be returned until that asynchronous read has completed and Read()
|
| // is called again.
|
| @@ -267,7 +267,7 @@ class SynchronousErrorStreamSocket : public WrappedStreamSocket {
|
| pending_read_error_ = error;
|
| }
|
|
|
| - // Sets the the next Write() call to return |error|.
|
| + // Sets the next Write() call and all future calls to return |error|.
|
| // If there is already a pending asynchronous write, the configured error
|
| // will not be returned until that asynchronous write has completed and
|
| // Write() is called again.
|
| @@ -300,10 +300,8 @@ int SynchronousErrorStreamSocket::Read(
|
| net::IOBuffer* buf,
|
| int buf_len,
|
| const net::CompletionCallback& callback) {
|
| - if (have_read_error_) {
|
| - have_read_error_ = false;
|
| + if (have_read_error_)
|
| return pending_read_error_;
|
| - }
|
| return transport_->Read(buf, buf_len, callback);
|
| }
|
|
|
| @@ -311,10 +309,8 @@ int SynchronousErrorStreamSocket::Write(
|
| net::IOBuffer* buf,
|
| int buf_len,
|
| const net::CompletionCallback& callback) {
|
| - if (have_write_error_) {
|
| - have_write_error_ = false;
|
| + if (have_write_error_)
|
| return pending_write_error_;
|
| - }
|
| return transport_->Write(buf, buf_len, callback);
|
| }
|
|
|
| @@ -916,10 +912,89 @@ TEST_F(SSLClientSocketTest, Read_WithSynchronousError) {
|
| rv = callback.GetResult(sock->Read(buf.get(), 4096, callback.callback()));
|
|
|
| #if !defined(USE_OPENSSL)
|
| - // NSS records the error exactly
|
| + // SSLClientSocketNSS records the error exactly
|
| EXPECT_EQ(net::ERR_CONNECTION_RESET, rv);
|
| #else
|
| - // OpenSSL treats any errors as a simple EOF.
|
| + // SSLClientSocketOpenSSL treats any errors as a simple EOF.
|
| + EXPECT_EQ(0, rv);
|
| +#endif
|
| +}
|
| +
|
| +// Tests that the SSLClientSocket properly handles when the underlying transport
|
| +// asynchronously returns an error code while writing data - such as if an
|
| +// intermediary terminates the socket connection uncleanly.
|
| +// This is a regression test for http://crbug.com/249848
|
| +TEST_F(SSLClientSocketTest, Write_WithSynchronousError) {
|
| + net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTPS,
|
| + net::SpawnedTestServer::kLocalhost,
|
| + base::FilePath());
|
| + ASSERT_TRUE(test_server.Start());
|
| +
|
| + net::AddressList addr;
|
| + ASSERT_TRUE(test_server.GetAddressList(&addr));
|
| +
|
| + net::TestCompletionCallback callback;
|
| + scoped_ptr<net::StreamSocket> real_transport(new net::TCPClientSocket(
|
| + addr, NULL, net::NetLog::Source()));
|
| + // Note: |error_socket|'s ownership is handed to |transport|, but the pointer
|
| + // is retained in order to configure additional errors.
|
| + SynchronousErrorStreamSocket* error_socket = new SynchronousErrorStreamSocket(
|
| + real_transport.Pass());
|
| + FakeBlockingStreamSocket* transport = new FakeBlockingStreamSocket(
|
| + scoped_ptr<net::StreamSocket>(error_socket));
|
| + int rv = callback.GetResult(transport->Connect(callback.callback()));
|
| + EXPECT_EQ(net::OK, rv);
|
| +
|
| + // Disable TLS False Start to avoid handshake non-determinism.
|
| + net::SSLConfig ssl_config;
|
| + ssl_config.false_start_enabled = false;
|
| +
|
| + scoped_ptr<net::SSLClientSocket> sock(
|
| + CreateSSLClientSocket(transport, test_server.host_port_pair(),
|
| + ssl_config));
|
| +
|
| + rv = callback.GetResult(sock->Connect(callback.callback()));
|
| + EXPECT_EQ(net::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<net::IOBuffer> request_buffer(
|
| + new net::IOBuffer(kRequestTextSize));
|
| + memcpy(request_buffer->data(), request_text, kRequestTextSize);
|
| +
|
| + // Simulate an unclean/forcible shutdown on the underlying socket.
|
| + // However, simulate this error asynchronously.
|
| + error_socket->SetNextWriteError(net::ERR_CONNECTION_RESET);
|
| + transport->SetNextWriteShouldBlock();
|
| +
|
| + // 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<net::IOBuffer> buf(new net::IOBuffer(4096));
|
| +
|
| + rv = sock->Read(buf.get(), 4096, callback.callback());
|
| + EXPECT_EQ(net::ERR_IO_PENDING, rv);
|
| +
|
| + // Now unblock the outgoing request, having it fail with the connection
|
| + // being reset.
|
| + 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);
|
| +
|
| +#if !defined(USE_OPENSSL)
|
| + // SSLClientSocketNSS records the error exactly
|
| + EXPECT_EQ(net::ERR_CONNECTION_RESET, rv);
|
| +#else
|
| + // SSLClientSocketOpenSSL treats any errors as a simple EOF.
|
| EXPECT_EQ(0, rv);
|
| #endif
|
| }
|
|
|