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/test_completion_callback.h"
13 #include "net/http/http_network_session.h"
14 #include "net/http/http_proxy_client_socket.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/socket/client_socket_handle.h"
17 #include "net/socket/client_socket_pool_histograms.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(kProtoSPDY3
) {}
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 HttpProxyClientSocketPoolTest
62 : public ::testing::TestWithParam
<HttpProxyClientSocketPoolTestParams
> {
64 HttpProxyClientSocketPoolTest()
65 : session_deps_(GetParam().protocol
),
66 tcp_histograms_("MockTCP"),
67 transport_socket_pool_(
71 session_deps_
.deterministic_socket_factory
.get()),
72 ssl_histograms_("MockSSL"),
73 ssl_socket_pool_(kMaxSockets
,
76 session_deps_
.host_resolver
.get(),
77 session_deps_
.cert_verifier
.get(),
78 NULL
/* channel_id_store */,
79 NULL
/* transport_security_state */,
80 NULL
/* cert_transparency_verifier */,
81 std::string() /* ssl_session_cache_shard */,
82 session_deps_
.deterministic_socket_factory
.get(),
83 &transport_socket_pool_
,
86 session_deps_
.ssl_config_service
.get(),
88 BoundNetLog().net_log()),
89 session_(CreateNetworkSession()),
90 http_proxy_histograms_("HttpProxyUnitTest"),
91 spdy_util_(GetParam().protocol
),
94 &http_proxy_histograms_
,
96 &transport_socket_pool_
,
100 virtual ~HttpProxyClientSocketPoolTest() {
103 void AddAuthToCache() {
104 const base::string16
kFoo(base::ASCIIToUTF16("foo"));
105 const base::string16
kBar(base::ASCIIToUTF16("bar"));
106 GURL
proxy_url(GetParam().proxy_type
== HTTP
?
107 (std::string("http://") + kHttpProxyHost
) :
108 (std::string("https://") + kHttpsProxyHost
));
109 session_
->http_auth_cache()->Add(proxy_url
,
111 HttpAuth::AUTH_SCHEME_BASIC
,
112 "Basic realm=MyRealm1",
113 AuthCredentials(kFoo
, kBar
),
117 scoped_refptr
<TransportSocketParams
> CreateHttpProxyParams() const {
118 if (GetParam().proxy_type
!= HTTP
)
120 return new TransportSocketParams(HostPortPair(kHttpProxyHost
, 80),
123 OnHostResolutionCallback());
126 scoped_refptr
<SSLSocketParams
> CreateHttpsProxyParams() const {
127 if (GetParam().proxy_type
== HTTP
)
129 return new SSLSocketParams(
130 new TransportSocketParams(
131 HostPortPair(kHttpsProxyHost
, 443),
134 OnHostResolutionCallback()),
137 HostPortPair(kHttpsProxyHost
, 443),
139 PRIVACY_MODE_DISABLED
,
145 // Returns the a correctly constructed HttpProxyParms
146 // for the HTTP or HTTPS proxy.
147 scoped_refptr
<HttpProxySocketParams
> CreateParams(bool tunnel
) {
148 return scoped_refptr
<HttpProxySocketParams
>(new HttpProxySocketParams(
149 CreateHttpProxyParams(),
150 CreateHttpsProxyParams(),
151 GURL(tunnel
? "https://www.google.com/" : "http://www.google.com"),
153 HostPortPair("www.google.com", tunnel
? 443 : 80),
154 session_
->http_auth_cache(),
155 session_
->http_auth_handler_factory(),
156 session_
->spdy_session_pool(),
160 scoped_refptr
<HttpProxySocketParams
> CreateTunnelParams() {
161 return CreateParams(true);
164 scoped_refptr
<HttpProxySocketParams
> CreateNoTunnelParams() {
165 return CreateParams(false);
168 DeterministicMockClientSocketFactory
* socket_factory() {
169 return session_deps_
.deterministic_socket_factory
.get();
172 void Initialize(MockRead
* reads
, size_t reads_count
,
173 MockWrite
* writes
, size_t writes_count
,
174 MockRead
* spdy_reads
, size_t spdy_reads_count
,
175 MockWrite
* spdy_writes
, size_t spdy_writes_count
) {
176 if (GetParam().proxy_type
== SPDY
) {
177 data_
.reset(new DeterministicSocketData(spdy_reads
, spdy_reads_count
,
178 spdy_writes
, spdy_writes_count
));
180 data_
.reset(new DeterministicSocketData(reads
, reads_count
, writes
,
184 data_
->set_connect_data(MockConnect(SYNCHRONOUS
, OK
));
185 data_
->StopAfter(2); // Request / Response
187 socket_factory()->AddSocketDataProvider(data_
.get());
189 if (GetParam().proxy_type
!= HTTP
) {
190 ssl_data_
.reset(new SSLSocketDataProvider(SYNCHRONOUS
, OK
));
191 if (GetParam().proxy_type
== SPDY
) {
194 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
198 void InitializeSpdySsl() {
199 ssl_data_
->SetNextProto(GetParam().protocol
);
202 HttpNetworkSession
* CreateNetworkSession() {
203 return SpdySessionDependencies::SpdyCreateSessionDeterministic(
207 RequestPriority
GetLastTransportRequestPriority() const {
208 return transport_socket_pool_
.last_request_priority();
212 SpdySessionDependencies session_deps_
;
214 ClientSocketPoolHistograms tcp_histograms_
;
215 MockTransportClientSocketPool transport_socket_pool_
;
216 ClientSocketPoolHistograms ssl_histograms_
;
217 MockHostResolver host_resolver_
;
218 scoped_ptr
<CertVerifier
> cert_verifier_
;
219 SSLClientSocketPool ssl_socket_pool_
;
221 const scoped_refptr
<HttpNetworkSession
> session_
;
222 ClientSocketPoolHistograms http_proxy_histograms_
;
225 SpdyTestUtil spdy_util_
;
226 scoped_ptr
<SSLSocketDataProvider
> ssl_data_
;
227 scoped_ptr
<DeterministicSocketData
> data_
;
228 HttpProxyClientSocketPool pool_
;
229 ClientSocketHandle handle_
;
230 TestCompletionCallback callback_
;
233 //-----------------------------------------------------------------------------
234 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
237 // TODO(akalin): Use ::testing::Combine() when we are able to use
239 INSTANTIATE_TEST_CASE_P(
240 HttpProxyClientSocketPoolTests
,
241 HttpProxyClientSocketPoolTest
,
243 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoDeprecatedSPDY2
),
244 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoDeprecatedSPDY2
),
245 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoDeprecatedSPDY2
),
246 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY3
),
247 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY3
),
248 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY3
),
249 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY31
),
250 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY31
),
251 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY31
),
252 HttpProxyClientSocketPoolTestParams(HTTP
, kProtoSPDY4
),
253 HttpProxyClientSocketPoolTestParams(HTTPS
, kProtoSPDY4
),
254 HttpProxyClientSocketPoolTestParams(SPDY
, kProtoSPDY4
)));
256 TEST_P(HttpProxyClientSocketPoolTest
, NoTunnel
) {
257 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
259 int rv
= handle_
.Init("a", CreateNoTunnelParams(), LOW
, CompletionCallback(),
260 &pool_
, BoundNetLog());
262 EXPECT_TRUE(handle_
.is_initialized());
263 ASSERT_TRUE(handle_
.socket());
264 HttpProxyClientSocket
* tunnel_socket
=
265 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
266 EXPECT_TRUE(tunnel_socket
->IsConnected());
269 // Make sure that HttpProxyConnectJob passes on its priority to its
270 // (non-SSL) socket request on Init.
271 TEST_P(HttpProxyClientSocketPoolTest
, SetSocketRequestPriorityOnInit
) {
272 Initialize(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0);
274 handle_
.Init("a", CreateNoTunnelParams(), HIGHEST
,
275 CompletionCallback(), &pool_
, BoundNetLog()));
276 EXPECT_EQ(HIGHEST
, GetLastTransportRequestPriority());
279 TEST_P(HttpProxyClientSocketPoolTest
, NeedAuth
) {
280 MockWrite writes
[] = {
281 MockWrite(ASYNC
, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
282 "Host: www.google.com\r\n"
283 "Proxy-Connection: keep-alive\r\n\r\n"),
287 MockRead(ASYNC
, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
288 MockRead(ASYNC
, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
289 MockRead(ASYNC
, 3, "Content-Length: 10\r\n\r\n"),
290 MockRead(ASYNC
, 4, "0123456789"),
292 scoped_ptr
<SpdyFrame
> req(
293 spdy_util_
.ConstructSpdyConnect(NULL
, 0, 1, LOW
));
294 scoped_ptr
<SpdyFrame
> rst(
295 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
296 MockWrite spdy_writes
[] = {
297 CreateMockWrite(*req
, 0, ASYNC
),
298 CreateMockWrite(*rst
, 2, ASYNC
),
300 SpdyHeaderBlock resp_block
;
301 resp_block
[spdy_util_
.GetStatusKey()] = "407";
302 resp_block
["proxy-authenticate"] = "Basic realm=\"MyRealm1\"";
303 spdy_util_
.MaybeAddVersionHeader(&resp_block
);
305 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyReply(1, resp_block
));
306 MockRead spdy_reads
[] = {
307 CreateMockRead(*resp
, 1, ASYNC
),
308 MockRead(ASYNC
, 0, 3)
311 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
312 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
313 arraysize(spdy_writes
));
316 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
317 &pool_
, BoundNetLog());
318 EXPECT_EQ(ERR_IO_PENDING
, rv
);
319 EXPECT_FALSE(handle_
.is_initialized());
320 EXPECT_FALSE(handle_
.socket());
322 data_
->RunFor(GetParam().proxy_type
== SPDY
? 2 : 4);
323 rv
= callback_
.WaitForResult();
324 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED
, rv
);
325 EXPECT_TRUE(handle_
.is_initialized());
326 ASSERT_TRUE(handle_
.socket());
327 ProxyClientSocket
* tunnel_socket
=
328 static_cast<ProxyClientSocket
*>(handle_
.socket());
329 if (GetParam().proxy_type
== SPDY
) {
330 EXPECT_TRUE(tunnel_socket
->IsConnected());
331 EXPECT_TRUE(tunnel_socket
->IsUsingSpdy());
333 EXPECT_FALSE(tunnel_socket
->IsConnected());
334 EXPECT_FALSE(tunnel_socket
->IsUsingSpdy());
338 TEST_P(HttpProxyClientSocketPoolTest
, HaveAuth
) {
339 // It's pretty much impossible to make the SPDY case behave synchronously
340 // so we skip this test for SPDY
341 if (GetParam().proxy_type
== SPDY
)
343 MockWrite writes
[] = {
344 MockWrite(SYNCHRONOUS
, 0,
345 "CONNECT www.google.com:443 HTTP/1.1\r\n"
346 "Host: www.google.com\r\n"
347 "Proxy-Connection: keep-alive\r\n"
348 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
351 MockRead(SYNCHRONOUS
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
354 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
), NULL
, 0,
358 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
359 &pool_
, BoundNetLog());
361 EXPECT_TRUE(handle_
.is_initialized());
362 ASSERT_TRUE(handle_
.socket());
363 HttpProxyClientSocket
* tunnel_socket
=
364 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
365 EXPECT_TRUE(tunnel_socket
->IsConnected());
368 TEST_P(HttpProxyClientSocketPoolTest
, AsyncHaveAuth
) {
369 MockWrite writes
[] = {
370 MockWrite(ASYNC
, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
371 "Host: www.google.com\r\n"
372 "Proxy-Connection: keep-alive\r\n"
373 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
376 MockRead(ASYNC
, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
379 scoped_ptr
<SpdyFrame
> req(
380 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
381 MockWrite spdy_writes
[] = {
382 CreateMockWrite(*req
, 0, ASYNC
)
384 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
385 MockRead spdy_reads
[] = {
386 CreateMockRead(*resp
, 1, ASYNC
),
387 MockRead(ASYNC
, 0, 2)
390 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
391 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
392 arraysize(spdy_writes
));
395 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
396 &pool_
, BoundNetLog());
397 EXPECT_EQ(ERR_IO_PENDING
, rv
);
398 EXPECT_FALSE(handle_
.is_initialized());
399 EXPECT_FALSE(handle_
.socket());
402 EXPECT_EQ(OK
, callback_
.WaitForResult());
403 EXPECT_TRUE(handle_
.is_initialized());
404 ASSERT_TRUE(handle_
.socket());
405 HttpProxyClientSocket
* tunnel_socket
=
406 static_cast<HttpProxyClientSocket
*>(handle_
.socket());
407 EXPECT_TRUE(tunnel_socket
->IsConnected());
410 // Make sure that HttpProxyConnectJob passes on its priority to its
411 // SPDY session's socket request on Init (if applicable).
412 TEST_P(HttpProxyClientSocketPoolTest
,
413 SetSpdySessionSocketRequestPriorityOnInit
) {
414 if (GetParam().proxy_type
!= SPDY
)
417 scoped_ptr
<SpdyFrame
> req(
418 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
,
420 MockWrite spdy_writes
[] = {
421 CreateMockWrite(*req
, 0, ASYNC
)
423 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
424 MockRead spdy_reads
[] = {
425 CreateMockRead(*resp
, 1, ASYNC
),
426 MockRead(ASYNC
, 0, 2)
429 Initialize(NULL
, 0, NULL
, 0,
430 spdy_reads
, arraysize(spdy_reads
),
431 spdy_writes
, arraysize(spdy_writes
));
434 EXPECT_EQ(ERR_IO_PENDING
,
435 handle_
.Init("a", CreateTunnelParams(), MEDIUM
,
436 callback_
.callback(), &pool_
, BoundNetLog()));
437 EXPECT_EQ(MEDIUM
, GetLastTransportRequestPriority());
440 EXPECT_EQ(OK
, callback_
.WaitForResult());
443 TEST_P(HttpProxyClientSocketPoolTest
, TCPError
) {
444 if (GetParam().proxy_type
== SPDY
) return;
445 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
446 data_
->set_connect_data(MockConnect(ASYNC
, ERR_CONNECTION_CLOSED
));
448 socket_factory()->AddSocketDataProvider(data_
.get());
450 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
451 &pool_
, BoundNetLog());
452 EXPECT_EQ(ERR_IO_PENDING
, rv
);
453 EXPECT_FALSE(handle_
.is_initialized());
454 EXPECT_FALSE(handle_
.socket());
456 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED
, callback_
.WaitForResult());
458 EXPECT_FALSE(handle_
.is_initialized());
459 EXPECT_FALSE(handle_
.socket());
462 TEST_P(HttpProxyClientSocketPoolTest
, SSLError
) {
463 if (GetParam().proxy_type
== HTTP
) return;
464 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
465 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
466 socket_factory()->AddSocketDataProvider(data_
.get());
468 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
469 ERR_CERT_AUTHORITY_INVALID
));
470 if (GetParam().proxy_type
== SPDY
) {
473 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
475 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
476 &pool_
, BoundNetLog());
477 EXPECT_EQ(ERR_IO_PENDING
, rv
);
478 EXPECT_FALSE(handle_
.is_initialized());
479 EXPECT_FALSE(handle_
.socket());
481 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID
, callback_
.WaitForResult());
483 EXPECT_FALSE(handle_
.is_initialized());
484 EXPECT_FALSE(handle_
.socket());
487 TEST_P(HttpProxyClientSocketPoolTest
, SslClientAuth
) {
488 if (GetParam().proxy_type
== HTTP
) return;
489 data_
.reset(new DeterministicSocketData(NULL
, 0, NULL
, 0));
490 data_
->set_connect_data(MockConnect(ASYNC
, OK
));
491 socket_factory()->AddSocketDataProvider(data_
.get());
493 ssl_data_
.reset(new SSLSocketDataProvider(ASYNC
,
494 ERR_SSL_CLIENT_AUTH_CERT_NEEDED
));
495 if (GetParam().proxy_type
== SPDY
) {
498 socket_factory()->AddSSLSocketDataProvider(ssl_data_
.get());
500 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
501 &pool_
, BoundNetLog());
502 EXPECT_EQ(ERR_IO_PENDING
, rv
);
503 EXPECT_FALSE(handle_
.is_initialized());
504 EXPECT_FALSE(handle_
.socket());
506 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED
, callback_
.WaitForResult());
508 EXPECT_FALSE(handle_
.is_initialized());
509 EXPECT_FALSE(handle_
.socket());
512 TEST_P(HttpProxyClientSocketPoolTest
, TunnelUnexpectedClose
) {
513 MockWrite writes
[] = {
515 "CONNECT www.google.com:443 HTTP/1.1\r\n"
516 "Host: www.google.com\r\n"
517 "Proxy-Connection: keep-alive\r\n"
518 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
521 MockRead(ASYNC
, 1, "HTTP/1.1 200 Conn"),
522 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 2),
524 scoped_ptr
<SpdyFrame
> req(
525 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
526 MockWrite spdy_writes
[] = {
527 CreateMockWrite(*req
, 0, ASYNC
)
529 MockRead spdy_reads
[] = {
530 MockRead(ASYNC
, ERR_CONNECTION_CLOSED
, 1),
533 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
534 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
535 arraysize(spdy_writes
));
538 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
539 &pool_
, BoundNetLog());
540 EXPECT_EQ(ERR_IO_PENDING
, rv
);
541 EXPECT_FALSE(handle_
.is_initialized());
542 EXPECT_FALSE(handle_
.socket());
545 if (GetParam().proxy_type
== SPDY
) {
546 // SPDY cannot process a headers block unless it's complete and so it
547 // returns ERR_CONNECTION_CLOSED in this case.
548 EXPECT_EQ(ERR_CONNECTION_CLOSED
, callback_
.WaitForResult());
550 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED
, callback_
.WaitForResult());
552 EXPECT_FALSE(handle_
.is_initialized());
553 EXPECT_FALSE(handle_
.socket());
556 TEST_P(HttpProxyClientSocketPoolTest
, Tunnel1xxResponse
) {
557 // Tests that 1xx responses are rejected for a CONNECT request.
558 if (GetParam().proxy_type
== SPDY
) {
559 // SPDY doesn't have 1xx responses.
563 MockWrite writes
[] = {
565 "CONNECT www.google.com:443 HTTP/1.1\r\n"
566 "Host: www.google.com\r\n"
567 "Proxy-Connection: keep-alive\r\n\r\n"),
570 MockRead(ASYNC
, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
571 MockRead(ASYNC
, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
574 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
577 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
578 &pool_
, BoundNetLog());
579 EXPECT_EQ(ERR_IO_PENDING
, rv
);
580 EXPECT_FALSE(handle_
.is_initialized());
581 EXPECT_FALSE(handle_
.socket());
584 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, callback_
.WaitForResult());
587 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupError
) {
588 MockWrite writes
[] = {
590 "CONNECT www.google.com:443 HTTP/1.1\r\n"
591 "Host: www.google.com\r\n"
592 "Proxy-Connection: keep-alive\r\n"
593 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
596 MockRead(ASYNC
, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
598 scoped_ptr
<SpdyFrame
> req(
599 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
600 scoped_ptr
<SpdyFrame
> rst(
601 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
602 MockWrite spdy_writes
[] = {
603 CreateMockWrite(*req
, 0, ASYNC
),
604 CreateMockWrite(*rst
, 2, ASYNC
),
606 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdySynReplyError(1));
607 MockRead spdy_reads
[] = {
608 CreateMockRead(*resp
, 1, ASYNC
),
609 MockRead(ASYNC
, 0, 3),
612 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
613 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
614 arraysize(spdy_writes
));
617 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
618 &pool_
, BoundNetLog());
619 EXPECT_EQ(ERR_IO_PENDING
, rv
);
620 EXPECT_FALSE(handle_
.is_initialized());
621 EXPECT_FALSE(handle_
.socket());
625 rv
= callback_
.WaitForResult();
626 // All Proxy CONNECT responses are not trustworthy
627 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
628 EXPECT_FALSE(handle_
.is_initialized());
629 EXPECT_FALSE(handle_
.socket());
632 TEST_P(HttpProxyClientSocketPoolTest
, TunnelSetupRedirect
) {
633 const std::string redirectTarget
= "https://foo.google.com/";
635 const std::string responseText
= "HTTP/1.1 302 Found\r\n"
636 "Location: " + redirectTarget
+ "\r\n"
637 "Set-Cookie: foo=bar\r\n"
639 MockWrite writes
[] = {
641 "CONNECT www.google.com:443 HTTP/1.1\r\n"
642 "Host: www.google.com\r\n"
643 "Proxy-Connection: keep-alive\r\n"
644 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
647 MockRead(ASYNC
, 1, responseText
.c_str()),
649 scoped_ptr
<SpdyFrame
> req(
650 spdy_util_
.ConstructSpdyConnect(kAuthHeaders
, kAuthHeadersSize
, 1, LOW
));
651 scoped_ptr
<SpdyFrame
> rst(
652 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_CANCEL
));
654 MockWrite spdy_writes
[] = {
655 CreateMockWrite(*req
, 0, ASYNC
),
656 CreateMockWrite(*rst
, 3, ASYNC
),
659 const char* const responseHeaders
[] = {
660 "location", redirectTarget
.c_str(),
661 "set-cookie", "foo=bar",
663 const int responseHeadersSize
= arraysize(responseHeaders
) / 2;
664 scoped_ptr
<SpdyFrame
> resp(
665 spdy_util_
.ConstructSpdySynReplyError(
667 responseHeaders
, responseHeadersSize
,
669 MockRead spdy_reads
[] = {
670 CreateMockRead(*resp
, 1, ASYNC
),
671 MockRead(ASYNC
, 0, 2),
674 Initialize(reads
, arraysize(reads
), writes
, arraysize(writes
),
675 spdy_reads
, arraysize(spdy_reads
), spdy_writes
,
676 arraysize(spdy_writes
));
679 int rv
= handle_
.Init("a", CreateTunnelParams(), LOW
, callback_
.callback(),
680 &pool_
, BoundNetLog());
681 EXPECT_EQ(ERR_IO_PENDING
, rv
);
682 EXPECT_FALSE(handle_
.is_initialized());
683 EXPECT_FALSE(handle_
.socket());
687 rv
= callback_
.WaitForResult();
689 if (GetParam().proxy_type
== HTTP
) {
690 // We don't trust 302 responses to CONNECT from HTTP proxies.
691 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED
, rv
);
692 EXPECT_FALSE(handle_
.is_initialized());
693 EXPECT_FALSE(handle_
.socket());
695 // Expect ProxyClientSocket to return the proxy's response, sanitized.
696 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE
, rv
);
697 EXPECT_TRUE(handle_
.is_initialized());
698 ASSERT_TRUE(handle_
.socket());
700 const ProxyClientSocket
* tunnel_socket
=
701 static_cast<ProxyClientSocket
*>(handle_
.socket());
702 const HttpResponseInfo
* response
= tunnel_socket
->GetConnectResponseInfo();
703 const HttpResponseHeaders
* headers
= response
->headers
.get();
705 // Make sure Set-Cookie header was stripped.
706 EXPECT_FALSE(headers
->HasHeader("set-cookie"));
708 // Make sure Content-Length: 0 header was added.
709 EXPECT_TRUE(headers
->HasHeaderValue("content-length", "0"));
711 // Make sure Location header was included and correct.
712 std::string location
;
713 EXPECT_TRUE(headers
->IsRedirect(&location
));
714 EXPECT_EQ(location
, redirectTarget
);
718 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.