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 |