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(kProtoSPDY3
) {}
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_headers_received_called_(false) {
69 virtual ~TestProxyDelegate() OVERRIDE
{
72 bool on_before_tunnel_request_called() const {
73 return on_before_tunnel_request_called_
;
76 bool on_tunnel_headers_received_called() const {
77 return on_tunnel_headers_received_called_
;
80 void VerifyOnTunnelHeadersReceived(const std::string
& origin
,
81 const std::string
& proxy_server
,
82 const std::string
& status_line
) const {
83 EXPECT_TRUE(on_tunnel_headers_received_called_
);
84 EXPECT_TRUE(HostPortPair::FromString(origin
).Equals(
85 on_tunnel_headers_received_origin_
));
86 EXPECT_TRUE(HostPortPair::FromString(proxy_server
).Equals(
87 on_tunnel_headers_received_proxy_server_
));
88 EXPECT_EQ(status_line
, on_tunnel_headers_received_status_line_
);
92 virtual void OnResolveProxy(const GURL
& url
,
94 const ProxyService
& proxy_service
,
95 ProxyInfo
* result
) OVERRIDE
{
98 virtual void OnFallback(const ProxyServer
& bad_proxy
,
99 int net_error
) OVERRIDE
{
102 virtual void OnBeforeSendHeaders(URLRequest
* request
,
103 const ProxyInfo
& proxy_info
,
104 HttpRequestHeaders
* headers
) OVERRIDE
{
107 virtual void OnBeforeTunnelRequest(
108 const net::HostPortPair
& proxy_server
,
109 net::HttpRequestHeaders
* extra_headers
) OVERRIDE
{
110 on_before_tunnel_request_called_
= true;
112 extra_headers
->SetHeader("Foo", proxy_server
.ToString());
116 virtual void OnTunnelHeadersReceived(
117 const net::HostPortPair
& origin
,
118 const net::HostPortPair
& proxy_server
,
119 const net::HttpResponseHeaders
& response_headers
) OVERRIDE
{
120 on_tunnel_headers_received_called_
= true;
121 on_tunnel_headers_received_origin_
= origin
;
122 on_tunnel_headers_received_proxy_server_
= proxy_server
;
123 on_tunnel_headers_received_status_line_
= response_headers
.GetStatusLine();
127 bool on_before_tunnel_request_called_
;
128 bool on_tunnel_headers_received_called_
;
129 HostPortPair on_tunnel_headers_received_origin_
;
130 HostPortPair on_tunnel_headers_received_proxy_server_
;
131 std::string on_tunnel_headers_received_status_line_
;
135 class HttpProxyClientSocketPoolTest
136 : public ::testing::TestWithParam
<HttpProxyClientSocketPoolTestParams
> {
138 HttpProxyClientSocketPoolTest()
139 : session_deps_(GetParam().protocol
),
140 tcp_histograms_("MockTCP"),
141 transport_socket_pool_(
145 session_deps_
.deterministic_socket_factory
.get()),
146 ssl_histograms_("MockSSL"),
147 ssl_socket_pool_(kMaxSockets
,
150 session_deps_
.host_resolver
.get(),
151 session_deps_
.cert_verifier
.get(),
152 NULL
/* channel_id_store */,
153 NULL
/* transport_security_state */,
154 NULL
/* cert_transparency_verifier */,
155 std::string() /* ssl_session_cache_shard */,
156 session_deps_
.deterministic_socket_factory
.get(),
157 &transport_socket_pool_
,
160 session_deps_
.ssl_config_service
.get(),
162 BoundNetLog().net_log()),
163 session_(CreateNetworkSession()),
164 http_proxy_histograms_("HttpProxyUnitTest"),
165 spdy_util_(GetParam().protocol
),
168 &http_proxy_histograms_
,
170 &transport_socket_pool_
,
175 virtual ~HttpProxyClientSocketPoolTest() {
178 void AddAuthToCache() {
179 const base::string16
kFoo(base::ASCIIToUTF16("foo"));
180 const base::string16
kBar(base::ASCIIToUTF16("bar"));
181 GURL
proxy_url(GetParam().proxy_type
== HTTP
?
182 (std::string("http://") + kHttpProxyHost
) :
183 (std::string("https://") + kHttpsProxyHost
));
184 session_
->http_auth_cache()->Add(proxy_url
,
186 HttpAuth::AUTH_SCHEME_BASIC
,
187 "Basic realm=MyRealm1",
188 AuthCredentials(kFoo
, kBar
),
192 scoped_refptr
<TransportSocketParams
> CreateHttpProxyParams() const {
193 if (GetParam().proxy_type
!= HTTP
)
195 return new TransportSocketParams(
196 HostPortPair(kHttpProxyHost
, 80),
199 OnHostResolutionCallback(),
200 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
);
203 scoped_refptr
<SSLSocketParams
> CreateHttpsProxyParams() const {
204 if (GetParam().proxy_type
== HTTP
)
206 return new SSLSocketParams(
207 new TransportSocketParams(
208 HostPortPair(kHttpsProxyHost
, 443),
211 OnHostResolutionCallback(),
212 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
),
215 HostPortPair(kHttpsProxyHost
, 443),
217 PRIVACY_MODE_DISABLED
,
223 // Returns the a correctly constructed HttpProxyParms
224 // for the HTTP or HTTPS proxy.
225 scoped_refptr
<HttpProxySocketParams
> CreateParams(
227 ProxyDelegate
* proxy_delegate
) {
228 return scoped_refptr
<HttpProxySocketParams
>(new HttpProxySocketParams(
229 CreateHttpProxyParams(),
230 CreateHttpsProxyParams(),
231 GURL(tunnel
? "https://www.google.com/" : "http://www.google.com"),
233 HostPortPair("www.google.com", tunnel
? 443 : 80),
234 session_
->http_auth_cache(),
235 session_
->http_auth_handler_factory(),
236 session_
->spdy_session_pool(),
241 scoped_refptr
<HttpProxySocketParams
> CreateTunnelParams(
242 ProxyDelegate
* proxy_delegate
) {
243 return CreateParams(true, proxy_delegate
);
246 scoped_refptr
<HttpProxySocketParams
> CreateNoTunnelParams(
247 ProxyDelegate
* proxy_delegate
) {
248 return CreateParams(false, proxy_delegate
);
251 DeterministicMockClientSocketFactory
* socket_factory() {
252 return session_deps_
.deterministic_socket_factory
.get();
255 void Initialize(MockRead
* reads
, size_t reads_count
,
256 MockWrite
* writes
, size_t writes_count
,
257 MockRead
* spdy_reads
, size_t spdy_reads_count
,
258 MockWrite
* spdy_writes
, size_t spdy_writes_count
) {
259 if (GetParam().proxy_type
== SPDY
) {
260 data_
.reset(new DeterministicSocketData(spdy_reads
, spdy_reads_count
,
261 spdy_writes
, spdy_writes_count
));
263 data_
.reset(new DeterministicSocketData(reads
, reads_count
, writes
,
267 data_
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
268 data_
->StopAfter(2); // Request / Response
270 socket_factory()->AddSocketDataProvider(data_
.get());
272 if (GetParam().proxy_type
!= HTTP
) {
273 ssl_data_
.reset(new SSLSocketDataProvider(SYNCHRONOUS
, OK
));
274 if (GetParam().proxy_type
== SPDY
) {
277 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
281 void InitializeSpdySsl() {
282 ssl_data_
->SetNextProto(GetParam().protocol
);
285 HttpNetworkSession
* CreateNetworkSession() {
286 return SpdySessionDependencies::SpdyCreateSessionDeterministic(
290 RequestPriority
GetLastTransportRequestPriority() const {
291 return transport_socket_pool_
.last_request_priority();
295 SpdySessionDependencies session_deps_
;
297 ClientSocketPoolHistograms tcp_histograms_
;
298 MockTransportClientSocketPool transport_socket_pool_
;
299 ClientSocketPoolHistograms ssl_histograms_
;
300 MockHostResolver host_resolver_
;
301 scoped_ptr
<CertVerifier
> cert_verifier_
;
302 SSLClientSocketPool ssl_socket_pool_
;
304 const scoped_refptr
<HttpNetworkSession
> session_
;
305 ClientSocketPoolHistograms http_proxy_histograms_
;
308 SpdyTestUtil spdy_util_
;
309 scoped_ptr
<SSLSocketDataProvider
> ssl_data_
;
310 scoped_ptr
<DeterministicSocketData
> data_
;
311 HttpProxyClientSocketPool pool_
;
312 ClientSocketHandle handle_
;
313 TestCompletionCallback callback_
;
316 //-----------------------------------------------------------------------------
317 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
320 // TODO(akalin): Use ::testing::Combine() when we are able to use
322 INSTANTIATE_TEST_CASE_P(
323 HttpProxyClientSocketPoolTests
,
324 HttpProxyClientSocketPoolTest
,
326 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoDeprecatedSPDY2
),
327 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoDeprecatedSPDY2
),
328 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoDeprecatedSPDY2
),
329 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY3
),
330 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY3
),
331 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY3
),
332 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY31
),
333 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY31
),
334 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY31
),
335 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY4
),
336 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY4
),
337 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY4
)));
339 TEST_P(HttpProxyClientSocketPoolTest
, NoTunnel
) {
340 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
342 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
343 int rv
= handle_
.Init("a", CreateNoTunnelParams(proxy_delegate
.get()), LOW
,
344 CompletionCallback(), &pool_
, BoundNetLog());
346 EXPECT_TRUE(handle_
.is_initialized());
347 ASSERT_TRUE(handle_
.socket());
348 HttpProxyClientSocket
* tunnel_socket
=
349 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
350 EXPECT_TRUE(tunnel_socket
->IsConnected());
351 EXPECT_FALSE(proxy_delegate
->on_before_tunnel_request_called());
352 EXPECT_FALSE(proxy_delegate
->on_tunnel_headers_received_called());
355 // Make sure that HttpProxyConnectJob passes on its priority to its
356 // (non-SSL) socket request on Init.
357 TEST_P(HttpProxyClientSocketPoolTest
, SetSocketRequestPriorityOnInit
) {
358 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
360 handle_
.Init("a", CreateNoTunnelParams(NULL
), HIGHEST
,
361 CompletionCallback(), &pool_
, BoundNetLog()));
362 EXPECT_EQ(HIGHEST
, GetLastTransportRequestPriority());
365 TEST_P(HttpProxyClientSocketPoolTest
, NeedAuth
) {
366 MockWrite writes
[] = {
367 MockWrite(ASYNC
, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
368 "Host: www.google.com\r\n"
369 "Proxy-Connection: keep-alive\r\n\r\n"),
373 MockRead(ASYNC
, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
374 MockRead(ASYNC
, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
375 MockRead(ASYNC
, 3, "Content-Length: 10\r\n\r\n"),
376 MockRead(ASYNC
, 4, "0123456789"),
378 scoped_ptr
<SpdyFrame
> req(
379 spdy_util_
.ConstructSpdyConnect(NULL
, 0, 1, LOW
));
380 scoped_ptr
<SpdyFrame
> rst(
381 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
382 MockWrite spdy_writes
[] = {
383 CreateMockWrite(*req
, 0, ASYNC
),
384 CreateMockWrite(*rst
, 2, ASYNC
),
386 SpdyHeaderBlock resp_block
;
387 resp_block
[spdy_util_
.GetStatusKey()] = "407";
388 resp_block
["proxy-authenticate"] = "Basic realm=\"MyRealm1\"";
389 spdy_util_
.MaybeAddVersionHeader(&resp_block
);
391 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyReply(1, resp_block
));
392 MockRead spdy_reads
[] = {
393 CreateMockRead(*resp
, 1, ASYNC
),
394 MockRead(ASYNC
, 0, 3)
397 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
398 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
399 arraysize(spdy_writes
));
402 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
403 callback_
.callback(), &pool_
, BoundNetLog());
404 EXPECT_EQ(ERR_IO_PENDING
, rv
);
405 EXPECT_FALSE(handle_
.is_initialized());
406 EXPECT_FALSE(handle_
.socket());
408 data_
->RunFor(GetParam().proxy_type
== SPDY
? 2 : 4);
409 rv
= callback_
.WaitForResult();
410 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED
, rv
);
411 EXPECT_TRUE(handle_
.is_initialized());
412 ASSERT_TRUE(handle_
.socket());
413 ProxyClientSocket
* tunnel_socket
=
414 static_cast<ProxyClientSocket
*>(handle_
.socket());
415 if (GetParam().proxy_type
== SPDY
) {
416 EXPECT_TRUE(tunnel_socket
->IsConnected());
417 EXPECT_TRUE(tunnel_socket
->IsUsingSpdy());
419 EXPECT_FALSE(tunnel_socket
->IsConnected());
420 EXPECT_FALSE(tunnel_socket
->IsUsingSpdy());
424 TEST_P(HttpProxyClientSocketPoolTest
, HaveAuth
) {
425 // It's pretty much impossible to make the SPDY case behave synchronously
426 // so we skip this test for SPDY
427 if (GetParam().proxy_type
== SPDY
)
429 std::string proxy_host_port
=
430 GetParam().proxy_type
== HTTP
?
431 (kHttpProxyHost
+ std::string(":80")) :
432 (kHttpsProxyHost
+ std::string(":443"));
433 std::string request
=
434 "CONNECT www.google.com:443 HTTP/1.1\r\n"
435 "Host: www.google.com\r\n"
436 "Proxy-Connection: keep-alive\r\n"
437 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
438 "Foo: " + proxy_host_port
+ "\r\n\r\n";
439 MockWrite writes
[] = {
440 MockWrite(SYNCHRONOUS
, 0, request
.c_str()),
443 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
446 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
), NULL
, 0,
450 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
451 int rv
= handle_
.Init("a", CreateTunnelParams(proxy_delegate
.get()), LOW
,
452 callback_
.callback(), &pool_
, BoundNetLog());
454 EXPECT_TRUE(handle_
.is_initialized());
455 ASSERT_TRUE(handle_
.socket());
456 HttpProxyClientSocket
* tunnel_socket
=
457 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
458 EXPECT_TRUE(tunnel_socket
->IsConnected());
459 proxy_delegate
->VerifyOnTunnelHeadersReceived(
460 "www.google.com:443",
461 proxy_host_port
.c_str(),
462 "HTTP/1.1 200 Connection Established");
465 TEST_P(HttpProxyClientSocketPoolTest
, AsyncHaveAuth
) {
466 MockWrite writes
[] = {
467 MockWrite(ASYNC
, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
468 "Host: www.google.com\r\n"
469 "Proxy-Connection: keep-alive\r\n"
470 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
473 MockRead(ASYNC
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
476 scoped_ptr
<SpdyFrame
> req(
477 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
478 MockWrite spdy_writes
[] = {
479 CreateMockWrite(*req
, 0, ASYNC
)
481 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
482 MockRead spdy_reads
[] = {
483 CreateMockRead(*resp
, 1, ASYNC
),
484 MockRead(ASYNC
, 0, 2)
487 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
488 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
489 arraysize(spdy_writes
));
492 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
493 callback_
.callback(), &pool_
, BoundNetLog());
494 EXPECT_EQ(ERR_IO_PENDING
, rv
);
495 EXPECT_FALSE(handle_
.is_initialized());
496 EXPECT_FALSE(handle_
.socket());
499 EXPECT_EQ(OK
, callback_
.WaitForResult());
500 EXPECT_TRUE(handle_
.is_initialized());
501 ASSERT_TRUE(handle_
.socket());
502 HttpProxyClientSocket
* tunnel_socket
=
503 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
504 EXPECT_TRUE(tunnel_socket
->IsConnected());
507 // Make sure that HttpProxyConnectJob passes on its priority to its
508 // SPDY session's socket request on Init (if applicable).
509 TEST_P(HttpProxyClientSocketPoolTest
,
510 SetSpdySessionSocketRequestPriorityOnInit
) {
511 if (GetParam().proxy_type
!= SPDY
)
514 scoped_ptr
<SpdyFrame
> req(
515 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
,
517 MockWrite spdy_writes
[] = {
518 CreateMockWrite(*req
, 0, ASYNC
)
520 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
521 MockRead spdy_reads
[] = {
522 CreateMockRead(*resp
, 1, ASYNC
),
523 MockRead(ASYNC
, 0, 2)
526 Initialize(NULL
, 0, NULL
, 0,
527 spdy_reads
, arraysize(spdy_reads
),
528 spdy_writes
, arraysize(spdy_writes
));
531 EXPECT_EQ(ERR_IO_PENDING
,
532 handle_
.Init("a", CreateTunnelParams(NULL
), MEDIUM
,
533 callback_
.callback(), &pool_
, BoundNetLog()));
534 EXPECT_EQ(MEDIUM
, GetLastTransportRequestPriority());
537 EXPECT_EQ(OK
, callback_
.WaitForResult());
540 TEST_P(HttpProxyClientSocketPoolTest
, TCPError
) {
541 if (GetParam().proxy_type
== SPDY
) return;
542 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
543 data_
->set_connect_data(MockConnect(ASYNC
, ERR_CONNECTION_CLOSED
));
545 socket_factory()->AddSocketDataProvider(data_
.get());
547 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
548 callback_
.callback(), &pool_
, BoundNetLog());
549 EXPECT_EQ(ERR_IO_PENDING
, rv
);
550 EXPECT_FALSE(handle_
.is_initialized());
551 EXPECT_FALSE(handle_
.socket());
553 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED
, callback_
.WaitForResult());
555 EXPECT_FALSE(handle_
.is_initialized());
556 EXPECT_FALSE(handle_
.socket());
559 TEST_P(HttpProxyClientSocketPoolTest
, SSLError
) {
560 if (GetParam().proxy_type
== HTTP
) return;
561 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
562 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
563 socket_factory()->AddSocketDataProvider(data_
.get());
565 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
566 ERR_CERT_AUTHORITY_INVALID
));
567 if (GetParam().proxy_type
== SPDY
) {
570 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
572 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
573 callback_
.callback(), &pool_
, BoundNetLog());
574 EXPECT_EQ(ERR_IO_PENDING
, rv
);
575 EXPECT_FALSE(handle_
.is_initialized());
576 EXPECT_FALSE(handle_
.socket());
578 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID
, callback_
.WaitForResult());
580 EXPECT_FALSE(handle_
.is_initialized());
581 EXPECT_FALSE(handle_
.socket());
584 TEST_P(HttpProxyClientSocketPoolTest
, SslClientAuth
) {
585 if (GetParam().proxy_type
== HTTP
) return;
586 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
587 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
588 socket_factory()->AddSocketDataProvider(data_
.get());
590 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
591 ERR_SSL_CLIENT_AUTH_CERT_NEEDED
));
592 if (GetParam().proxy_type
== SPDY
) {
595 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
597 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
598 callback_
.callback(), &pool_
, BoundNetLog());
599 EXPECT_EQ(ERR_IO_PENDING
, rv
);
600 EXPECT_FALSE(handle_
.is_initialized());
601 EXPECT_FALSE(handle_
.socket());
603 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED
, callback_
.WaitForResult());
605 EXPECT_FALSE(handle_
.is_initialized());
606 EXPECT_FALSE(handle_
.socket());
609 TEST_P(HttpProxyClientSocketPoolTest
, TunnelUnexpectedClose
) {
610 MockWrite writes
[] = {
612 "CONNECT www.google.com:443 HTTP/1.1\r\n"
613 "Host: www.google.com\r\n"
614 "Proxy-Connection: keep-alive\r\n"
615 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
618 MockRead(ASYNC
, 1, "HTTP/1.1 200 Conn"),
619 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 2),
621 scoped_ptr
<SpdyFrame
> req(
622 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
623 MockWrite spdy_writes
[] = {
624 CreateMockWrite(*req
, 0, ASYNC
)
626 MockRead spdy_reads
[] = {
627 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 1),
630 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
631 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
632 arraysize(spdy_writes
));
635 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
636 callback_
.callback(), &pool_
, BoundNetLog());
637 EXPECT_EQ(ERR_IO_PENDING
, rv
);
638 EXPECT_FALSE(handle_
.is_initialized());
639 EXPECT_FALSE(handle_
.socket());
642 if (GetParam().proxy_type
== SPDY
) {
643 // SPDY cannot process a headers block unless it's complete and so it
644 // returns ERR_CONNECTION_CLOSED in this case.
645 EXPECT_EQ(ERR_CONNECTION_CLOSED
, callback_
.WaitForResult());
647 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED
, callback_
.WaitForResult());
649 EXPECT_FALSE(handle_
.is_initialized());
650 EXPECT_FALSE(handle_
.socket());
653 TEST_P(HttpProxyClientSocketPoolTest
, Tunnel1xxResponse
) {
654 // Tests that 1xx responses are rejected for a CONNECT request.
655 if (GetParam().proxy_type
== SPDY
) {
656 // SPDY doesn't have 1xx responses.
660 MockWrite writes
[] = {
662 "CONNECT www.google.com:443 HTTP/1.1\r\n"
663 "Host: www.google.com\r\n"
664 "Proxy-Connection: keep-alive\r\n\r\n"),
667 MockRead(ASYNC
, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
668 MockRead(ASYNC
, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
671 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
674 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
675 callback_
.callback(), &pool_
, BoundNetLog());
676 EXPECT_EQ(ERR_IO_PENDING
, rv
);
677 EXPECT_FALSE(handle_
.is_initialized());
678 EXPECT_FALSE(handle_
.socket());
681 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, callback_
.WaitForResult());
684 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupError
) {
685 MockWrite writes
[] = {
687 "CONNECT www.google.com:443 HTTP/1.1\r\n"
688 "Host: www.google.com\r\n"
689 "Proxy-Connection: keep-alive\r\n"
690 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
693 MockRead(ASYNC
, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
695 scoped_ptr
<SpdyFrame
> req(
696 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
697 scoped_ptr
<SpdyFrame
> rst(
698 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
699 MockWrite spdy_writes
[] = {
700 CreateMockWrite(*req
, 0, ASYNC
),
701 CreateMockWrite(*rst
, 2, ASYNC
),
703 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdySynReplyError(1));
704 MockRead spdy_reads
[] = {
705 CreateMockRead(*resp
, 1, ASYNC
),
706 MockRead(ASYNC
, 0, 3),
709 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
710 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
711 arraysize(spdy_writes
));
714 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
715 callback_
.callback(), &pool_
, BoundNetLog());
716 EXPECT_EQ(ERR_IO_PENDING
, rv
);
717 EXPECT_FALSE(handle_
.is_initialized());
718 EXPECT_FALSE(handle_
.socket());
722 rv
= callback_
.WaitForResult();
723 // All Proxy CONNECT responses are not trustworthy
724 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
725 EXPECT_FALSE(handle_
.is_initialized());
726 EXPECT_FALSE(handle_
.socket());
729 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupRedirect
) {
730 const std::string redirectTarget
= "https://foo.google.com/";
732 const std::string responseText
= "HTTP/1.1 302 Found\r\n"
733 "Location: " + redirectTarget
+ "\r\n"
734 "Set-Cookie: foo=bar\r\n"
736 MockWrite writes
[] = {
738 "CONNECT www.google.com:443 HTTP/1.1\r\n"
739 "Host: www.google.com\r\n"
740 "Proxy-Connection: keep-alive\r\n"
741 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
744 MockRead(ASYNC
, 1, responseText
.c_str()),
746 scoped_ptr
<SpdyFrame
> req(
747 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
748 scoped_ptr
<SpdyFrame
> rst(
749 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
751 MockWrite spdy_writes
[] = {
752 CreateMockWrite(*req
, 0, ASYNC
),
753 CreateMockWrite(*rst
, 3, ASYNC
),
756 const char* const responseHeaders
[] = {
757 "location", redirectTarget
.c_str(),
758 "set-cookie", "foo=bar",
760 const int responseHeadersSize
= arraysize(responseHeaders
) / 2;
761 scoped_ptr
<SpdyFrame
> resp(
762 spdy_util_
.ConstructSpdySynReplyError(
764 responseHeaders
, responseHeadersSize
,
766 MockRead spdy_reads
[] = {
767 CreateMockRead(*resp
, 1, ASYNC
),
768 MockRead(ASYNC
, 0, 2),
771 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
772 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
773 arraysize(spdy_writes
));
776 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
777 callback_
.callback(), &pool_
, BoundNetLog());
778 EXPECT_EQ(ERR_IO_PENDING
, rv
);
779 EXPECT_FALSE(handle_
.is_initialized());
780 EXPECT_FALSE(handle_
.socket());
784 rv
= callback_
.WaitForResult();
786 if (GetParam().proxy_type
== HTTP
) {
787 // We don't trust 302 responses to CONNECT from HTTP proxies.
788 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
789 EXPECT_FALSE(handle_
.is_initialized());
790 EXPECT_FALSE(handle_
.socket());
792 // Expect ProxyClientSocket to return the proxy's response, sanitized.
793 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE
, rv
);
794 EXPECT_TRUE(handle_
.is_initialized());
795 ASSERT_TRUE(handle_
.socket());
797 const ProxyClientSocket
* tunnel_socket
=
798 static_cast<ProxyClientSocket
*>(handle_
.socket());
799 const HttpResponseInfo
* response
= tunnel_socket
->GetConnectResponseInfo();
800 const HttpResponseHeaders
* headers
= response
->headers
.get();
802 // Make sure Set-Cookie header was stripped.
803 EXPECT_FALSE(headers
->HasHeader("set-cookie"));
805 // Make sure Content-Length: 0 header was added.
806 EXPECT_TRUE(headers
->HasHeaderValue("content-length", "0"));
808 // Make sure Location header was included and correct.
809 std::string location
;
810 EXPECT_TRUE(headers
->IsRedirect(&location
));
811 EXPECT_EQ(location
, redirectTarget
);
815 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.