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. 4 5#include "net/http/http_proxy_client_socket_pool.h" 6 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" 23 24namespace net { 25 26namespace { 27 28const int kMaxSockets = 32; 29const int kMaxSocketsPerGroup = 6; 30const char * const kAuthHeaders[] = { 31 "proxy-authorization", "Basic Zm9vOmJhcg==" 32}; 33const int kAuthHeadersSize = arraysize(kAuthHeaders) / 2; 34 35enum HttpProxyType { 36 HTTP, 37 HTTPS, 38 SPDY 39}; 40 41struct HttpProxyClientSocketPoolTestParams { 42 HttpProxyClientSocketPoolTestParams() 43 : proxy_type(HTTP), 44 protocol(kProtoSPDY2) {} 45 46 HttpProxyClientSocketPoolTestParams( 47 HttpProxyType proxy_type, 48 NextProto protocol) 49 : proxy_type(proxy_type), 50 protocol(protocol) {} 51 52 HttpProxyType proxy_type; 53 NextProto protocol; 54}; 55 56typedef ::testing::TestWithParam<HttpProxyType> TestWithHttpParam; 57 58} // namespace 59 60class HttpProxyClientSocketPoolTest 61 : public ::testing::TestWithParam<HttpProxyClientSocketPoolTestParams> { 62 protected: 63 HttpProxyClientSocketPoolTest() 64 : session_deps_(GetParam().protocol), 65 ssl_config_(), 66 ignored_transport_socket_params_( 67 new TransportSocketParams(HostPortPair("proxy", 80), 68 LOWEST, 69 false, 70 false, 71 OnHostResolutionCallback())), 72 ignored_ssl_socket_params_( 73 new SSLSocketParams(ignored_transport_socket_params_, 74 NULL, 75 NULL, 76 ProxyServer::SCHEME_DIRECT, 77 HostPortPair("www.google.com", 443), 78 ssl_config_, 79 kPrivacyModeDisabled, 80 0, 81 false, 82 false)), 83 tcp_histograms_("MockTCP"), 84 transport_socket_pool_( 85 kMaxSockets, 86 kMaxSocketsPerGroup, 87 &tcp_histograms_, 88 session_deps_.deterministic_socket_factory.get()), 89 ssl_histograms_("MockSSL"), 90 ssl_socket_pool_(kMaxSockets, 91 kMaxSocketsPerGroup, 92 &ssl_histograms_, 93 session_deps_.host_resolver.get(), 94 session_deps_.cert_verifier.get(), 95 NULL /* server_bound_cert_store */, 96 NULL /* transport_security_state */, 97 std::string() /* ssl_session_cache_shard */, 98 session_deps_.deterministic_socket_factory.get(), 99 &transport_socket_pool_, 100 NULL, 101 NULL, 102 session_deps_.ssl_config_service.get(), 103 BoundNetLog().net_log()), 104 session_(CreateNetworkSession()), 105 http_proxy_histograms_("HttpProxyUnitTest"), 106 spdy_util_(GetParam().protocol), 107 pool_(kMaxSockets, 108 kMaxSocketsPerGroup, 109 &http_proxy_histograms_, 110 NULL, 111 &transport_socket_pool_, 112 &ssl_socket_pool_, 113 NULL) {} 114 115 virtual ~HttpProxyClientSocketPoolTest() { 116 } 117 118 void AddAuthToCache() { 119 const base::string16 kFoo(ASCIIToUTF16("foo")); 120 const base::string16 kBar(ASCIIToUTF16("bar")); 121 GURL proxy_url(GetParam().proxy_type == HTTP ? "http://proxy" : "https://proxy:80"); 122 session_->http_auth_cache()->Add(proxy_url, 123 "MyRealm1", 124 HttpAuth::AUTH_SCHEME_BASIC, 125 "Basic realm=MyRealm1", 126 AuthCredentials(kFoo, kBar), 127 "/"); 128 } 129 130 scoped_refptr<TransportSocketParams> GetTcpParams() { 131 if (GetParam().proxy_type != HTTP) 132 return scoped_refptr<TransportSocketParams>(); 133 return ignored_transport_socket_params_; 134 } 135 136 scoped_refptr<SSLSocketParams> GetSslParams() { 137 if (GetParam().proxy_type == HTTP) 138 return scoped_refptr<SSLSocketParams>(); 139 return ignored_ssl_socket_params_; 140 } 141 142 // Returns the a correctly constructed HttpProxyParms 143 // for the HTTP or HTTPS proxy. 144 scoped_refptr<HttpProxySocketParams> GetParams(bool tunnel) { 145 return scoped_refptr<HttpProxySocketParams>(new HttpProxySocketParams( 146 GetTcpParams(), 147 GetSslParams(), 148 GURL(tunnel ? "https://www.google.com/" : "http://www.google.com"), 149 std::string(), 150 HostPortPair("www.google.com", tunnel ? 443 : 80), 151 session_->http_auth_cache(), 152 session_->http_auth_handler_factory(), 153 session_->spdy_session_pool(), 154 tunnel)); 155 } 156 157 scoped_refptr<HttpProxySocketParams> GetTunnelParams() { 158 return GetParams(true); 159 } 160 161 scoped_refptr<HttpProxySocketParams> GetNoTunnelParams() { 162 return GetParams(false); 163 } 164 165 DeterministicMockClientSocketFactory& socket_factory() { 166 return *session_deps_.deterministic_socket_factory.get(); 167 } 168 169 void Initialize(MockRead* reads, size_t reads_count, 170 MockWrite* writes, size_t writes_count, 171 MockRead* spdy_reads, size_t spdy_reads_count, 172 MockWrite* spdy_writes, size_t spdy_writes_count) { 173 if (GetParam().proxy_type == SPDY) { 174 data_.reset(new DeterministicSocketData(spdy_reads, spdy_reads_count, 175 spdy_writes, spdy_writes_count)); 176 } else { 177 data_.reset(new DeterministicSocketData(reads, reads_count, writes, 178 writes_count)); 179 } 180 181 data_->set_connect_data(MockConnect(SYNCHRONOUS, OK)); 182 data_->StopAfter(2); // Request / Response 183 184 socket_factory().AddSocketDataProvider(data_.get()); 185 186 if (GetParam().proxy_type != HTTP) { 187 ssl_data_.reset(new SSLSocketDataProvider(SYNCHRONOUS, OK)); 188 if (GetParam().proxy_type == SPDY) { 189 InitializeSpdySsl(); 190 } 191 socket_factory().AddSSLSocketDataProvider(ssl_data_.get()); 192 } 193 } 194 195 void InitializeSpdySsl() { 196 ssl_data_->SetNextProto(GetParam().protocol); 197 } 198 199 HttpNetworkSession* CreateNetworkSession() { 200 return SpdySessionDependencies::SpdyCreateSessionDeterministic( 201 &session_deps_); 202 } 203 204 private: 205 SpdySessionDependencies session_deps_; 206 SSLConfig ssl_config_; 207 208 scoped_refptr<TransportSocketParams> ignored_transport_socket_params_; 209 scoped_refptr<SSLSocketParams> ignored_ssl_socket_params_; 210 ClientSocketPoolHistograms tcp_histograms_; 211 MockTransportClientSocketPool transport_socket_pool_; 212 ClientSocketPoolHistograms ssl_histograms_; 213 MockHostResolver host_resolver_; 214 scoped_ptr<CertVerifier> cert_verifier_; 215 SSLClientSocketPool ssl_socket_pool_; 216 217 const scoped_refptr<HttpNetworkSession> session_; 218 ClientSocketPoolHistograms http_proxy_histograms_; 219 220 protected: 221 SpdyTestUtil spdy_util_; 222 scoped_ptr<SSLSocketDataProvider> ssl_data_; 223 scoped_ptr<DeterministicSocketData> data_; 224 HttpProxyClientSocketPool pool_; 225 ClientSocketHandle handle_; 226 TestCompletionCallback callback_; 227}; 228 229//----------------------------------------------------------------------------- 230// All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY) 231// and SPDY. 232// 233// TODO(akalin): Use ::testing::Combine() when we are able to use 234// <tr1/tuple>. 235INSTANTIATE_TEST_CASE_P( 236 HttpProxyClientSocketPoolTests, 237 HttpProxyClientSocketPoolTest, 238 ::testing::Values( 239 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY2), 240 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY2), 241 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY2), 242 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY3), 243 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY3), 244 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY3), 245 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY31), 246 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY31), 247 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY31), 248 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4a2), 249 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4a2), 250 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4a2), 251 HttpProxyClientSocketPoolTestParams(HTTP, kProtoHTTP2Draft04), 252 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoHTTP2Draft04), 253 HttpProxyClientSocketPoolTestParams(SPDY, kProtoHTTP2Draft04))); 254 255TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) { 256 Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0); 257 258 int rv = handle_.Init("a", GetNoTunnelParams(), LOW, CompletionCallback(), 259 &pool_, BoundNetLog()); 260 EXPECT_EQ(OK, rv); 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()); 266} 267 268TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) { 269 MockWrite writes[] = { 270 MockWrite(ASYNC, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n" 271 "Host: www.google.com\r\n" 272 "Proxy-Connection: keep-alive\r\n\r\n"), 273 }; 274 MockRead reads[] = { 275 // No credentials. 276 MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"), 277 MockRead(ASYNC, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"), 278 MockRead(ASYNC, 3, "Content-Length: 10\r\n\r\n"), 279 MockRead(ASYNC, 4, "0123456789"), 280 }; 281 scoped_ptr<SpdyFrame> req( 282 spdy_util_.ConstructSpdyConnect(NULL, 0, 1)); 283 scoped_ptr<SpdyFrame> rst( 284 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); 285 MockWrite spdy_writes[] = { 286 CreateMockWrite(*req, 0, ASYNC), 287 CreateMockWrite(*rst, 2, ASYNC), 288 }; 289 const char* const kAuthChallenge[] = { 290 spdy_util_.GetStatusKey(), "407 Proxy Authentication Required", 291 spdy_util_.GetVersionKey(), "HTTP/1.1", 292 "proxy-authenticate", "Basic realm=\"MyRealm1\"", 293 }; 294 scoped_ptr<SpdyFrame> resp( 295 spdy_util_.ConstructSpdyControlFrame(NULL, 296 0, 297 false, 298 1, 299 LOWEST, 300 SYN_REPLY, 301 CONTROL_FLAG_NONE, 302 kAuthChallenge, 303 arraysize(kAuthChallenge), 304 0)); 305 MockRead spdy_reads[] = { 306 CreateMockRead(*resp, 1, ASYNC), 307 MockRead(ASYNC, 0, 3) 308 }; 309 310 Initialize(reads, arraysize(reads), writes, arraysize(writes), 311 spdy_reads, arraysize(spdy_reads), spdy_writes, 312 arraysize(spdy_writes)); 313 314 data_->StopAfter(4); 315 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 316 &pool_, BoundNetLog()); 317 EXPECT_EQ(ERR_IO_PENDING, rv); 318 EXPECT_FALSE(handle_.is_initialized()); 319 EXPECT_FALSE(handle_.socket()); 320 321 data_->RunFor(GetParam().proxy_type == SPDY ? 2 : 4); 322 rv = callback_.WaitForResult(); 323 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, rv); 324 EXPECT_TRUE(handle_.is_initialized()); 325 ASSERT_TRUE(handle_.socket()); 326 ProxyClientSocket* tunnel_socket = 327 static_cast<ProxyClientSocket*>(handle_.socket()); 328 if (GetParam().proxy_type == SPDY) { 329 EXPECT_TRUE(tunnel_socket->IsConnected()); 330 EXPECT_TRUE(tunnel_socket->IsUsingSpdy()); 331 } else { 332 EXPECT_FALSE(tunnel_socket->IsConnected()); 333 EXPECT_FALSE(tunnel_socket->IsUsingSpdy()); 334 EXPECT_FALSE(tunnel_socket->IsUsingSpdy()); 335 } 336} 337 338TEST_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) 342 return; 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"), 349 }; 350 MockRead reads[] = { 351 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), 352 }; 353 354 Initialize(reads, arraysize(reads), writes, arraysize(writes), NULL, 0, 355 NULL, 0); 356 AddAuthToCache(); 357 358 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 359 &pool_, BoundNetLog()); 360 EXPECT_EQ(OK, rv); 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()); 366} 367 368TEST_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"), 374 }; 375 MockRead reads[] = { 376 MockRead(ASYNC, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), 377 }; 378 379 scoped_ptr<SpdyFrame> req( 380 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1)); 381 MockWrite spdy_writes[] = { 382 CreateMockWrite(*req, 0, ASYNC) 383 }; 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) 388 }; 389 390 Initialize(reads, arraysize(reads), writes, arraysize(writes), 391 spdy_reads, arraysize(spdy_reads), spdy_writes, 392 arraysize(spdy_writes)); 393 AddAuthToCache(); 394 395 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 396 &pool_, BoundNetLog()); 397 EXPECT_EQ(ERR_IO_PENDING, rv); 398 EXPECT_FALSE(handle_.is_initialized()); 399 EXPECT_FALSE(handle_.socket()); 400 401 data_->RunFor(2); 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()); 408} 409 410TEST_P(HttpProxyClientSocketPoolTest, TCPError) { 411 if (GetParam().proxy_type == SPDY) return; 412 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0)); 413 data_->set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_CLOSED)); 414 415 socket_factory().AddSocketDataProvider(data_.get()); 416 417 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 418 &pool_, BoundNetLog()); 419 EXPECT_EQ(ERR_IO_PENDING, rv); 420 EXPECT_FALSE(handle_.is_initialized()); 421 EXPECT_FALSE(handle_.socket()); 422 423 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, callback_.WaitForResult()); 424 425 EXPECT_FALSE(handle_.is_initialized()); 426 EXPECT_FALSE(handle_.socket()); 427} 428 429TEST_P(HttpProxyClientSocketPoolTest, SSLError) { 430 if (GetParam().proxy_type == HTTP) return; 431 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0)); 432 data_->set_connect_data(MockConnect(ASYNC, OK)); 433 socket_factory().AddSocketDataProvider(data_.get()); 434 435 ssl_data_.reset(new SSLSocketDataProvider(ASYNC, 436 ERR_CERT_AUTHORITY_INVALID)); 437 if (GetParam().proxy_type == SPDY) { 438 InitializeSpdySsl(); 439 } 440 socket_factory().AddSSLSocketDataProvider(ssl_data_.get()); 441 442 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 443 &pool_, BoundNetLog()); 444 EXPECT_EQ(ERR_IO_PENDING, rv); 445 EXPECT_FALSE(handle_.is_initialized()); 446 EXPECT_FALSE(handle_.socket()); 447 448 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID, callback_.WaitForResult()); 449 450 EXPECT_FALSE(handle_.is_initialized()); 451 EXPECT_FALSE(handle_.socket()); 452} 453 454TEST_P(HttpProxyClientSocketPoolTest, SslClientAuth) { 455 if (GetParam().proxy_type == HTTP) return; 456 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0)); 457 data_->set_connect_data(MockConnect(ASYNC, OK)); 458 socket_factory().AddSocketDataProvider(data_.get()); 459 460 ssl_data_.reset(new SSLSocketDataProvider(ASYNC, 461 ERR_SSL_CLIENT_AUTH_CERT_NEEDED)); 462 if (GetParam().proxy_type == SPDY) { 463 InitializeSpdySsl(); 464 } 465 socket_factory().AddSSLSocketDataProvider(ssl_data_.get()); 466 467 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 468 &pool_, BoundNetLog()); 469 EXPECT_EQ(ERR_IO_PENDING, rv); 470 EXPECT_FALSE(handle_.is_initialized()); 471 EXPECT_FALSE(handle_.socket()); 472 473 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, callback_.WaitForResult()); 474 475 EXPECT_FALSE(handle_.is_initialized()); 476 EXPECT_FALSE(handle_.socket()); 477} 478 479TEST_P(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) { 480 MockWrite writes[] = { 481 MockWrite(ASYNC, 0, 482 "CONNECT www.google.com:443 HTTP/1.1\r\n" 483 "Host: www.google.com\r\n" 484 "Proxy-Connection: keep-alive\r\n" 485 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), 486 }; 487 MockRead reads[] = { 488 MockRead(ASYNC, 1, "HTTP/1.1 200 Conn"), 489 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2), 490 }; 491 scoped_ptr<SpdyFrame> req( 492 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1)); 493 MockWrite spdy_writes[] = { 494 CreateMockWrite(*req, 0, ASYNC) 495 }; 496 MockRead spdy_reads[] = { 497 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1), 498 }; 499 500 Initialize(reads, arraysize(reads), writes, arraysize(writes), 501 spdy_reads, arraysize(spdy_reads), spdy_writes, 502 arraysize(spdy_writes)); 503 AddAuthToCache(); 504 505 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 506 &pool_, BoundNetLog()); 507 EXPECT_EQ(ERR_IO_PENDING, rv); 508 EXPECT_FALSE(handle_.is_initialized()); 509 EXPECT_FALSE(handle_.socket()); 510 511 data_->RunFor(3); 512 if (GetParam().proxy_type == SPDY) { 513 // SPDY cannot process a headers block unless it's complete and so it 514 // returns ERR_CONNECTION_CLOSED in this case. 515 EXPECT_EQ(ERR_CONNECTION_CLOSED, callback_.WaitForResult()); 516 } else { 517 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, callback_.WaitForResult()); 518 } 519 EXPECT_FALSE(handle_.is_initialized()); 520 EXPECT_FALSE(handle_.socket()); 521} 522 523TEST_P(HttpProxyClientSocketPoolTest, Tunnel1xxResponse) { 524 // Tests that 1xx responses are rejected for a CONNECT request. 525 if (GetParam().proxy_type == SPDY) { 526 // SPDY doesn't have 1xx responses. 527 return; 528 } 529 530 MockWrite writes[] = { 531 MockWrite(ASYNC, 0, 532 "CONNECT www.google.com:443 HTTP/1.1\r\n" 533 "Host: www.google.com\r\n" 534 "Proxy-Connection: keep-alive\r\n\r\n"), 535 }; 536 MockRead reads[] = { 537 MockRead(ASYNC, 1, "HTTP/1.1 100 Continue\r\n\r\n"), 538 MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"), 539 }; 540 541 Initialize(reads, arraysize(reads), writes, arraysize(writes), 542 NULL, 0, NULL, 0); 543 544 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(), 545 &pool_, BoundNetLog()); 546 EXPECT_EQ(ERR_IO_PENDING, rv); 547 EXPECT_FALSE(handle_.is_initialized()); 548 EXPECT_FALSE(handle_.socket()); 549 550 data_->RunFor(2); 551 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult()); 552} 553 554TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) { 555 MockWrite writes[] = { 556 MockWrite(ASYNC, 0, 557 "CONNECT www.google.com:443 HTTP/1.1\r\n" 558 "Host: www.google.com\r\n" 559 "Proxy-Connection: keep-alive\r\n" 560 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), 561 }; 562 MockRead reads[] = { 563 MockRead(ASYNC, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"), 564 }; 565 scoped_ptr<SpdyFrame> req( 566 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1)); 567 scoped_ptr<SpdyFrame> rst( 568 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); 569 MockWrite spdy_writes[] = { 570 CreateMockWrite(*req, 0, ASYNC), 571 CreateMockWrite(*rst, 2, ASYNC), 572 }; 573 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdySynReplyError(1)); 574 MockRead spdy_reads[] = { 575 CreateMockRead(*resp, 1, ASYNC), 576 MockRead(ASYNC, 0, 3), 577 }; 578 579 Initialize(reads, arraysize(reads), writes, arraysize(writes), 580 spdy_reads, arraysize(spdy_reads), spdy_writes, 581 arraysize(spdy_writes)); 582 AddAuthToCache(); 583 584 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 585 &pool_, BoundNetLog()); 586 EXPECT_EQ(ERR_IO_PENDING, rv); 587 EXPECT_FALSE(handle_.is_initialized()); 588 EXPECT_FALSE(handle_.socket()); 589 590 data_->RunFor(2); 591 592 rv = callback_.WaitForResult(); 593 // All Proxy CONNECT responses are not trustworthy 594 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv); 595 EXPECT_FALSE(handle_.is_initialized()); 596 EXPECT_FALSE(handle_.socket()); 597} 598 599TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupRedirect) { 600 const std::string redirectTarget = "https://foo.google.com/"; 601 602 const std::string responseText = "HTTP/1.1 302 Found\r\n" 603 "Location: " + redirectTarget + "\r\n" 604 "Set-Cookie: foo=bar\r\n" 605 "\r\n"; 606 MockWrite writes[] = { 607 MockWrite(ASYNC, 0, 608 "CONNECT www.google.com:443 HTTP/1.1\r\n" 609 "Host: www.google.com\r\n" 610 "Proxy-Connection: keep-alive\r\n" 611 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), 612 }; 613 MockRead reads[] = { 614 MockRead(ASYNC, 1, responseText.c_str()), 615 }; 616 scoped_ptr<SpdyFrame> req( 617 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1)); 618 scoped_ptr<SpdyFrame> rst( 619 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); 620 621 MockWrite spdy_writes[] = { 622 CreateMockWrite(*req, 0, ASYNC), 623 CreateMockWrite(*rst, 3, ASYNC), 624 }; 625 626 const char* const responseHeaders[] = { 627 "location", redirectTarget.c_str(), 628 "set-cookie", "foo=bar", 629 }; 630 const int responseHeadersSize = arraysize(responseHeaders) / 2; 631 scoped_ptr<SpdyFrame> resp( 632 spdy_util_.ConstructSpdySynReplyError( 633 "302 Found", 634 responseHeaders, responseHeadersSize, 635 1)); 636 MockRead spdy_reads[] = { 637 CreateMockRead(*resp, 1, ASYNC), 638 MockRead(ASYNC, 0, 2), 639 }; 640 641 Initialize(reads, arraysize(reads), writes, arraysize(writes), 642 spdy_reads, arraysize(spdy_reads), spdy_writes, 643 arraysize(spdy_writes)); 644 AddAuthToCache(); 645 646 int rv = handle_.Init("a", GetTunnelParams(), LOW, callback_.callback(), 647 &pool_, BoundNetLog()); 648 EXPECT_EQ(ERR_IO_PENDING, rv); 649 EXPECT_FALSE(handle_.is_initialized()); 650 EXPECT_FALSE(handle_.socket()); 651 652 data_->RunFor(2); 653 654 rv = callback_.WaitForResult(); 655 656 if (GetParam().proxy_type == HTTP) { 657 // We don't trust 302 responses to CONNECT from HTTP proxies. 658 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv); 659 EXPECT_FALSE(handle_.is_initialized()); 660 EXPECT_FALSE(handle_.socket()); 661 } else { 662 // Expect ProxyClientSocket to return the proxy's response, sanitized. 663 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE, rv); 664 EXPECT_TRUE(handle_.is_initialized()); 665 ASSERT_TRUE(handle_.socket()); 666 667 const ProxyClientSocket* tunnel_socket = 668 static_cast<ProxyClientSocket*>(handle_.socket()); 669 const HttpResponseInfo* response = tunnel_socket->GetConnectResponseInfo(); 670 const HttpResponseHeaders* headers = response->headers.get(); 671 672 // Make sure Set-Cookie header was stripped. 673 EXPECT_FALSE(headers->HasHeader("set-cookie")); 674 675 // Make sure Content-Length: 0 header was added. 676 EXPECT_TRUE(headers->HasHeaderValue("content-length", "0")); 677 678 // Make sure Location header was included and correct. 679 std::string location; 680 EXPECT_TRUE(headers->IsRedirect(&location)); 681 EXPECT_EQ(location, redirectTarget); 682 } 683} 684 685// It would be nice to also test the timeouts in HttpProxyClientSocketPool. 686 687} // namespace net 688