| OLD | NEW |
| (Empty) |
| 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/proxy_delegate.h" | |
| 13 #include "net/base/test_completion_callback.h" | |
| 14 #include "net/http/http_network_session.h" | |
| 15 #include "net/http/http_proxy_client_socket.h" | |
| 16 #include "net/http/http_response_headers.h" | |
| 17 #include "net/socket/client_socket_handle.h" | |
| 18 #include "net/socket/client_socket_pool_histograms.h" | |
| 19 #include "net/socket/next_proto.h" | |
| 20 #include "net/socket/socket_test_util.h" | |
| 21 #include "net/spdy/spdy_protocol.h" | |
| 22 #include "net/spdy/spdy_test_util_common.h" | |
| 23 #include "testing/gtest/include/gtest/gtest.h" | |
| 24 | |
| 25 namespace net { | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 const int kMaxSockets = 32; | |
| 30 const int kMaxSocketsPerGroup = 6; | |
| 31 const char * const kAuthHeaders[] = { | |
| 32 "proxy-authorization", "Basic Zm9vOmJhcg==" | |
| 33 }; | |
| 34 const int kAuthHeadersSize = arraysize(kAuthHeaders) / 2; | |
| 35 | |
| 36 enum HttpProxyType { | |
| 37 HTTP, | |
| 38 HTTPS, | |
| 39 SPDY | |
| 40 }; | |
| 41 | |
| 42 struct HttpProxyClientSocketPoolTestParams { | |
| 43 HttpProxyClientSocketPoolTestParams() | |
| 44 : proxy_type(HTTP), | |
| 45 protocol(kProtoSPDY31) {} | |
| 46 | |
| 47 HttpProxyClientSocketPoolTestParams( | |
| 48 HttpProxyType proxy_type, | |
| 49 NextProto protocol) | |
| 50 : proxy_type(proxy_type), | |
| 51 protocol(protocol) {} | |
| 52 | |
| 53 HttpProxyType proxy_type; | |
| 54 NextProto protocol; | |
| 55 }; | |
| 56 | |
| 57 typedef ::testing::TestWithParam<HttpProxyType> TestWithHttpParam; | |
| 58 | |
| 59 const char kHttpProxyHost[] = "httpproxy.example.com"; | |
| 60 const char kHttpsProxyHost[] = "httpsproxy.example.com"; | |
| 61 | |
| 62 class TestProxyDelegate : public ProxyDelegate { | |
| 63 public: | |
| 64 TestProxyDelegate() | |
| 65 : on_before_tunnel_request_called_(false), | |
| 66 on_tunnel_request_completed_called_(false), | |
| 67 on_tunnel_headers_received_called_(false) { | |
| 68 } | |
| 69 | |
| 70 ~TestProxyDelegate() override {} | |
| 71 | |
| 72 bool on_before_tunnel_request_called() const { | |
| 73 return on_before_tunnel_request_called_; | |
| 74 } | |
| 75 | |
| 76 bool on_tunnel_request_completed_called() const { | |
| 77 return on_tunnel_request_completed_called_; | |
| 78 } | |
| 79 | |
| 80 bool on_tunnel_headers_received_called() const { | |
| 81 return on_tunnel_headers_received_called_; | |
| 82 } | |
| 83 | |
| 84 void VerifyOnTunnelRequestCompleted(const std::string& endpoint, | |
| 85 const std::string& proxy_server) const { | |
| 86 EXPECT_TRUE(on_tunnel_request_completed_called_); | |
| 87 EXPECT_TRUE(HostPortPair::FromString(endpoint).Equals( | |
| 88 on_tunnel_request_completed_endpoint_)); | |
| 89 EXPECT_TRUE(HostPortPair::FromString(proxy_server).Equals( | |
| 90 on_tunnel_request_completed_proxy_server_)); | |
| 91 } | |
| 92 | |
| 93 void VerifyOnTunnelHeadersReceived(const std::string& origin, | |
| 94 const std::string& proxy_server, | |
| 95 const std::string& status_line) const { | |
| 96 EXPECT_TRUE(on_tunnel_headers_received_called_); | |
| 97 EXPECT_TRUE(HostPortPair::FromString(origin).Equals( | |
| 98 on_tunnel_headers_received_origin_)); | |
| 99 EXPECT_TRUE(HostPortPair::FromString(proxy_server).Equals( | |
| 100 on_tunnel_headers_received_proxy_server_)); | |
| 101 EXPECT_EQ(status_line, on_tunnel_headers_received_status_line_); | |
| 102 } | |
| 103 | |
| 104 // ProxyDelegate: | |
| 105 void OnResolveProxy(const GURL& url, | |
| 106 int load_flags, | |
| 107 const ProxyService& proxy_service, | |
| 108 ProxyInfo* result) override {} | |
| 109 | |
| 110 void OnTunnelConnectCompleted(const HostPortPair& endpoint, | |
| 111 const HostPortPair& proxy_server, | |
| 112 int net_error) override { | |
| 113 on_tunnel_request_completed_called_ = true; | |
| 114 on_tunnel_request_completed_endpoint_ = endpoint; | |
| 115 on_tunnel_request_completed_proxy_server_ = proxy_server; | |
| 116 } | |
| 117 | |
| 118 void OnFallback(const ProxyServer& bad_proxy, int net_error) override {} | |
| 119 | |
| 120 void OnBeforeSendHeaders(URLRequest* request, | |
| 121 const ProxyInfo& proxy_info, | |
| 122 HttpRequestHeaders* headers) override {} | |
| 123 | |
| 124 void OnBeforeTunnelRequest(const net::HostPortPair& proxy_server, | |
| 125 net::HttpRequestHeaders* extra_headers) override { | |
| 126 on_before_tunnel_request_called_ = true; | |
| 127 if (extra_headers) { | |
| 128 extra_headers->SetHeader("Foo", proxy_server.ToString()); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 void OnTunnelHeadersReceived( | |
| 133 const net::HostPortPair& origin, | |
| 134 const net::HostPortPair& proxy_server, | |
| 135 const net::HttpResponseHeaders& response_headers) override { | |
| 136 on_tunnel_headers_received_called_ = true; | |
| 137 on_tunnel_headers_received_origin_ = origin; | |
| 138 on_tunnel_headers_received_proxy_server_ = proxy_server; | |
| 139 on_tunnel_headers_received_status_line_ = response_headers.GetStatusLine(); | |
| 140 } | |
| 141 | |
| 142 private: | |
| 143 bool on_before_tunnel_request_called_; | |
| 144 bool on_tunnel_request_completed_called_; | |
| 145 bool on_tunnel_headers_received_called_; | |
| 146 HostPortPair on_tunnel_request_completed_endpoint_; | |
| 147 HostPortPair on_tunnel_request_completed_proxy_server_; | |
| 148 HostPortPair on_tunnel_headers_received_origin_; | |
| 149 HostPortPair on_tunnel_headers_received_proxy_server_; | |
| 150 std::string on_tunnel_headers_received_status_line_; | |
| 151 }; | |
| 152 | |
| 153 | |
| 154 class HttpProxyClientSocketPoolTest | |
| 155 : public ::testing::TestWithParam<HttpProxyClientSocketPoolTestParams> { | |
| 156 protected: | |
| 157 HttpProxyClientSocketPoolTest() | |
| 158 : session_deps_(GetParam().protocol), | |
| 159 tcp_histograms_("MockTCP"), | |
| 160 transport_socket_pool_( | |
| 161 kMaxSockets, | |
| 162 kMaxSocketsPerGroup, | |
| 163 &tcp_histograms_, | |
| 164 session_deps_.deterministic_socket_factory.get()), | |
| 165 ssl_histograms_("MockSSL"), | |
| 166 ssl_socket_pool_(kMaxSockets, | |
| 167 kMaxSocketsPerGroup, | |
| 168 &ssl_histograms_, | |
| 169 session_deps_.cert_verifier.get(), | |
| 170 NULL /* channel_id_store */, | |
| 171 NULL /* transport_security_state */, | |
| 172 NULL /* cert_transparency_verifier */, | |
| 173 NULL /* cert_policy_enforcer */, | |
| 174 std::string() /* ssl_session_cache_shard */, | |
| 175 session_deps_.deterministic_socket_factory.get(), | |
| 176 &transport_socket_pool_, | |
| 177 NULL, | |
| 178 NULL, | |
| 179 session_deps_.ssl_config_service.get(), | |
| 180 false, | |
| 181 BoundNetLog().net_log()), | |
| 182 session_(CreateNetworkSession()), | |
| 183 http_proxy_histograms_("HttpProxyUnitTest"), | |
| 184 spdy_util_(GetParam().protocol), | |
| 185 pool_(kMaxSockets, | |
| 186 kMaxSocketsPerGroup, | |
| 187 &http_proxy_histograms_, | |
| 188 &transport_socket_pool_, | |
| 189 &ssl_socket_pool_, | |
| 190 NULL) {} | |
| 191 | |
| 192 virtual ~HttpProxyClientSocketPoolTest() { | |
| 193 } | |
| 194 | |
| 195 void AddAuthToCache() { | |
| 196 const base::string16 kFoo(base::ASCIIToUTF16("foo")); | |
| 197 const base::string16 kBar(base::ASCIIToUTF16("bar")); | |
| 198 GURL proxy_url(GetParam().proxy_type == HTTP ? | |
| 199 (std::string("http://") + kHttpProxyHost) : | |
| 200 (std::string("https://") + kHttpsProxyHost)); | |
| 201 session_->http_auth_cache()->Add(proxy_url, | |
| 202 "MyRealm1", | |
| 203 HttpAuth::AUTH_SCHEME_BASIC, | |
| 204 "Basic realm=MyRealm1", | |
| 205 AuthCredentials(kFoo, kBar), | |
| 206 "/"); | |
| 207 } | |
| 208 | |
| 209 scoped_refptr<TransportSocketParams> CreateHttpProxyParams() const { | |
| 210 if (GetParam().proxy_type != HTTP) | |
| 211 return NULL; | |
| 212 return new TransportSocketParams( | |
| 213 HostPortPair(kHttpProxyHost, 80), | |
| 214 false, | |
| 215 false, | |
| 216 OnHostResolutionCallback(), | |
| 217 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT); | |
| 218 } | |
| 219 | |
| 220 scoped_refptr<SSLSocketParams> CreateHttpsProxyParams() const { | |
| 221 if (GetParam().proxy_type == HTTP) | |
| 222 return NULL; | |
| 223 return new SSLSocketParams( | |
| 224 new TransportSocketParams( | |
| 225 HostPortPair(kHttpsProxyHost, 443), | |
| 226 false, | |
| 227 false, | |
| 228 OnHostResolutionCallback(), | |
| 229 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT), | |
| 230 NULL, | |
| 231 NULL, | |
| 232 HostPortPair(kHttpsProxyHost, 443), | |
| 233 SSLConfig(), | |
| 234 PRIVACY_MODE_DISABLED, | |
| 235 0, | |
| 236 false, | |
| 237 false); | |
| 238 } | |
| 239 | |
| 240 // Returns the a correctly constructed HttpProxyParms | |
| 241 // for the HTTP or HTTPS proxy. | |
| 242 scoped_refptr<HttpProxySocketParams> CreateParams( | |
| 243 bool tunnel, | |
| 244 ProxyDelegate* proxy_delegate) { | |
| 245 return scoped_refptr<HttpProxySocketParams>(new HttpProxySocketParams( | |
| 246 CreateHttpProxyParams(), | |
| 247 CreateHttpsProxyParams(), | |
| 248 GURL(tunnel ? "https://www.google.com/" : "http://www.google.com"), | |
| 249 std::string(), | |
| 250 HostPortPair("www.google.com", tunnel ? 443 : 80), | |
| 251 session_->http_auth_cache(), | |
| 252 session_->http_auth_handler_factory(), | |
| 253 session_->spdy_session_pool(), | |
| 254 tunnel, | |
| 255 proxy_delegate)); | |
| 256 } | |
| 257 | |
| 258 scoped_refptr<HttpProxySocketParams> CreateTunnelParams( | |
| 259 ProxyDelegate* proxy_delegate) { | |
| 260 return CreateParams(true, proxy_delegate); | |
| 261 } | |
| 262 | |
| 263 scoped_refptr<HttpProxySocketParams> CreateNoTunnelParams( | |
| 264 ProxyDelegate* proxy_delegate) { | |
| 265 return CreateParams(false, proxy_delegate); | |
| 266 } | |
| 267 | |
| 268 DeterministicMockClientSocketFactory* socket_factory() { | |
| 269 return session_deps_.deterministic_socket_factory.get(); | |
| 270 } | |
| 271 | |
| 272 void Initialize(MockRead* reads, size_t reads_count, | |
| 273 MockWrite* writes, size_t writes_count, | |
| 274 MockRead* spdy_reads, size_t spdy_reads_count, | |
| 275 MockWrite* spdy_writes, size_t spdy_writes_count) { | |
| 276 if (GetParam().proxy_type == SPDY) { | |
| 277 data_.reset(new DeterministicSocketData(spdy_reads, spdy_reads_count, | |
| 278 spdy_writes, spdy_writes_count)); | |
| 279 } else { | |
| 280 data_.reset(new DeterministicSocketData(reads, reads_count, writes, | |
| 281 writes_count)); | |
| 282 } | |
| 283 | |
| 284 data_->set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
| 285 data_->StopAfter(2); // Request / Response | |
| 286 | |
| 287 socket_factory()->AddSocketDataProvider(data_.get()); | |
| 288 | |
| 289 if (GetParam().proxy_type != HTTP) { | |
| 290 ssl_data_.reset(new SSLSocketDataProvider(SYNCHRONOUS, OK)); | |
| 291 if (GetParam().proxy_type == SPDY) { | |
| 292 InitializeSpdySsl(); | |
| 293 } | |
| 294 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 void InitializeSpdySsl() { | |
| 299 ssl_data_->SetNextProto(GetParam().protocol); | |
| 300 } | |
| 301 | |
| 302 HttpNetworkSession* CreateNetworkSession() { | |
| 303 return SpdySessionDependencies::SpdyCreateSessionDeterministic( | |
| 304 &session_deps_); | |
| 305 } | |
| 306 | |
| 307 RequestPriority GetLastTransportRequestPriority() const { | |
| 308 return transport_socket_pool_.last_request_priority(); | |
| 309 } | |
| 310 | |
| 311 private: | |
| 312 SpdySessionDependencies session_deps_; | |
| 313 | |
| 314 ClientSocketPoolHistograms tcp_histograms_; | |
| 315 MockTransportClientSocketPool transport_socket_pool_; | |
| 316 ClientSocketPoolHistograms ssl_histograms_; | |
| 317 MockHostResolver host_resolver_; | |
| 318 scoped_ptr<CertVerifier> cert_verifier_; | |
| 319 SSLClientSocketPool ssl_socket_pool_; | |
| 320 | |
| 321 const scoped_refptr<HttpNetworkSession> session_; | |
| 322 ClientSocketPoolHistograms http_proxy_histograms_; | |
| 323 | |
| 324 protected: | |
| 325 SpdyTestUtil spdy_util_; | |
| 326 scoped_ptr<SSLSocketDataProvider> ssl_data_; | |
| 327 scoped_ptr<DeterministicSocketData> data_; | |
| 328 HttpProxyClientSocketPool pool_; | |
| 329 ClientSocketHandle handle_; | |
| 330 TestCompletionCallback callback_; | |
| 331 }; | |
| 332 | |
| 333 //----------------------------------------------------------------------------- | |
| 334 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY) | |
| 335 // and SPDY. | |
| 336 // | |
| 337 // TODO(akalin): Use ::testing::Combine() when we are able to use | |
| 338 // <tr1/tuple>. | |
| 339 INSTANTIATE_TEST_CASE_P( | |
| 340 HttpProxyClientSocketPoolTests, | |
| 341 HttpProxyClientSocketPoolTest, | |
| 342 ::testing::Values( | |
| 343 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY31), | |
| 344 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY31), | |
| 345 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY31), | |
| 346 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4_14), | |
| 347 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4_14), | |
| 348 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4_14), | |
| 349 HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4_15), | |
| 350 HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4_15), | |
| 351 HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4_15))); | |
| 352 | |
| 353 TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) { | |
| 354 Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0); | |
| 355 | |
| 356 scoped_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate()); | |
| 357 int rv = handle_.Init("a", CreateNoTunnelParams(proxy_delegate.get()), LOW, | |
| 358 CompletionCallback(), &pool_, BoundNetLog()); | |
| 359 EXPECT_EQ(OK, rv); | |
| 360 EXPECT_TRUE(handle_.is_initialized()); | |
| 361 ASSERT_TRUE(handle_.socket()); | |
| 362 HttpProxyClientSocket* tunnel_socket = | |
| 363 static_cast<HttpProxyClientSocket*>(handle_.socket()); | |
| 364 EXPECT_TRUE(tunnel_socket->IsConnected()); | |
| 365 EXPECT_FALSE(proxy_delegate->on_before_tunnel_request_called()); | |
| 366 EXPECT_FALSE(proxy_delegate->on_tunnel_headers_received_called()); | |
| 367 EXPECT_TRUE(proxy_delegate->on_tunnel_request_completed_called()); | |
| 368 } | |
| 369 | |
| 370 // Make sure that HttpProxyConnectJob passes on its priority to its | |
| 371 // (non-SSL) socket request on Init. | |
| 372 TEST_P(HttpProxyClientSocketPoolTest, SetSocketRequestPriorityOnInit) { | |
| 373 Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0); | |
| 374 EXPECT_EQ(OK, | |
| 375 handle_.Init("a", CreateNoTunnelParams(NULL), HIGHEST, | |
| 376 CompletionCallback(), &pool_, BoundNetLog())); | |
| 377 EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority()); | |
| 378 } | |
| 379 | |
| 380 TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) { | |
| 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\r\n"), | |
| 385 }; | |
| 386 MockRead reads[] = { | |
| 387 // No credentials. | |
| 388 MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"), | |
| 389 MockRead(ASYNC, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"), | |
| 390 MockRead(ASYNC, 3, "Content-Length: 10\r\n\r\n"), | |
| 391 MockRead(ASYNC, 4, "0123456789"), | |
| 392 }; | |
| 393 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyConnect( | |
| 394 NULL, 0, 1, LOW, HostPortPair("www.google.com", 443))); | |
| 395 scoped_ptr<SpdyFrame> rst( | |
| 396 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); | |
| 397 MockWrite spdy_writes[] = { | |
| 398 CreateMockWrite(*req, 0, ASYNC), | |
| 399 CreateMockWrite(*rst, 2, ASYNC), | |
| 400 }; | |
| 401 SpdyHeaderBlock resp_block; | |
| 402 resp_block[spdy_util_.GetStatusKey()] = "407"; | |
| 403 resp_block["proxy-authenticate"] = "Basic realm=\"MyRealm1\""; | |
| 404 spdy_util_.MaybeAddVersionHeader(&resp_block); | |
| 405 | |
| 406 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyReply(1, resp_block)); | |
| 407 MockRead spdy_reads[] = { | |
| 408 CreateMockRead(*resp, 1, ASYNC), | |
| 409 MockRead(ASYNC, 0, 3) | |
| 410 }; | |
| 411 | |
| 412 Initialize(reads, arraysize(reads), writes, arraysize(writes), | |
| 413 spdy_reads, arraysize(spdy_reads), spdy_writes, | |
| 414 arraysize(spdy_writes)); | |
| 415 | |
| 416 data_->StopAfter(4); | |
| 417 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 418 callback_.callback(), &pool_, BoundNetLog()); | |
| 419 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 420 EXPECT_FALSE(handle_.is_initialized()); | |
| 421 EXPECT_FALSE(handle_.socket()); | |
| 422 | |
| 423 data_->RunFor(GetParam().proxy_type == SPDY ? 2 : 4); | |
| 424 rv = callback_.WaitForResult(); | |
| 425 EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, rv); | |
| 426 EXPECT_TRUE(handle_.is_initialized()); | |
| 427 ASSERT_TRUE(handle_.socket()); | |
| 428 ProxyClientSocket* tunnel_socket = | |
| 429 static_cast<ProxyClientSocket*>(handle_.socket()); | |
| 430 if (GetParam().proxy_type == SPDY) { | |
| 431 EXPECT_TRUE(tunnel_socket->IsConnected()); | |
| 432 EXPECT_TRUE(tunnel_socket->IsUsingSpdy()); | |
| 433 } else { | |
| 434 EXPECT_FALSE(tunnel_socket->IsConnected()); | |
| 435 EXPECT_FALSE(tunnel_socket->IsUsingSpdy()); | |
| 436 } | |
| 437 } | |
| 438 | |
| 439 TEST_P(HttpProxyClientSocketPoolTest, HaveAuth) { | |
| 440 // It's pretty much impossible to make the SPDY case behave synchronously | |
| 441 // so we skip this test for SPDY | |
| 442 if (GetParam().proxy_type == SPDY) | |
| 443 return; | |
| 444 std::string proxy_host_port = | |
| 445 GetParam().proxy_type == HTTP ? | |
| 446 (kHttpProxyHost + std::string(":80")) : | |
| 447 (kHttpsProxyHost + std::string(":443")); | |
| 448 std::string request = | |
| 449 "CONNECT www.google.com:443 HTTP/1.1\r\n" | |
| 450 "Host: www.google.com\r\n" | |
| 451 "Proxy-Connection: keep-alive\r\n" | |
| 452 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n" | |
| 453 "Foo: " + proxy_host_port + "\r\n\r\n"; | |
| 454 MockWrite writes[] = { | |
| 455 MockWrite(SYNCHRONOUS, 0, request.c_str()), | |
| 456 }; | |
| 457 MockRead reads[] = { | |
| 458 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), | |
| 459 }; | |
| 460 | |
| 461 Initialize(reads, arraysize(reads), writes, arraysize(writes), NULL, 0, | |
| 462 NULL, 0); | |
| 463 AddAuthToCache(); | |
| 464 | |
| 465 scoped_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate()); | |
| 466 int rv = handle_.Init("a", CreateTunnelParams(proxy_delegate.get()), LOW, | |
| 467 callback_.callback(), &pool_, BoundNetLog()); | |
| 468 EXPECT_EQ(OK, rv); | |
| 469 EXPECT_TRUE(handle_.is_initialized()); | |
| 470 ASSERT_TRUE(handle_.socket()); | |
| 471 HttpProxyClientSocket* tunnel_socket = | |
| 472 static_cast<HttpProxyClientSocket*>(handle_.socket()); | |
| 473 EXPECT_TRUE(tunnel_socket->IsConnected()); | |
| 474 proxy_delegate->VerifyOnTunnelHeadersReceived( | |
| 475 "www.google.com:443", | |
| 476 proxy_host_port.c_str(), | |
| 477 "HTTP/1.1 200 Connection Established"); | |
| 478 proxy_delegate->VerifyOnTunnelRequestCompleted( | |
| 479 "www.google.com:443", | |
| 480 proxy_host_port.c_str()); | |
| 481 } | |
| 482 | |
| 483 TEST_P(HttpProxyClientSocketPoolTest, AsyncHaveAuth) { | |
| 484 std::string proxy_host_port = | |
| 485 GetParam().proxy_type == HTTP ? | |
| 486 (kHttpProxyHost + std::string(":80")) : | |
| 487 (kHttpsProxyHost + std::string(":443")); | |
| 488 std::string request = | |
| 489 "CONNECT www.google.com:443 HTTP/1.1\r\n" | |
| 490 "Host: www.google.com\r\n" | |
| 491 "Proxy-Connection: keep-alive\r\n" | |
| 492 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n" | |
| 493 "Foo: " + proxy_host_port + "\r\n\r\n"; | |
| 494 MockWrite writes[] = { | |
| 495 MockWrite(ASYNC, 0, request.c_str()), | |
| 496 }; | |
| 497 MockRead reads[] = { | |
| 498 MockRead(ASYNC, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), | |
| 499 }; | |
| 500 | |
| 501 scoped_ptr<SpdyFrame> req( | |
| 502 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | |
| 503 HostPortPair("www.google.com", 443))); | |
| 504 MockWrite spdy_writes[] = { | |
| 505 CreateMockWrite(*req, 0, ASYNC) | |
| 506 }; | |
| 507 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); | |
| 508 MockRead spdy_reads[] = { | |
| 509 CreateMockRead(*resp, 1, ASYNC), | |
| 510 MockRead(ASYNC, 0, 2) | |
| 511 }; | |
| 512 | |
| 513 Initialize(reads, arraysize(reads), writes, arraysize(writes), | |
| 514 spdy_reads, arraysize(spdy_reads), spdy_writes, | |
| 515 arraysize(spdy_writes)); | |
| 516 AddAuthToCache(); | |
| 517 | |
| 518 scoped_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate()); | |
| 519 int rv = handle_.Init("a", CreateTunnelParams(proxy_delegate.get()), LOW, | |
| 520 callback_.callback(), &pool_, BoundNetLog()); | |
| 521 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 522 EXPECT_FALSE(handle_.is_initialized()); | |
| 523 EXPECT_FALSE(handle_.socket()); | |
| 524 | |
| 525 data_->RunFor(2); | |
| 526 EXPECT_EQ(OK, callback_.WaitForResult()); | |
| 527 EXPECT_TRUE(handle_.is_initialized()); | |
| 528 ASSERT_TRUE(handle_.socket()); | |
| 529 HttpProxyClientSocket* tunnel_socket = | |
| 530 static_cast<HttpProxyClientSocket*>(handle_.socket()); | |
| 531 EXPECT_TRUE(tunnel_socket->IsConnected()); | |
| 532 proxy_delegate->VerifyOnTunnelRequestCompleted( | |
| 533 "www.google.com:443", | |
| 534 proxy_host_port.c_str()); | |
| 535 } | |
| 536 | |
| 537 // Make sure that HttpProxyConnectJob passes on its priority to its | |
| 538 // SPDY session's socket request on Init (if applicable). | |
| 539 TEST_P(HttpProxyClientSocketPoolTest, | |
| 540 SetSpdySessionSocketRequestPriorityOnInit) { | |
| 541 if (GetParam().proxy_type != SPDY) | |
| 542 return; | |
| 543 | |
| 544 scoped_ptr<SpdyFrame> req( | |
| 545 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, MEDIUM, | |
| 546 HostPortPair("www.google.com", 443))); | |
| 547 MockWrite spdy_writes[] = { | |
| 548 CreateMockWrite(*req, 0, ASYNC) | |
| 549 }; | |
| 550 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); | |
| 551 MockRead spdy_reads[] = { | |
| 552 CreateMockRead(*resp, 1, ASYNC), | |
| 553 MockRead(ASYNC, 0, 2) | |
| 554 }; | |
| 555 | |
| 556 Initialize(NULL, 0, NULL, 0, | |
| 557 spdy_reads, arraysize(spdy_reads), | |
| 558 spdy_writes, arraysize(spdy_writes)); | |
| 559 AddAuthToCache(); | |
| 560 | |
| 561 EXPECT_EQ(ERR_IO_PENDING, | |
| 562 handle_.Init("a", CreateTunnelParams(NULL), MEDIUM, | |
| 563 callback_.callback(), &pool_, BoundNetLog())); | |
| 564 EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority()); | |
| 565 | |
| 566 data_->RunFor(2); | |
| 567 EXPECT_EQ(OK, callback_.WaitForResult()); | |
| 568 } | |
| 569 | |
| 570 TEST_P(HttpProxyClientSocketPoolTest, TCPError) { | |
| 571 if (GetParam().proxy_type == SPDY) return; | |
| 572 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0)); | |
| 573 data_->set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_CLOSED)); | |
| 574 | |
| 575 socket_factory()->AddSocketDataProvider(data_.get()); | |
| 576 | |
| 577 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 578 callback_.callback(), &pool_, BoundNetLog()); | |
| 579 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 580 EXPECT_FALSE(handle_.is_initialized()); | |
| 581 EXPECT_FALSE(handle_.socket()); | |
| 582 | |
| 583 EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, callback_.WaitForResult()); | |
| 584 | |
| 585 EXPECT_FALSE(handle_.is_initialized()); | |
| 586 EXPECT_FALSE(handle_.socket()); | |
| 587 } | |
| 588 | |
| 589 TEST_P(HttpProxyClientSocketPoolTest, SSLError) { | |
| 590 if (GetParam().proxy_type == HTTP) return; | |
| 591 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0)); | |
| 592 data_->set_connect_data(MockConnect(ASYNC, OK)); | |
| 593 socket_factory()->AddSocketDataProvider(data_.get()); | |
| 594 | |
| 595 ssl_data_.reset(new SSLSocketDataProvider(ASYNC, | |
| 596 ERR_CERT_AUTHORITY_INVALID)); | |
| 597 if (GetParam().proxy_type == SPDY) { | |
| 598 InitializeSpdySsl(); | |
| 599 } | |
| 600 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); | |
| 601 | |
| 602 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 603 callback_.callback(), &pool_, BoundNetLog()); | |
| 604 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 605 EXPECT_FALSE(handle_.is_initialized()); | |
| 606 EXPECT_FALSE(handle_.socket()); | |
| 607 | |
| 608 EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID, callback_.WaitForResult()); | |
| 609 | |
| 610 EXPECT_FALSE(handle_.is_initialized()); | |
| 611 EXPECT_FALSE(handle_.socket()); | |
| 612 } | |
| 613 | |
| 614 TEST_P(HttpProxyClientSocketPoolTest, SslClientAuth) { | |
| 615 if (GetParam().proxy_type == HTTP) return; | |
| 616 data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0)); | |
| 617 data_->set_connect_data(MockConnect(ASYNC, OK)); | |
| 618 socket_factory()->AddSocketDataProvider(data_.get()); | |
| 619 | |
| 620 ssl_data_.reset(new SSLSocketDataProvider(ASYNC, | |
| 621 ERR_SSL_CLIENT_AUTH_CERT_NEEDED)); | |
| 622 if (GetParam().proxy_type == SPDY) { | |
| 623 InitializeSpdySsl(); | |
| 624 } | |
| 625 socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); | |
| 626 | |
| 627 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 628 callback_.callback(), &pool_, BoundNetLog()); | |
| 629 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 630 EXPECT_FALSE(handle_.is_initialized()); | |
| 631 EXPECT_FALSE(handle_.socket()); | |
| 632 | |
| 633 EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, callback_.WaitForResult()); | |
| 634 | |
| 635 EXPECT_FALSE(handle_.is_initialized()); | |
| 636 EXPECT_FALSE(handle_.socket()); | |
| 637 } | |
| 638 | |
| 639 TEST_P(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) { | |
| 640 MockWrite writes[] = { | |
| 641 MockWrite(ASYNC, 0, | |
| 642 "CONNECT www.google.com:443 HTTP/1.1\r\n" | |
| 643 "Host: www.google.com\r\n" | |
| 644 "Proxy-Connection: keep-alive\r\n" | |
| 645 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), | |
| 646 }; | |
| 647 MockRead reads[] = { | |
| 648 MockRead(ASYNC, 1, "HTTP/1.1 200 Conn"), | |
| 649 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2), | |
| 650 }; | |
| 651 scoped_ptr<SpdyFrame> req( | |
| 652 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | |
| 653 HostPortPair("www.google.com", 443))); | |
| 654 MockWrite spdy_writes[] = { | |
| 655 CreateMockWrite(*req, 0, ASYNC) | |
| 656 }; | |
| 657 MockRead spdy_reads[] = { | |
| 658 MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1), | |
| 659 }; | |
| 660 | |
| 661 Initialize(reads, arraysize(reads), writes, arraysize(writes), | |
| 662 spdy_reads, arraysize(spdy_reads), spdy_writes, | |
| 663 arraysize(spdy_writes)); | |
| 664 AddAuthToCache(); | |
| 665 | |
| 666 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 667 callback_.callback(), &pool_, BoundNetLog()); | |
| 668 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 669 EXPECT_FALSE(handle_.is_initialized()); | |
| 670 EXPECT_FALSE(handle_.socket()); | |
| 671 | |
| 672 data_->RunFor(3); | |
| 673 if (GetParam().proxy_type == SPDY) { | |
| 674 // SPDY cannot process a headers block unless it's complete and so it | |
| 675 // returns ERR_CONNECTION_CLOSED in this case. | |
| 676 EXPECT_EQ(ERR_CONNECTION_CLOSED, callback_.WaitForResult()); | |
| 677 } else { | |
| 678 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, callback_.WaitForResult()); | |
| 679 } | |
| 680 EXPECT_FALSE(handle_.is_initialized()); | |
| 681 EXPECT_FALSE(handle_.socket()); | |
| 682 } | |
| 683 | |
| 684 TEST_P(HttpProxyClientSocketPoolTest, Tunnel1xxResponse) { | |
| 685 // Tests that 1xx responses are rejected for a CONNECT request. | |
| 686 if (GetParam().proxy_type == SPDY) { | |
| 687 // SPDY doesn't have 1xx responses. | |
| 688 return; | |
| 689 } | |
| 690 | |
| 691 MockWrite writes[] = { | |
| 692 MockWrite(ASYNC, 0, | |
| 693 "CONNECT www.google.com:443 HTTP/1.1\r\n" | |
| 694 "Host: www.google.com\r\n" | |
| 695 "Proxy-Connection: keep-alive\r\n\r\n"), | |
| 696 }; | |
| 697 MockRead reads[] = { | |
| 698 MockRead(ASYNC, 1, "HTTP/1.1 100 Continue\r\n\r\n"), | |
| 699 MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"), | |
| 700 }; | |
| 701 | |
| 702 Initialize(reads, arraysize(reads), writes, arraysize(writes), | |
| 703 NULL, 0, NULL, 0); | |
| 704 | |
| 705 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 706 callback_.callback(), &pool_, BoundNetLog()); | |
| 707 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 708 EXPECT_FALSE(handle_.is_initialized()); | |
| 709 EXPECT_FALSE(handle_.socket()); | |
| 710 | |
| 711 data_->RunFor(2); | |
| 712 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult()); | |
| 713 } | |
| 714 | |
| 715 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) { | |
| 716 MockWrite writes[] = { | |
| 717 MockWrite(ASYNC, 0, | |
| 718 "CONNECT www.google.com:443 HTTP/1.1\r\n" | |
| 719 "Host: www.google.com\r\n" | |
| 720 "Proxy-Connection: keep-alive\r\n" | |
| 721 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), | |
| 722 }; | |
| 723 MockRead reads[] = { | |
| 724 MockRead(ASYNC, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"), | |
| 725 }; | |
| 726 scoped_ptr<SpdyFrame> req( | |
| 727 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | |
| 728 HostPortPair("www.google.com", 443))); | |
| 729 scoped_ptr<SpdyFrame> rst( | |
| 730 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); | |
| 731 MockWrite spdy_writes[] = { | |
| 732 CreateMockWrite(*req, 0, ASYNC), | |
| 733 CreateMockWrite(*rst, 2, ASYNC), | |
| 734 }; | |
| 735 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdySynReplyError(1)); | |
| 736 MockRead spdy_reads[] = { | |
| 737 CreateMockRead(*resp, 1, ASYNC), | |
| 738 MockRead(ASYNC, 0, 3), | |
| 739 }; | |
| 740 | |
| 741 Initialize(reads, arraysize(reads), writes, arraysize(writes), | |
| 742 spdy_reads, arraysize(spdy_reads), spdy_writes, | |
| 743 arraysize(spdy_writes)); | |
| 744 AddAuthToCache(); | |
| 745 | |
| 746 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 747 callback_.callback(), &pool_, BoundNetLog()); | |
| 748 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 749 EXPECT_FALSE(handle_.is_initialized()); | |
| 750 EXPECT_FALSE(handle_.socket()); | |
| 751 | |
| 752 data_->RunFor(2); | |
| 753 | |
| 754 rv = callback_.WaitForResult(); | |
| 755 // All Proxy CONNECT responses are not trustworthy | |
| 756 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv); | |
| 757 EXPECT_FALSE(handle_.is_initialized()); | |
| 758 EXPECT_FALSE(handle_.socket()); | |
| 759 } | |
| 760 | |
| 761 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupRedirect) { | |
| 762 const std::string redirectTarget = "https://foo.google.com/"; | |
| 763 | |
| 764 const std::string responseText = "HTTP/1.1 302 Found\r\n" | |
| 765 "Location: " + redirectTarget + "\r\n" | |
| 766 "Set-Cookie: foo=bar\r\n" | |
| 767 "\r\n"; | |
| 768 MockWrite writes[] = { | |
| 769 MockWrite(ASYNC, 0, | |
| 770 "CONNECT www.google.com:443 HTTP/1.1\r\n" | |
| 771 "Host: www.google.com\r\n" | |
| 772 "Proxy-Connection: keep-alive\r\n" | |
| 773 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), | |
| 774 }; | |
| 775 MockRead reads[] = { | |
| 776 MockRead(ASYNC, 1, responseText.c_str()), | |
| 777 }; | |
| 778 scoped_ptr<SpdyFrame> req( | |
| 779 spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | |
| 780 HostPortPair("www.google.com", 443))); | |
| 781 scoped_ptr<SpdyFrame> rst( | |
| 782 spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); | |
| 783 | |
| 784 MockWrite spdy_writes[] = { | |
| 785 CreateMockWrite(*req, 0, ASYNC), | |
| 786 CreateMockWrite(*rst, 3, ASYNC), | |
| 787 }; | |
| 788 | |
| 789 const char* const responseHeaders[] = { | |
| 790 "location", redirectTarget.c_str(), | |
| 791 "set-cookie", "foo=bar", | |
| 792 }; | |
| 793 const int responseHeadersSize = arraysize(responseHeaders) / 2; | |
| 794 scoped_ptr<SpdyFrame> resp( | |
| 795 spdy_util_.ConstructSpdySynReplyError( | |
| 796 "302 Found", | |
| 797 responseHeaders, responseHeadersSize, | |
| 798 1)); | |
| 799 MockRead spdy_reads[] = { | |
| 800 CreateMockRead(*resp, 1, ASYNC), | |
| 801 MockRead(ASYNC, 0, 2), | |
| 802 }; | |
| 803 | |
| 804 Initialize(reads, arraysize(reads), writes, arraysize(writes), | |
| 805 spdy_reads, arraysize(spdy_reads), spdy_writes, | |
| 806 arraysize(spdy_writes)); | |
| 807 AddAuthToCache(); | |
| 808 | |
| 809 int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW, | |
| 810 callback_.callback(), &pool_, BoundNetLog()); | |
| 811 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 812 EXPECT_FALSE(handle_.is_initialized()); | |
| 813 EXPECT_FALSE(handle_.socket()); | |
| 814 | |
| 815 data_->RunFor(2); | |
| 816 | |
| 817 rv = callback_.WaitForResult(); | |
| 818 | |
| 819 if (GetParam().proxy_type == HTTP) { | |
| 820 // We don't trust 302 responses to CONNECT from HTTP proxies. | |
| 821 EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv); | |
| 822 EXPECT_FALSE(handle_.is_initialized()); | |
| 823 EXPECT_FALSE(handle_.socket()); | |
| 824 } else { | |
| 825 // Expect ProxyClientSocket to return the proxy's response, sanitized. | |
| 826 EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE, rv); | |
| 827 EXPECT_TRUE(handle_.is_initialized()); | |
| 828 ASSERT_TRUE(handle_.socket()); | |
| 829 | |
| 830 const ProxyClientSocket* tunnel_socket = | |
| 831 static_cast<ProxyClientSocket*>(handle_.socket()); | |
| 832 const HttpResponseInfo* response = tunnel_socket->GetConnectResponseInfo(); | |
| 833 const HttpResponseHeaders* headers = response->headers.get(); | |
| 834 | |
| 835 // Make sure Set-Cookie header was stripped. | |
| 836 EXPECT_FALSE(headers->HasHeader("set-cookie")); | |
| 837 | |
| 838 // Make sure Content-Length: 0 header was added. | |
| 839 EXPECT_TRUE(headers->HasHeaderValue("content-length", "0")); | |
| 840 | |
| 841 // Make sure Location header was included and correct. | |
| 842 std::string location; | |
| 843 EXPECT_TRUE(headers->IsRedirect(&location)); | |
| 844 EXPECT_EQ(location, redirectTarget); | |
| 845 } | |
| 846 } | |
| 847 | |
| 848 // It would be nice to also test the timeouts in HttpProxyClientSocketPool. | |
| 849 | |
| 850 } // namespace | |
| 851 | |
| 852 } // namespace net | |
| OLD | NEW |