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
25namespace net {
26
27namespace {
28
29const int kMaxSockets = 32;
30const int kMaxSocketsPerGroup = 6;
31const char * const kAuthHeaders[] = {
32  "proxy-authorization", "Basic Zm9vOmJhcg=="
33};
34const int kAuthHeadersSize = arraysize(kAuthHeaders) / 2;
35
36enum HttpProxyType {
37  HTTP,
38  HTTPS,
39  SPDY
40};
41
42struct HttpProxyClientSocketPoolTestParams {
43  HttpProxyClientSocketPoolTestParams()
44      : proxy_type(HTTP),
45        protocol(kProtoSPDY3) {}
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
57typedef ::testing::TestWithParam<HttpProxyType> TestWithHttpParam;
58
59const char kHttpProxyHost[] = "httpproxy.example.com";
60const char kHttpsProxyHost[] = "httpsproxy.example.com";
61
62class TestProxyDelegate : public ProxyDelegate {
63 public:
64  TestProxyDelegate()
65      : on_before_tunnel_request_called_(false),
66        on_tunnel_headers_received_called_(false) {
67  }
68
69  virtual ~TestProxyDelegate() OVERRIDE {
70  }
71
72  bool on_before_tunnel_request_called() const {
73    return on_before_tunnel_request_called_;
74  }
75
76  bool on_tunnel_headers_received_called() const {
77    return on_tunnel_headers_received_called_;
78  }
79
80  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:
92  virtual void OnResolveProxy(const GURL& url,
93                              int load_flags,
94                              const ProxyService& proxy_service,
95                              ProxyInfo* result) OVERRIDE {
96  }
97
98  virtual void OnFallback(const ProxyServer& bad_proxy,
99                          int net_error) OVERRIDE {
100  }
101
102  virtual void OnBeforeSendHeaders(URLRequest* request,
103                                   const ProxyInfo& proxy_info,
104                                   HttpRequestHeaders* headers) OVERRIDE {
105  }
106
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
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
135class HttpProxyClientSocketPoolTest
136    : public ::testing::TestWithParam<HttpProxyClientSocketPoolTestParams> {
137 protected:
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
175  virtual ~HttpProxyClientSocketPoolTest() {
176  }
177
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
192  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
203  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.
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
241  scoped_refptr<HttpProxySocketParams> CreateTunnelParams(
242      ProxyDelegate* proxy_delegate) {
243    return CreateParams(true, proxy_delegate);
244  }
245
246  scoped_refptr<HttpProxySocketParams> CreateNoTunnelParams(
247      ProxyDelegate* proxy_delegate) {
248    return CreateParams(false, proxy_delegate);
249  }
250
251  DeterministicMockClientSocketFactory* socket_factory() {
252    return session_deps_.deterministic_socket_factory.get();
253  }
254
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
281  void InitializeSpdySsl() {
282    ssl_data_->SetNextProto(GetParam().protocol);
283  }
284
285  HttpNetworkSession* CreateNetworkSession() {
286    return SpdySessionDependencies::SpdyCreateSessionDeterministic(
287        &session_deps_);
288  }
289
290  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>.
322INSTANTIATE_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
339TEST_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.
357TEST_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
365TEST_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
424TEST_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
465TEST_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).
509TEST_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
540TEST_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
559TEST_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
584TEST_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
609TEST_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
653TEST_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
684TEST_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
729TEST_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