diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-17 18:31:03 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-17 18:31:03 +0000 |
commit | 4d87a94b75030786e356c3b4a25a06e9634a0012 (patch) | |
tree | fd2d17a248d34298ed79ee9ef0d4abe9e85781e2 /net | |
parent | 33699e6296c05af9ab0b14ac172e001a0ee0398a (diff) | |
download | chromium_src-4d87a94b75030786e356c3b4a25a06e9634a0012.zip chromium_src-4d87a94b75030786e356c3b4a25a06e9634a0012.tar.gz chromium_src-4d87a94b75030786e356c3b4a25a06e9634a0012.tar.bz2 |
Allow chrome to handle 407 auth challenges to CONNECT requests
through HTTPS Proxies. This also changes the mechanism used
to restart HttpProxyClientSocket requests with auth. Previously
the transport socket would be Disconnected, and then re-Connected
(which was not implemented for SSLClientSockets). However, the
approach was problematic in the face of, for example, ipv6. The
new approach is to close the HttpProxyClientSocket, and request
a new socket from the pool.
Review URL: http://codereview.chromium.org/8502024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110529 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 422 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket.cc | 45 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket.h | 17 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket_pool_unittest.cc | 30 | ||||
-rw-r--r-- | net/http/http_proxy_utils.cc | 20 | ||||
-rw-r--r-- | net/http/http_proxy_utils.h | 11 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.cc | 18 | ||||
-rw-r--r-- | net/http/proxy_client_socket.h | 10 | ||||
-rw-r--r-- | net/spdy/spdy_proxy_client_socket.cc | 23 | ||||
-rw-r--r-- | net/spdy/spdy_proxy_client_socket.h | 10 | ||||
-rw-r--r-- | net/spdy/spdy_proxy_client_socket_unittest.cc | 74 |
11 files changed, 583 insertions, 97 deletions
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index bcc83bb..8bbd17c 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -1682,7 +1682,9 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) { 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 data_writes2[] = { // 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" @@ -1702,7 +1704,9 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) { 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 data_reads2[] = { MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), MockRead("HTTP/1.1 200 OK\r\n"), @@ -1713,7 +1717,10 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) { StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), data_writes1, arraysize(data_writes1)); + StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), + data_writes2, arraysize(data_writes2)); session_deps.socket_factory.AddSocketDataProvider(&data1); + session_deps.socket_factory.AddSocketDataProvider(&data2); SSLSocketDataProvider ssl(true, OK); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); @@ -1926,6 +1933,368 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyCancelTunnel) { session->CloseAllConnections(); } +// 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, BasicAuthHttpsProxyNoKeepAlive) { + 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; + + // Configure against https proxy server "myproxy:70". + SessionDependencies session_deps( + ProxyService::CreateFixed("https://myproxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // 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 data_writes2[] = { + // 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 data_reads2[] = { + 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: 5\r\n\r\n"), + MockRead(false, "hello"), + }; + + StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), + data_writes1, arraysize(data_writes1)); + StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), + data_writes2, arraysize(data_writes2)); + session_deps.socket_factory.AddSocketDataProvider(&data1); + session_deps.socket_factory.AddSocketDataProvider(&data2); + SSLSocketDataProvider proxy(true, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&proxy); + SSLSocketDataProvider proxy2(true, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&proxy2); + SSLSocketDataProvider server(true, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&server); + + TestOldCompletionCallback callback1; + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + + int rv = trans->Start(&request, &callback1, log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback1.WaitForResult(); + EXPECT_EQ(OK, rv); + net::CapturingNetLog::EntryList entries; + log.GetEntries(&entries); + size_t pos = ExpectLogContainsSomewhere( + entries, 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, + NetLog::PHASE_NONE); + ExpectLogContainsSomewhere( + entries, pos, + NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, + NetLog::PHASE_NONE); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_FALSE(response->headers == NULL); + EXPECT_EQ(407, response->headers->response_code()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + EXPECT_TRUE(CheckBasicProxyAuth(response->auth_challenge.get())); + + TestOldCompletionCallback callback2; + + rv = trans->RestartWithAuth(AuthCredentials(kFoo, kBar), &callback2); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback2.WaitForResult(); + EXPECT_EQ(OK, rv); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->headers->IsKeepAlive()); + EXPECT_EQ(200, response->headers->response_code()); + EXPECT_EQ(5, 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); + + trans.reset(); + session->CloseAllConnections(); +} + +// Test the request-challenge-retry sequence for basic auth, over a keep-alive +// proxy connection, when setting up an SSL tunnel. +TEST_F(HttpNetworkTransactionTest, BasicAuthHttpsProxyKeepAlive) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + // Ensure that proxy authentication is attempted even + // when the no authentication data flag is set. + request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; + + // Configure against https proxy server "myproxy:70". + SessionDependencies session_deps( + ProxyService::CreateFixed("https://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)); + + // 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 Zm9vOmJheg==\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("Content-Length: 10\r\n\r\n"), + MockRead("0123456789"), + + // Wrong credentials (wrong password). + 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"), + // No response body because the test stops reading here. + MockRead(false, ERR_UNEXPECTED), // Should not be reached. + }; + + 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); + + TestOldCompletionCallback callback1; + + int rv = trans->Start(&request, &callback1, log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback1.WaitForResult(); + EXPECT_EQ(OK, rv); + net::CapturingNetLog::EntryList entries; + log.GetEntries(&entries); + size_t pos = ExpectLogContainsSomewhere( + entries, 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, + NetLog::PHASE_NONE); + ExpectLogContainsSomewhere( + entries, pos, + NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, + NetLog::PHASE_NONE); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_FALSE(response->headers == NULL); + EXPECT_TRUE(response->headers->IsKeepAlive()); + EXPECT_EQ(407, response->headers->response_code()); + EXPECT_EQ(10, response->headers->GetContentLength()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + EXPECT_TRUE(CheckBasicProxyAuth(response->auth_challenge.get())); + + TestOldCompletionCallback callback2; + + // Wrong password (should be "bar"). + rv = trans->RestartWithAuth(AuthCredentials(kFoo, kBaz), &callback2); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback2.WaitForResult(); + EXPECT_EQ(OK, rv); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_FALSE(response->headers == NULL); + EXPECT_TRUE(response->headers->IsKeepAlive()); + EXPECT_EQ(407, response->headers->response_code()); + EXPECT_EQ(10, response->headers->GetContentLength()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + EXPECT_TRUE(CheckBasicProxyAuth(response->auth_challenge.get())); + + // Flush the idle socket before the NetLog and HttpNetworkTransaction go + // out of scope. + session->CloseAllConnections(); +} + +// Test the request-challenge-retry sequence for basic auth, through +// a SPDY proxy over a single SPDY session. +TEST_F(HttpNetworkTransactionTest, BasicAuthSpdyProxy) { + 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; + + // Configure against https proxy server "myproxy:70". + SessionDependencies session_deps( + ProxyService::CreateFixed("https://myproxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Since we have proxy, should try to establish tunnel. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyConnect(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> rst(ConstructSpdyRstStream(1, spdy::CANCEL)); + + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + const char* const kAuthCredentials[] = { + "proxy-authorization", "Basic Zm9vOmJhcg==", + }; + scoped_ptr<spdy::SpdyFrame> connect2( + ConstructSpdyConnect(kAuthCredentials, arraysize(kAuthCredentials)/2, 3)); + // fetch https://www.google.com/ via HTTP + const char get[] = "GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n\r\n"; + scoped_ptr<spdy::SpdyFrame> wrapped_get( + ConstructSpdyBodyFrame(3, get, strlen(get), false)); + + MockWrite spdy_writes[] = { + CreateMockWrite(*req, 0, true), + CreateMockWrite(*rst, 2, true), + CreateMockWrite(*connect2, 3), + CreateMockWrite(*wrapped_get, 5) + }; + + // The proxy responds to the connect with a 407, using a persistent + // connection. + const char* const kAuthChallenge[] = { + "status", "407 Proxy Authentication Required", + "version", "HTTP/1.1", + "proxy-authenticate", "Basic realm=\"MyRealm1\"", + }; + + scoped_ptr<spdy::SpdyFrame> conn_auth_resp( + ConstructSpdyControlFrame(NULL, + 0, + false, + 1, + LOWEST, + spdy::SYN_REPLY, + spdy::CONTROL_FLAG_NONE, + kAuthChallenge, + arraysize(kAuthChallenge))); + + scoped_ptr<spdy::SpdyFrame> conn_resp(ConstructSpdyGetSynReply(NULL, 0, 3)); + const char resp[] = "HTTP/1.1 200 OK\r\n" + "Content-Length: 5\r\n\r\n"; + + scoped_ptr<spdy::SpdyFrame> wrapped_get_resp( + ConstructSpdyBodyFrame(3, resp, strlen(resp), false)); + scoped_ptr<spdy::SpdyFrame> wrapped_body( + ConstructSpdyBodyFrame(3, "hello", 10, false)); + MockRead spdy_reads[] = { + CreateMockRead(*conn_auth_resp, 1, true), + CreateMockRead(*conn_resp, 4, true), + CreateMockRead(*wrapped_get_resp, 5, true), + CreateMockRead(*wrapped_body, 6, true), + MockRead(false, ERR_IO_PENDING), + }; + + scoped_refptr<OrderedSocketData> spdy_data( + new OrderedSocketData( + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + session_deps.socket_factory.AddSocketDataProvider(spdy_data); + // Negotiate SPDY to the proxy + SSLSocketDataProvider proxy(true, OK); + proxy.next_proto_status = SSLClientSocket::kNextProtoNegotiated; + proxy.next_proto = "spdy/2"; + proxy.was_npn_negotiated = true; + session_deps.socket_factory.AddSSLSocketDataProvider(&proxy); + // Vanilla SSL to the server + SSLSocketDataProvider server(true, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&server); + + TestOldCompletionCallback callback1; + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + + int rv = trans->Start(&request, &callback1, log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback1.WaitForResult(); + EXPECT_EQ(OK, rv); + net::CapturingNetLog::EntryList entries; + log.GetEntries(&entries); + size_t pos = ExpectLogContainsSomewhere( + entries, 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, + NetLog::PHASE_NONE); + ExpectLogContainsSomewhere( + entries, pos, + NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, + NetLog::PHASE_NONE); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_FALSE(response->headers == NULL); + EXPECT_EQ(407, response->headers->response_code()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + EXPECT_TRUE(response->auth_challenge.get() != NULL); + EXPECT_TRUE(CheckBasicProxyAuth(response->auth_challenge.get())); + + TestOldCompletionCallback callback2; + + rv = trans->RestartWithAuth(AuthCredentials(kFoo, kBar), &callback2); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback2.WaitForResult(); + EXPECT_EQ(OK, rv); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->headers->IsKeepAlive()); + EXPECT_EQ(200, response->headers->response_code()); + EXPECT_EQ(5, 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); + + trans.reset(); + session->CloseAllConnections(); +} + // Test when a server (non-proxy) returns a 407 (proxy-authenticate). // The request should fail with ERR_UNEXPECTED_PROXY_AUTH. TEST_F(HttpNetworkTransactionTest, UnexpectedProxyAuth) { @@ -7328,7 +7697,7 @@ TEST_F(HttpNetworkTransactionTest, // - HTTP or HTTPS backend (to include proxy tunneling). // - Non-authenticating and authenticating backend. // -// In all, there are 44 reasonable permuations (for example, if there are +// In all, there are 44 reasonable permutations (for example, if there are // problems generating an auth token for an authenticating proxy, we don't // need to test all permutations of the backend server). // @@ -8148,8 +8517,10 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); HttpAuthHandlerMock::Factory* auth_factory = new HttpAuthHandlerMock::Factory(); - HttpAuthHandlerMock* auth_handler = new HttpAuthHandlerMock(); - auth_factory->AddMockHandler(auth_handler, HttpAuth::AUTH_PROXY); + HttpAuthHandlerMock* auth_handler1 = new HttpAuthHandlerMock(); + auth_factory->AddMockHandler(auth_handler1, HttpAuth::AUTH_PROXY); + HttpAuthHandlerMock* auth_handler2 = new HttpAuthHandlerMock(); + auth_factory->AddMockHandler(auth_handler2, HttpAuth::AUTH_PROXY); auth_factory->set_do_init_from_challenge(true); session_deps.http_auth_handler_factory.reset(auth_factory); @@ -8181,11 +8552,6 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { // 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 @@ -8202,7 +8568,17 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { "Host: www.google.com\r\n" "Proxy-Connection: keep-alive\r\n" "\r\n"), + }; + + MockWrite data_writes_3[] = { + // Non-alternate protocol job that will run in parallel + 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"), + }; + MockWrite data_writes_4[] = { // Second connection attempt with Proxy-Authorization. MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -8216,6 +8592,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { const char kRejectConnectResponse[] = ("HTTP/1.1 407 Unauthorized\r\n" "Proxy-Authenticate: Mock\r\n" "Proxy-Connection: close\r\n" + "Content-Length: 0\r\n" "\r\n"); const char kAcceptConnectResponse[] = "HTTP/1.1 200 Connected\r\n\r\n"; MockRead data_reads_2[] = { @@ -8223,19 +8600,35 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { MockRead(false, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ, 1), MockRead(true, kRejectConnectResponse, arraysize(kRejectConnectResponse) - 1, 1), + }; + // Hang forever so we can ensure the alt job wins + MockRead data_reads_3[] = { + MockRead(false, ERR_IO_PENDING), + }; + + MockRead data_reads_4[] = { // Second connection attempt passes MockRead(true, kAcceptConnectResponse, - arraysize(kAcceptConnectResponse) -1, 4), + arraysize(kAcceptConnectResponse) -1, 1), // SPDY response - CreateMockRead(*resp.get(), 6), - CreateMockRead(*data.get(), 6), - MockRead(true, 0, 0, 6), + CreateMockRead(*resp.get(), 3), + CreateMockRead(*data.get(), 3), + MockRead(true, 0, 0, 4), }; scoped_refptr<OrderedSocketData> data_2( new OrderedSocketData(data_reads_2, arraysize(data_reads_2), data_writes_2, arraysize(data_writes_2))); + scoped_refptr<OrderedSocketData> data_3( + new OrderedSocketData(data_reads_3, arraysize(data_reads_3), + data_writes_3, arraysize(data_writes_3))); + // Hang forever so we can ensure the alt job wins + MockConnect conn_3(false, ERR_IO_PENDING); + data_3->set_connect_data(conn_3); + scoped_refptr<OrderedSocketData> data_4( + new OrderedSocketData(data_reads_4, arraysize(data_reads_4), + data_writes_4, arraysize(data_writes_4))); SSLSocketDataProvider ssl(true, OK); ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated; @@ -8250,6 +8643,9 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { session_deps.socket_factory.AddSocketDataProvider(&data_1); session_deps.socket_factory.AddSocketDataProvider(data_2.get()); + session_deps.socket_factory.AddSocketDataProvider(data_3.get()); + session_deps.socket_factory.AddSocketDataProvider(data_4.get()); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); session_deps.socket_factory.AddSocketDataProvider( &hanging_non_alternate_protocol_socket); @@ -8280,7 +8676,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { // After all that work, these two lines (or actually, just the scheme) are // what this test is all about. Make sure it happens correctly. - const GURL& request_url = auth_handler->request_url(); + const GURL& request_url = auth_handler2->request_url(); EXPECT_EQ("https", request_url.scheme()); EXPECT_EQ("www.google.com", request_url.host()); diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index 32ccb1b..1434c655 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc @@ -68,7 +68,7 @@ int HttpProxyClientSocket::RestartWithAuth(OldCompletionCallback* callback) { DCHECK(!user_callback_); int rv = PrepareForAuthRestart(); - if (rv != OK) + if (rv != OK || next_state_ == STATE_NONE) return rv; rv = DoLoop(OK); @@ -77,6 +77,11 @@ int HttpProxyClientSocket::RestartWithAuth(OldCompletionCallback* callback) { return rv; } +const +scoped_refptr<HttpAuthController>& HttpProxyClientSocket::auth_controller() { + return auth_; +} + const HttpResponseInfo* HttpProxyClientSocket::GetConnectResponseInfo() const { return response_.headers ? &response_ : NULL; } @@ -251,10 +256,7 @@ int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { 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(); + next_state_ = STATE_NONE; } // Reset the other member variables. @@ -267,17 +269,6 @@ int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { return OK; } -int HttpProxyClientSocket::HandleAuthChallenge() { - DCHECK(response_.headers); - - int rv = auth_->HandleAuthChallenge(response_.headers, false, true, net_log_); - response_.auth_challenge = auth_->auth_info(); - if (rv == OK) - return ERR_PROXY_AUTH_REQUESTED; - - return rv; -} - void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const { LOG(WARNING) << "Blocked proxy response with status " << response_code << " to CONNECT request for " @@ -347,13 +338,6 @@ 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: @@ -452,7 +436,7 @@ int HttpProxyClientSocket::DoReadHeadersComplete(int result) { // authentication code is smart enough to avoid being tricked by an // active network attacker. // The next state is intentionally not set as it should be STATE_NONE; - return HandleAuthChallenge(); + return HandleAuthChallenge(auth_, &response_, net_log_); default: if (is_https_proxy_) @@ -488,17 +472,4 @@ 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; -} - } // namespace net diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index def0221..70db3707 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -50,15 +50,6 @@ class HttpProxyClientSocket : public ProxyClientSocket { // On destruction Disconnect() is called. virtual ~HttpProxyClientSocket(); - // If Connect (or its callback) returns PROXY_AUTH_REQUESTED, then - // credentials should be added to the HttpAuthController before calling - // RestartWithAuth. - int RestartWithAuth(OldCompletionCallback* callback); - - const scoped_refptr<HttpAuthController>& auth_controller() { - return auth_; - } - bool using_spdy() { return using_spdy_; } @@ -66,6 +57,8 @@ class HttpProxyClientSocket : public ProxyClientSocket { // ProxyClientSocket methods: virtual const HttpResponseInfo* GetConnectResponseInfo() const OVERRIDE; virtual HttpStream* CreateConnectResponseStream() OVERRIDE; + virtual int RestartWithAuth(OldCompletionCallback* callback) OVERRIDE; + virtual const scoped_refptr<HttpAuthController>& auth_controller() OVERRIDE; // StreamSocket methods: virtual int Connect(OldCompletionCallback* callback) OVERRIDE; @@ -103,8 +96,6 @@ class HttpProxyClientSocket : public ProxyClientSocket { STATE_READ_HEADERS_COMPLETE, STATE_DRAIN_BODY, STATE_DRAIN_BODY_COMPLETE, - STATE_TCP_RESTART, - STATE_TCP_RESTART_COMPLETE, STATE_DONE, }; @@ -116,8 +107,6 @@ class HttpProxyClientSocket : public ProxyClientSocket { int PrepareForAuthRestart(); int DidDrainBodyForAuthRestart(bool keep_alive); - int HandleAuthChallenge(); - void LogBlockedTunnelResponse(int response_code) const; void DoCallback(int result); @@ -132,8 +121,6 @@ class HttpProxyClientSocket : public ProxyClientSocket { int DoReadHeadersComplete(int result); int DoDrainBody(); int DoDrainBodyComplete(int result); - int DoTCPRestart(); - int DoTCPRestartComplete(int result); OldCompletionCallbackImpl<HttpProxyClientSocket> io_callback_; State next_state_; diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc index 175f167..d7b56867 100644 --- a/net/http/http_proxy_client_socket_pool_unittest.cc +++ b/net/http/http_proxy_client_socket_pool_unittest.cc @@ -257,9 +257,22 @@ TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) { CreateMockWrite(*req, 0, true), CreateMockWrite(*rst, 2, true), }; + static const char* const kAuthChallenge[] = { + "status", "407 Proxy Authentication Required", + "version", "HTTP/1.1", + "proxy-authenticate", "Basic realm=\"MyRealm1\"", + }; + scoped_ptr<spdy::SpdyFrame> resp( - ConstructSpdySynReplyError( - "407 Proxy Authentication Required", NULL, 0, 1)); + ConstructSpdyControlFrame(NULL, + 0, + false, + 1, + LOWEST, + spdy::SYN_REPLY, + spdy::CONTROL_FLAG_NONE, + kAuthChallenge, + arraysize(kAuthChallenge))); MockRead spdy_reads[] = { CreateMockWrite(*resp, 1, true), MockRead(true, 0, 3) @@ -276,21 +289,16 @@ TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) { EXPECT_FALSE(handle_.is_initialized()); EXPECT_FALSE(handle_.socket()); - data_->RunFor(4); + data_->RunFor(GetParam() == SPDY ? 2 : 4); rv = callback_.WaitForResult(); + EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, rv); + EXPECT_TRUE(handle_.is_initialized()); + ASSERT_TRUE(handle_.socket()); if (GetParam() != SPDY) { - EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, rv); - EXPECT_TRUE(handle_.is_initialized()); - ASSERT_TRUE(handle_.socket()); HttpProxyClientSocket* tunnel_socket = static_cast<HttpProxyClientSocket*>(handle_.socket()); EXPECT_FALSE(tunnel_socket->IsConnected()); EXPECT_FALSE(tunnel_socket->using_spdy()); - } else { - // Proxy auth is not really implemented for SPDY yet - EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv); - EXPECT_FALSE(handle_.is_initialized()); - EXPECT_FALSE(handle_.socket()); } } diff --git a/net/http/http_proxy_utils.cc b/net/http/http_proxy_utils.cc index e93c8ee..459c346 100644 --- a/net/http/http_proxy_utils.cc +++ b/net/http/http_proxy_utils.cc @@ -1,14 +1,19 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/http/http_proxy_utils.h" +#include "base/logging.h" #include "base/stringprintf.h" #include "googleurl/src/gurl.h" #include "net/base/host_port_pair.h" +#include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "net/http/http_auth_controller.h" #include "net/http/http_request_info.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" namespace net { @@ -36,4 +41,17 @@ void BuildTunnelRequest( request_headers->MergeFrom(auth_headers); } +int HandleAuthChallenge(HttpAuthController *auth, + HttpResponseInfo* response, + const BoundNetLog& net_log) { + DCHECK(response->headers); + + int rv = auth->HandleAuthChallenge(response->headers, false, true, net_log); + response->auth_challenge = auth->auth_info(); + if (rv == OK) + return ERR_PROXY_AUTH_REQUESTED; + + return rv; +} + } // namespace net diff --git a/net/http/http_proxy_utils.h b/net/http/http_proxy_utils.h index 9d18fc8..3257f7f 100644 --- a/net/http/http_proxy_utils.h +++ b/net/http/http_proxy_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,9 @@ namespace net { +class BoundNetLog; +class HttpAuthController; +class HttpResponseInfo; struct HttpRequestInfo; class HttpRequestHeaders; class HostPortPair; @@ -23,6 +26,12 @@ void BuildTunnelRequest(const HttpRequestInfo& request_info, std::string* request_line, HttpRequestHeaders* request_headers); +// When an auth challenge (407 response) is received during tunnel construction +// this method should be called. +int HandleAuthChallenge(HttpAuthController *auth, + HttpResponseInfo* response, + const BoundNetLog& net_log); + } // namespace net #endif // NET_HTTP_HTTP_PROXY_UTILS_H_ diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index 6fb88dc..a6ca05e 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -369,10 +369,10 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { DCHECK(connection_->socket()); DCHECK(establishing_tunnel_); - HttpProxyClientSocket* http_proxy_socket = - static_cast<HttpProxyClientSocket*>(connection_->socket()); + ProxyClientSocket* proxy_socket = + static_cast<ProxyClientSocket*>(connection_->socket()); const HttpResponseInfo* tunnel_auth_response = - http_proxy_socket->GetConnectResponseInfo(); + proxy_socket->GetConnectResponseInfo(); next_state_ = STATE_WAITING_USER_ACTION; MessageLoop::current()->PostTask( @@ -381,7 +381,7 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { &HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(), *tunnel_auth_response, - http_proxy_socket->auth_controller())); + proxy_socket->auth_controller())); } return ERR_IO_PENDING; @@ -898,9 +898,9 @@ int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) { int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() { next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE; - HttpProxyClientSocket* http_proxy_socket = - static_cast<HttpProxyClientSocket*>(connection_->socket()); - return http_proxy_socket->RestartWithAuth(&io_callback_); + ProxyClientSocket* proxy_socket = + static_cast<ProxyClientSocket*>(connection_->socket()); + return proxy_socket->RestartWithAuth(&io_callback_); } int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) { @@ -908,14 +908,14 @@ int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) { return result; if (result == OK) { - // Now that we've got the HttpProxyClientSocket connected. We have + // Now that we've got the ProxyClientSocket prepared to restart. 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. establishing_tunnel_ = false; - ReturnToStateInitConnection(false /* do not close connection */); + ReturnToStateInitConnection(!connection_->socket()->IsConnectedAndIdle()); return OK; } diff --git a/net/http/proxy_client_socket.h b/net/http/proxy_client_socket.h index 451e098..1fd4f79 100644 --- a/net/http/proxy_client_socket.h +++ b/net/http/proxy_client_socket.h @@ -10,6 +10,7 @@ namespace net { +class HttpAuthController; class HttpStream; class HttpResponseInfo; @@ -26,6 +27,15 @@ class NET_EXPORT_PRIVATE ProxyClientSocket : public StreamSocket { // which can be used to read the response body. virtual HttpStream* CreateConnectResponseStream() = 0; + // Returns the HttpAuthController which can be used + // to interact with an HTTP Proxy Authorization Required (407) request. + virtual const scoped_refptr<HttpAuthController>& auth_controller() = 0; + + // If Connect (or its callback) returns PROXY_AUTH_REQUESTED, then + // credentials should be added to the HttpAuthController before calling + // RestartWithAuth. + virtual int RestartWithAuth(OldCompletionCallback* callback) = 0; + private: DISALLOW_COPY_AND_ASSIGN(ProxyClientSocket); }; diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc index 1b2674d..6ef88e7 100644 --- a/net/spdy/spdy_proxy_client_socket.cc +++ b/net/spdy/spdy_proxy_client_socket.cc @@ -63,6 +63,19 @@ const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const { return response_.headers ? &response_ : NULL; } +int SpdyProxyClientSocket::RestartWithAuth(OldCompletionCallback* callback) { + // A SPDY Stream can only handle a single request, so the underlying + // stream may not be reused and a new SpdyProxyClientSocket must be + // created (possibly on top of the same SPDY Session). + next_state_ = STATE_DISCONNECTED; + return OK; +} + +const +scoped_refptr<HttpAuthController>& SpdyProxyClientSocket::auth_controller() { + return auth_; +} + HttpStream* SpdyProxyClientSocket::CreateConnectResponseStream() { DCHECK(response_stream_.get()); return response_stream_.release(); @@ -384,6 +397,16 @@ int SpdyProxyClientSocket::DoReadReplyComplete(int result) { if (response_.headers->response_code() == 200) { return OK; } else if (response_.headers->response_code() == 407) { + int rv = HandleAuthChallenge(auth_, &response_, net_log_); + if (rv != ERR_PROXY_AUTH_REQUESTED) { + return rv; + } + // SPDY only supports basic and digest auth + if (auth_->auth_info() && + (auth_->auth_info()->scheme == "basic" || + auth_->auth_info()->scheme == "digest")) { + return ERR_PROXY_AUTH_REQUESTED; + } return ERR_TUNNEL_CONNECTION_FAILED; } else { // Immediately hand off our SpdyStream to a newly created SpdyHttpStream diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h index 8a9237b..9875a05 100644 --- a/net/spdy/spdy_proxy_client_socket.h +++ b/net/spdy/spdy_proxy_client_socket.h @@ -53,17 +53,11 @@ class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket, // On destruction Disconnect() is called. virtual ~SpdyProxyClientSocket(); - const scoped_refptr<HttpAuthController>& auth_controller() { - return auth_; - } - // ProxyClientSocket methods: virtual const HttpResponseInfo* GetConnectResponseInfo() const OVERRIDE; - - // In the event of a non-200 response to the CONNECT request, this - // method may be called to return an HttpStream in order to read - // the response body. virtual HttpStream* CreateConnectResponseStream() OVERRIDE; + virtual int RestartWithAuth(OldCompletionCallback* callback) OVERRIDE; + virtual const scoped_refptr<HttpAuthController>& auth_controller() OVERRIDE; // StreamSocket methods: virtual int Connect(OldCompletionCallback* callback) OVERRIDE; diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc index 323b6db..89aba7e 100644 --- a/net/spdy/spdy_proxy_client_socket_unittest.cc +++ b/net/spdy/spdy_proxy_client_socket_unittest.cc @@ -66,6 +66,7 @@ class SpdyProxyClientSocketTest : public PlatformTest { spdy::SpdyFrame* ConstructConnectAuthRequestFrame(); spdy::SpdyFrame* ConstructConnectReplyFrame(); spdy::SpdyFrame* ConstructConnectAuthReplyFrame(); + spdy::SpdyFrame* ConstructNtlmAuthReplyFrame(); spdy::SpdyFrame* ConstructConnectErrorReplyFrame(); spdy::SpdyFrame* ConstructBodyFrame(const char* data, int length); scoped_refptr<IOBufferWithSize> CreateBuffer(const char* data, int size); @@ -387,6 +388,26 @@ spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectAuthReplyFrame() { arraysize(kStandardReplyHeaders)); } +// Constructs a SPDY SYN_REPLY frame to match the SPDY CONNECT which +// requires Proxy Authentication using NTLM. +spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructNtlmAuthReplyFrame() { + const char* const kStandardReplyHeaders[] = { + "status", "407 Proxy Authentication Required", + "version", "HTTP/1.1", + "proxy-authenticate", "NTLM", + }; + + return ConstructSpdyControlFrame(NULL, + 0, + false, + kStreamId, + LOWEST, + spdy::SYN_REPLY, + spdy::CONTROL_FLAG_NONE, + kStandardReplyHeaders, + arraysize(kStandardReplyHeaders)); +} + // Constructs a SPDY SYN_REPLY frame with an HTTP 500 error. spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectErrorReplyFrame() { const char* const kStandardReplyHeaders[] = { @@ -433,6 +454,23 @@ TEST_F(SpdyProxyClientSocketTest, ConnectSendsCorrectRequest) { AssertConnectionEstablished(); } +TEST_F(SpdyProxyClientSocketTest, ConnectWithUnsupportedAuth) { + scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame()); + MockWrite writes[] = { + CreateMockWrite(*conn, 0, false), + }; + + scoped_ptr<spdy::SpdyFrame> resp(ConstructNtlmAuthReplyFrame()); + MockRead reads[] = { + CreateMockRead(*resp, 1, true), + MockRead(true, 0, 3), // EOF + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + AssertConnectFails(ERR_TUNNEL_CONNECTION_FAILED); +} + TEST_F(SpdyProxyClientSocketTest, ConnectWithAuthRequested) { scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame()); MockWrite writes[] = { @@ -447,7 +485,7 @@ TEST_F(SpdyProxyClientSocketTest, ConnectWithAuthRequested) { Initialize(reads, arraysize(reads), writes, arraysize(writes)); - AssertConnectFails(ERR_TUNNEL_CONNECTION_FAILED); + AssertConnectFails(ERR_PROXY_AUTH_REQUESTED); const HttpResponseInfo* response = sock_->GetConnectResponseInfo(); ASSERT_TRUE(response != NULL); @@ -476,6 +514,38 @@ TEST_F(SpdyProxyClientSocketTest, ConnectWithAuthCredentials) { AssertConnectionEstablished(); } +TEST_F(SpdyProxyClientSocketTest, ConnectWithAuthRestart) { + scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame()); + scoped_ptr<spdy::SpdyFrame> auth(ConstructConnectAuthRequestFrame()); + MockWrite writes[] = { + CreateMockWrite(*conn, 0, false), + }; + + scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectAuthReplyFrame()); + scoped_ptr<spdy::SpdyFrame> auth_resp(ConstructConnectReplyFrame()); + MockRead reads[] = { + CreateMockRead(*resp, 1, true), + MockRead(true, 0, 3), // EOF + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + AssertConnectFails(ERR_PROXY_AUTH_REQUESTED); + + const HttpResponseInfo* response = sock_->GetConnectResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_EQ(407, response->headers->response_code()); + ASSERT_EQ("Proxy Authentication Required", + response->headers->GetStatusText()); + + AddAuthToCache(); + + ASSERT_EQ(OK, sock_->RestartWithAuth(&read_callback_)); + // A SpdyProxyClientSocket sits on a single SPDY stream which can + // only be used for a single request/response. + ASSERT_FALSE(sock_->IsConnectedAndIdle()); +} + TEST_F(SpdyProxyClientSocketTest, ConnectFails) { scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame()); MockWrite writes[] = { @@ -821,7 +891,7 @@ TEST_F(SpdyProxyClientSocketTest, ReadAuthResponseBody) { Initialize(reads, arraysize(reads), writes, arraysize(writes)); - AssertConnectFails(ERR_TUNNEL_CONNECTION_FAILED); + AssertConnectFails(ERR_PROXY_AUTH_REQUESTED); Run(2); // SpdySession consumes the next two reads and sends then to // sock_ to be buffered. |