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