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 HostPortPair
& proxy_server
,
124 HttpRequestHeaders
* extra_headers
) override
{
125 on_before_tunnel_request_called_
= true;
127 extra_headers
->SetHeader("Foo", proxy_server
.ToString());
131 void OnTunnelHeadersReceived(
132 const HostPortPair
& origin
,
133 const HostPortPair
& proxy_server
,
134 const 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_(kMaxSockets
,
160 session_deps_
.socket_factory
.get()),
161 ssl_socket_pool_(kMaxSockets
,
163 session_deps_
.cert_verifier
.get(),
164 NULL
/* channel_id_store */,
165 NULL
/* transport_security_state */,
166 NULL
/* cert_transparency_verifier */,
167 NULL
/* cert_policy_enforcer */,
168 std::string() /* ssl_session_cache_shard */,
169 session_deps_
.socket_factory
.get(),
170 &transport_socket_pool_
,
173 session_deps_
.ssl_config_service
.get(),
174 BoundNetLog().net_log()),
175 session_(CreateNetworkSession()),
176 spdy_util_(GetParam().protocol
),
179 &transport_socket_pool_
,
183 virtual ~HttpProxyClientSocketPoolTest() {
186 void AddAuthToCache() {
187 const base::string16
kFoo(base::ASCIIToUTF16("foo"));
188 const base::string16
kBar(base::ASCIIToUTF16("bar"));
189 GURL
proxy_url(GetParam().proxy_type
== HTTP
?
190 (std::string("http://") + kHttpProxyHost
) :
191 (std::string("https://") + kHttpsProxyHost
));
192 session_
->http_auth_cache()->Add(proxy_url
,
194 HttpAuth::AUTH_SCHEME_BASIC
,
195 "Basic realm=MyRealm1",
196 AuthCredentials(kFoo
, kBar
),
200 scoped_refptr
<TransportSocketParams
> CreateHttpProxyParams() const {
201 if (GetParam().proxy_type
!= HTTP
)
203 return new TransportSocketParams(
204 HostPortPair(kHttpProxyHost
, 80),
207 OnHostResolutionCallback(),
208 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
);
211 scoped_refptr
<SSLSocketParams
> CreateHttpsProxyParams() const {
212 if (GetParam().proxy_type
== HTTP
)
214 return new SSLSocketParams(
215 new TransportSocketParams(
216 HostPortPair(kHttpsProxyHost
, 443),
219 OnHostResolutionCallback(),
220 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT
),
223 HostPortPair(kHttpsProxyHost
, 443),
225 PRIVACY_MODE_DISABLED
,
230 // Returns the a correctly constructed HttpProxyParms
231 // for the HTTP or HTTPS proxy.
232 scoped_refptr
<HttpProxySocketParams
> CreateParams(
234 ProxyDelegate
* proxy_delegate
) {
235 return scoped_refptr
<HttpProxySocketParams
>(new HttpProxySocketParams(
236 CreateHttpProxyParams(),
237 CreateHttpsProxyParams(),
239 HostPortPair("www.google.com", tunnel
? 443 : 80),
240 session_
->http_auth_cache(),
241 session_
->http_auth_handler_factory(),
242 session_
->spdy_session_pool(),
247 scoped_refptr
<HttpProxySocketParams
> CreateTunnelParams(
248 ProxyDelegate
* proxy_delegate
) {
249 return CreateParams(true, proxy_delegate
);
252 scoped_refptr
<HttpProxySocketParams
> CreateNoTunnelParams(
253 ProxyDelegate
* proxy_delegate
) {
254 return CreateParams(false, proxy_delegate
);
257 MockClientSocketFactory
* socket_factory() {
258 return session_deps_
.socket_factory
.get();
261 void Initialize(MockRead
* reads
, size_t reads_count
,
262 MockWrite
* writes
, size_t writes_count
,
263 MockRead
* spdy_reads
, size_t spdy_reads_count
,
264 MockWrite
* spdy_writes
, size_t spdy_writes_count
) {
265 if (GetParam().proxy_type
== SPDY
) {
266 data_
.reset(new SequencedSocketData(spdy_reads
, spdy_reads_count
,
267 spdy_writes
, spdy_writes_count
));
270 new SequencedSocketData(reads
, reads_count
, writes
, writes_count
));
273 data_
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
275 socket_factory()->AddSocketDataProvider(data_
.get());
277 if (GetParam().proxy_type
!= HTTP
) {
278 ssl_data_
.reset(new SSLSocketDataProvider(SYNCHRONOUS
, OK
));
279 if (GetParam().proxy_type
== SPDY
) {
282 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
286 void InitializeSpdySsl() {
287 ssl_data_
->SetNextProto(GetParam().protocol
);
290 HttpNetworkSession
* CreateNetworkSession() {
291 return SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
294 RequestPriority
GetLastTransportRequestPriority() const {
295 return transport_socket_pool_
.last_request_priority();
299 SpdySessionDependencies session_deps_
;
301 MockTransportClientSocketPool transport_socket_pool_
;
302 MockHostResolver host_resolver_
;
303 scoped_ptr
<CertVerifier
> cert_verifier_
;
304 SSLClientSocketPool ssl_socket_pool_
;
306 const scoped_refptr
<HttpNetworkSession
> session_
;
309 SpdyTestUtil spdy_util_
;
310 scoped_ptr
<SSLSocketDataProvider
> ssl_data_
;
311 scoped_ptr
<SequencedSocketData
> data_
;
312 HttpProxyClientSocketPool pool_
;
313 ClientSocketHandle handle_
;
314 TestCompletionCallback callback_
;
317 //-----------------------------------------------------------------------------
318 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
321 // TODO(akalin): Use ::testing::Combine() when we are able to use
323 INSTANTIATE_TEST_CASE_P(
324 HttpProxyClientSocketPoolTests
,
325 HttpProxyClientSocketPoolTest
,
326 ::testing::Values(HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY31
),
327 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY31
),
328 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY31
),
329 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoHTTP2_14
),
330 HttpProxyClientSocketPoolTestParams(HTTPS
,
332 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoHTTP2_14
),
333 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoHTTP2
),
334 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoHTTP2
),
335 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoHTTP2
)));
337 TEST_P(HttpProxyClientSocketPoolTest
, NoTunnel
) {
338 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
340 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
341 int rv
= handle_
.Init("a", CreateNoTunnelParams(proxy_delegate
.get()), LOW
,
342 CompletionCallback(), &pool_
, BoundNetLog());
344 EXPECT_TRUE(handle_
.is_initialized());
345 ASSERT_TRUE(handle_
.socket());
346 HttpProxyClientSocket
* tunnel_socket
=
347 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
348 EXPECT_TRUE(tunnel_socket
->IsConnected());
349 EXPECT_FALSE(proxy_delegate
->on_before_tunnel_request_called());
350 EXPECT_FALSE(proxy_delegate
->on_tunnel_headers_received_called());
351 EXPECT_TRUE(proxy_delegate
->on_tunnel_request_completed_called());
354 // Make sure that HttpProxyConnectJob passes on its priority to its
355 // (non-SSL) socket request on Init.
356 TEST_P(HttpProxyClientSocketPoolTest
, SetSocketRequestPriorityOnInit
) {
357 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
359 handle_
.Init("a", CreateNoTunnelParams(NULL
), HIGHEST
,
360 CompletionCallback(), &pool_
, BoundNetLog()));
361 EXPECT_EQ(HIGHEST
, GetLastTransportRequestPriority());
364 TEST_P(HttpProxyClientSocketPoolTest
, NeedAuth
) {
365 MockWrite writes
[] = {
366 MockWrite(ASYNC
, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
367 "Host: www.google.com\r\n"
368 "Proxy-Connection: keep-alive\r\n\r\n"),
372 MockRead(ASYNC
, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
373 MockRead(ASYNC
, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
374 MockRead(ASYNC
, 3, "Content-Length: 10\r\n\r\n"),
375 MockRead(ASYNC
, 4, "0123456789"),
377 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructSpdyConnect(
378 NULL
, 0, 1, LOW
, HostPortPair("www.google.com", 443)));
379 scoped_ptr
<SpdyFrame
> rst(
380 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
381 MockWrite spdy_writes
[] = {
382 CreateMockWrite(*req
, 0, ASYNC
),
383 CreateMockWrite(*rst
, 2, ASYNC
),
385 SpdyHeaderBlock resp_block
;
386 resp_block
[spdy_util_
.GetStatusKey()] = "407";
387 resp_block
["proxy-authenticate"] = "Basic realm=\"MyRealm1\"";
388 spdy_util_
.MaybeAddVersionHeader(&resp_block
);
390 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyReply(1, resp_block
));
391 MockRead spdy_reads
[] = {
392 CreateMockRead(*resp
, 1, ASYNC
),
393 MockRead(ASYNC
, 0, 3)
396 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
397 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
398 arraysize(spdy_writes
));
400 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
401 callback_
.callback(), &pool_
, BoundNetLog());
402 EXPECT_EQ(ERR_IO_PENDING
, rv
);
403 EXPECT_FALSE(handle_
.is_initialized());
404 EXPECT_FALSE(handle_
.socket());
406 rv
= callback_
.WaitForResult();
407 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED
, rv
);
408 EXPECT_TRUE(handle_
.is_initialized());
409 ASSERT_TRUE(handle_
.socket());
410 ProxyClientSocket
* tunnel_socket
=
411 static_cast<ProxyClientSocket
*>(handle_
.socket());
412 if (GetParam().proxy_type
== SPDY
) {
413 EXPECT_TRUE(tunnel_socket
->IsConnected());
414 EXPECT_TRUE(tunnel_socket
->IsUsingSpdy());
416 EXPECT_FALSE(tunnel_socket
->IsConnected());
417 EXPECT_FALSE(tunnel_socket
->IsUsingSpdy());
421 TEST_P(HttpProxyClientSocketPoolTest
, HaveAuth
) {
422 // It's pretty much impossible to make the SPDY case behave synchronously
423 // so we skip this test for SPDY
424 if (GetParam().proxy_type
== SPDY
)
426 std::string proxy_host_port
=
427 GetParam().proxy_type
== HTTP
?
428 (kHttpProxyHost
+ std::string(":80")) :
429 (kHttpsProxyHost
+ std::string(":443"));
430 std::string request
=
431 "CONNECT www.google.com:443 HTTP/1.1\r\n"
432 "Host: www.google.com\r\n"
433 "Proxy-Connection: keep-alive\r\n"
434 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
435 "Foo: " + proxy_host_port
+ "\r\n\r\n";
436 MockWrite writes
[] = {
437 MockWrite(SYNCHRONOUS
, 0, request
.c_str()),
440 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
443 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
), NULL
, 0,
447 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
448 int rv
= handle_
.Init("a", CreateTunnelParams(proxy_delegate
.get()), LOW
,
449 callback_
.callback(), &pool_
, BoundNetLog());
451 EXPECT_TRUE(handle_
.is_initialized());
452 ASSERT_TRUE(handle_
.socket());
453 HttpProxyClientSocket
* tunnel_socket
=
454 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
455 EXPECT_TRUE(tunnel_socket
->IsConnected());
456 proxy_delegate
->VerifyOnTunnelHeadersReceived(
457 "www.google.com:443",
458 proxy_host_port
.c_str(),
459 "HTTP/1.1 200 Connection Established");
460 proxy_delegate
->VerifyOnTunnelRequestCompleted(
461 "www.google.com:443",
462 proxy_host_port
.c_str());
465 TEST_P(HttpProxyClientSocketPoolTest
, AsyncHaveAuth
) {
466 std::string proxy_host_port
=
467 GetParam().proxy_type
== HTTP
?
468 (kHttpProxyHost
+ std::string(":80")) :
469 (kHttpsProxyHost
+ std::string(":443"));
470 std::string request
=
471 "CONNECT www.google.com:443 HTTP/1.1\r\n"
472 "Host: www.google.com\r\n"
473 "Proxy-Connection: keep-alive\r\n"
474 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
475 "Foo: " + proxy_host_port
+ "\r\n\r\n";
476 MockWrite writes
[] = {
477 MockWrite(ASYNC
, 0, request
.c_str()),
480 MockRead(ASYNC
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
483 scoped_ptr
<SpdyFrame
> req(
484 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
485 HostPortPair("www.google.com", 443)));
486 MockWrite spdy_writes
[] = {
487 CreateMockWrite(*req
, 0, ASYNC
)
489 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
490 MockRead spdy_reads
[] = {
491 CreateMockRead(*resp
, 1, ASYNC
),
492 // Connection stays open.
493 MockRead(SYNCHRONOUS
, ERR_IO_PENDING
, 2),
496 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
497 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
498 arraysize(spdy_writes
));
501 scoped_ptr
<TestProxyDelegate
> proxy_delegate(new TestProxyDelegate());
502 int rv
= handle_
.Init("a", CreateTunnelParams(proxy_delegate
.get()), LOW
,
503 callback_
.callback(), &pool_
, BoundNetLog());
504 EXPECT_EQ(ERR_IO_PENDING
, rv
);
505 EXPECT_FALSE(handle_
.is_initialized());
506 EXPECT_FALSE(handle_
.socket());
508 EXPECT_EQ(OK
, callback_
.WaitForResult());
509 EXPECT_TRUE(handle_
.is_initialized());
510 ASSERT_TRUE(handle_
.socket());
511 EXPECT_TRUE(handle_
.socket()->IsConnected());
512 proxy_delegate
->VerifyOnTunnelRequestCompleted(
513 "www.google.com:443",
514 proxy_host_port
.c_str());
517 // Make sure that HttpProxyConnectJob passes on its priority to its
518 // SPDY session's socket request on Init (if applicable).
519 TEST_P(HttpProxyClientSocketPoolTest
,
520 SetSpdySessionSocketRequestPriorityOnInit
) {
521 if (GetParam().proxy_type
!= SPDY
)
524 scoped_ptr
<SpdyFrame
> req(
525 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, MEDIUM
,
526 HostPortPair("www.google.com", 443)));
527 MockWrite spdy_writes
[] = {
528 CreateMockWrite(*req
, 0, ASYNC
)
530 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
531 MockRead spdy_reads
[] = {
532 CreateMockRead(*resp
, 1, ASYNC
),
533 MockRead(ASYNC
, 0, 2)
536 Initialize(NULL
, 0, NULL
, 0,
537 spdy_reads
, arraysize(spdy_reads
),
538 spdy_writes
, arraysize(spdy_writes
));
541 EXPECT_EQ(ERR_IO_PENDING
,
542 handle_
.Init("a", CreateTunnelParams(NULL
), MEDIUM
,
543 callback_
.callback(), &pool_
, BoundNetLog()));
544 EXPECT_EQ(MEDIUM
, GetLastTransportRequestPriority());
546 EXPECT_EQ(OK
, callback_
.WaitForResult());
549 TEST_P(HttpProxyClientSocketPoolTest
, TCPError
) {
550 if (GetParam().proxy_type
== SPDY
) return;
551 data_
.reset(new SequencedSocketData(NULL
, 0, NULL
, 0));
552 data_
->set_connect_data(MockConnect(ASYNC
, ERR_CONNECTION_CLOSED
));
554 socket_factory()->AddSocketDataProvider(data_
.get());
556 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
557 callback_
.callback(), &pool_
, BoundNetLog());
558 EXPECT_EQ(ERR_IO_PENDING
, rv
);
559 EXPECT_FALSE(handle_
.is_initialized());
560 EXPECT_FALSE(handle_
.socket());
562 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED
, callback_
.WaitForResult());
564 EXPECT_FALSE(handle_
.is_initialized());
565 EXPECT_FALSE(handle_
.socket());
568 TEST_P(HttpProxyClientSocketPoolTest
, SSLError
) {
569 if (GetParam().proxy_type
== HTTP
) return;
570 data_
.reset(new SequencedSocketData(NULL
, 0, NULL
, 0));
571 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
572 socket_factory()->AddSocketDataProvider(data_
.get());
574 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
575 ERR_CERT_AUTHORITY_INVALID
));
576 if (GetParam().proxy_type
== SPDY
) {
579 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
581 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
582 callback_
.callback(), &pool_
, BoundNetLog());
583 EXPECT_EQ(ERR_IO_PENDING
, rv
);
584 EXPECT_FALSE(handle_
.is_initialized());
585 EXPECT_FALSE(handle_
.socket());
587 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID
, callback_
.WaitForResult());
589 EXPECT_FALSE(handle_
.is_initialized());
590 EXPECT_FALSE(handle_
.socket());
593 TEST_P(HttpProxyClientSocketPoolTest
, SslClientAuth
) {
594 if (GetParam().proxy_type
== HTTP
) return;
595 data_
.reset(new SequencedSocketData(NULL
, 0, NULL
, 0));
596 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
597 socket_factory()->AddSocketDataProvider(data_
.get());
599 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
600 ERR_SSL_CLIENT_AUTH_CERT_NEEDED
));
601 if (GetParam().proxy_type
== SPDY
) {
604 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
606 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
607 callback_
.callback(), &pool_
, BoundNetLog());
608 EXPECT_EQ(ERR_IO_PENDING
, rv
);
609 EXPECT_FALSE(handle_
.is_initialized());
610 EXPECT_FALSE(handle_
.socket());
612 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED
, callback_
.WaitForResult());
614 EXPECT_FALSE(handle_
.is_initialized());
615 EXPECT_FALSE(handle_
.socket());
618 TEST_P(HttpProxyClientSocketPoolTest
, TunnelUnexpectedClose
) {
619 MockWrite writes
[] = {
621 "CONNECT www.google.com:443 HTTP/1.1\r\n"
622 "Host: www.google.com\r\n"
623 "Proxy-Connection: keep-alive\r\n"
624 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
627 MockRead(ASYNC
, 1, "HTTP/1.1 200 Conn"),
628 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 2),
630 scoped_ptr
<SpdyFrame
> req(
631 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
632 HostPortPair("www.google.com", 443)));
633 MockWrite spdy_writes
[] = {
634 CreateMockWrite(*req
, 0, ASYNC
)
636 MockRead spdy_reads
[] = {
637 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 1),
640 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
641 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
642 arraysize(spdy_writes
));
645 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
646 callback_
.callback(), &pool_
, BoundNetLog());
647 EXPECT_EQ(ERR_IO_PENDING
, rv
);
648 EXPECT_FALSE(handle_
.is_initialized());
649 EXPECT_FALSE(handle_
.socket());
651 if (GetParam().proxy_type
== SPDY
) {
652 // SPDY cannot process a headers block unless it's complete and so it
653 // returns ERR_CONNECTION_CLOSED in this case.
654 EXPECT_EQ(ERR_CONNECTION_CLOSED
, callback_
.WaitForResult());
656 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED
, callback_
.WaitForResult());
658 EXPECT_FALSE(handle_
.is_initialized());
659 EXPECT_FALSE(handle_
.socket());
662 TEST_P(HttpProxyClientSocketPoolTest
, Tunnel1xxResponse
) {
663 // Tests that 1xx responses are rejected for a CONNECT request.
664 if (GetParam().proxy_type
== SPDY
) {
665 // SPDY doesn't have 1xx responses.
669 MockWrite writes
[] = {
671 "CONNECT www.google.com:443 HTTP/1.1\r\n"
672 "Host: www.google.com\r\n"
673 "Proxy-Connection: keep-alive\r\n\r\n"),
676 MockRead(ASYNC
, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
677 MockRead(ASYNC
, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
680 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
683 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
684 callback_
.callback(), &pool_
, BoundNetLog());
685 EXPECT_EQ(ERR_IO_PENDING
, rv
);
686 EXPECT_FALSE(handle_
.is_initialized());
687 EXPECT_FALSE(handle_
.socket());
689 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, callback_
.WaitForResult());
692 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupError
) {
693 MockWrite writes
[] = {
695 "CONNECT www.google.com:443 HTTP/1.1\r\n"
696 "Host: www.google.com\r\n"
697 "Proxy-Connection: keep-alive\r\n"
698 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
701 MockRead(ASYNC
, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
703 scoped_ptr
<SpdyFrame
> req(
704 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
705 HostPortPair("www.google.com", 443)));
706 scoped_ptr
<SpdyFrame
> rst(
707 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
708 MockWrite spdy_writes
[] = {
709 CreateMockWrite(*req
, 0, ASYNC
),
710 CreateMockWrite(*rst
, 2, ASYNC
),
712 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdySynReplyError(1));
713 MockRead spdy_reads
[] = {
714 CreateMockRead(*resp
, 1, ASYNC
),
715 MockRead(ASYNC
, 0, 3),
718 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
719 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
720 arraysize(spdy_writes
));
723 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
724 callback_
.callback(), &pool_
, BoundNetLog());
725 EXPECT_EQ(ERR_IO_PENDING
, rv
);
726 EXPECT_FALSE(handle_
.is_initialized());
727 EXPECT_FALSE(handle_
.socket());
729 rv
= callback_
.WaitForResult();
730 // All Proxy CONNECT responses are not trustworthy
731 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
732 EXPECT_FALSE(handle_
.is_initialized());
733 EXPECT_FALSE(handle_
.socket());
736 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupRedirect
) {
737 const std::string redirectTarget
= "https://foo.google.com/";
739 const std::string responseText
= "HTTP/1.1 302 Found\r\n"
740 "Location: " + redirectTarget
+ "\r\n"
741 "Set-Cookie: foo=bar\r\n"
743 MockWrite writes
[] = {
745 "CONNECT www.google.com:443 HTTP/1.1\r\n"
746 "Host: www.google.com\r\n"
747 "Proxy-Connection: keep-alive\r\n"
748 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
751 MockRead(ASYNC
, 1, responseText
.c_str()),
753 scoped_ptr
<SpdyFrame
> req(
754 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
,
755 HostPortPair("www.google.com", 443)));
756 scoped_ptr
<SpdyFrame
> rst(
757 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
759 MockWrite spdy_writes
[] = {
760 CreateMockWrite(*req
, 0, ASYNC
),
761 CreateMockWrite(*rst
, 3, ASYNC
),
764 const char* const responseHeaders
[] = {
765 "location", redirectTarget
.c_str(),
766 "set-cookie", "foo=bar",
768 const int responseHeadersSize
= arraysize(responseHeaders
) / 2;
769 scoped_ptr
<SpdyFrame
> resp(
770 spdy_util_
.ConstructSpdySynReplyError(
772 responseHeaders
, responseHeadersSize
,
774 MockRead spdy_reads
[] = {
775 CreateMockRead(*resp
, 1, ASYNC
),
776 MockRead(ASYNC
, 0, 2),
779 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
780 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
781 arraysize(spdy_writes
));
784 int rv
= handle_
.Init("a", CreateTunnelParams(NULL
), LOW
,
785 callback_
.callback(), &pool_
, BoundNetLog());
786 EXPECT_EQ(ERR_IO_PENDING
, rv
);
787 EXPECT_FALSE(handle_
.is_initialized());
788 EXPECT_FALSE(handle_
.socket());
790 rv
= callback_
.WaitForResult();
792 if (GetParam().proxy_type
== HTTP
) {
793 // We don't trust 302 responses to CONNECT from HTTP proxies.
794 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
795 EXPECT_FALSE(handle_
.is_initialized());
796 EXPECT_FALSE(handle_
.socket());
798 // Expect ProxyClientSocket to return the proxy's response, sanitized.
799 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE
, rv
);
800 EXPECT_TRUE(handle_
.is_initialized());
801 ASSERT_TRUE(handle_
.socket());
803 const ProxyClientSocket
* tunnel_socket
=
804 static_cast<ProxyClientSocket
*>(handle_
.socket());
805 const HttpResponseInfo
* response
= tunnel_socket
->GetConnectResponseInfo();
806 const HttpResponseHeaders
* headers
= response
->headers
.get();
808 // Make sure Set-Cookie header was stripped.
809 EXPECT_FALSE(headers
->HasHeader("set-cookie"));
811 // Make sure Content-Length: 0 header was added.
812 EXPECT_TRUE(headers
->HasHeaderValue("content-length", "0"));
814 // Make sure Location header was included and correct.
815 std::string location
;
816 EXPECT_TRUE(headers
->IsRedirect(&location
));
817 EXPECT_EQ(location
, redirectTarget
);
821 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.