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 809ae5ec0e29c405027e2303212ce3da71777105..0c13c5add3e1cc7f980c9dc3b026031767c1c346 100644 |
| --- a/net/socket/ssl_client_socket_unittest.cc |
| +++ b/net/socket/ssl_client_socket_unittest.cc |
| @@ -1170,6 +1170,111 @@ TEST_F(SSLClientSocketTest, Read_DeleteWhilePendingFullDuplex) { |
| EXPECT_FALSE(callback.have_result()); |
| } |
| +// Tests that the SSLClientSocket does not crash if data is received on the |
| +// transport socket after a failing write. This can occur if we have a Write |
| +// error in a SPDY socket. |
| +// Regression test for http://crbug.com/335557 |
| +TEST_F(SSLClientSocketTest, Read_WithWriteError) { |
| + 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); |
| + |
| + // Disable TLS False Start to avoid handshake non-determinism. |
| + SSLConfig ssl_config; |
| + ssl_config.false_start_enabled = false; |
| + |
| + scoped_ptr<SSLClientSocket> sock( |
| + CreateSSLClientSocket(transport.PassAs<StreamSocket>(), |
| + test_server.host_port_pair(), |
| + ssl_config)); |
| + |
| + rv = callback.GetResult(sock->Connect(callback.callback())); |
| + EXPECT_EQ(OK, rv); |
| + EXPECT_TRUE(sock->IsConnected()); |
| + |
| + // Send a request so there is something to read from the socket. |
| + 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); |
| + |
| + // Start a hanging read. |
| + TestCompletionCallback read_callback; |
| + raw_transport->SetNextReadShouldBlock(); |
| + scoped_refptr<IOBuffer> buf(new IOBuffer(4096)); |
| + rv = sock->Read(buf.get(), 4096, read_callback.callback()); |
| + EXPECT_EQ(ERR_IO_PENDING, rv); |
| + |
| + // Perform another write, but have it fail. This time use a very long request |
| + // to exceed internal buffers and flush out the socket error. |
| + std::string long_request_text = |
| + "GET / HTTP/1.1\r\nUser-Agent: long browser name "; |
| + long_request_text.append(20 * 1024, '*'); |
| + long_request_text.append("\r\n\r\n"); |
| + scoped_refptr<DrainableIOBuffer> long_request_buffer(new DrainableIOBuffer( |
| + new StringIOBuffer(long_request_text), long_request_text.size())); |
| + |
| + raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET); |
| + |
| + // Write as much data as possible until hitting an error. This is necessary |
| + // for NSS. PR_Write will only consume as much data as it can encode into |
| + // application data records before the internal memio buffer is full, which |
| + // should only fill if writing a large amount of data and the underlying |
| + // transport is blocked. Once this happens, NSS will return (total size of all |
| + // application data records it wrote) - 1, with the caller expected to resume |
| + // with the remaining unsent data. |
| + do { |
| + rv = callback.GetResult(sock->Write(long_request_buffer.get(), |
| + long_request_buffer->BytesRemaining(), |
| + callback.callback())); |
| + if (rv > 0) { |
| + long_request_buffer->DidConsume(rv); |
| + // Abort if the entire buffer is ever consumed. |
| + ASSERT_LT(0, long_request_buffer->BytesRemaining()); |
| + } |
| + } while (rv > 0); |
| + |
| +#if !defined(USE_OPENSSL) |
| + // NSS records the error exactly. |
| + EXPECT_EQ(ERR_CONNECTION_RESET, rv); |
| +#else |
| + // OpenSSL treats the reset as a generic protocol error. |
| + EXPECT_EQ(ERR_SSL_PROTOCOL_ERROR, rv); |
|
davidben
2014/02/04 19:27:57
Is it possible for this to give us fallback pains?
|
| +#endif |
| + |
| + // Release the read. Some bytes should go through. |
| + raw_transport->UnblockRead(); |
| + rv = read_callback.WaitForResult(); |
| + |
| + // Per the fix for http://crbug.com/249848, write failures currently break |
| + // reads. Change this assertion if they're changed to not collide. |
| + EXPECT_EQ(ERR_CONNECTION_RESET, rv); |
| +} |
| + |
| TEST_F(SSLClientSocketTest, Read_SmallChunks) { |
| SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
| SpawnedTestServer::kLocalhost, |