Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Unified Diff: net/socket/ssl_client_socket_unittest.cc

Issue 17105003: Gracefully handle an asynchronous write disconnect when an SSL read is pending (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comment updates Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/base/nss_memio.c ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
}
« no previous file with comments | « net/base/nss_memio.c ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698