diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/base/net_error_list.h | 5 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 128 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 7 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 188 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket.cc | 52 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket.h | 17 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket_pool.cc | 14 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket_pool.h | 9 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket_pool_unittest.cc | 70 | ||||
-rw-r--r-- | net/socket/client_socket_handle.cc | 1 | ||||
-rw-r--r-- | net/socket/client_socket_handle.h | 7 | ||||
-rw-r--r-- | net/socket/socket_test_util.cc | 48 | ||||
-rw-r--r-- | net/socket/socket_test_util.h | 40 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_pool.cc | 28 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_pool.h | 4 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_pool_unittest.cc | 236 |
16 files changed, 417 insertions, 437 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index e302a9f..53849631 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -167,11 +167,6 @@ NET_ERROR(PROXY_AUTH_REQUESTED, -127) // A known TLS strict server didn't offer the renegotiation extension. NET_ERROR(SSL_UNSAFE_NEGOTIATION, -128) -// The socket is reporting that we tried to provide new credentials after a -// a failed attempt on a connection without keep alive. We need to -// reestablish the transport socket in order to retry the authentication. -NET_ERROR(RETRY_CONNECTION, -129) - // Certificate error codes // // The values of certificate error codes must be consecutive. diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 09a4772..6d77eb1 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -348,7 +348,8 @@ int HttpNetworkTransaction::RestartWithAuth( if (target == HttpAuth::AUTH_PROXY && using_ssl_ && proxy_info_.is_http()) { DCHECK(establishing_tunnel_); - next_state_ = STATE_INIT_CONNECTION; + next_state_ = STATE_RESTART_TUNNEL_AUTH; + auth_controllers_[target] = NULL; ResetStateForRestart(); } else { PrepareForAuthRestart(target); @@ -549,6 +550,13 @@ int HttpNetworkTransaction::DoLoop(int result) { case STATE_INIT_CONNECTION_COMPLETE: rv = DoInitConnectionComplete(rv); break; + case STATE_RESTART_TUNNEL_AUTH: + DCHECK_EQ(OK, rv); + rv = DoRestartTunnelAuth(); + break; + case STATE_RESTART_TUNNEL_AUTH_COMPLETE: + rv = DoRestartTunnelAuthComplete(rv); + break; case STATE_GENERATE_PROXY_AUTH_TOKEN: DCHECK_EQ(OK, rv); rv = DoGenerateProxyAuthToken(); @@ -726,15 +734,6 @@ int HttpNetworkTransaction::DoInitConnection() { DCHECK(proxy_info_.proxy_server().is_valid()); next_state_ = STATE_INIT_CONNECTION_COMPLETE; - // Now that the proxy server has been resolved, create the auth_controllers_. - for (int i = 0; i < HttpAuth::AUTH_NUM_TARGETS; i++) { - HttpAuth::Target target = static_cast<HttpAuth::Target>(i); - if (!auth_controllers_[target].get()) - auth_controllers_[target] = new HttpAuthController(target, - AuthURL(target), - session_); - } - bool want_spdy_over_npn = alternate_protocol_mode_ == kUsingAlternateProtocol && alternate_protocol_ == HttpAlternateProtocols::NPN_SPDY_2; using_ssl_ = request_->url.SchemeIs("https") || @@ -784,29 +783,24 @@ int HttpNetworkTransaction::DoInitConnection() { request_->referrer, disable_resolver_cache); if (proxy_info_.is_http()) { - scoped_refptr<HttpAuthController> http_proxy_auth; GURL authentication_url = request_->url; - if (using_ssl_) { - if (!authentication_url.SchemeIs("https")) { - // If a proxy tunnel connection needs to be established due to - // an Alternate-Protocol, the URL needs to be changed to indicate - // https or digest authentication attempts will fail. - // For example, suppose the initial request was for - // "http://www.example.com/index.html". If this is an SSL - // upgrade due to alternate protocol, the digest authorization - // should have a uri="www.example.com:443" field rather than a - // "/index.html" entry, even though the original request URL has not - // changed. - authentication_url = UpgradeUrlToHttps(authentication_url); - } - http_proxy_auth = auth_controllers_[HttpAuth::AUTH_PROXY]; - establishing_tunnel_ = true; + if (using_ssl_ && !authentication_url.SchemeIs("https")) { + // If a proxy tunnel connection needs to be established due to + // an Alternate-Protocol, the URL needs to be changed to indicate + // https or digest authentication attempts will fail. + // For example, suppose the initial request was for + // "http://www.example.com/index.html". If this is an SSL + // upgrade due to alternate protocol, the digest authorization + // should have a uri="www.example.com:443" field rather than a + // "/index.html" entry, even though the original request URL has not + // changed. + authentication_url = UpgradeUrlToHttps(authentication_url); } + establishing_tunnel_ = using_ssl_; http_proxy_params = new HttpProxySocketParams(proxy_tcp_params, authentication_url, endpoint_, - http_proxy_auth, - using_ssl_); + session_, using_ssl_); } else { DCHECK(proxy_info_.is_socks()); char socks_version; @@ -914,14 +908,13 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) { if (result == ERR_PROXY_AUTH_REQUESTED) { DCHECK(!ssl_started); - const HttpResponseInfo& tunnel_auth_response = - connection_->ssl_error_response_info(); - - response_.headers = tunnel_auth_response.headers; - response_.auth_challenge = tunnel_auth_response.auth_challenge; - headers_valid_ = true; - pending_auth_target_ = HttpAuth::AUTH_PROXY; - return OK; + // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an + // SSL socket, but there was an error before that could happened. This + // puts the in progress HttpProxy socket into |connection_| in order to + // complete the auth. The tunnel restart code is carefully to remove it + // before returning control to the rest of this class. + connection_.reset(connection_->release_pending_http_proxy_connection()); + return HandleTunnelAuthFailure(result); } if ((!ssl_started && result < 0 && @@ -1001,12 +994,44 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) { return OK; } +int HttpNetworkTransaction::DoRestartTunnelAuth() { + next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE; + HttpProxyClientSocket* http_proxy_socket = + static_cast<HttpProxyClientSocket*>(connection_->socket()); + return http_proxy_socket->RestartWithAuth(&io_callback_); +} + +int HttpNetworkTransaction::DoRestartTunnelAuthComplete(int result) { + if (result == ERR_PROXY_AUTH_REQUESTED) + return HandleTunnelAuthFailure(result); + + if (result == OK) { + // Now that we've got the HttpProxyClientSocket connected. We have + // to release it as an idle socket into the pool and start the connection + // process from the beginning. Trying to pass it in with the + // SSLSocketParams might cause a deadlock since params are dispatched + // interchangeably. This request won't necessarily get this http proxy + // socket, but there will be forward progress. + connection_->Reset(); + establishing_tunnel_ = false; + next_state_ = STATE_INIT_CONNECTION; + return OK; + } + + return ReconsiderProxyAfterError(result); +} + int HttpNetworkTransaction::DoGenerateProxyAuthToken() { next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE; if (!ShouldApplyProxyAuth()) return OK; - return auth_controllers_[HttpAuth::AUTH_PROXY]->MaybeGenerateAuthToken( - request_, &io_callback_, net_log_); + HttpAuth::Target target = HttpAuth::AUTH_PROXY; + if (!auth_controllers_[target].get()) + auth_controllers_[target] = new HttpAuthController(target, AuthURL(target), + session_); + return auth_controllers_[target]->MaybeGenerateAuthToken(request_, + &io_callback_, + net_log_); } int HttpNetworkTransaction::DoGenerateProxyAuthTokenComplete(int rv) { @@ -1018,10 +1043,15 @@ int HttpNetworkTransaction::DoGenerateProxyAuthTokenComplete(int rv) { int HttpNetworkTransaction::DoGenerateServerAuthToken() { next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE; + HttpAuth::Target target = HttpAuth::AUTH_SERVER; + if (!auth_controllers_[target].get()) + auth_controllers_[target] = new HttpAuthController(target, AuthURL(target), + session_); if (!ShouldApplyServerAuth()) return OK; - return auth_controllers_[HttpAuth::AUTH_SERVER]->MaybeGenerateAuthToken( - request_, &io_callback_, net_log_); + return auth_controllers_[target]->MaybeGenerateAuthToken(request_, + &io_callback_, + net_log_); } int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) { @@ -1544,6 +1574,24 @@ void HttpNetworkTransaction::LogTransactionMetrics() const { } } +int HttpNetworkTransaction::HandleTunnelAuthFailure(int error) { + DCHECK(establishing_tunnel_); + DCHECK_EQ(ERR_PROXY_AUTH_REQUESTED, error); + HttpProxyClientSocket* http_proxy_socket = + static_cast<HttpProxyClientSocket*>(connection_->socket()); + + const HttpResponseInfo* tunnel_auth_response = + http_proxy_socket->GetResponseInfo(); + response_.headers = tunnel_auth_response->headers; + response_.auth_challenge = tunnel_auth_response->auth_challenge; + headers_valid_ = true; + + auth_controllers_[HttpAuth::AUTH_PROXY] = + http_proxy_socket->auth_controller(); + pending_auth_target_ = HttpAuth::AUTH_PROXY; + return OK; +} + int HttpNetworkTransaction::HandleCertificateError(int error) { DCHECK(using_ssl_); DCHECK(IsCertificateError(error)); diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 8117325..b3a083c 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -91,6 +91,8 @@ class HttpNetworkTransaction : public HttpTransaction { STATE_RESOLVE_PROXY_COMPLETE, STATE_INIT_CONNECTION, STATE_INIT_CONNECTION_COMPLETE, + STATE_RESTART_TUNNEL_AUTH, + STATE_RESTART_TUNNEL_AUTH_COMPLETE, STATE_GENERATE_PROXY_AUTH_TOKEN, STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE, STATE_GENERATE_SERVER_AUTH_TOKEN, @@ -134,6 +136,8 @@ class HttpNetworkTransaction : public HttpTransaction { int DoResolveProxyComplete(int result); int DoInitConnection(); int DoInitConnectionComplete(int result); + int DoRestartTunnelAuth(); + int DoRestartTunnelAuthComplete(int result); int DoGenerateProxyAuthToken(); int DoGenerateProxyAuthTokenComplete(int result); int DoGenerateServerAuthToken(); @@ -170,6 +174,9 @@ class HttpNetworkTransaction : public HttpTransaction { static void LogIOErrorMetrics(const ClientSocketHandle& handle); + // Called to handle an HTTP proxy tunnel request for auth. + int HandleTunnelAuthFailure(int error); + // Called to handle a certificate error. Returns OK if the error should be // ignored. Otherwise, stores the certificate in response_.ssl_info and // returns the same error code. diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 72fa8aa..4e0d354 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -1343,6 +1343,111 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) { EXPECT_EQ(100, response->headers->GetContentLength()); } +// Test the request-challenge-retry sequence for basic auth, over a connection +// that requires a restart when setting up an SSL tunnel. +TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) { + // Configure against proxy server "myproxy:70". + 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/"); + // when the no authentication data flag is set. + request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; + + // 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"), + + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n\r\n"), + }; + + // The proxy responds to the connect with a 407, using a persistent + // connection. + MockRead data_reads1[] = { + // No credentials. + MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"), + MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"), + MockRead("Proxy-Connection: close\r\n\r\n"), + + 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_EQ(407, response->headers->response_code()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + + // The password prompt info should have been set in response->auth_challenge. + ASSERT_FALSE(response->auth_challenge.get() == NULL); + + EXPECT_EQ(L"myproxy:70", response->auth_challenge->host_and_port); + EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm); + EXPECT_EQ(L"basic", response->auth_challenge->scheme); + + TestCompletionCallback callback2; + + rv = trans->RestartWithAuth(kFoo, kBar, &callback2); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback2.WaitForResult(); + EXPECT_EQ(OK, rv); + + 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()); + + // The password prompt info should not be set. + EXPECT_TRUE(response->auth_challenge.get() == NULL); +} + // Test the request-challenge-retry sequence for basic auth, over a keep-alive // proxy connection, when setting up an SSL tunnel. TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) { @@ -6296,6 +6401,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { "\r\n"), }; MockRead data_reads_1[] = { + MockRead(false, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), MockRead("HTTP/1.1 200 OK\r\n" "Alternate-Protocol: 443:npn-spdy/2\r\n" "Proxy-Connection: close\r\n" @@ -6307,55 +6413,64 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { // Second round tries to tunnel to www.google.com due to the // Alternate-Protocol announcement in the first round. It fails due // to a proxy authentication challenge. + // After the failure, a tunnel is established to www.google.com using + // Proxy-Authorization headers. There is then a SPDY request round. + // + // NOTE: Despite the "Proxy-Connection: Close", these are done on the + // same MockTCPClientSocket since the underlying HttpNetworkClientSocket + // does a Disconnect and Connect on the same socket, rather than trying + // to obtain a new one. + // + // NOTE: Originally, the proxy response to the second CONNECT request + // simply returned another 407 so the unit test could skip the SSL connection + // establishment and SPDY framing issues. Alas, the + // retry-http-when-alternate-protocol fails logic kicks in, which was more + // complicated to set up expectations for than the SPDY session. + + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> data(ConstructSpdyBodyFrame(1, true)); + MockWrite data_writes_2[] = { + // First connection attempt without Proxy-Authorization. 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"), - }; - MockRead data_reads_2[] = { - MockRead("HTTP/1.0 407 Unauthorized\r\n" - "Proxy-Authenticate: Mock\r\n" - "Proxy-Connection: close\r\n" - "\r\n"), - }; - StaticSocketDataProvider data_2(data_reads_2, arraysize(data_reads_2), - data_writes_2, arraysize(data_writes_2)); - // Third round establishes a tunnel to www.google.com due to the - // Alternate-Protocol announcement in the first round, and does a SPDY - // request round. - // TODO(cbentzel): Originally, this just returned another 407 so the unit test - // could skip the SSL connection establishment and SPDY framing issues. Alas, - // the retry-http-when-alternate-protocol fails logic kicks in, which was more - // complicated to set up expectations for than the SPDY session. - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); - scoped_ptr<spdy::SpdyFrame> data(ConstructSpdyBodyFrame(1, true)); - - MockWrite data_writes_3[] = { - // TUNNEL connection established + // Second connection attempt with Proxy-Authorization. MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" "Host: www.google.com\r\n" "Proxy-Connection: keep-alive\r\n" "Proxy-Authorization: auth_token\r\n" "\r\n"), + // SPDY request - CreateMockWrite(*req), // 3 + CreateMockWrite(*req), }; + const char kRejectConnectResponse[] = ("HTTP/1.1 407 Unauthorized\r\n" + "Proxy-Authenticate: Mock\r\n" + "Proxy-Connection: close\r\n" + "\r\n"); + const char kAcceptConnectResponse[] = "HTTP/1.1 200 Connected\r\n\r\n"; + MockRead data_reads_2[] = { + // First connection attempt fails + MockRead(false, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ, 1), + MockRead(true, kRejectConnectResponse, + arraysize(kRejectConnectResponse) - 1, 1), - const char kCONNECTResponse[] = "HTTP/1.1 200 Connected\r\n\r\n"; - MockRead data_reads_3[] = { - // Proxy response - MockRead(true, kCONNECTResponse, arraysize(kCONNECTResponse) - 1, 1), - // SPDY response. - CreateMockRead(*resp.get(), 4), - CreateMockRead(*data.get(), 4), - MockRead(true, 0, 0, 4), - }; - scoped_refptr<OrderedSocketData> data_3( - new OrderedSocketData(data_reads_3, arraysize(data_reads_3), - data_writes_3, arraysize(data_writes_3))); + // Second connection attempt passes + MockRead(true, kAcceptConnectResponse, + arraysize(kAcceptConnectResponse) -1, 4), + + // SPDY response + CreateMockRead(*resp.get(), 6), + CreateMockRead(*data.get(), 6), + MockRead(true, 0, 0, 6), + }; + scoped_refptr<OrderedSocketData> data_2( + new OrderedSocketData(data_reads_2, arraysize(data_reads_2), + data_writes_2, arraysize(data_writes_2))); SSLSocketDataProvider ssl(true, OK); ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated; @@ -6363,8 +6478,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { ssl.was_npn_negotiated = true; session_deps.socket_factory.AddSocketDataProvider(&data_1); - session_deps.socket_factory.AddSocketDataProvider(&data_2); - session_deps.socket_factory.AddSocketDataProvider(data_3.get()); + session_deps.socket_factory.AddSocketDataProvider(data_2.get()); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index 02c2666..1c76c36 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc @@ -50,18 +50,20 @@ void BuildTunnelRequest(const HttpRequestInfo* request_info, HttpProxyClientSocket::HttpProxyClientSocket( ClientSocketHandle* transport_socket, const GURL& request_url, - const HostPortPair& endpoint, const scoped_refptr<HttpAuthController>& auth, - bool tunnel) + const HostPortPair& endpoint, const HostPortPair& proxy_server, + const scoped_refptr<HttpNetworkSession>& session, bool tunnel) : ALLOW_THIS_IN_INITIALIZER_LIST( io_callback_(this, &HttpProxyClientSocket::OnIOComplete)), next_state_(STATE_NONE), user_callback_(NULL), transport_(transport_socket), endpoint_(endpoint), - auth_(auth), + auth_(tunnel ? + new HttpAuthController(HttpAuth::AUTH_PROXY, + GURL("http://" + proxy_server.ToString()), + session) : NULL), tunnel_(tunnel), net_log_(transport_socket->socket()->NetLog()) { - DCHECK_EQ(tunnel, auth != NULL); // Synthesize the bits of a request that we actually use. request_.url = request_url; request_.method = "GET"; @@ -126,13 +128,14 @@ int HttpProxyClientSocket::PrepareForAuthRestart() { } int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { - int rc = OK; if (keep_alive && transport_->socket()->IsConnectedAndIdle()) { next_state_ = STATE_GENERATE_AUTH_TOKEN; transport_->set_is_reused(true); } else { + // This assumes that the underlying transport socket is a TCP socket, + // since only TCP sockets are restartable. + next_state_ = STATE_TCP_RESTART; transport_->socket()->Disconnect(); - rc = ERR_RETRY_CONNECTION; } // Reset the other member variables. @@ -140,7 +143,7 @@ int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { http_stream_.reset(); request_headers_.clear(); response_ = HttpResponseInfo(); - return rc; + return OK; } void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const { @@ -159,19 +162,16 @@ void HttpProxyClientSocket::Disconnect() { } bool HttpProxyClientSocket::IsConnected() const { - return transport_->socket()->IsConnected(); + return next_state_ == STATE_DONE && transport_->socket()->IsConnected(); } bool HttpProxyClientSocket::IsConnectedAndIdle() const { - return transport_->socket()->IsConnectedAndIdle(); -} - -bool HttpProxyClientSocket::NeedsRestartWithAuth() const { - return next_state_ != STATE_DONE; + return next_state_ == STATE_DONE && + transport_->socket()->IsConnectedAndIdle(); } int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len, - CompletionCallback* callback) { + CompletionCallback* callback) { DCHECK(!user_callback_); if (next_state_ != STATE_DONE) { // We're trying to read the body of the response but we're still trying @@ -273,6 +273,13 @@ int HttpProxyClientSocket::DoLoop(int last_io_result) { case STATE_DRAIN_BODY_COMPLETE: rv = DoDrainBodyComplete(rv); break; + case STATE_TCP_RESTART: + DCHECK_EQ(OK, rv); + rv = DoTCPRestart(); + break; + case STATE_TCP_RESTART_COMPLETE: + rv = DoTCPRestartComplete(rv); + break; case STATE_DONE: break; default: @@ -280,8 +287,8 @@ int HttpProxyClientSocket::DoLoop(int last_io_result) { rv = ERR_UNEXPECTED; break; } - } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE - && next_state_ != STATE_DONE); + } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE && + next_state_ != STATE_DONE); return rv; } @@ -406,6 +413,19 @@ int HttpProxyClientSocket::DoDrainBodyComplete(int result) { return OK; } +int HttpProxyClientSocket::DoTCPRestart() { + next_state_ = STATE_TCP_RESTART_COMPLETE; + return transport_->socket()->Connect(&io_callback_); +} + +int HttpProxyClientSocket::DoTCPRestartComplete(int result) { + if (result != OK) + return result; + + next_state_ = STATE_GENERATE_AUTH_TOKEN; + return result; +} + int HttpProxyClientSocket::HandleAuthChallenge() { DCHECK(response_.headers); diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index 317b220..ef4f76c 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -35,7 +35,8 @@ class HttpProxyClientSocket : public ClientSocket { // this socket will establish an Http tunnel. HttpProxyClientSocket(ClientSocketHandle* transport_socket, const GURL& request_url, const HostPortPair& endpoint, - const scoped_refptr<HttpAuthController>& auth, + const HostPortPair& proxy_server, + const scoped_refptr<HttpNetworkSession>& session, bool tunnel); // On destruction Disconnect() is called. @@ -46,12 +47,12 @@ class HttpProxyClientSocket : public ClientSocket { // RestartWithAuth. int RestartWithAuth(CompletionCallback* callback); - // Indicates if RestartWithAuth needs to be called. i.e. if Connect - // returned PROXY_AUTH_REQUESTED. Only valid after Connect has been called. - bool NeedsRestartWithAuth() const; - const HttpResponseInfo* GetResponseInfo() const { - return response_.headers ? &response_ : NULL; + return response_.headers ? &response_ : NULL; + } + + const scoped_refptr<HttpAuthController>& auth_controller() { + return auth_; } // ClientSocket methods: @@ -85,6 +86,8 @@ class HttpProxyClientSocket : public ClientSocket { STATE_RESOLVE_CANONICAL_NAME_COMPLETE, STATE_DRAIN_BODY, STATE_DRAIN_BODY_COMPLETE, + STATE_TCP_RESTART, + STATE_TCP_RESTART_COMPLETE, STATE_DONE, }; @@ -112,6 +115,8 @@ class HttpProxyClientSocket : public ClientSocket { int DoReadHeadersComplete(int result); int DoDrainBody(); int DoDrainBodyComplete(int result); + int DoTCPRestart(); + int DoTCPRestartComplete(int result); CompletionCallbackImpl<HttpProxyClientSocket> io_callback_; State next_state_; diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc index a142576..783c10f 100644 --- a/net/http/http_proxy_client_socket_pool.cc +++ b/net/http/http_proxy_client_socket_pool.cc @@ -7,6 +7,7 @@ #include "base/time.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" +#include "net/http/http_network_session.h" #include "net/http/http_proxy_client_socket.h" #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_handle.h" @@ -18,12 +19,12 @@ HttpProxySocketParams::HttpProxySocketParams( const scoped_refptr<TCPSocketParams>& proxy_server, const GURL& request_url, HostPortPair endpoint, - scoped_refptr<HttpAuthController> auth_controller, + scoped_refptr<HttpNetworkSession> session, bool tunnel) : tcp_params_(proxy_server), request_url_(request_url), endpoint_(endpoint), - auth_controller_(auth_controller), + session_(session), tunnel_(tunnel) { } @@ -133,19 +134,22 @@ int HttpProxyConnectJob::DoTCPConnectComplete(int result) { int HttpProxyConnectJob::DoHttpProxyConnect() { next_state_ = kStateHttpProxyConnectComplete; + const HostResolver::RequestInfo& tcp_destination = + params_->tcp_params()->destination(); + HostPortPair proxy_server(tcp_destination.hostname(), + tcp_destination.port()); // Add a HttpProxy connection on top of the tcp socket. socket_.reset(new HttpProxyClientSocket(tcp_socket_handle_.release(), params_->request_url(), params_->endpoint(), - params_->auth_controller(), + proxy_server, + params_->session(), params_->tunnel())); return socket_->Connect(&callback_); } int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) { - DCHECK_NE(result, ERR_RETRY_CONNECTION); - if (result == OK || result == ERR_PROXY_AUTH_REQUESTED) set_socket(socket_.release()); diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h index 63d5c02..347a2427 100644 --- a/net/http/http_proxy_client_socket_pool.h +++ b/net/http/http_proxy_client_socket_pool.h @@ -26,12 +26,13 @@ namespace net { class ClientSocketFactory; class ConnectJobFactory; class HttpAuthController; +class HttpNetworkSession; class HttpProxySocketParams : public base::RefCounted<HttpProxySocketParams> { public: HttpProxySocketParams(const scoped_refptr<TCPSocketParams>& proxy_server, const GURL& request_url, HostPortPair endpoint, - scoped_refptr<HttpAuthController> auth_controller, + scoped_refptr<HttpNetworkSession> session, bool tunnel); const scoped_refptr<TCPSocketParams>& tcp_params() const { @@ -39,8 +40,8 @@ class HttpProxySocketParams : public base::RefCounted<HttpProxySocketParams> { } const GURL& request_url() const { return request_url_; } const HostPortPair& endpoint() const { return endpoint_; } - const scoped_refptr<HttpAuthController>& auth_controller() { - return auth_controller_; + const scoped_refptr<HttpNetworkSession>& session() { + return session_; } bool tunnel() const { return tunnel_; } @@ -51,7 +52,7 @@ class HttpProxySocketParams : public base::RefCounted<HttpProxySocketParams> { const scoped_refptr<TCPSocketParams> tcp_params_; const GURL request_url_; const HostPortPair endpoint_; - const scoped_refptr<HttpAuthController> auth_controller_; + const scoped_refptr<HttpNetworkSession> session_; const bool tunnel_; DISALLOW_COPY_AND_ASSIGN(HttpProxySocketParams); diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc index 9400dd8..3e06c3b 100644 --- a/net/http/http_proxy_client_socket_pool_unittest.cc +++ b/net/http/http_proxy_client_socket_pool_unittest.cc @@ -6,12 +6,19 @@ #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/string_util.h" +#include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" +#include "net/base/ssl_config_service_defaults.h" #include "net/base/test_completion_callback.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_network_session.h" #include "net/http/http_proxy_client_socket.h" +#include "net/proxy/proxy_service.h" #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_histograms.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_session_pool.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -29,19 +36,34 @@ class HttpProxyClientSocketPoolTest : public ClientSocketPoolTest { tcp_histograms_(new ClientSocketPoolHistograms("MockTCP")), tcp_socket_pool_(new MockTCPClientSocketPool(kMaxSockets, kMaxSocketsPerGroup, tcp_histograms_, &tcp_client_socket_factory_)), + http_auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()), + session_(new HttpNetworkSession(new MockHostResolver, + ProxyService::CreateNull(), + &socket_factory_, + new SSLConfigServiceDefaults, + new SpdySessionPool(), + http_auth_handler_factory_.get(), + NULL, + NULL)), notunnel_socket_params_(new HttpProxySocketParams( ignored_tcp_socket_params_, GURL("http://host"), HostPortPair("host", 80), NULL, false)), - auth_controller_(new MockHttpAuthController), tunnel_socket_params_(new HttpProxySocketParams( ignored_tcp_socket_params_, GURL("http://host"), - HostPortPair("host", 80), auth_controller_, true)), + HostPortPair("host", 80), session_, true)), http_proxy_histograms_( new ClientSocketPoolHistograms("HttpProxyUnitTest")), pool_(new HttpProxyClientSocketPool(kMaxSockets, kMaxSocketsPerGroup, http_proxy_histograms_, NULL, tcp_socket_pool_, NULL)) { } + void AddAuthToCache() { + const string16 kFoo(ASCIIToUTF16("foo")); + const string16 kBar(ASCIIToUTF16("bar")); + session_->auth_cache()->Add(GURL("http://proxy/"), "MyRealm1", "Basic", + "Basic realm=MyRealm1", kFoo, kBar, "/"); + } + int StartRequest(const std::string& group_name, RequestPriority priority) { return StartRequestUsingPool( pool_, group_name, priority, tunnel_socket_params_); @@ -52,8 +74,10 @@ class HttpProxyClientSocketPoolTest : public ClientSocketPoolTest { MockClientSocketFactory tcp_client_socket_factory_; scoped_refptr<MockTCPClientSocketPool> tcp_socket_pool_; + MockClientSocketFactory socket_factory_; + scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_; + scoped_refptr<HttpNetworkSession> session_; scoped_refptr<HttpProxySocketParams> notunnel_socket_params_; - scoped_refptr<MockHttpAuthController> auth_controller_; scoped_refptr<HttpProxySocketParams> tunnel_socket_params_; scoped_refptr<ClientSocketPoolHistograms> http_proxy_histograms_; scoped_refptr<HttpProxyClientSocketPool> pool_; @@ -72,7 +96,7 @@ TEST_F(HttpProxyClientSocketPoolTest, NoTunnel) { EXPECT_TRUE(handle.socket()); HttpProxyClientSocket* tunnel_socket = static_cast<HttpProxyClientSocket*>(handle.socket()); - EXPECT_FALSE(tunnel_socket->NeedsRestartWithAuth()); + EXPECT_TRUE(tunnel_socket->IsConnected()); } TEST_F(HttpProxyClientSocketPoolTest, NeedAuth) { @@ -92,10 +116,6 @@ TEST_F(HttpProxyClientSocketPoolTest, NeedAuth) { arraysize(writes)); tcp_client_socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData(""), - }; - auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data)); ClientSocketHandle handle; TestCompletionCallback callback; @@ -110,7 +130,7 @@ TEST_F(HttpProxyClientSocketPoolTest, NeedAuth) { EXPECT_TRUE(handle.socket()); HttpProxyClientSocket* tunnel_socket = static_cast<HttpProxyClientSocket*>(handle.socket()); - EXPECT_TRUE(tunnel_socket->NeedsRestartWithAuth()); + EXPECT_FALSE(tunnel_socket->IsConnected()); } TEST_F(HttpProxyClientSocketPoolTest, HaveAuth) { @@ -119,7 +139,7 @@ TEST_F(HttpProxyClientSocketPoolTest, HaveAuth) { "CONNECT host:80 HTTP/1.1\r\n" "Host: host\r\n" "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead reads[] = { MockRead(false, "HTTP/1.1 200 Connection Established\r\n\r\n"), @@ -129,10 +149,7 @@ TEST_F(HttpProxyClientSocketPoolTest, HaveAuth) { data.set_connect_data(MockConnect(false, 0)); tcp_client_socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; - auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data)); + AddAuthToCache(); ClientSocketHandle handle; TestCompletionCallback callback; @@ -143,7 +160,7 @@ TEST_F(HttpProxyClientSocketPoolTest, HaveAuth) { EXPECT_TRUE(handle.socket()); HttpProxyClientSocket* tunnel_socket = static_cast<HttpProxyClientSocket*>(handle.socket()); - EXPECT_FALSE(tunnel_socket->NeedsRestartWithAuth()); + EXPECT_TRUE(tunnel_socket->IsConnected()); } TEST_F(HttpProxyClientSocketPoolTest, AsyncHaveAuth) { @@ -151,7 +168,7 @@ TEST_F(HttpProxyClientSocketPoolTest, AsyncHaveAuth) { MockWrite("CONNECT host:80 HTTP/1.1\r\n" "Host: host\r\n" "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead reads[] = { MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), @@ -160,10 +177,7 @@ TEST_F(HttpProxyClientSocketPoolTest, AsyncHaveAuth) { arraysize(writes)); tcp_client_socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; - auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data)); + AddAuthToCache(); ClientSocketHandle handle; TestCompletionCallback callback; @@ -178,7 +192,7 @@ TEST_F(HttpProxyClientSocketPoolTest, AsyncHaveAuth) { EXPECT_TRUE(handle.socket()); HttpProxyClientSocket* tunnel_socket = static_cast<HttpProxyClientSocket*>(handle.socket()); - EXPECT_FALSE(tunnel_socket->NeedsRestartWithAuth()); + EXPECT_TRUE(tunnel_socket->IsConnected()); } TEST_F(HttpProxyClientSocketPoolTest, TCPError) { @@ -205,7 +219,7 @@ TEST_F(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) { MockWrite("CONNECT host:80 HTTP/1.1\r\n" "Host: host\r\n" "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead reads[] = { MockRead("HTTP/1.1 200 Conn"), @@ -215,10 +229,7 @@ TEST_F(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) { arraysize(writes)); tcp_client_socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; - auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data)); + AddAuthToCache(); ClientSocketHandle handle; TestCompletionCallback callback; @@ -238,7 +249,7 @@ TEST_F(HttpProxyClientSocketPoolTest, TunnelSetupError) { MockWrite("CONNECT host:80 HTTP/1.1\r\n" "Host: host\r\n" "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead reads[] = { MockRead("HTTP/1.1 304 Not Modified\r\n\r\n"), @@ -247,10 +258,7 @@ TEST_F(HttpProxyClientSocketPoolTest, TunnelSetupError) { arraysize(writes)); tcp_client_socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; - auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data)); + AddAuthToCache(); ClientSocketHandle handle; TestCompletionCallback callback; diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc index de2fd94..d9ccbd5 100644 --- a/net/socket/client_socket_handle.cc +++ b/net/socket/client_socket_handle.cc @@ -59,6 +59,7 @@ void ClientSocketHandle::ResetInternal(bool cancel) { void ClientSocketHandle::ResetErrorState() { is_ssl_error_ = false; ssl_error_response_info_ = HttpResponseInfo(); + pending_http_proxy_connection_.reset(); } LoadState ClientSocketHandle::GetLoadState() const { diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h index a25bfdb..adccc89 100644 --- a/net/socket/client_socket_handle.h +++ b/net/socket/client_socket_handle.h @@ -109,6 +109,9 @@ class ClientSocketHandle { void set_ssl_error_response_info(const HttpResponseInfo& ssl_error_state) { ssl_error_response_info_ = ssl_error_state; } + void set_pending_http_proxy_connection(ClientSocketHandle* connection) { + pending_http_proxy_connection_.reset(connection); + } // Only valid if there is no |socket_|. bool is_ssl_error() const { @@ -121,6 +124,9 @@ class ClientSocketHandle { const HttpResponseInfo& ssl_error_response_info() const { return ssl_error_response_info_; } + ClientSocketHandle* release_pending_http_proxy_connection() { + return pending_http_proxy_connection_.release(); + } // These may only be used if is_initialized() is true. const std::string& group_name() const { return group_name_; } @@ -179,6 +185,7 @@ class ClientSocketHandle { int pool_id_; // See ClientSocketPool::ReleaseSocket() for an explanation. bool is_ssl_error_; HttpResponseInfo ssl_error_response_info_; + scoped_ptr<ClientSocketHandle> pending_http_proxy_connection_; base::TimeTicks init_time_; base::TimeDelta setup_time_; diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index d4d74a7..142a39e 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -187,6 +187,7 @@ int MockTCPClientSocket::Connect(net::CompletionCallback* callback) { if (connected_) return net::OK; connected_ = true; + peer_closed_connection_ = false; if (data_->connect_data().async) { RunCallbackAsync(callback, data_->connect_data().result); return net::ERR_IO_PENDING; @@ -886,53 +887,6 @@ void MockSOCKSClientSocketPool::ReleaseSocket(const std::string& group_name, MockSOCKSClientSocketPool::~MockSOCKSClientSocketPool() {} -MockHttpAuthController::MockHttpAuthController() - : HttpAuthController(HttpAuth::AUTH_PROXY, GURL(), - scoped_refptr<HttpNetworkSession>(NULL)), - data_(NULL), - data_index_(0), - data_count_(0) { -} - -void MockHttpAuthController::SetMockAuthControllerData( - struct MockHttpAuthControllerData* data, size_t count) { - data_ = data; - data_count_ = count; -} - -int MockHttpAuthController::MaybeGenerateAuthToken( - const HttpRequestInfo* request, - CompletionCallback* callback, - const BoundNetLog& net_log) { - return OK; -} - -void MockHttpAuthController::AddAuthorizationHeader( - HttpRequestHeaders* authorization_headers) { - authorization_headers->AddHeadersFromString(CurrentData().auth_header); -} - -int MockHttpAuthController::HandleAuthChallenge( - scoped_refptr<HttpResponseHeaders> headers, - bool do_not_send_server_auth, - bool establishing_tunnel, - const BoundNetLog& net_log) { - return OK; -} - -void MockHttpAuthController::ResetAuth(const string16& username, - const string16& password) { - data_index_++; -} - -bool MockHttpAuthController::HaveAuth() const { - return CurrentData().auth_header.size() != 0; -} - -bool MockHttpAuthController::HaveAuthHandler() const { - return HaveAuth(); -} - const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 }; const int kSOCKS5GreetRequestLength = arraysize(kSOCKS5GreetRequest); diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index 1a0ab11..0ccf878 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -695,46 +695,6 @@ class MockSOCKSClientSocketPool : public SOCKSClientSocketPool { DISALLOW_COPY_AND_ASSIGN(MockSOCKSClientSocketPool); }; -struct MockHttpAuthControllerData { - explicit MockHttpAuthControllerData(std::string header) - : auth_header(header) {} - - std::string auth_header; -}; - -class MockHttpAuthController : public HttpAuthController { - public: - MockHttpAuthController(); - void SetMockAuthControllerData(struct MockHttpAuthControllerData* data, - size_t data_length); - - // HttpAuthController methods. - virtual int MaybeGenerateAuthToken(const HttpRequestInfo* request, - CompletionCallback* callback, - const BoundNetLog& net_log); - virtual void AddAuthorizationHeader( - HttpRequestHeaders* authorization_headers); - virtual int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers, - bool do_not_send_server_auth, - bool establishing_tunnel, - const BoundNetLog& net_log); - virtual void ResetAuth(const string16& username, - const string16& password); - virtual bool HaveAuthHandler() const; - virtual bool HaveAuth() const; - - private: - virtual ~MockHttpAuthController() {} - const struct MockHttpAuthControllerData& CurrentData() const { - DCHECK(data_index_ < data_count_); - return data_[data_index_]; - } - - MockHttpAuthControllerData* data_; - size_t data_index_; - size_t data_count_; -}; - // Constants for a successful SOCKS v5 handshake. extern const char kSOCKS5GreetRequest[]; extern const int kSOCKS5GreetRequestLength; diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc index 03e2e80..5ed450e 100644 --- a/net/socket/ssl_client_socket_pool.cc +++ b/net/socket/ssl_client_socket_pool.cc @@ -103,11 +103,6 @@ LoadState SSLConnectJob::GetLoadState() const { } int SSLConnectJob::ConnectInternal() { - DetermineFirstState(); - return DoLoop(OK); -} - -void SSLConnectJob::DetermineFirstState() { switch (params_->proxy()) { case ProxyServer::SCHEME_DIRECT: next_state_ = STATE_TCP_CONNECT; @@ -123,6 +118,7 @@ void SSLConnectJob::DetermineFirstState() { NOTREACHED() << "unknown proxy type"; break; } + return DoLoop(OK); } void SSLConnectJob::OnIOComplete(int result) { @@ -214,6 +210,7 @@ int SSLConnectJob::DoSOCKSConnectComplete(int result) { int SSLConnectJob::DoTunnelConnect() { DCHECK(http_proxy_pool_.get()); next_state_ = STATE_TUNNEL_CONNECT_COMPLETE; + transport_socket_handle_.reset(new ClientSocketHandle()); scoped_refptr<HttpProxySocketParams> http_proxy_params = params_->http_proxy_params(); @@ -228,12 +225,6 @@ int SSLConnectJob::DoTunnelConnectComplete(int result) { HttpProxyClientSocket* tunnel_socket = static_cast<HttpProxyClientSocket*>(socket); - if (result == ERR_RETRY_CONNECTION) { - DetermineFirstState(); - transport_socket_handle_->socket()->Disconnect(); - return OK; - } - // Extract the information needed to prompt for the proxy authentication. // so that when ClientSocketPoolBaseHelper calls |GetAdditionalErrorState|, // we can easily set the state. @@ -243,20 +234,17 @@ int SSLConnectJob::DoTunnelConnectComplete(int result) { if (result < 0) return result; - if (tunnel_socket->NeedsRestartWithAuth()) { - // We must have gotten an 'idle' tunnel socket that is waiting for auth. - // The HttpAuthController should have new credentials, we just need - // to retry. - next_state_ = STATE_TUNNEL_CONNECT_COMPLETE; - return tunnel_socket->RestartWithAuth(&callback_); - } - + DCHECK(tunnel_socket->IsConnected()); next_state_ = STATE_SSL_CONNECT; return result; } void SSLConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) { - handle->set_ssl_error_response_info(error_response_info_); + if (error_response_info_.headers) { + handle->set_ssl_error_response_info(error_response_info_); + handle->set_pending_http_proxy_connection( + transport_socket_handle_.release()); + } if (!ssl_connect_start_time_.is_null()) handle->set_is_ssl_error(true); } diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h index b44985f..9857c61 100644 --- a/net/socket/ssl_client_socket_pool.h +++ b/net/socket/ssl_client_socket_pool.h @@ -44,7 +44,7 @@ class SSLSocketParams : public base::RefCounted<SSLSocketParams> { bool want_spdy_over_npn); const scoped_refptr<TCPSocketParams>& tcp_params() { return tcp_params_; } - const scoped_refptr<HttpProxySocketParams>& http_proxy_params () { + const scoped_refptr<HttpProxySocketParams>& http_proxy_params() { return http_proxy_params_; } const scoped_refptr<SOCKSSocketParams>& socks_params() { @@ -114,8 +114,6 @@ class SSLConnectJob : public ConnectJob { // Otherwise, it returns a net error code. virtual int ConnectInternal(); - void DetermineFirstState(); - void OnIOComplete(int result); // Runs the state transition loop. diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc index efc4795..047a647 100644 --- a/net/socket/ssl_client_socket_pool_unittest.cc +++ b/net/socket/ssl_client_socket_pool_unittest.cc @@ -6,13 +6,14 @@ #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/string_util.h" #include "base/time.h" #include "net/base/auth.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/base/ssl_config_service_defaults.h" -#include "net/http/http_auth_controller.h" +#include "net/http/http_auth_handler_factory.h" #include "net/http/http_network_session.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" @@ -20,6 +21,7 @@ #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_histograms.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_session_pool.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -32,7 +34,16 @@ const int kMaxSocketsPerGroup = 6; class SSLClientSocketPoolTest : public ClientSocketPoolTest { protected: SSLClientSocketPoolTest() - : direct_tcp_socket_params_(new TCPSocketParams( + : http_auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()), + session_(new HttpNetworkSession(new MockHostResolver, + ProxyService::CreateNull(), + &socket_factory_, + new SSLConfigServiceDefaults, + new SpdySessionPool(), + http_auth_handler_factory_.get(), + NULL, + NULL)), + direct_tcp_socket_params_(new TCPSocketParams( HostPortPair("host", 443), MEDIUM, GURL(), false)), tcp_socket_pool_(new MockTCPClientSocketPool( kMaxSockets, @@ -41,6 +52,9 @@ class SSLClientSocketPoolTest : public ClientSocketPoolTest { &socket_factory_)), proxy_tcp_socket_params_(new TCPSocketParams( HostPortPair("proxy", 443), MEDIUM, GURL(), false)), + http_proxy_socket_params_(new HttpProxySocketParams( + proxy_tcp_socket_params_, GURL("http://host"), + HostPortPair("host", 80), session_, true)), http_proxy_socket_pool_(new HttpProxyClientSocketPool( kMaxSockets, kMaxSocketsPerGroup, @@ -74,33 +88,30 @@ class SSLClientSocketPoolTest : public ClientSocketPoolTest { NULL); } - scoped_refptr<SSLSocketParams> SSLParams( - ProxyServer::Scheme proxy, struct MockHttpAuthControllerData* auth_data, - size_t auth_data_len, bool want_spdy_over_ssl, bool want_spdy_over_npn) { - scoped_refptr<HttpProxySocketParams> http_proxy_params; - if (proxy == ProxyServer::SCHEME_HTTP) { - scoped_refptr<MockHttpAuthController> auth_controller = - new MockHttpAuthController(); - auth_controller->SetMockAuthControllerData(auth_data, auth_data_len); - http_proxy_params = new HttpProxySocketParams(proxy_tcp_socket_params_, - GURL("http://host"), - HostPortPair("host", 80), - auth_controller, true); - } - + scoped_refptr<SSLSocketParams> SSLParams(ProxyServer::Scheme proxy, + bool want_spdy_over_npn) { return make_scoped_refptr(new SSLSocketParams( proxy == ProxyServer::SCHEME_DIRECT ? direct_tcp_socket_params_ : NULL, - http_proxy_params, + proxy == ProxyServer::SCHEME_HTTP ? http_proxy_socket_params_ : NULL, proxy == ProxyServer::SCHEME_SOCKS5 ? socks_socket_params_ : NULL, proxy, "host", ssl_config_, 0, - want_spdy_over_ssl, + false, want_spdy_over_npn)); } + void AddAuthToCache() { + const string16 kFoo(ASCIIToUTF16("foo")); + const string16 kBar(ASCIIToUTF16("bar")); + session_->auth_cache()->Add(GURL("http://proxy:443/"), "MyRealm1", "Basic", + "Basic realm=MyRealm1", kFoo, kBar, "/"); + } + MockClientSocketFactory socket_factory_; + scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_; + scoped_refptr<HttpNetworkSession> session_; scoped_refptr<TCPSocketParams> direct_tcp_socket_params_; scoped_refptr<MockTCPClientSocketPool> tcp_socket_pool_; @@ -123,7 +134,7 @@ TEST_F(SSLClientSocketPoolTest, TCPFail) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, false); + false); ClientSocketHandle handle; int rv = handle.Init("a", params, MEDIUM, NULL, pool_, BoundNetLog()); @@ -140,7 +151,7 @@ TEST_F(SSLClientSocketPoolTest, TCPFailAsync) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -164,7 +175,7 @@ TEST_F(SSLClientSocketPoolTest, BasicDirect) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -182,7 +193,7 @@ TEST_F(SSLClientSocketPoolTest, BasicDirectAsync) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -204,7 +215,7 @@ TEST_F(SSLClientSocketPoolTest, DirectCertError) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -226,7 +237,7 @@ TEST_F(SSLClientSocketPoolTest, DirectSSLError) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -251,7 +262,7 @@ TEST_F(SSLClientSocketPoolTest, DirectWithNPN) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -277,7 +288,7 @@ TEST_F(SSLClientSocketPoolTest, DirectNoSPDY) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, true); + true); ClientSocketHandle handle; TestCompletionCallback callback; @@ -302,7 +313,7 @@ TEST_F(SSLClientSocketPoolTest, DirectGotSPDY) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, true); + true); ClientSocketHandle handle; TestCompletionCallback callback; @@ -333,7 +344,7 @@ TEST_F(SSLClientSocketPoolTest, DirectGotBonusSPDY) { CreatePool(true /* tcp pool */, false, false); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT, - NULL, 0, false, true); + true); ClientSocketHandle handle; TestCompletionCallback callback; @@ -361,7 +372,7 @@ TEST_F(SSLClientSocketPoolTest, SOCKSFail) { CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -379,7 +390,7 @@ TEST_F(SSLClientSocketPoolTest, SOCKSFailAsync) { CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -403,7 +414,7 @@ TEST_F(SSLClientSocketPoolTest, SOCKSBasic) { CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -421,7 +432,7 @@ TEST_F(SSLClientSocketPoolTest, SOCKSBasicAsync) { CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -442,7 +453,7 @@ TEST_F(SSLClientSocketPoolTest, HttpProxyFail) { CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -460,7 +471,7 @@ TEST_F(SSLClientSocketPoolTest, HttpProxyFailAsync) { CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP, - NULL, 0, false, false); + false); ClientSocketHandle handle; TestCompletionCallback callback; @@ -481,7 +492,7 @@ TEST_F(SSLClientSocketPoolTest, HttpProxyBasic) { "CONNECT host:80 HTTP/1.1\r\n" "Host: host\r\n" "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead reads[] = { MockRead(false, "HTTP/1.1 200 Connection Established\r\n\r\n"), @@ -490,17 +501,12 @@ TEST_F(SSLClientSocketPoolTest, HttpProxyBasic) { arraysize(writes)); data.set_connect_data(MockConnect(false, OK)); socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; + AddAuthToCache(); SSLSocketDataProvider ssl(false, OK); socket_factory_.AddSSLSocketDataProvider(&ssl); CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP, - auth_data, - arraysize(auth_data), - false, false); ClientSocketHandle handle; @@ -516,7 +522,7 @@ TEST_F(SSLClientSocketPoolTest, HttpProxyBasicAsync) { MockWrite("CONNECT host:80 HTTP/1.1\r\n" "Host: host\r\n" "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead reads[] = { MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), @@ -524,17 +530,12 @@ TEST_F(SSLClientSocketPoolTest, HttpProxyBasicAsync) { StaticSocketDataProvider data(reads, arraysize(reads), writes, arraysize(writes)); socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; + AddAuthToCache(); SSLSocketDataProvider ssl(true, OK); socket_factory_.AddSSLSocketDataProvider(&ssl); CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP, - auth_data, - arraysize(auth_data), - false, false); ClientSocketHandle handle; @@ -564,135 +565,11 @@ TEST_F(SSLClientSocketPoolTest, NeedProxyAuth) { StaticSocketDataProvider data(reads, arraysize(reads), writes, arraysize(writes)); socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData(""), - }; - SSLSocketDataProvider ssl(true, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl); - - CreatePool(false, true /* http proxy pool */, true /* socks pool */); - scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP, - auth_data, - arraysize(auth_data), - false, - false); - - ClientSocketHandle handle; - TestCompletionCallback callback; - int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_FALSE(handle.is_initialized()); - EXPECT_FALSE(handle.socket()); - - EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult()); - EXPECT_FALSE(handle.is_initialized()); - EXPECT_FALSE(handle.socket()); - EXPECT_FALSE(handle.is_ssl_error()); - const HttpResponseInfo& tunnel_info = handle.ssl_error_response_info(); - EXPECT_EQ(tunnel_info.headers->response_code(), 407); -} - -TEST_F(SSLClientSocketPoolTest, DoProxyAuth) { - MockWrite writes[] = { - MockWrite("CONNECT host:80 HTTP/1.1\r\n" - "Host: host\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("CONNECT host:80 HTTP/1.1\r\n" - "Host: host\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), - }; - MockRead reads[] = { - MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"), - MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"), - MockRead("Content-Length: 10\r\n\r\n"), - MockRead("0123456789"), - MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), - }; - StaticSocketDataProvider data(reads, arraysize(reads), writes, - arraysize(writes)); - socket_factory_.AddSocketDataProvider(&data); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData(""), - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; - SSLSocketDataProvider ssl(true, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl); - - CreatePool(false, true /* http proxy pool */, true /* socks pool */); - scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP, - auth_data, - arraysize(auth_data), - false, - false); - - ClientSocketHandle handle; - TestCompletionCallback callback; - int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_FALSE(handle.is_initialized()); - EXPECT_FALSE(handle.socket()); - - EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult()); - EXPECT_FALSE(handle.is_initialized()); - EXPECT_FALSE(handle.socket()); - EXPECT_FALSE(handle.is_ssl_error()); - const HttpResponseInfo& tunnel_info = handle.ssl_error_response_info(); - EXPECT_EQ(tunnel_info.headers->response_code(), 407); - - params->http_proxy_params()->auth_controller()->ResetAuth(string16(), - string16()); - rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_FALSE(handle.is_initialized()); - EXPECT_FALSE(handle.socket()); - - // Test that http://crbug.com/49325 doesn't regress. - EXPECT_EQ(handle.GetLoadState(), LOAD_STATE_ESTABLISHING_PROXY_TUNNEL); - - EXPECT_EQ(OK, callback.WaitForResult()); - EXPECT_TRUE(handle.is_initialized()); - EXPECT_TRUE(handle.socket()); -} - -TEST_F(SSLClientSocketPoolTest, DoProxyAuthNoKeepAlive) { - MockWrite writes1[] = { - MockWrite("CONNECT host:80 HTTP/1.1\r\n" - "Host: host\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - }; - MockWrite writes2[] = { - MockWrite("CONNECT host:80 HTTP/1.1\r\n" - "Host: host\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), - }; - MockRead reads1[] = { - MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"), - MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n\r\n"), - MockRead("Content0123456789"), - }; - MockRead reads2[] = { - MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), - }; - StaticSocketDataProvider data1(reads1, arraysize(reads1), writes1, - arraysize(writes1)); - socket_factory_.AddSocketDataProvider(&data1); - StaticSocketDataProvider data2(reads2, arraysize(reads2), writes2, - arraysize(writes2)); - socket_factory_.AddSocketDataProvider(&data2); - MockHttpAuthControllerData auth_data[] = { - MockHttpAuthControllerData(""), - MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="), - }; SSLSocketDataProvider ssl(true, OK); socket_factory_.AddSSLSocketDataProvider(&ssl); CreatePool(false, true /* http proxy pool */, true /* socks pool */); scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP, - auth_data, - arraysize(auth_data), - false, false); ClientSocketHandle handle; @@ -708,17 +585,10 @@ TEST_F(SSLClientSocketPoolTest, DoProxyAuthNoKeepAlive) { EXPECT_FALSE(handle.is_ssl_error()); const HttpResponseInfo& tunnel_info = handle.ssl_error_response_info(); EXPECT_EQ(tunnel_info.headers->response_code(), 407); - - params->http_proxy_params()->auth_controller()->ResetAuth(string16(), - string16()); - rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_FALSE(handle.is_initialized()); - EXPECT_FALSE(handle.socket()); - - EXPECT_EQ(OK, callback.WaitForResult()); - EXPECT_TRUE(handle.is_initialized()); - EXPECT_TRUE(handle.socket()); + scoped_ptr<ClientSocketHandle> tunnel_handle( + handle.release_pending_http_proxy_connection()); + EXPECT_TRUE(tunnel_handle->socket()); + EXPECT_FALSE(tunnel_handle->socket()->IsConnected()); } // It would be nice to also test the timeouts in SSLClientSocketPool. |