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 /* server_bound_cert_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(),
87 BoundNetLog().net_log()),
88 session_(CreateNetworkSession()),
89 http_proxy_histograms_("HttpProxyUnitTest"),
90 spdy_util_(GetParam().protocol),
93 &http_proxy_histograms_,
95 &transport_socket_pool_,
99 virtual ~HttpProxyClientSocketPoolTest() {
102 void AddAuthToCache() {
103 const base::string16 kFoo(base::ASCIIToUTF16("foo"));
104 const base::string16 kBar(base::ASCIIToUTF16("bar"));
105 GURL proxy_url(GetParam().proxy_type == HTTP ?
106 (std::string("http://") + kHttpProxyHost) :
107 (std::string("https://") + kHttpsProxyHost));
108 session_->http_auth_cache()->Add(proxy_url,
110 HttpAuth::AUTH_SCHEME_BASIC,
111 "Basic realm=MyRealm1",
112 AuthCredentials(kFoo, kBar),
116 scoped_refptr<TransportSocketParams> CreateHttpProxyParams() const {
117 if (GetParam().proxy_type != HTTP)
119 return new TransportSocketParams(HostPortPair(kHttpProxyHost, 80),
122 OnHostResolutionCallback());
125 scoped_refptr<SSLSocketParams> CreateHttpsProxyParams() const {
126 if (GetParam().proxy_type == HTTP)
128 return new SSLSocketParams(
129 new TransportSocketParams(
130 HostPortPair(kHttpsProxyHost, 443),
133 OnHostResolutionCallback()),
136 HostPortPair(kHttpsProxyHost, 443),
138 PRIVACY_MODE_DISABLED,
144 // Returns the a correctly constructed HttpProxyParms
145 // for the HTTP or HTTPS proxy.
146 scoped_refptr<HttpProxySocketParams> CreateParams(bool tunnel) {
147 return scoped_refptr<HttpProxySocketParams>(new HttpProxySocketParams(
148 CreateHttpProxyParams(),
149 CreateHttpsProxyParams(),
150 GURL(tunnel ? "https://www.google.com/" : "http://www.google.com"),
152 HostPortPair("www.google.com", tunnel ? 443 : 80),
153 session_->http_auth_cache(),
154 session_->http_auth_handler_factory(),
155 session_->spdy_session_pool(),
159 scoped_refptr<HttpProxySocketParams> CreateTunnelParams() {
160 return CreateParams(true);
163 scoped_refptr<HttpProxySocketParams> CreateNoTunnelParams() {
164 return CreateParams(false);
167 DeterministicMockClientSocketFactory* socket_factory() {
168 return session_deps_.deterministic_socket_factory.get();
171 void Initialize(MockRead* reads, size_t reads_count,
172 MockWrite* writes, size_t writes_count,
173 MockRead* spdy_reads, size_t spdy_reads_count,
174 MockWrite* spdy_writes, size_t spdy_writes_count) {
175 if (GetParam().proxy_type == SPDY) {
176 data_.reset(new DeterministicSocketData(spdy_reads, spdy_reads_count,
177 spdy_writes, spdy_writes_count));
179 data_.reset(new DeterministicSocketData(reads, reads_count, writes,
183 data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
184 data_->StopAfter(2); // Request / Response
186 socket_factory()->AddSocketDataProvider(data_.get());
188 if (GetParam().proxy_type != HTTP) {
189 ssl_data_.reset(new SSLSocketDataProvider(SYNCHRONOUS, OK));
190 if (GetParam().proxy_type == SPDY) {
193 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get());
197 void InitializeSpdySsl() {
198 ssl_data_->SetNextProto(GetParam().protocol);
201 HttpNetworkSession* CreateNetworkSession() {
202 return SpdySessionDependencies::SpdyCreateSessionDeterministic(
206 RequestPriority GetLastTransportRequestPriority() const {
207 return transport_socket_pool_.last_request_priority();
211 SpdySessionDependencies session_deps_;
213 ClientSocketPoolHistograms tcp_histograms_;
214 MockTransportClientSocketPool transport_socket_pool_;
215 ClientSocketPoolHistograms ssl_histograms_;
216 MockHostResolver host_resolver_;
217 scoped_ptr<CertVerifier> cert_verifier_;
218 SSLClientSocketPool ssl_socket_pool_;
220 const scoped_refptr<HttpNetworkSession> session_;
221 ClientSocketPoolHistograms http_proxy_histograms_;
224 SpdyTestUtil spdy_util_;
225 scoped_ptr<SSLSocketDataProvider> ssl_data_;
226 scoped_ptr<DeterministicSocketData> data_;
227 HttpProxyClientSocketPool pool_;
228 ClientSocketHandle handle_;
229 TestCompletionCallback callback_;
232 //-----------------------------------------------------------------------------
233 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
236 // TODO(akalin): Use ::testing::Combine() when we are able to use
238 INSTANTIATE_TEST_CASE_P(
239 HttpProxyClientSocketPoolTests,
240 HttpProxyClientSocketPoolTest,
242 HttpProxyClientSocketPoolTestParams(HTTP, kProtoDeprecatedSPDY2),
243 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoDeprecatedSPDY2),
244 HttpProxyClientSocketPoolTestParams(SPDY, kProtoDeprecatedSPDY2),
245 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY3),
246 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY3),
247 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY3),
248 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY31),
249 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY31),
250 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY31),
251 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4),
252 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4),
253 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4)));
255 TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) {
256 Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0);
258 int rv = handle_.Init("a", CreateNoTunnelParams(), LOW, CompletionCallback(),
259 &pool_, BoundNetLog());
261 EXPECT_TRUE(handle_.is_initialized());
262 ASSERT_TRUE(handle_.socket());
263 HttpProxyClientSocket* tunnel_socket =
264 static_cast<HttpProxyClientSocket*>(handle_.socket());
265 EXPECT_TRUE(tunnel_socket->IsConnected());
268 // Make sure that HttpProxyConnectJob passes on its priority to its
269 // (non-SSL) socket request on Init.
270 TEST_P(HttpProxyClientSocketPoolTest, SetSocketRequestPriorityOnInit) {
271 Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0);
273 handle_.Init("a", CreateNoTunnelParams(), HIGHEST,
274 CompletionCallback(), &pool_, BoundNetLog()));
275 EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority());
278 TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) {
279 MockWrite writes[] = {
280 MockWrite(ASYNC, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
281 "Host: www.google.com\r\n"
282 "Proxy-Connection: keep-alive\r\n\r\n"),
286 MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
287 MockRead(ASYNC, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
288 MockRead(ASYNC, 3, "Content-Length: 10\r\n\r\n"),
289 MockRead(ASYNC, 4, "0123456789"),
291 scoped_ptr<SpdyFrame> req(
292 spdy_util_.ConstructSpdyConnect(NULL, 0, 1, LOW));
293 scoped_ptr<SpdyFrame> rst(
294 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
295 MockWrite spdy_writes[] = {
296 CreateMockWrite(*req, 0, ASYNC),
297 CreateMockWrite(*rst, 2, ASYNC),
299 const char* const kAuthChallenge[] = {
300 spdy_util_.GetStatusKey(), "407 Proxy Authentication Required",
301 spdy_util_.GetVersionKey(), "HTTP/1.1",
302 "proxy-authenticate", "Basic realm=\"MyRealm1\"",
304 scoped_ptr<SpdyFrame> resp(
305 spdy_util_.ConstructSpdyControlFrame(NULL,
313 arraysize(kAuthChallenge),
315 MockRead spdy_reads[] = {
316 CreateMockRead(*resp, 1, ASYNC),
317 MockRead(ASYNC, 0, 3)
320 Initialize(reads, arraysize(reads), writes, arraysize(writes),
321 spdy_reads, arraysize(spdy_reads), spdy_writes,
322 arraysize(spdy_writes));
325 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
326 &pool_, BoundNetLog());
327 EXPECT_EQ(ERR_IO_PENDING, rv);
328 EXPECT_FALSE(handle_.is_initialized());
329 EXPECT_FALSE(handle_.socket());
331 data_->RunFor(GetParam().proxy_type == SPDY ? 2 : 4);
332 rv = callback_.WaitForResult();
333 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, rv);
334 EXPECT_TRUE(handle_.is_initialized());
335 ASSERT_TRUE(handle_.socket());
336 ProxyClientSocket* tunnel_socket =
337 static_cast<ProxyClientSocket*>(handle_.socket());
338 if (GetParam().proxy_type == SPDY) {
339 EXPECT_TRUE(tunnel_socket->IsConnected());
340 EXPECT_TRUE(tunnel_socket->IsUsingSpdy());
342 EXPECT_FALSE(tunnel_socket->IsConnected());
343 EXPECT_FALSE(tunnel_socket->IsUsingSpdy());
347 TEST_P(HttpProxyClientSocketPoolTest, HaveAuth) {
348 // It's pretty much impossible to make the SPDY case behave synchronously
349 // so we skip this test for SPDY
350 if (GetParam().proxy_type == SPDY)
352 MockWrite writes[] = {
353 MockWrite(SYNCHRONOUS, 0,
354 "CONNECT www.google.com:443 HTTP/1.1\r\n"
355 "Host: www.google.com\r\n"
356 "Proxy-Connection: keep-alive\r\n"
357 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
360 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
363 Initialize(reads, arraysize(reads), writes, arraysize(writes), NULL, 0,
367 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
368 &pool_, BoundNetLog());
370 EXPECT_TRUE(handle_.is_initialized());
371 ASSERT_TRUE(handle_.socket());
372 HttpProxyClientSocket* tunnel_socket =
373 static_cast<HttpProxyClientSocket*>(handle_.socket());
374 EXPECT_TRUE(tunnel_socket->IsConnected());
377 TEST_P(HttpProxyClientSocketPoolTest, AsyncHaveAuth) {
378 MockWrite writes[] = {
379 MockWrite(ASYNC, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
380 "Host: www.google.com\r\n"
381 "Proxy-Connection: keep-alive\r\n"
382 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
385 MockRead(ASYNC, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
388 scoped_ptr<SpdyFrame> req(
389 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
390 MockWrite spdy_writes[] = {
391 CreateMockWrite(*req, 0, ASYNC)
393 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
394 MockRead spdy_reads[] = {
395 CreateMockRead(*resp, 1, ASYNC),
396 MockRead(ASYNC, 0, 2)
399 Initialize(reads, arraysize(reads), writes, arraysize(writes),
400 spdy_reads, arraysize(spdy_reads), spdy_writes,
401 arraysize(spdy_writes));
404 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
405 &pool_, BoundNetLog());
406 EXPECT_EQ(ERR_IO_PENDING, rv);
407 EXPECT_FALSE(handle_.is_initialized());
408 EXPECT_FALSE(handle_.socket());
411 EXPECT_EQ(OK, callback_.WaitForResult());
412 EXPECT_TRUE(handle_.is_initialized());
413 ASSERT_TRUE(handle_.socket());
414 HttpProxyClientSocket* tunnel_socket =
415 static_cast<HttpProxyClientSocket*>(handle_.socket());
416 EXPECT_TRUE(tunnel_socket->IsConnected());
419 // Make sure that HttpProxyConnectJob passes on its priority to its
420 // SPDY session's socket request on Init (if applicable).
421 TEST_P(HttpProxyClientSocketPoolTest,
422 SetSpdySessionSocketRequestPriorityOnInit) {
423 if (GetParam().proxy_type != SPDY)
426 scoped_ptr<SpdyFrame> req(
427 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize,
429 MockWrite spdy_writes[] = {
430 CreateMockWrite(*req, 0, ASYNC)
432 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
433 MockRead spdy_reads[] = {
434 CreateMockRead(*resp, 1, ASYNC),
435 MockRead(ASYNC, 0, 2)
438 Initialize(NULL, 0, NULL, 0,
439 spdy_reads, arraysize(spdy_reads),
440 spdy_writes, arraysize(spdy_writes));
443 EXPECT_EQ(ERR_IO_PENDING,
444 handle_.Init("a", CreateTunnelParams(), MEDIUM,
445 callback_.callback(), &pool_, BoundNetLog()));
446 EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority());
449 EXPECT_EQ(OK, callback_.WaitForResult());
452 TEST_P(HttpProxyClientSocketPoolTest, TCPError) {
453 if (GetParam().proxy_type == SPDY) return;
454 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0));
455 data_->set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_CLOSED));
457 socket_factory()->AddSocketDataProvider(data_.get());
459 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
460 &pool_, BoundNetLog());
461 EXPECT_EQ(ERR_IO_PENDING, rv);
462 EXPECT_FALSE(handle_.is_initialized());
463 EXPECT_FALSE(handle_.socket());
465 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, callback_.WaitForResult());
467 EXPECT_FALSE(handle_.is_initialized());
468 EXPECT_FALSE(handle_.socket());
471 TEST_P(HttpProxyClientSocketPoolTest, SSLError) {
472 if (GetParam().proxy_type == HTTP) return;
473 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0));
474 data_->set_connect_data(MockConnect(ASYNC, OK));
475 socket_factory()->AddSocketDataProvider(data_.get());
477 ssl_data_.reset(new SSLSocketDataProvider(ASYNC,
478 ERR_CERT_AUTHORITY_INVALID));
479 if (GetParam().proxy_type == SPDY) {
482 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get());
484 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
485 &pool_, BoundNetLog());
486 EXPECT_EQ(ERR_IO_PENDING, rv);
487 EXPECT_FALSE(handle_.is_initialized());
488 EXPECT_FALSE(handle_.socket());
490 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID, callback_.WaitForResult());
492 EXPECT_FALSE(handle_.is_initialized());
493 EXPECT_FALSE(handle_.socket());
496 TEST_P(HttpProxyClientSocketPoolTest, SslClientAuth) {
497 if (GetParam().proxy_type == HTTP) return;
498 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0));
499 data_->set_connect_data(MockConnect(ASYNC, OK));
500 socket_factory()->AddSocketDataProvider(data_.get());
502 ssl_data_.reset(new SSLSocketDataProvider(ASYNC,
503 ERR_SSL_CLIENT_AUTH_CERT_NEEDED));
504 if (GetParam().proxy_type == SPDY) {
507 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get());
509 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
510 &pool_, BoundNetLog());
511 EXPECT_EQ(ERR_IO_PENDING, rv);
512 EXPECT_FALSE(handle_.is_initialized());
513 EXPECT_FALSE(handle_.socket());
515 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, callback_.WaitForResult());
517 EXPECT_FALSE(handle_.is_initialized());
518 EXPECT_FALSE(handle_.socket());
521 TEST_P(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) {
522 MockWrite writes[] = {
524 "CONNECT www.google.com:443 HTTP/1.1\r\n"
525 "Host: www.google.com\r\n"
526 "Proxy-Connection: keep-alive\r\n"
527 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
530 MockRead(ASYNC, 1, "HTTP/1.1 200 Conn"),
531 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2),
533 scoped_ptr<SpdyFrame> req(
534 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
535 MockWrite spdy_writes[] = {
536 CreateMockWrite(*req, 0, ASYNC)
538 MockRead spdy_reads[] = {
539 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1),
542 Initialize(reads, arraysize(reads), writes, arraysize(writes),
543 spdy_reads, arraysize(spdy_reads), spdy_writes,
544 arraysize(spdy_writes));
547 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
548 &pool_, BoundNetLog());
549 EXPECT_EQ(ERR_IO_PENDING, rv);
550 EXPECT_FALSE(handle_.is_initialized());
551 EXPECT_FALSE(handle_.socket());
554 if (GetParam().proxy_type == SPDY) {
555 // SPDY cannot process a headers block unless it's complete and so it
556 // returns ERR_CONNECTION_CLOSED in this case.
557 EXPECT_EQ(ERR_CONNECTION_CLOSED, callback_.WaitForResult());
559 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, callback_.WaitForResult());
561 EXPECT_FALSE(handle_.is_initialized());
562 EXPECT_FALSE(handle_.socket());
565 TEST_P(HttpProxyClientSocketPoolTest, Tunnel1xxResponse) {
566 // Tests that 1xx responses are rejected for a CONNECT request.
567 if (GetParam().proxy_type == SPDY) {
568 // SPDY doesn't have 1xx responses.
572 MockWrite writes[] = {
574 "CONNECT www.google.com:443 HTTP/1.1\r\n"
575 "Host: www.google.com\r\n"
576 "Proxy-Connection: keep-alive\r\n\r\n"),
579 MockRead(ASYNC, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
580 MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
583 Initialize(reads, arraysize(reads), writes, arraysize(writes),
586 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
587 &pool_, BoundNetLog());
588 EXPECT_EQ(ERR_IO_PENDING, rv);
589 EXPECT_FALSE(handle_.is_initialized());
590 EXPECT_FALSE(handle_.socket());
593 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
596 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) {
597 MockWrite writes[] = {
599 "CONNECT www.google.com:443 HTTP/1.1\r\n"
600 "Host: www.google.com\r\n"
601 "Proxy-Connection: keep-alive\r\n"
602 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
605 MockRead(ASYNC, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
607 scoped_ptr<SpdyFrame> req(
608 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
609 scoped_ptr<SpdyFrame> rst(
610 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
611 MockWrite spdy_writes[] = {
612 CreateMockWrite(*req, 0, ASYNC),
613 CreateMockWrite(*rst, 2, ASYNC),
615 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdySynReplyError(1));
616 MockRead spdy_reads[] = {
617 CreateMockRead(*resp, 1, ASYNC),
618 MockRead(ASYNC, 0, 3),
621 Initialize(reads, arraysize(reads), writes, arraysize(writes),
622 spdy_reads, arraysize(spdy_reads), spdy_writes,
623 arraysize(spdy_writes));
626 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
627 &pool_, BoundNetLog());
628 EXPECT_EQ(ERR_IO_PENDING, rv);
629 EXPECT_FALSE(handle_.is_initialized());
630 EXPECT_FALSE(handle_.socket());
634 rv = callback_.WaitForResult();
635 // All Proxy CONNECT responses are not trustworthy
636 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
637 EXPECT_FALSE(handle_.is_initialized());
638 EXPECT_FALSE(handle_.socket());
641 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupRedirect) {
642 const std::string redirectTarget = "https://foo.google.com/";
644 const std::string responseText = "HTTP/1.1 302 Found\r\n"
645 "Location: " + redirectTarget + "\r\n"
646 "Set-Cookie: foo=bar\r\n"
648 MockWrite writes[] = {
650 "CONNECT www.google.com:443 HTTP/1.1\r\n"
651 "Host: www.google.com\r\n"
652 "Proxy-Connection: keep-alive\r\n"
653 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
656 MockRead(ASYNC, 1, responseText.c_str()),
658 scoped_ptr<SpdyFrame> req(
659 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
660 scoped_ptr<SpdyFrame> rst(
661 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
663 MockWrite spdy_writes[] = {
664 CreateMockWrite(*req, 0, ASYNC),
665 CreateMockWrite(*rst, 3, ASYNC),
668 const char* const responseHeaders[] = {
669 "location", redirectTarget.c_str(),
670 "set-cookie", "foo=bar",
672 const int responseHeadersSize = arraysize(responseHeaders) / 2;
673 scoped_ptr<SpdyFrame> resp(
674 spdy_util_.ConstructSpdySynReplyError(
676 responseHeaders, responseHeadersSize,
678 MockRead spdy_reads[] = {
679 CreateMockRead(*resp, 1, ASYNC),
680 MockRead(ASYNC, 0, 2),
683 Initialize(reads, arraysize(reads), writes, arraysize(writes),
684 spdy_reads, arraysize(spdy_reads), spdy_writes,
685 arraysize(spdy_writes));
688 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
689 &pool_, BoundNetLog());
690 EXPECT_EQ(ERR_IO_PENDING, rv);
691 EXPECT_FALSE(handle_.is_initialized());
692 EXPECT_FALSE(handle_.socket());
696 rv = callback_.WaitForResult();
698 if (GetParam().proxy_type == HTTP) {
699 // We don't trust 302 responses to CONNECT from HTTP proxies.
700 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
701 EXPECT_FALSE(handle_.is_initialized());
702 EXPECT_FALSE(handle_.socket());
704 // Expect ProxyClientSocket to return the proxy's response, sanitized.
705 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE, rv);
706 EXPECT_TRUE(handle_.is_initialized());
707 ASSERT_TRUE(handle_.socket());
709 const ProxyClientSocket* tunnel_socket =
710 static_cast<ProxyClientSocket*>(handle_.socket());
711 const HttpResponseInfo* response = tunnel_socket->GetConnectResponseInfo();
712 const HttpResponseHeaders* headers = response->headers.get();
714 // Make sure Set-Cookie header was stripped.
715 EXPECT_FALSE(headers->HasHeader("set-cookie"));
717 // Make sure Content-Length: 0 header was added.
718 EXPECT_TRUE(headers->HasHeaderValue("content-length", "0"));
720 // Make sure Location header was included and correct.
721 std::string location;
722 EXPECT_TRUE(headers->IsRedirect(&location));
723 EXPECT_EQ(location, redirectTarget);
727 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.