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