1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/http_proxy_client_socket_pool.h"
7 #include "base/callback.h"
8 #include "base/compiler_specific.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/proxy_delegate.h"
13 #include "net/base/test_completion_callback.h"
14 #include "net/http/http_network_session.h"
15 #include "net/http/http_proxy_client_socket.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/socket/client_socket_handle.h"
18 #include "net/socket/client_socket_pool_histograms.h"
19 #include "net/socket/next_proto.h"
20 #include "net/socket/socket_test_util.h"
21 #include "net/spdy/spdy_protocol.h"
22 #include "net/spdy/spdy_test_util_common.h"
23 #include "testing/gtest/include/gtest/gtest.h"
29 const int kMaxSockets
= 32;
30 const int kMaxSocketsPerGroup
= 6;
31 const char * const kAuthHeaders
[] = {
32 "proxy-authorization", "Basic Zm9vOmJhcg=="
34 const int kAuthHeadersSize
= arraysize(kAuthHeaders
) / 2;
42 struct HttpProxyClientSocketPoolTestParams
{
43 HttpProxyClientSocketPoolTestParams()
45 protocol(kProtoSPDY31
) {}
47 HttpProxyClientSocketPoolTestParams(
48 HttpProxyType proxy_type
,
50 : proxy_type(proxy_type
),
53 HttpProxyType proxy_type
;
57 typedef ::testing::TestWithParam
<HttpProxyType
> TestWithHttpParam
;
59 const char kHttpProxyHost
[] = "httpproxy.example.com";
60 const char kHttpsProxyHost
[] = "httpsproxy.example.com";
62 class TestProxyDelegate
: public ProxyDelegate
{
65 : on_before_tunnel_request_called_(false),
66 on_tunnel_request_completed_called_(false),
67 on_tunnel_headers_received_called_(false) {
70 ~TestProxyDelegate() override
{}
72 bool on_before_tunnel_request_called() const {
73 return on_before_tunnel_request_called_
;
76 bool on_tunnel_request_completed_called() const {
77 return on_tunnel_request_completed_called_
;
80 bool on_tunnel_headers_received_called() const {
81 return on_tunnel_headers_received_called_
;
84 void VerifyOnTunnelRequestCompleted(const std::string
& endpoint
,
85 const std::string
& proxy_server
) const {
86 EXPECT_TRUE(on_tunnel_request_completed_called_
);
87 EXPECT_TRUE(HostPortPair::FromString(endpoint
).Equals(
88 on_tunnel_request_completed_endpoint_
));
89 EXPECT_TRUE(HostPortPair::FromString(proxy_server
).Equals(
90 on_tunnel_request_completed_proxy_server_
));
93 void VerifyOnTunnelHeadersReceived(const std::string
& origin
,
94 const std::string
& proxy_server
,
95 const std::string
& status_line
) const {
96 EXPECT_TRUE(on_tunnel_headers_received_called_
);
97 EXPECT_TRUE(HostPortPair::FromString(origin
).Equals(
98 on_tunnel_headers_received_origin_
));
99 EXPECT_TRUE(HostPortPair::FromString(proxy_server
).Equals(
100 on_tunnel_headers_received_proxy_server_
));
101 EXPECT_EQ(status_line
, on_tunnel_headers_received_status_line_
);
105 void OnResolveProxy(const GURL
& url
,
107 const ProxyService
& proxy_service
,
108 ProxyInfo
* result
) override
{}
110 void OnTunnelConnectCompleted(const HostPortPair
& endpoint
,
111 const HostPortPair
& proxy_server
,
112 int net_error
) override
{
113 on_tunnel_request_completed_called_
= true;
114 on_tunnel_request_completed_endpoint_
= endpoint
;
115 on_tunnel_request_completed_proxy_server_
= proxy_server
;
118 void OnFallback(const ProxyServer
& bad_proxy
, int net_error
) override
{}
120 void OnBeforeSendHeaders(URLRequest
* request
,
121 const ProxyInfo
& proxy_info
,
122 HttpRequestHeaders
* headers
) override
{}
124 void OnBeforeTunnelRequest(const net::HostPortPair
& proxy_server
,
125 net::HttpRequestHeaders
* extra_headers
) override
{
126 on_before_tunnel_request_called_
= true;
128 extra_headers
->SetHeader("Foo", proxy_server
.ToString());
132 void OnTunnelHeadersReceived(
133 const net::HostPortPair
& origin
,
134 const net::HostPortPair
& proxy_server
,
135 const net::HttpResponseHeaders
& response_headers
) override
{
136 on_tunnel_headers_received_called_
= true;
137 on_tunnel_headers_received_origin_
= origin
;
138 on_tunnel_headers_received_proxy_server_
= proxy_server
;
139 on_tunnel_headers_received_status_line_
= response_headers
.GetStatusLine();
143 bool on_before_tunnel_request_called_
;
144 bool on_tunnel_request_completed_called_
;
145 bool on_tunnel_headers_received_called_
;
146 HostPortPair on_tunnel_request_completed_endpoint_
;
147 HostPortPair on_tunnel_request_completed_proxy_server_
;
148 HostPortPair on_tunnel_headers_received_origin_
;
149 HostPortPair on_tunnel_headers_received_proxy_server_
;
150 std::string on_tunnel_headers_received_status_line_
;
154 class HttpProxyClientSocketPoolTest
155 : public ::testing::TestWithParam
<HttpProxyClientSocketPoolTestParams
> {
157 HttpProxyClientSocketPoolTest()
158 : session_deps_(GetParam().protocol
),
159 tcp_histograms_("MockTCP"),
160 transport_socket_pool_(
164 session_deps_
.deterministic_socket_factory
.get()),
165 ssl_histograms_("MockSSL"),
166 ssl_socket_pool_(kMaxSockets
,
169 session_deps_
.cert_verifier
.get(),
170 NULL
/* channel_id_store */,
171 NULL
/* transport_security_state */,
172 NULL
/* cert_transparency_verifier */,
173 NULL
/* cert_policy_enforcer */,
174 std::string() /* ssl_session_cache_shard */,
175 session_deps_
.deterministic_socket_factory
.get(),
176 &transport_socket_pool_
,
179 session_deps_
.ssl_config_service
.get(),
181 BoundNetLog().net_log()),
182 session_(CreateNetworkSession()),
183 http_proxy_histograms_("HttpProxyUnitTest"),
184 spdy_util_(GetParam().protocol
),
187 &http_proxy_histograms_
,
188 &transport_socket_pool_
,
192 virtual ~HttpProxyClientSocketPoolTest() {
195 void AddAuthToCache() {
196 const base::string16
kFoo(base::ASCIIToUTF16("foo"));
197 const base::string16
kBar(base::ASCIIToUTF16("bar"));
198 GURL
proxy_url(GetParam().proxy_type
== HTTP
?
199 (std::string("http://") + kHttpProxyHost
) :
200 (std::string("https://") + kHttpsProxyHost
));
201 session_
->http_auth_cache()->Add(proxy_url
,
203 HttpAuth::AUTH_SCHEME_BASIC
,
204 "Basic realm=MyRealm1",
205 AuthCredentials(kFoo
, kBar
),
209 scoped_refptr
<TransportSocketParams
> CreateHttpProxyParams() const {
210 if (GetParam().proxy_type
!= HTTP
)
212 return new TransportSocketParams(
213 HostPortPair(kHttpProxyHost
, 80),
216 OnHostResolutionCallback(),
217 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
);
220 scoped_refptr
<SSLSocketParams
> CreateHttpsProxyParams() const {
221 if (GetParam().proxy_type
== HTTP
)
223 return new SSLSocketParams(
224 new TransportSocketParams(
225 HostPortPair(kHttpsProxyHost
, 443),
228 OnHostResolutionCallback(),
229 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
),
232 HostPortPair(kHttpsProxyHost
, 443),
234 PRIVACY_MODE_DISABLED
,
240 // Returns the a correctly constructed HttpProxyParms
241 // for the HTTP or HTTPS proxy.
242 scoped_refptr
<HttpProxySocketParams
> CreateParams(
244 ProxyDelegate
* proxy_delegate
) {
245 return scoped_refptr
<HttpProxySocketParams
>(new HttpProxySocketParams(
246 CreateHttpProxyParams(),
247 CreateHttpsProxyParams(),
248 GURL(tunnel
? "https://www.google.com/" : "http://www.google.com"),
250 HostPortPair("www.google.com", tunnel
? 443 : 80),
251 session_
->http_auth_cache(),
252 session_
->http_auth_handler_factory(),
253 session_
->spdy_session_pool(),
258 scoped_refptr
<HttpProxySocketParams
> CreateTunnelParams(
259 ProxyDelegate
* proxy_delegate
) {
260 return CreateParams(true, proxy_delegate
);
263 scoped_refptr
<HttpProxySocketParams
> CreateNoTunnelParams(
264 ProxyDelegate
* proxy_delegate
) {
265 return CreateParams(false, proxy_delegate
);
268 DeterministicMockClientSocketFactory
* socket_factory() {
269 return session_deps_
.deterministic_socket_factory
.get();
272 void Initialize(MockRead
* reads
, size_t reads_count
,
273 MockWrite
* writes
, size_t writes_count
,
274 MockRead
* spdy_reads
, size_t spdy_reads_count
,
275 MockWrite
* spdy_writes
, size_t spdy_writes_count
) {
276 if (GetParam().proxy_type
== SPDY
) {
277 data_
.reset(new DeterministicSocketData(spdy_reads
, spdy_reads_count
,
278 spdy_writes
, spdy_writes_count
));
280 data_
.reset(new DeterministicSocketData(reads
, reads_count
, writes
,
284 data_
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
285 data_
->StopAfter(2); // Request / Response
287 socket_factory()->AddSocketDataProvider(data_
.get());
289 if (GetParam().proxy_type
!= HTTP
) {
290 ssl_data_
.reset(new SSLSocketDataProvider(SYNCHRONOUS
, OK
));
291 if (GetParam().proxy_type
== SPDY
) {
294 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
298 void InitializeSpdySsl() {
299 ssl_data_
->SetNextProto(GetParam().protocol
);
302 HttpNetworkSession
* CreateNetworkSession() {
303 return SpdySessionDependencies::SpdyCreateSessionDeterministic(
307 RequestPriority
GetLastTransportRequestPriority() const {
308 return transport_socket_pool_
.last_request_priority();
312 SpdySessionDependencies session_deps_
;
314 ClientSocketPoolHistograms tcp_histograms_
;
315 MockTransportClientSocketPool transport_socket_pool_
;
316 ClientSocketPoolHistograms ssl_histograms_
;
317 MockHostResolver host_resolver_
;
318 scoped_ptr
<CertVerifier
> cert_verifier_
;
319 SSLClientSocketPool ssl_socket_pool_
;
321 const scoped_refptr
<HttpNetworkSession
> session_
;
322 ClientSocketPoolHistograms http_proxy_histograms_
;
325 SpdyTestUtil spdy_util_
;
326 scoped_ptr
<SSLSocketDataProvider
> ssl_data_
;
327 scoped_ptr
<DeterministicSocketData
> data_
;
328 HttpProxyClientSocketPool pool_
;
329 ClientSocketHandle handle_
;
330 TestCompletionCallback callback_
;
333 //-----------------------------------------------------------------------------
334 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
337 // TODO(akalin): Use ::testing::Combine() when we are able to use
339 INSTANTIATE_TEST_CASE_P(
340 HttpProxyClientSocketPoolTests
,
341 HttpProxyClientSocketPoolTest
,
343 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY31
),
344 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY31
),
345 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY31
),
346 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY4_14
),
347 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY4_14
),
348 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY4_14
),
349 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY4_15
),
350 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY4_15
),
351 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY4_15
)));
353 TEST_P(HttpProxyClientSocketPoolTest
, NoTunnel
) {
354 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
356 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
357 int rv
= handle_
.Init("a", CreateNoTunnelParams(proxy_delegate
.get()), LOW
,
358 CompletionCallback(), &pool_
, BoundNetLog());
360 EXPECT_TRUE(handle_
.is_initialized());
361 ASSERT_TRUE(handle_
.socket());
362 HttpProxyClientSocket
* tunnel_socket
=
363 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
364 EXPECT_TRUE(tunnel_socket
->IsConnected());
365 EXPECT_FALSE(proxy_delegate
->on_before_tunnel_request_called());
366 EXPECT_FALSE(proxy_delegate
->on_tunnel_headers_received_called());
367 EXPECT_TRUE(proxy_delegate
->on_tunnel_request_completed_called());
370 // Make sure that HttpProxyConnectJob passes on its priority to its
371 // (non-SSL) socket request on Init.
372 TEST_P(HttpProxyClientSocketPoolTest
, SetSocketRequestPriorityOnInit
) {
373 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
375 handle_
.Init("a", CreateNoTunnelParams(NULL
), HIGHEST
,
376 CompletionCallback(), &pool_
, BoundNetLog()));
377 EXPECT_EQ(HIGHEST
, GetLastTransportRequestPriority());
380 TEST_P(HttpProxyClientSocketPoolTest
, NeedAuth
) {
381 MockWrite writes
[] = {
382 MockWrite(ASYNC
, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
383 "Host: www.google.com\r\n"
384 "Proxy-Connection: keep-alive\r\n\r\n"),
388 MockRead(ASYNC
, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
389 MockRead(ASYNC
, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
390 MockRead(ASYNC
, 3, "Content-Length: 10\r\n\r\n"),
391 MockRead(ASYNC
, 4, "0123456789"),
393 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructSpdyConnect(
394 NULL
, 0, 1, LOW
, HostPortPair("www.google.com", 443)));
395 scoped_ptr
<SpdyFrame
> rst(
396 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
397 MockWrite spdy_writes
[] = {
398 CreateMockWrite(*req
, 0, ASYNC
),
399 CreateMockWrite(*rst
, 2, ASYNC
),
401 SpdyHeaderBlock resp_block
;
402 resp_block
[spdy_util_
.GetStatusKey()] = "407";
403 resp_block
["proxy-authenticate"] = "Basic realm=\"MyRealm1\"";
404 spdy_util_
.MaybeAddVersionHeader(&resp_block
);
406 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyReply(1, resp_block
));
407 MockRead spdy_reads
[] = {
408 CreateMockRead(*resp
, 1, ASYNC
),
409 MockRead(ASYNC
, 0, 3)
412 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
413 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
414 arraysize(spdy_writes
));
417 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
418 callback_
.callback(), &pool_
, BoundNetLog());
419 EXPECT_EQ(ERR_IO_PENDING
, rv
);
420 EXPECT_FALSE(handle_
.is_initialized());
421 EXPECT_FALSE(handle_
.socket());
423 data_
->RunFor(GetParam().proxy_type
== SPDY
? 2 : 4);
424 rv
= callback_
.WaitForResult();
425 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED
, rv
);
426 EXPECT_TRUE(handle_
.is_initialized());
427 ASSERT_TRUE(handle_
.socket());
428 ProxyClientSocket
* tunnel_socket
=
429 static_cast<ProxyClientSocket
*>(handle_
.socket());
430 if (GetParam().proxy_type
== SPDY
) {
431 EXPECT_TRUE(tunnel_socket
->IsConnected());
432 EXPECT_TRUE(tunnel_socket
->IsUsingSpdy());
434 EXPECT_FALSE(tunnel_socket
->IsConnected());
435 EXPECT_FALSE(tunnel_socket
->IsUsingSpdy());
439 TEST_P(HttpProxyClientSocketPoolTest
, HaveAuth
) {
440 // It's pretty much impossible to make the SPDY case behave synchronously
441 // so we skip this test for SPDY
442 if (GetParam().proxy_type
== SPDY
)
444 std::string proxy_host_port
=
445 GetParam().proxy_type
== HTTP
?
446 (kHttpProxyHost
+ std::string(":80")) :
447 (kHttpsProxyHost
+ std::string(":443"));
448 std::string request
=
449 "CONNECT www.google.com:443 HTTP/1.1\r\n"
450 "Host: www.google.com\r\n"
451 "Proxy-Connection: keep-alive\r\n"
452 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
453 "Foo: " + proxy_host_port
+ "\r\n\r\n";
454 MockWrite writes
[] = {
455 MockWrite(SYNCHRONOUS
, 0, request
.c_str()),
458 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
461 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
), NULL
, 0,
465 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
466 int rv
= handle_
.Init("a", CreateTunnelParams(proxy_delegate
.get()), LOW
,
467 callback_
.callback(), &pool_
, BoundNetLog());
469 EXPECT_TRUE(handle_
.is_initialized());
470 ASSERT_TRUE(handle_
.socket());
471 HttpProxyClientSocket
* tunnel_socket
=
472 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
473 EXPECT_TRUE(tunnel_socket
->IsConnected());
474 proxy_delegate
->VerifyOnTunnelHeadersReceived(
475 "www.google.com:443",
476 proxy_host_port
.c_str(),
477 "HTTP/1.1 200 Connection Established");
478 proxy_delegate
->VerifyOnTunnelRequestCompleted(
479 "www.google.com:443",
480 proxy_host_port
.c_str());
483 TEST_P(HttpProxyClientSocketPoolTest
, AsyncHaveAuth
) {
484 std::string proxy_host_port
=
485 GetParam().proxy_type
== HTTP
?
486 (kHttpProxyHost
+ std::string(":80")) :
487 (kHttpsProxyHost
+ std::string(":443"));
488 std::string request
=
489 "CONNECT www.google.com:443 HTTP/1.1\r\n"
490 "Host: www.google.com\r\n"
491 "Proxy-Connection: keep-alive\r\n"
492 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
493 "Foo: " + proxy_host_port
+ "\r\n\r\n";
494 MockWrite writes
[] = {
495 MockWrite(ASYNC
, 0, request
.c_str()),
498 MockRead(ASYNC
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
501 scoped_ptr
<SpdyFrame
> req(
502 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
503 HostPortPair("www.google.com", 443)));
504 MockWrite spdy_writes
[] = {
505 CreateMockWrite(*req
, 0, ASYNC
)
507 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
508 MockRead spdy_reads
[] = {
509 CreateMockRead(*resp
, 1, ASYNC
),
510 MockRead(ASYNC
, 0, 2)
513 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
514 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
515 arraysize(spdy_writes
));
518 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
519 int rv
= handle_
.Init("a", CreateTunnelParams(proxy_delegate
.get()), LOW
,
520 callback_
.callback(), &pool_
, BoundNetLog());
521 EXPECT_EQ(ERR_IO_PENDING
, rv
);
522 EXPECT_FALSE(handle_
.is_initialized());
523 EXPECT_FALSE(handle_
.socket());
526 EXPECT_EQ(OK
, callback_
.WaitForResult());
527 EXPECT_TRUE(handle_
.is_initialized());
528 ASSERT_TRUE(handle_
.socket());
529 HttpProxyClientSocket
* tunnel_socket
=
530 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
531 EXPECT_TRUE(tunnel_socket
->IsConnected());
532 proxy_delegate
->VerifyOnTunnelRequestCompleted(
533 "www.google.com:443",
534 proxy_host_port
.c_str());
537 // Make sure that HttpProxyConnectJob passes on its priority to its
538 // SPDY session's socket request on Init (if applicable).
539 TEST_P(HttpProxyClientSocketPoolTest
,
540 SetSpdySessionSocketRequestPriorityOnInit
) {
541 if (GetParam().proxy_type
!= SPDY
)
544 scoped_ptr
<SpdyFrame
> req(
545 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, MEDIUM
,
546 HostPortPair("www.google.com", 443)));
547 MockWrite spdy_writes
[] = {
548 CreateMockWrite(*req
, 0, ASYNC
)
550 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
551 MockRead spdy_reads
[] = {
552 CreateMockRead(*resp
, 1, ASYNC
),
553 MockRead(ASYNC
, 0, 2)
556 Initialize(NULL
, 0, NULL
, 0,
557 spdy_reads
, arraysize(spdy_reads
),
558 spdy_writes
, arraysize(spdy_writes
));
561 EXPECT_EQ(ERR_IO_PENDING
,
562 handle_
.Init("a", CreateTunnelParams(NULL
), MEDIUM
,
563 callback_
.callback(), &pool_
, BoundNetLog()));
564 EXPECT_EQ(MEDIUM
, GetLastTransportRequestPriority());
567 EXPECT_EQ(OK
, callback_
.WaitForResult());
570 TEST_P(HttpProxyClientSocketPoolTest
, TCPError
) {
571 if (GetParam().proxy_type
== SPDY
) return;
572 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
573 data_
->set_connect_data(MockConnect(ASYNC
, ERR_CONNECTION_CLOSED
));
575 socket_factory()->AddSocketDataProvider(data_
.get());
577 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
578 callback_
.callback(), &pool_
, BoundNetLog());
579 EXPECT_EQ(ERR_IO_PENDING
, rv
);
580 EXPECT_FALSE(handle_
.is_initialized());
581 EXPECT_FALSE(handle_
.socket());
583 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED
, callback_
.WaitForResult());
585 EXPECT_FALSE(handle_
.is_initialized());
586 EXPECT_FALSE(handle_
.socket());
589 TEST_P(HttpProxyClientSocketPoolTest
, SSLError
) {
590 if (GetParam().proxy_type
== HTTP
) return;
591 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
592 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
593 socket_factory()->AddSocketDataProvider(data_
.get());
595 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
596 ERR_CERT_AUTHORITY_INVALID
));
597 if (GetParam().proxy_type
== SPDY
) {
600 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
602 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
603 callback_
.callback(), &pool_
, BoundNetLog());
604 EXPECT_EQ(ERR_IO_PENDING
, rv
);
605 EXPECT_FALSE(handle_
.is_initialized());
606 EXPECT_FALSE(handle_
.socket());
608 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID
, callback_
.WaitForResult());
610 EXPECT_FALSE(handle_
.is_initialized());
611 EXPECT_FALSE(handle_
.socket());
614 TEST_P(HttpProxyClientSocketPoolTest
, SslClientAuth
) {
615 if (GetParam().proxy_type
== HTTP
) return;
616 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
617 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
618 socket_factory()->AddSocketDataProvider(data_
.get());
620 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
621 ERR_SSL_CLIENT_AUTH_CERT_NEEDED
));
622 if (GetParam().proxy_type
== SPDY
) {
625 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
627 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
628 callback_
.callback(), &pool_
, BoundNetLog());
629 EXPECT_EQ(ERR_IO_PENDING
, rv
);
630 EXPECT_FALSE(handle_
.is_initialized());
631 EXPECT_FALSE(handle_
.socket());
633 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED
, callback_
.WaitForResult());
635 EXPECT_FALSE(handle_
.is_initialized());
636 EXPECT_FALSE(handle_
.socket());
639 TEST_P(HttpProxyClientSocketPoolTest
, TunnelUnexpectedClose
) {
640 MockWrite writes
[] = {
642 "CONNECT www.google.com:443 HTTP/1.1\r\n"
643 "Host: www.google.com\r\n"
644 "Proxy-Connection: keep-alive\r\n"
645 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
648 MockRead(ASYNC
, 1, "HTTP/1.1 200 Conn"),
649 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 2),
651 scoped_ptr
<SpdyFrame
> req(
652 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
653 HostPortPair("www.google.com", 443)));
654 MockWrite spdy_writes
[] = {
655 CreateMockWrite(*req
, 0, ASYNC
)
657 MockRead spdy_reads
[] = {
658 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 1),
661 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
662 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
663 arraysize(spdy_writes
));
666 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
667 callback_
.callback(), &pool_
, BoundNetLog());
668 EXPECT_EQ(ERR_IO_PENDING
, rv
);
669 EXPECT_FALSE(handle_
.is_initialized());
670 EXPECT_FALSE(handle_
.socket());
673 if (GetParam().proxy_type
== SPDY
) {
674 // SPDY cannot process a headers block unless it's complete and so it
675 // returns ERR_CONNECTION_CLOSED in this case.
676 EXPECT_EQ(ERR_CONNECTION_CLOSED
, callback_
.WaitForResult());
678 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED
, callback_
.WaitForResult());
680 EXPECT_FALSE(handle_
.is_initialized());
681 EXPECT_FALSE(handle_
.socket());
684 TEST_P(HttpProxyClientSocketPoolTest
, Tunnel1xxResponse
) {
685 // Tests that 1xx responses are rejected for a CONNECT request.
686 if (GetParam().proxy_type
== SPDY
) {
687 // SPDY doesn't have 1xx responses.
691 MockWrite writes
[] = {
693 "CONNECT www.google.com:443 HTTP/1.1\r\n"
694 "Host: www.google.com\r\n"
695 "Proxy-Connection: keep-alive\r\n\r\n"),
698 MockRead(ASYNC
, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
699 MockRead(ASYNC
, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
702 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
705 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
706 callback_
.callback(), &pool_
, BoundNetLog());
707 EXPECT_EQ(ERR_IO_PENDING
, rv
);
708 EXPECT_FALSE(handle_
.is_initialized());
709 EXPECT_FALSE(handle_
.socket());
712 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, callback_
.WaitForResult());
715 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupError
) {
716 MockWrite writes
[] = {
718 "CONNECT www.google.com:443 HTTP/1.1\r\n"
719 "Host: www.google.com\r\n"
720 "Proxy-Connection: keep-alive\r\n"
721 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
724 MockRead(ASYNC
, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
726 scoped_ptr
<SpdyFrame
> req(
727 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
728 HostPortPair("www.google.com", 443)));
729 scoped_ptr
<SpdyFrame
> rst(
730 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
731 MockWrite spdy_writes
[] = {
732 CreateMockWrite(*req
, 0, ASYNC
),
733 CreateMockWrite(*rst
, 2, ASYNC
),
735 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdySynReplyError(1));
736 MockRead spdy_reads
[] = {
737 CreateMockRead(*resp
, 1, ASYNC
),
738 MockRead(ASYNC
, 0, 3),
741 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
742 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
743 arraysize(spdy_writes
));
746 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
747 callback_
.callback(), &pool_
, BoundNetLog());
748 EXPECT_EQ(ERR_IO_PENDING
, rv
);
749 EXPECT_FALSE(handle_
.is_initialized());
750 EXPECT_FALSE(handle_
.socket());
754 rv
= callback_
.WaitForResult();
755 // All Proxy CONNECT responses are not trustworthy
756 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
757 EXPECT_FALSE(handle_
.is_initialized());
758 EXPECT_FALSE(handle_
.socket());
761 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupRedirect
) {
762 const std::string redirectTarget
= "https://foo.google.com/";
764 const std::string responseText
= "HTTP/1.1 302 Found\r\n"
765 "Location: " + redirectTarget
+ "\r\n"
766 "Set-Cookie: foo=bar\r\n"
768 MockWrite writes
[] = {
770 "CONNECT www.google.com:443 HTTP/1.1\r\n"
771 "Host: www.google.com\r\n"
772 "Proxy-Connection: keep-alive\r\n"
773 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
776 MockRead(ASYNC
, 1, responseText
.c_str()),
778 scoped_ptr
<SpdyFrame
> req(
779 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
780 HostPortPair("www.google.com", 443)));
781 scoped_ptr
<SpdyFrame
> rst(
782 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
784 MockWrite spdy_writes
[] = {
785 CreateMockWrite(*req
, 0, ASYNC
),
786 CreateMockWrite(*rst
, 3, ASYNC
),
789 const char* const responseHeaders
[] = {
790 "location", redirectTarget
.c_str(),
791 "set-cookie", "foo=bar",
793 const int responseHeadersSize
= arraysize(responseHeaders
) / 2;
794 scoped_ptr
<SpdyFrame
> resp(
795 spdy_util_
.ConstructSpdySynReplyError(
797 responseHeaders
, responseHeadersSize
,
799 MockRead spdy_reads
[] = {
800 CreateMockRead(*resp
, 1, ASYNC
),
801 MockRead(ASYNC
, 0, 2),
804 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
805 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
806 arraysize(spdy_writes
));
809 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
810 callback_
.callback(), &pool_
, BoundNetLog());
811 EXPECT_EQ(ERR_IO_PENDING
, rv
);
812 EXPECT_FALSE(handle_
.is_initialized());
813 EXPECT_FALSE(handle_
.socket());
817 rv
= callback_
.WaitForResult();
819 if (GetParam().proxy_type
== HTTP
) {
820 // We don't trust 302 responses to CONNECT from HTTP proxies.
821 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
822 EXPECT_FALSE(handle_
.is_initialized());
823 EXPECT_FALSE(handle_
.socket());
825 // Expect ProxyClientSocket to return the proxy's response, sanitized.
826 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE
, rv
);
827 EXPECT_TRUE(handle_
.is_initialized());
828 ASSERT_TRUE(handle_
.socket());
830 const ProxyClientSocket
* tunnel_socket
=
831 static_cast<ProxyClientSocket
*>(handle_
.socket());
832 const HttpResponseInfo
* response
= tunnel_socket
->GetConnectResponseInfo();
833 const HttpResponseHeaders
* headers
= response
->headers
.get();
835 // Make sure Set-Cookie header was stripped.
836 EXPECT_FALSE(headers
->HasHeader("set-cookie"));
838 // Make sure Content-Length: 0 header was added.
839 EXPECT_TRUE(headers
->HasHeaderValue("content-length", "0"));
841 // Make sure Location header was included and correct.
842 std::string location
;
843 EXPECT_TRUE(headers
->IsRedirect(&location
));
844 EXPECT_EQ(location
, redirectTarget
);
848 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.