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(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, 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 PRIVACY_MODE_DISABLED, 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, kProtoSPDY4), 252 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4), 253 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4))); 254 255 TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) { 256 Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0); 257 258 int rv = handle_.Init("a", CreateNoTunnelParams(), 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 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); 272 EXPECT_EQ(OK, 273 handle_.Init("a", CreateNoTunnelParams(), HIGHEST, 274 CompletionCallback(), &pool_, BoundNetLog())); 275 EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority()); 276 } 277 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"), 283 }; 284 MockRead reads[] = { 285 // No credentials. 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"), 290 }; 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), 298 }; 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\"", 303 }; 304 scoped_ptr<SpdyFrame> resp( 305 spdy_util_.ConstructSpdyControlFrame(NULL, 306 0, 307 false, 308 1, 309 LOW, 310 SYN_REPLY, 311 CONTROL_FLAG_NONE, 312 kAuthChallenge, 313 arraysize(kAuthChallenge), 314 0)); 315 MockRead spdy_reads[] = { 316 CreateMockRead(*resp, 1, ASYNC), 317 MockRead(ASYNC, 0, 3) 318 }; 319 320 Initialize(reads, arraysize(reads), writes, arraysize(writes), 321 spdy_reads, arraysize(spdy_reads), spdy_writes, 322 arraysize(spdy_writes)); 323 324 data_->StopAfter(4); 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()); 330 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()); 341 } else { 342 EXPECT_FALSE(tunnel_socket->IsConnected()); 343 EXPECT_FALSE(tunnel_socket->IsUsingSpdy()); 344 } 345 } 346 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) 351 return; 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"), 358 }; 359 MockRead reads[] = { 360 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), 361 }; 362 363 Initialize(reads, arraysize(reads), writes, arraysize(writes), NULL, 0, 364 NULL, 0); 365 AddAuthToCache(); 366 367 int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(), 368 &pool_, BoundNetLog()); 369 EXPECT_EQ(OK, rv); 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()); 375 } 376 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"), 383 }; 384 MockRead reads[] = { 385 MockRead(ASYNC, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), 386 }; 387 388 scoped_ptr<SpdyFrame> req( 389 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW)); 390 MockWrite spdy_writes[] = { 391 CreateMockWrite(*req, 0, ASYNC) 392 }; 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) 397 }; 398 399 Initialize(reads, arraysize(reads), writes, arraysize(writes), 400 spdy_reads, arraysize(spdy_reads), spdy_writes, 401 arraysize(spdy_writes)); 402 AddAuthToCache(); 403 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()); 409 410 data_->RunFor(2); 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()); 417 } 418 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) 424 return; 425 426 scoped_ptr<SpdyFrame> req( 427 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 428 1, MEDIUM)); 429 MockWrite spdy_writes[] = { 430 CreateMockWrite(*req, 0, ASYNC) 431 }; 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) 436 }; 437 438 Initialize(NULL, 0, NULL, 0, 439 spdy_reads, arraysize(spdy_reads), 440 spdy_writes, arraysize(spdy_writes)); 441 AddAuthToCache(); 442 443 EXPECT_EQ(ERR_IO_PENDING, 444 handle_.Init("a", CreateTunnelParams(), MEDIUM, 445 callback_.callback(), &pool_, BoundNetLog())); 446 EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority()); 447 448 data_->RunFor(2); 449 EXPECT_EQ(OK, callback_.WaitForResult()); 450 } 451 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)); 456 457 socket_factory()->AddSocketDataProvider(data_.get()); 458 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()); 464 465 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, callback_.WaitForResult()); 466 467 EXPECT_FALSE(handle_.is_initialized()); 468 EXPECT_FALSE(handle_.socket()); 469 } 470 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()); 476 477 ssl_data_.reset(new SSLSocketDataProvider(ASYNC, 478 ERR_CERT_AUTHORITY_INVALID)); 479 if (GetParam().proxy_type == SPDY) { 480 InitializeSpdySsl(); 481 } 482 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); 483 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()); 489 490 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID, callback_.WaitForResult()); 491 492 EXPECT_FALSE(handle_.is_initialized()); 493 EXPECT_FALSE(handle_.socket()); 494 } 495 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()); 501 502 ssl_data_.reset(new SSLSocketDataProvider(ASYNC, 503 ERR_SSL_CLIENT_AUTH_CERT_NEEDED)); 504 if (GetParam().proxy_type == SPDY) { 505 InitializeSpdySsl(); 506 } 507 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); 508 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()); 514 515 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, callback_.WaitForResult()); 516 517 EXPECT_FALSE(handle_.is_initialized()); 518 EXPECT_FALSE(handle_.socket()); 519 } 520 521 TEST_P(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) { 522 MockWrite writes[] = { 523 MockWrite(ASYNC, 0, 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"), 528 }; 529 MockRead reads[] = { 530 MockRead(ASYNC, 1, "HTTP/1.1 200 Conn"), 531 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2), 532 }; 533 scoped_ptr<SpdyFrame> req( 534 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW)); 535 MockWrite spdy_writes[] = { 536 CreateMockWrite(*req, 0, ASYNC) 537 }; 538 MockRead spdy_reads[] = { 539 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1), 540 }; 541 542 Initialize(reads, arraysize(reads), writes, arraysize(writes), 543 spdy_reads, arraysize(spdy_reads), spdy_writes, 544 arraysize(spdy_writes)); 545 AddAuthToCache(); 546 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()); 552 553 data_->RunFor(3); 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()); 558 } else { 559 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, callback_.WaitForResult()); 560 } 561 EXPECT_FALSE(handle_.is_initialized()); 562 EXPECT_FALSE(handle_.socket()); 563 } 564 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. 569 return; 570 } 571 572 MockWrite writes[] = { 573 MockWrite(ASYNC, 0, 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"), 577 }; 578 MockRead reads[] = { 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"), 581 }; 582 583 Initialize(reads, arraysize(reads), writes, arraysize(writes), 584 NULL, 0, NULL, 0); 585 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()); 591 592 data_->RunFor(2); 593 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult()); 594 } 595 596 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) { 597 MockWrite writes[] = { 598 MockWrite(ASYNC, 0, 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"), 603 }; 604 MockRead reads[] = { 605 MockRead(ASYNC, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"), 606 }; 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), 614 }; 615 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdySynReplyError(1)); 616 MockRead spdy_reads[] = { 617 CreateMockRead(*resp, 1, ASYNC), 618 MockRead(ASYNC, 0, 3), 619 }; 620 621 Initialize(reads, arraysize(reads), writes, arraysize(writes), 622 spdy_reads, arraysize(spdy_reads), spdy_writes, 623 arraysize(spdy_writes)); 624 AddAuthToCache(); 625 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()); 631 632 data_->RunFor(2); 633 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()); 639 } 640 641 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupRedirect) { 642 const std::string redirectTarget = "https://foo.google.com/"; 643 644 const std::string responseText = "HTTP/1.1 302 Found\r\n" 645 "Location: " + redirectTarget + "\r\n" 646 "Set-Cookie: foo=bar\r\n" 647 "\r\n"; 648 MockWrite writes[] = { 649 MockWrite(ASYNC, 0, 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"), 654 }; 655 MockRead reads[] = { 656 MockRead(ASYNC, 1, responseText.c_str()), 657 }; 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)); 662 663 MockWrite spdy_writes[] = { 664 CreateMockWrite(*req, 0, ASYNC), 665 CreateMockWrite(*rst, 3, ASYNC), 666 }; 667 668 const char* const responseHeaders[] = { 669 "location", redirectTarget.c_str(), 670 "set-cookie", "foo=bar", 671 }; 672 const int responseHeadersSize = arraysize(responseHeaders) / 2; 673 scoped_ptr<SpdyFrame> resp( 674 spdy_util_.ConstructSpdySynReplyError( 675 "302 Found", 676 responseHeaders, responseHeadersSize, 677 1)); 678 MockRead spdy_reads[] = { 679 CreateMockRead(*resp, 1, ASYNC), 680 MockRead(ASYNC, 0, 2), 681 }; 682 683 Initialize(reads, arraysize(reads), writes, arraysize(writes), 684 spdy_reads, arraysize(spdy_reads), spdy_writes, 685 arraysize(spdy_writes)); 686 AddAuthToCache(); 687 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()); 693 694 data_->RunFor(2); 695 696 rv = callback_.WaitForResult(); 697 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()); 703 } else { 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()); 708 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(); 713 714 // Make sure Set-Cookie header was stripped. 715 EXPECT_FALSE(headers->HasHeader("set-cookie")); 716 717 // Make sure Content-Length: 0 header was added. 718 EXPECT_TRUE(headers->HasHeaderValue("content-length", "0")); 719 720 // Make sure Location header was included and correct. 721 std::string location; 722 EXPECT_TRUE(headers->IsRedirect(&location)); 723 EXPECT_EQ(location, redirectTarget); 724 } 725 } 726 727 // It would be nice to also test the timeouts in HttpProxyClientSocketPool. 728 729 } // namespace 730 731 } // namespace net 732