| Index: net/socket/ssl_client_socket_unittest.cc
|
| ===================================================================
|
| --- net/socket/ssl_client_socket_unittest.cc (revision 250122)
|
| +++ net/socket/ssl_client_socket_unittest.cc (working copy)
|
| @@ -1171,6 +1171,112 @@
|
| 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. Write a request larger than the
|
| + // internal socket buffers so that the request hits the underlying transport
|
| + // socket and detects the 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);
|
| +#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,
|
|
|