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/next_proto.h"
19 #include "net/socket/socket_test_util.h"
20 #include "net/spdy/spdy_protocol.h"
21 #include "net/spdy/spdy_test_util_common.h"
22 #include "testing/gtest/include/gtest/gtest.h"
28 const int kMaxSockets
= 32;
29 const int kMaxSocketsPerGroup
= 6;
30 const char * const kAuthHeaders
[] = {
31 "proxy-authorization", "Basic Zm9vOmJhcg=="
33 const int kAuthHeadersSize
= arraysize(kAuthHeaders
) / 2;
41 struct HttpProxyClientSocketPoolTestParams
{
42 HttpProxyClientSocketPoolTestParams()
44 protocol(kProtoSPDY31
) {}
46 HttpProxyClientSocketPoolTestParams(
47 HttpProxyType proxy_type
,
49 : proxy_type(proxy_type
),
52 HttpProxyType proxy_type
;
56 typedef ::testing::TestWithParam
<HttpProxyType
> TestWithHttpParam
;
58 const char kHttpProxyHost
[] = "httpproxy.example.com";
59 const char kHttpsProxyHost
[] = "httpsproxy.example.com";
61 class TestProxyDelegate
: public ProxyDelegate
{
64 : on_before_tunnel_request_called_(false),
65 on_tunnel_request_completed_called_(false),
66 on_tunnel_headers_received_called_(false) {
69 ~TestProxyDelegate() override
{}
71 bool on_before_tunnel_request_called() const {
72 return on_before_tunnel_request_called_
;
75 bool on_tunnel_request_completed_called() const {
76 return on_tunnel_request_completed_called_
;
79 bool on_tunnel_headers_received_called() const {
80 return on_tunnel_headers_received_called_
;
83 void VerifyOnTunnelRequestCompleted(const std::string
& endpoint
,
84 const std::string
& proxy_server
) const {
85 EXPECT_TRUE(on_tunnel_request_completed_called_
);
86 EXPECT_TRUE(HostPortPair::FromString(endpoint
).Equals(
87 on_tunnel_request_completed_endpoint_
));
88 EXPECT_TRUE(HostPortPair::FromString(proxy_server
).Equals(
89 on_tunnel_request_completed_proxy_server_
));
92 void VerifyOnTunnelHeadersReceived(const std::string
& origin
,
93 const std::string
& proxy_server
,
94 const std::string
& status_line
) const {
95 EXPECT_TRUE(on_tunnel_headers_received_called_
);
96 EXPECT_TRUE(HostPortPair::FromString(origin
).Equals(
97 on_tunnel_headers_received_origin_
));
98 EXPECT_TRUE(HostPortPair::FromString(proxy_server
).Equals(
99 on_tunnel_headers_received_proxy_server_
));
100 EXPECT_EQ(status_line
, on_tunnel_headers_received_status_line_
);
104 void OnResolveProxy(const GURL
& url
,
106 const ProxyService
& proxy_service
,
107 ProxyInfo
* result
) override
{}
109 void OnTunnelConnectCompleted(const HostPortPair
& endpoint
,
110 const HostPortPair
& proxy_server
,
111 int net_error
) override
{
112 on_tunnel_request_completed_called_
= true;
113 on_tunnel_request_completed_endpoint_
= endpoint
;
114 on_tunnel_request_completed_proxy_server_
= proxy_server
;
117 void OnFallback(const ProxyServer
& bad_proxy
, int net_error
) override
{}
119 void OnBeforeSendHeaders(URLRequest
* request
,
120 const ProxyInfo
& proxy_info
,
121 HttpRequestHeaders
* headers
) override
{}
123 void OnBeforeTunnelRequest(const net::HostPortPair
& proxy_server
,
124 net::HttpRequestHeaders
* extra_headers
) override
{
125 on_before_tunnel_request_called_
= true;
127 extra_headers
->SetHeader("Foo", proxy_server
.ToString());
131 void OnTunnelHeadersReceived(
132 const net::HostPortPair
& origin
,
133 const net::HostPortPair
& proxy_server
,
134 const net::HttpResponseHeaders
& response_headers
) override
{
135 on_tunnel_headers_received_called_
= true;
136 on_tunnel_headers_received_origin_
= origin
;
137 on_tunnel_headers_received_proxy_server_
= proxy_server
;
138 on_tunnel_headers_received_status_line_
= response_headers
.GetStatusLine();
142 bool on_before_tunnel_request_called_
;
143 bool on_tunnel_request_completed_called_
;
144 bool on_tunnel_headers_received_called_
;
145 HostPortPair on_tunnel_request_completed_endpoint_
;
146 HostPortPair on_tunnel_request_completed_proxy_server_
;
147 HostPortPair on_tunnel_headers_received_origin_
;
148 HostPortPair on_tunnel_headers_received_proxy_server_
;
149 std::string on_tunnel_headers_received_status_line_
;
153 class HttpProxyClientSocketPoolTest
154 : public ::testing::TestWithParam
<HttpProxyClientSocketPoolTestParams
> {
156 HttpProxyClientSocketPoolTest()
157 : session_deps_(GetParam().protocol
),
158 transport_socket_pool_(
161 session_deps_
.deterministic_socket_factory
.get()),
162 ssl_socket_pool_(kMaxSockets
,
164 session_deps_
.cert_verifier
.get(),
165 NULL
/* channel_id_store */,
166 NULL
/* transport_security_state */,
167 NULL
/* cert_transparency_verifier */,
168 NULL
/* cert_policy_enforcer */,
169 std::string() /* ssl_session_cache_shard */,
170 session_deps_
.deterministic_socket_factory
.get(),
171 &transport_socket_pool_
,
174 session_deps_
.ssl_config_service
.get(),
175 BoundNetLog().net_log()),
176 session_(CreateNetworkSession()),
177 spdy_util_(GetParam().protocol
),
180 &transport_socket_pool_
,
184 virtual ~HttpProxyClientSocketPoolTest() {
187 void AddAuthToCache() {
188 const base::string16
kFoo(base::ASCIIToUTF16("foo"));
189 const base::string16
kBar(base::ASCIIToUTF16("bar"));
190 GURL
proxy_url(GetParam().proxy_type
== HTTP
?
191 (std::string("http://") + kHttpProxyHost
) :
192 (std::string("https://") + kHttpsProxyHost
));
193 session_
->http_auth_cache()->Add(proxy_url
,
195 HttpAuth::AUTH_SCHEME_BASIC
,
196 "Basic realm=MyRealm1",
197 AuthCredentials(kFoo
, kBar
),
201 scoped_refptr
<TransportSocketParams
> CreateHttpProxyParams() const {
202 if (GetParam().proxy_type
!= HTTP
)
204 return new TransportSocketParams(
205 HostPortPair(kHttpProxyHost
, 80),
208 OnHostResolutionCallback(),
209 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
);
212 scoped_refptr
<SSLSocketParams
> CreateHttpsProxyParams() const {
213 if (GetParam().proxy_type
== HTTP
)
215 return new SSLSocketParams(
216 new TransportSocketParams(
217 HostPortPair(kHttpsProxyHost
, 443),
220 OnHostResolutionCallback(),
221 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
),
224 HostPortPair(kHttpsProxyHost
, 443),
226 PRIVACY_MODE_DISABLED
,
232 // Returns the a correctly constructed HttpProxyParms
233 // for the HTTP or HTTPS proxy.
234 scoped_refptr
<HttpProxySocketParams
> CreateParams(
236 ProxyDelegate
* proxy_delegate
) {
237 return scoped_refptr
<HttpProxySocketParams
>(new HttpProxySocketParams(
238 CreateHttpProxyParams(),
239 CreateHttpsProxyParams(),
240 GURL(tunnel
? "https://www.google.com/" : "http://www.google.com"),
242 HostPortPair("www.google.com", tunnel
? 443 : 80),
243 session_
->http_auth_cache(),
244 session_
->http_auth_handler_factory(),
245 session_
->spdy_session_pool(),
250 scoped_refptr
<HttpProxySocketParams
> CreateTunnelParams(
251 ProxyDelegate
* proxy_delegate
) {
252 return CreateParams(true, proxy_delegate
);
255 scoped_refptr
<HttpProxySocketParams
> CreateNoTunnelParams(
256 ProxyDelegate
* proxy_delegate
) {
257 return CreateParams(false, proxy_delegate
);
260 DeterministicMockClientSocketFactory
* socket_factory() {
261 return session_deps_
.deterministic_socket_factory
.get();
264 void Initialize(MockRead
* reads
, size_t reads_count
,
265 MockWrite
* writes
, size_t writes_count
,
266 MockRead
* spdy_reads
, size_t spdy_reads_count
,
267 MockWrite
* spdy_writes
, size_t spdy_writes_count
) {
268 if (GetParam().proxy_type
== SPDY
) {
269 data_
.reset(new DeterministicSocketData(spdy_reads
, spdy_reads_count
,
270 spdy_writes
, spdy_writes_count
));
272 data_
.reset(new DeterministicSocketData(reads
, reads_count
, writes
,
276 data_
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
277 data_
->StopAfter(2); // Request / Response
279 socket_factory()->AddSocketDataProvider(data_
.get());
281 if (GetParam().proxy_type
!= HTTP
) {
282 ssl_data_
.reset(new SSLSocketDataProvider(SYNCHRONOUS
, OK
));
283 if (GetParam().proxy_type
== SPDY
) {
286 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
290 void InitializeSpdySsl() {
291 ssl_data_
->SetNextProto(GetParam().protocol
);
294 HttpNetworkSession
* CreateNetworkSession() {
295 return SpdySessionDependencies::SpdyCreateSessionDeterministic(
299 RequestPriority
GetLastTransportRequestPriority() const {
300 return transport_socket_pool_
.last_request_priority();
304 SpdySessionDependencies session_deps_
;
306 MockTransportClientSocketPool transport_socket_pool_
;
307 MockHostResolver host_resolver_
;
308 scoped_ptr
<CertVerifier
> cert_verifier_
;
309 SSLClientSocketPool ssl_socket_pool_
;
311 const scoped_refptr
<HttpNetworkSession
> session_
;
314 SpdyTestUtil spdy_util_
;
315 scoped_ptr
<SSLSocketDataProvider
> ssl_data_
;
316 scoped_ptr
<DeterministicSocketData
> data_
;
317 HttpProxyClientSocketPool pool_
;
318 ClientSocketHandle handle_
;
319 TestCompletionCallback callback_
;
322 //-----------------------------------------------------------------------------
323 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
326 // TODO(akalin): Use ::testing::Combine() when we are able to use
328 INSTANTIATE_TEST_CASE_P(
329 HttpProxyClientSocketPoolTests
,
330 HttpProxyClientSocketPoolTest
,
332 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY31
),
333 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY31
),
334 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY31
),
335 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY4_14
),
336 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY4_14
),
337 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY4_14
),
338 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY4
),
339 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY4
),
340 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY4
)));
342 TEST_P(HttpProxyClientSocketPoolTest
, NoTunnel
) {
343 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
345 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
346 int rv
= handle_
.Init("a", CreateNoTunnelParams(proxy_delegate
.get()), LOW
,
347 CompletionCallback(), &pool_
, BoundNetLog());
349 EXPECT_TRUE(handle_
.is_initialized());
350 ASSERT_TRUE(handle_
.socket());
351 HttpProxyClientSocket
* tunnel_socket
=
352 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
353 EXPECT_TRUE(tunnel_socket
->IsConnected());
354 EXPECT_FALSE(proxy_delegate
->on_before_tunnel_request_called());
355 EXPECT_FALSE(proxy_delegate
->on_tunnel_headers_received_called());
356 EXPECT_TRUE(proxy_delegate
->on_tunnel_request_completed_called());
359 // Make sure that HttpProxyConnectJob passes on its priority to its
360 // (non-SSL) socket request on Init.
361 TEST_P(HttpProxyClientSocketPoolTest
, SetSocketRequestPriorityOnInit
) {
362 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
364 handle_
.Init("a", CreateNoTunnelParams(NULL
), HIGHEST
,
365 CompletionCallback(), &pool_
, BoundNetLog()));
366 EXPECT_EQ(HIGHEST
, GetLastTransportRequestPriority());
369 TEST_P(HttpProxyClientSocketPoolTest
, NeedAuth
) {
370 MockWrite writes
[] = {
371 MockWrite(ASYNC
, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
372 "Host: www.google.com\r\n"
373 "Proxy-Connection: keep-alive\r\n\r\n"),
377 MockRead(ASYNC
, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
378 MockRead(ASYNC
, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
379 MockRead(ASYNC
, 3, "Content-Length: 10\r\n\r\n"),
380 MockRead(ASYNC
, 4, "0123456789"),
382 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructSpdyConnect(
383 NULL
, 0, 1, LOW
, HostPortPair("www.google.com", 443)));
384 scoped_ptr
<SpdyFrame
> rst(
385 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
386 MockWrite spdy_writes
[] = {
387 CreateMockWrite(*req
, 0, ASYNC
),
388 CreateMockWrite(*rst
, 2, ASYNC
),
390 SpdyHeaderBlock resp_block
;
391 resp_block
[spdy_util_
.GetStatusKey()] = "407";
392 resp_block
["proxy-authenticate"] = "Basic realm=\"MyRealm1\"";
393 spdy_util_
.MaybeAddVersionHeader(&resp_block
);
395 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyReply(1, resp_block
));
396 MockRead spdy_reads
[] = {
397 CreateMockRead(*resp
, 1, ASYNC
),
398 MockRead(ASYNC
, 0, 3)
401 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
402 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
403 arraysize(spdy_writes
));
406 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
407 callback_
.callback(), &pool_
, BoundNetLog());
408 EXPECT_EQ(ERR_IO_PENDING
, rv
);
409 EXPECT_FALSE(handle_
.is_initialized());
410 EXPECT_FALSE(handle_
.socket());
412 data_
->RunFor(GetParam().proxy_type
== SPDY
? 2 : 4);
413 rv
= callback_
.WaitForResult();
414 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED
, rv
);
415 EXPECT_TRUE(handle_
.is_initialized());
416 ASSERT_TRUE(handle_
.socket());
417 ProxyClientSocket
* tunnel_socket
=
418 static_cast<ProxyClientSocket
*>(handle_
.socket());
419 if (GetParam().proxy_type
== SPDY
) {
420 EXPECT_TRUE(tunnel_socket
->IsConnected());
421 EXPECT_TRUE(tunnel_socket
->IsUsingSpdy());
423 EXPECT_FALSE(tunnel_socket
->IsConnected());
424 EXPECT_FALSE(tunnel_socket
->IsUsingSpdy());
428 TEST_P(HttpProxyClientSocketPoolTest
, HaveAuth
) {
429 // It's pretty much impossible to make the SPDY case behave synchronously
430 // so we skip this test for SPDY
431 if (GetParam().proxy_type
== SPDY
)
433 std::string proxy_host_port
=
434 GetParam().proxy_type
== HTTP
?
435 (kHttpProxyHost
+ std::string(":80")) :
436 (kHttpsProxyHost
+ std::string(":443"));
437 std::string request
=
438 "CONNECT www.google.com:443 HTTP/1.1\r\n"
439 "Host: www.google.com\r\n"
440 "Proxy-Connection: keep-alive\r\n"
441 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
442 "Foo: " + proxy_host_port
+ "\r\n\r\n";
443 MockWrite writes
[] = {
444 MockWrite(SYNCHRONOUS
, 0, request
.c_str()),
447 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
450 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
), NULL
, 0,
454 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
455 int rv
= handle_
.Init("a", CreateTunnelParams(proxy_delegate
.get()), LOW
,
456 callback_
.callback(), &pool_
, BoundNetLog());
458 EXPECT_TRUE(handle_
.is_initialized());
459 ASSERT_TRUE(handle_
.socket());
460 HttpProxyClientSocket
* tunnel_socket
=
461 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
462 EXPECT_TRUE(tunnel_socket
->IsConnected());
463 proxy_delegate
->VerifyOnTunnelHeadersReceived(
464 "www.google.com:443",
465 proxy_host_port
.c_str(),
466 "HTTP/1.1 200 Connection Established");
467 proxy_delegate
->VerifyOnTunnelRequestCompleted(
468 "www.google.com:443",
469 proxy_host_port
.c_str());
472 TEST_P(HttpProxyClientSocketPoolTest
, AsyncHaveAuth
) {
473 std::string proxy_host_port
=
474 GetParam().proxy_type
== HTTP
?
475 (kHttpProxyHost
+ std::string(":80")) :
476 (kHttpsProxyHost
+ std::string(":443"));
477 std::string request
=
478 "CONNECT www.google.com:443 HTTP/1.1\r\n"
479 "Host: www.google.com\r\n"
480 "Proxy-Connection: keep-alive\r\n"
481 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
482 "Foo: " + proxy_host_port
+ "\r\n\r\n";
483 MockWrite writes
[] = {
484 MockWrite(ASYNC
, 0, request
.c_str()),
487 MockRead(ASYNC
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
490 scoped_ptr
<SpdyFrame
> req(
491 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
492 HostPortPair("www.google.com", 443)));
493 MockWrite spdy_writes
[] = {
494 CreateMockWrite(*req
, 0, ASYNC
)
496 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
497 MockRead spdy_reads
[] = {
498 CreateMockRead(*resp
, 1, ASYNC
),
499 MockRead(ASYNC
, 0, 2)
502 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
503 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
504 arraysize(spdy_writes
));
507 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
508 int rv
= handle_
.Init("a", CreateTunnelParams(proxy_delegate
.get()), LOW
,
509 callback_
.callback(), &pool_
, BoundNetLog());
510 EXPECT_EQ(ERR_IO_PENDING
, rv
);
511 EXPECT_FALSE(handle_
.is_initialized());
512 EXPECT_FALSE(handle_
.socket());
515 EXPECT_EQ(OK
, callback_
.WaitForResult());
516 EXPECT_TRUE(handle_
.is_initialized());
517 ASSERT_TRUE(handle_
.socket());
518 EXPECT_TRUE(handle_
.socket()->IsConnected());
519 proxy_delegate
->VerifyOnTunnelRequestCompleted(
520 "www.google.com:443",
521 proxy_host_port
.c_str());
524 // Make sure that HttpProxyConnectJob passes on its priority to its
525 // SPDY session's socket request on Init (if applicable).
526 TEST_P(HttpProxyClientSocketPoolTest
,
527 SetSpdySessionSocketRequestPriorityOnInit
) {
528 if (GetParam().proxy_type
!= SPDY
)
531 scoped_ptr
<SpdyFrame
> req(
532 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, MEDIUM
,
533 HostPortPair("www.google.com", 443)));
534 MockWrite spdy_writes
[] = {
535 CreateMockWrite(*req
, 0, ASYNC
)
537 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
538 MockRead spdy_reads
[] = {
539 CreateMockRead(*resp
, 1, ASYNC
),
540 MockRead(ASYNC
, 0, 2)
543 Initialize(NULL
, 0, NULL
, 0,
544 spdy_reads
, arraysize(spdy_reads
),
545 spdy_writes
, arraysize(spdy_writes
));
548 EXPECT_EQ(ERR_IO_PENDING
,
549 handle_
.Init("a", CreateTunnelParams(NULL
), MEDIUM
,
550 callback_
.callback(), &pool_
, BoundNetLog()));
551 EXPECT_EQ(MEDIUM
, GetLastTransportRequestPriority());
554 EXPECT_EQ(OK
, callback_
.WaitForResult());
557 TEST_P(HttpProxyClientSocketPoolTest
, TCPError
) {
558 if (GetParam().proxy_type
== SPDY
) return;
559 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
560 data_
->set_connect_data(MockConnect(ASYNC
, ERR_CONNECTION_CLOSED
));
562 socket_factory()->AddSocketDataProvider(data_
.get());
564 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
565 callback_
.callback(), &pool_
, BoundNetLog());
566 EXPECT_EQ(ERR_IO_PENDING
, rv
);
567 EXPECT_FALSE(handle_
.is_initialized());
568 EXPECT_FALSE(handle_
.socket());
570 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED
, callback_
.WaitForResult());
572 EXPECT_FALSE(handle_
.is_initialized());
573 EXPECT_FALSE(handle_
.socket());
576 TEST_P(HttpProxyClientSocketPoolTest
, SSLError
) {
577 if (GetParam().proxy_type
== HTTP
) return;
578 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
579 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
580 socket_factory()->AddSocketDataProvider(data_
.get());
582 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
583 ERR_CERT_AUTHORITY_INVALID
));
584 if (GetParam().proxy_type
== SPDY
) {
587 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
589 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
590 callback_
.callback(), &pool_
, BoundNetLog());
591 EXPECT_EQ(ERR_IO_PENDING
, rv
);
592 EXPECT_FALSE(handle_
.is_initialized());
593 EXPECT_FALSE(handle_
.socket());
595 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID
, callback_
.WaitForResult());
597 EXPECT_FALSE(handle_
.is_initialized());
598 EXPECT_FALSE(handle_
.socket());
601 TEST_P(HttpProxyClientSocketPoolTest
, SslClientAuth
) {
602 if (GetParam().proxy_type
== HTTP
) return;
603 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
604 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
605 socket_factory()->AddSocketDataProvider(data_
.get());
607 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
608 ERR_SSL_CLIENT_AUTH_CERT_NEEDED
));
609 if (GetParam().proxy_type
== SPDY
) {
612 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
614 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
615 callback_
.callback(), &pool_
, BoundNetLog());
616 EXPECT_EQ(ERR_IO_PENDING
, rv
);
617 EXPECT_FALSE(handle_
.is_initialized());
618 EXPECT_FALSE(handle_
.socket());
620 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED
, callback_
.WaitForResult());
622 EXPECT_FALSE(handle_
.is_initialized());
623 EXPECT_FALSE(handle_
.socket());
626 TEST_P(HttpProxyClientSocketPoolTest
, TunnelUnexpectedClose
) {
627 MockWrite writes
[] = {
629 "CONNECT www.google.com:443 HTTP/1.1\r\n"
630 "Host: www.google.com\r\n"
631 "Proxy-Connection: keep-alive\r\n"
632 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
635 MockRead(ASYNC
, 1, "HTTP/1.1 200 Conn"),
636 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 2),
638 scoped_ptr
<SpdyFrame
> req(
639 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
640 HostPortPair("www.google.com", 443)));
641 MockWrite spdy_writes
[] = {
642 CreateMockWrite(*req
, 0, ASYNC
)
644 MockRead spdy_reads
[] = {
645 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 1),
648 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
649 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
650 arraysize(spdy_writes
));
653 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
654 callback_
.callback(), &pool_
, BoundNetLog());
655 EXPECT_EQ(ERR_IO_PENDING
, rv
);
656 EXPECT_FALSE(handle_
.is_initialized());
657 EXPECT_FALSE(handle_
.socket());
660 if (GetParam().proxy_type
== SPDY
) {
661 // SPDY cannot process a headers block unless it's complete and so it
662 // returns ERR_CONNECTION_CLOSED in this case.
663 EXPECT_EQ(ERR_CONNECTION_CLOSED
, callback_
.WaitForResult());
665 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED
, callback_
.WaitForResult());
667 EXPECT_FALSE(handle_
.is_initialized());
668 EXPECT_FALSE(handle_
.socket());
671 TEST_P(HttpProxyClientSocketPoolTest
, Tunnel1xxResponse
) {
672 // Tests that 1xx responses are rejected for a CONNECT request.
673 if (GetParam().proxy_type
== SPDY
) {
674 // SPDY doesn't have 1xx responses.
678 MockWrite writes
[] = {
680 "CONNECT www.google.com:443 HTTP/1.1\r\n"
681 "Host: www.google.com\r\n"
682 "Proxy-Connection: keep-alive\r\n\r\n"),
685 MockRead(ASYNC
, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
686 MockRead(ASYNC
, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
689 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
692 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
693 callback_
.callback(), &pool_
, BoundNetLog());
694 EXPECT_EQ(ERR_IO_PENDING
, rv
);
695 EXPECT_FALSE(handle_
.is_initialized());
696 EXPECT_FALSE(handle_
.socket());
699 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, callback_
.WaitForResult());
702 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupError
) {
703 MockWrite writes
[] = {
705 "CONNECT www.google.com:443 HTTP/1.1\r\n"
706 "Host: www.google.com\r\n"
707 "Proxy-Connection: keep-alive\r\n"
708 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
711 MockRead(ASYNC
, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
713 scoped_ptr
<SpdyFrame
> req(
714 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
715 HostPortPair("www.google.com", 443)));
716 scoped_ptr
<SpdyFrame
> rst(
717 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
718 MockWrite spdy_writes
[] = {
719 CreateMockWrite(*req
, 0, ASYNC
),
720 CreateMockWrite(*rst
, 2, ASYNC
),
722 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdySynReplyError(1));
723 MockRead spdy_reads
[] = {
724 CreateMockRead(*resp
, 1, ASYNC
),
725 MockRead(ASYNC
, 0, 3),
728 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
729 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
730 arraysize(spdy_writes
));
733 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
734 callback_
.callback(), &pool_
, BoundNetLog());
735 EXPECT_EQ(ERR_IO_PENDING
, rv
);
736 EXPECT_FALSE(handle_
.is_initialized());
737 EXPECT_FALSE(handle_
.socket());
741 rv
= callback_
.WaitForResult();
742 // All Proxy CONNECT responses are not trustworthy
743 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
744 EXPECT_FALSE(handle_
.is_initialized());
745 EXPECT_FALSE(handle_
.socket());
748 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupRedirect
) {
749 const std::string redirectTarget
= "https://foo.google.com/";
751 const std::string responseText
= "HTTP/1.1 302 Found\r\n"
752 "Location: " + redirectTarget
+ "\r\n"
753 "Set-Cookie: foo=bar\r\n"
755 MockWrite writes
[] = {
757 "CONNECT www.google.com:443 HTTP/1.1\r\n"
758 "Host: www.google.com\r\n"
759 "Proxy-Connection: keep-alive\r\n"
760 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
763 MockRead(ASYNC
, 1, responseText
.c_str()),
765 scoped_ptr
<SpdyFrame
> req(
766 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
767 HostPortPair("www.google.com", 443)));
768 scoped_ptr
<SpdyFrame
> rst(
769 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
771 MockWrite spdy_writes
[] = {
772 CreateMockWrite(*req
, 0, ASYNC
),
773 CreateMockWrite(*rst
, 3, ASYNC
),
776 const char* const responseHeaders
[] = {
777 "location", redirectTarget
.c_str(),
778 "set-cookie", "foo=bar",
780 const int responseHeadersSize
= arraysize(responseHeaders
) / 2;
781 scoped_ptr
<SpdyFrame
> resp(
782 spdy_util_
.ConstructSpdySynReplyError(
784 responseHeaders
, responseHeadersSize
,
786 MockRead spdy_reads
[] = {
787 CreateMockRead(*resp
, 1, ASYNC
),
788 MockRead(ASYNC
, 0, 2),
791 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
792 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
793 arraysize(spdy_writes
));
796 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
797 callback_
.callback(), &pool_
, BoundNetLog());
798 EXPECT_EQ(ERR_IO_PENDING
, rv
);
799 EXPECT_FALSE(handle_
.is_initialized());
800 EXPECT_FALSE(handle_
.socket());
804 rv
= callback_
.WaitForResult();
806 if (GetParam().proxy_type
== HTTP
) {
807 // We don't trust 302 responses to CONNECT from HTTP proxies.
808 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
809 EXPECT_FALSE(handle_
.is_initialized());
810 EXPECT_FALSE(handle_
.socket());
812 // Expect ProxyClientSocket to return the proxy's response, sanitized.
813 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE
, rv
);
814 EXPECT_TRUE(handle_
.is_initialized());
815 ASSERT_TRUE(handle_
.socket());
817 const ProxyClientSocket
* tunnel_socket
=
818 static_cast<ProxyClientSocket
*>(handle_
.socket());
819 const HttpResponseInfo
* response
= tunnel_socket
->GetConnectResponseInfo();
820 const HttpResponseHeaders
* headers
= response
->headers
.get();
822 // Make sure Set-Cookie header was stripped.
823 EXPECT_FALSE(headers
->HasHeader("set-cookie"));
825 // Make sure Content-Length: 0 header was added.
826 EXPECT_TRUE(headers
->HasHeaderValue("content-length", "0"));
828 // Make sure Location header was included and correct.
829 std::string location
;
830 EXPECT_TRUE(headers
->IsRedirect(&location
));
831 EXPECT_EQ(location
, redirectTarget
);
835 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.