summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authormbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-25 06:23:00 +0000
committermbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-25 06:23:00 +0000
commit76a505b0776481b4bb2ec41969c8c9dcefd6e2a2 (patch)
treee7dd53d4e8f5897815df364e9f1248a87646eccf /net
parent2b6ed3dc5fc23bdadbd5052f896648d2d62b9ca2 (diff)
downloadchromium_src-76a505b0776481b4bb2ec41969c8c9dcefd6e2a2.zip
chromium_src-76a505b0776481b4bb2ec41969c8c9dcefd6e2a2.tar.gz
chromium_src-76a505b0776481b4bb2ec41969c8c9dcefd6e2a2.tar.bz2
Fix a crash where we are checking IsConnected(). If you look into the
implementation of IsConnected() on windows, you'll find that it can synchronously check if the socket is connected. This makes it so that a CHECK (or DCHECK) on this attribute is dependent on the peer. The peer could send us a TCP-FIN at any time - even 1 nanosecond before our call to IsConnected(). We can only use CHECK(!IsConnected()) to check before we connect, we simply can't use it at runtime. I searched the code and found 4 other instances of this broken check. Most were DCHECKs, which is good, but I've removed them just the same. Turns out our MockSockets have a mechanism for simulating this error, but it wasn't tested nor plumbed through the MockSSLClientSocket. In the process I added a couple of basic proxy test cases, which may be slightly redundant with other test cases that are more advanced. But they seem like a good idea to cover anyway. Added 6 tests in total. BUG=53099 TEST=ProxyTunnelGetHangup, RecycleDeadSSLSocket Review URL: http://codereview.chromium.org/3107036 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57295 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/http/http_network_transaction_unittest.cc319
-rw-r--r--net/http/http_proxy_client_socket.cc1
-rw-r--r--net/http/http_stream_parser.cc4
-rw-r--r--net/socket/socket_test_util.cc4
-rw-r--r--net/socket/socket_test_util.h1
-rw-r--r--net/socket/socks5_client_socket.cc1
-rw-r--r--net/socket/socks_client_socket.cc1
-rw-r--r--net/socket/ssl_client_socket_pool.cc1
8 files changed, 326 insertions, 6 deletions
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index fbff846..56b73c5 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -2494,6 +2494,160 @@ TEST_F(HttpNetworkTransactionTest, RecycleSocket) {
EXPECT_EQ(1, session->tcp_socket_pool()->IdleSocketCount());
}
+// Make sure that we recycle a SSL socket after reading all of the response
+// body.
+TEST_F(HttpNetworkTransactionTest, RecycleSSLSocket) {
+ SessionDependencies session_deps;
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ MockWrite data_writes[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead("Content-Length: 11\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(false, OK),
+ };
+
+ SSLSocketDataProvider ssl(true, OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ session_deps.socket_factory.AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ EXPECT_EQ(0, session->tcp_socket_pool()->IdleSocketCount());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+
+ // Empty the current queue. This is necessary because idle sockets are
+ // added to the connection pool asynchronously with a PostTask.
+ MessageLoop::current()->RunAllPending();
+
+ // We now check to make sure the socket was added back to the pool.
+ EXPECT_EQ(1, session->ssl_socket_pool()->IdleSocketCount());
+}
+
+// Grab a SSL socket, use it, and put it back into the pool. Then, reuse it
+// from the pool and make sure that we recover okay.
+TEST_F(HttpNetworkTransactionTest, RecycleDeadSSLSocket) {
+ SessionDependencies session_deps;
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ MockWrite data_writes[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead("Content-Length: 11\r\n\r\n"),
+ MockRead(false, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead("hello world"),
+ MockRead(true, 0, 0) // EOF
+ };
+
+ SSLSocketDataProvider ssl(true, OK);
+ SSLSocketDataProvider ssl2(true, OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl2);
+
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ StaticSocketDataProvider data2(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ session_deps.socket_factory.AddSocketDataProvider(&data);
+ session_deps.socket_factory.AddSocketDataProvider(&data2);
+
+ TestCompletionCallback callback;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ EXPECT_EQ(0, session->tcp_socket_pool()->IdleSocketCount());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+
+ // Empty the current queue. This is necessary because idle sockets are
+ // added to the connection pool asynchronously with a PostTask.
+ MessageLoop::current()->RunAllPending();
+
+ // We now check to make sure the socket was added back to the pool.
+ EXPECT_EQ(1, session->ssl_socket_pool()->IdleSocketCount());
+
+ // Now start the second transaction, which should reuse the previous socket.
+
+ trans.reset(new HttpNetworkTransaction(session));
+
+ rv = trans->Start(&request, &callback, BoundNetLog());
+
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ EXPECT_EQ(0, session->tcp_socket_pool()->IdleSocketCount());
+
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+
+ // Empty the current queue. This is necessary because idle sockets are
+ // added to the connection pool asynchronously with a PostTask.
+ MessageLoop::current()->RunAllPending();
+
+ // We now check to make sure the socket was added back to the pool.
+ EXPECT_EQ(1, session->ssl_socket_pool()->IdleSocketCount());
+}
+
// Make sure that we recycle a socket after a zero-length response.
// http://crbug.com/9880
TEST_F(HttpNetworkTransactionTest, RecycleSocketAfterZeroContentLength) {
@@ -6595,4 +6749,169 @@ TEST_F(HttpNetworkTransactionTest, SimpleCancel) {
MessageLoop::current()->RunAllPending();
}
+// Test a basic GET request through a proxy.
+TEST_F(HttpNetworkTransactionTest, ProxyGet) {
+ SessionDependencies session_deps(CreateFixedProxyService("myproxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET http://www.google.com/ HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, OK),
+ };
+
+ StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+ data_writes1, arraysize(data_writes1));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_FALSE(response == NULL);
+
+ EXPECT_TRUE(response->headers->IsKeepAlive());
+ EXPECT_EQ(200, response->headers->response_code());
+ EXPECT_EQ(100, response->headers->GetContentLength());
+ EXPECT_TRUE(response->was_fetched_via_proxy);
+ EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+}
+
+// Test a basic HTTPS GET request through a proxy.
+TEST_F(HttpNetworkTransactionTest, ProxyTunnelGet) {
+ SessionDependencies session_deps(CreateFixedProxyService("myproxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+
+ // Since we have proxy, should try to establish tunnel.
+ MockWrite data_writes1[] = {
+ MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, OK),
+ };
+
+ StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+ data_writes1, arraysize(data_writes1));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+ SSLSocketDataProvider ssl(true, OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ size_t pos = ExpectLogContainsSomewhere(
+ log.entries(), 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
+ NetLog::PHASE_NONE);
+ ExpectLogContainsSomewhere(
+ log.entries(), pos,
+ NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
+ NetLog::PHASE_NONE);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_FALSE(response == NULL);
+
+ EXPECT_TRUE(response->headers->IsKeepAlive());
+ EXPECT_EQ(200, response->headers->response_code());
+ EXPECT_EQ(100, response->headers->GetContentLength());
+ EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+ EXPECT_TRUE(response->was_fetched_via_proxy);
+}
+
+// Test a basic HTTPS GET request through a proxy, but the server hangs up
+// while establishing the tunnel.
+TEST_F(HttpNetworkTransactionTest, ProxyTunnelGetHangup) {
+ SessionDependencies session_deps(CreateFixedProxyService("myproxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+
+ // Since we have proxy, should try to establish tunnel.
+ MockWrite data_writes1[] = {
+ MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead(false, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+ MockRead(true, 0, 0), // EOF
+ };
+
+ StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+ data_writes1, arraysize(data_writes1));
+ session_deps.socket_factory.AddSocketDataProvider(&data1);
+ SSLSocketDataProvider ssl(true, OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(ERR_EMPTY_RESPONSE, rv);
+ size_t pos = ExpectLogContainsSomewhere(
+ log.entries(), 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
+ NetLog::PHASE_NONE);
+ ExpectLogContainsSomewhere(
+ log.entries(), pos,
+ NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
+ NetLog::PHASE_NONE);
+}
+
} // namespace net
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index 78d470b..76882fa 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -81,7 +81,6 @@ HttpProxyClientSocket::~HttpProxyClientSocket() {
int HttpProxyClientSocket::Connect(CompletionCallback* callback) {
DCHECK(transport_.get());
DCHECK(transport_->socket());
- DCHECK(transport_->socket()->IsConnected());
DCHECK(!user_callback_);
if (!tunnel_)
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 5fbcb5b..b114519 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -579,7 +579,8 @@ void HttpStreamParser::SetConnectionReused() {
void HttpStreamParser::GetSSLInfo(SSLInfo* ssl_info) {
if (request_->url.SchemeIs("https")) {
- CHECK(connection_->socket()->IsConnected());
+ if (!connection_->socket() || !connection_->socket()->IsConnected())
+ return;
SSLClientSocket* ssl_socket =
static_cast<SSLClientSocket*>(connection_->socket());
ssl_socket->GetSSLInfo(ssl_info);
@@ -591,7 +592,6 @@ void HttpStreamParser::GetSSLCertRequestInfo(
if (request_->url.SchemeIs("https")) {
if (!connection_->socket() || !connection_->socket()->IsConnected())
return;
- CHECK(connection_->socket()->IsConnected());
SSLClientSocket* ssl_socket =
static_cast<SSLClientSocket*>(connection_->socket());
ssl_socket->GetSSLCertRequestInfo(cert_request_info);
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 069c293..5ffd1d4 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -493,6 +493,10 @@ void MockSSLClientSocket::Disconnect() {
transport_->socket()->Disconnect();
}
+bool MockSSLClientSocket::IsConnected() const {
+ return transport_->socket()->IsConnected();
+}
+
int MockSSLClientSocket::Read(net::IOBuffer* buf, int buf_len,
net::CompletionCallback* callback) {
return transport_->socket()->Read(buf, buf_len, callback);
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index ad30353..533a18d 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -636,6 +636,7 @@ class MockSSLClientSocket : public MockClientSocket {
// ClientSocket methods:
virtual int Connect(net::CompletionCallback* callback);
virtual void Disconnect();
+ virtual bool IsConnected() const;
// Socket methods:
virtual int Read(net::IOBuffer* buf, int buf_len,
diff --git a/net/socket/socks5_client_socket.cc b/net/socket/socks5_client_socket.cc
index 997fb25..9c9ecbe 100644
--- a/net/socket/socks5_client_socket.cc
+++ b/net/socket/socks5_client_socket.cc
@@ -67,7 +67,6 @@ SOCKS5ClientSocket::~SOCKS5ClientSocket() {
int SOCKS5ClientSocket::Connect(CompletionCallback* callback) {
DCHECK(transport_.get());
DCHECK(transport_->socket());
- DCHECK(transport_->socket()->IsConnected());
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(!user_callback_);
diff --git a/net/socket/socks_client_socket.cc b/net/socket/socks_client_socket.cc
index 32c4e3b..2000754 100644
--- a/net/socket/socks_client_socket.cc
+++ b/net/socket/socks_client_socket.cc
@@ -102,7 +102,6 @@ SOCKSClientSocket::~SOCKSClientSocket() {
int SOCKSClientSocket::Connect(CompletionCallback* callback) {
DCHECK(transport_.get());
DCHECK(transport_->socket());
- DCHECK(transport_->socket()->IsConnected());
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(!user_callback_);
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 059354e..541792f 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -240,7 +240,6 @@ int SSLConnectJob::DoTunnelConnectComplete(int result) {
if (result < 0)
return result;
- DCHECK(tunnel_socket->IsConnected());
next_state_ = STATE_SSL_CONNECT;
return result;
}