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