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(ASCIIToUTF16("foo"));
    104     const base::string16 kBar(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         kPrivacyModeDisabled,
    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, kProtoSPDY4a2),
    252         HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4a2),
    253         HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4a2),
    254         HttpProxyClientSocketPoolTestParams(HTTP, kProtoHTTP2Draft04),
    255         HttpProxyClientSocketPoolTestParams(HTTPS, kProtoHTTP2Draft04),
    256         HttpProxyClientSocketPoolTestParams(SPDY, kProtoHTTP2Draft04)));
    257 
    258 TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) {
    259   Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0);
    260 
    261   int rv = handle_.Init("a", CreateNoTunnelParams(), LOW, CompletionCallback(),
    262                         &pool_, BoundNetLog());
    263   EXPECT_EQ(OK, rv);
    264   EXPECT_TRUE(handle_.is_initialized());
    265   ASSERT_TRUE(handle_.socket());
    266   HttpProxyClientSocket* tunnel_socket =
    267           static_cast<HttpProxyClientSocket*>(handle_.socket());
    268   EXPECT_TRUE(tunnel_socket->IsConnected());
    269 }
    270 
    271 // Make sure that HttpProxyConnectJob passes on its priority to its
    272 // (non-SSL) socket request on Init.
    273 TEST_P(HttpProxyClientSocketPoolTest, SetSocketRequestPriorityOnInit) {
    274   Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0);
    275   EXPECT_EQ(OK,
    276             handle_.Init("a", CreateNoTunnelParams(), HIGHEST,
    277                          CompletionCallback(), &pool_, BoundNetLog()));
    278   EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority());
    279 }
    280 
    281 TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) {
    282   MockWrite writes[] = {
    283     MockWrite(ASYNC, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
    284               "Host: www.google.com\r\n"
    285               "Proxy-Connection: keep-alive\r\n\r\n"),
    286   };
    287   MockRead reads[] = {
    288     // No credentials.
    289     MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
    290     MockRead(ASYNC, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
    291     MockRead(ASYNC, 3, "Content-Length: 10\r\n\r\n"),
    292     MockRead(ASYNC, 4, "0123456789"),
    293   };
    294   scoped_ptr<SpdyFrame> req(
    295       spdy_util_.ConstructSpdyConnect(NULL, 0, 1, LOW));
    296   scoped_ptr<SpdyFrame> rst(
    297       spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
    298   MockWrite spdy_writes[] = {
    299     CreateMockWrite(*req, 0, ASYNC),
    300     CreateMockWrite(*rst, 2, ASYNC),
    301   };
    302   const char* const kAuthChallenge[] = {
    303     spdy_util_.GetStatusKey(), "407 Proxy Authentication Required",
    304     spdy_util_.GetVersionKey(), "HTTP/1.1",
    305     "proxy-authenticate", "Basic realm=\"MyRealm1\"",
    306   };
    307   scoped_ptr<SpdyFrame> resp(
    308       spdy_util_.ConstructSpdyControlFrame(NULL,
    309                                            0,
    310                                            false,
    311                                            1,
    312                                            LOW,
    313                                            SYN_REPLY,
    314                                            CONTROL_FLAG_NONE,
    315                                            kAuthChallenge,
    316                                            arraysize(kAuthChallenge),
    317                                            0));
    318   MockRead spdy_reads[] = {
    319     CreateMockRead(*resp, 1, ASYNC),
    320     MockRead(ASYNC, 0, 3)
    321   };
    322 
    323   Initialize(reads, arraysize(reads), writes, arraysize(writes),
    324              spdy_reads, arraysize(spdy_reads), spdy_writes,
    325              arraysize(spdy_writes));
    326 
    327   data_->StopAfter(4);
    328   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    329                         &pool_, BoundNetLog());
    330   EXPECT_EQ(ERR_IO_PENDING, rv);
    331   EXPECT_FALSE(handle_.is_initialized());
    332   EXPECT_FALSE(handle_.socket());
    333 
    334   data_->RunFor(GetParam().proxy_type == SPDY ? 2 : 4);
    335   rv = callback_.WaitForResult();
    336   EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, rv);
    337   EXPECT_TRUE(handle_.is_initialized());
    338   ASSERT_TRUE(handle_.socket());
    339   ProxyClientSocket* tunnel_socket =
    340       static_cast<ProxyClientSocket*>(handle_.socket());
    341   if (GetParam().proxy_type == SPDY) {
    342     EXPECT_TRUE(tunnel_socket->IsConnected());
    343     EXPECT_TRUE(tunnel_socket->IsUsingSpdy());
    344   } else {
    345     EXPECT_FALSE(tunnel_socket->IsConnected());
    346     EXPECT_FALSE(tunnel_socket->IsUsingSpdy());
    347   }
    348 }
    349 
    350 TEST_P(HttpProxyClientSocketPoolTest, HaveAuth) {
    351   // It's pretty much impossible to make the SPDY case behave synchronously
    352   // so we skip this test for SPDY
    353   if (GetParam().proxy_type == SPDY)
    354     return;
    355   MockWrite writes[] = {
    356     MockWrite(SYNCHRONOUS, 0,
    357               "CONNECT www.google.com:443 HTTP/1.1\r\n"
    358               "Host: www.google.com\r\n"
    359               "Proxy-Connection: keep-alive\r\n"
    360               "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
    361   };
    362   MockRead reads[] = {
    363     MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
    364   };
    365 
    366   Initialize(reads, arraysize(reads), writes, arraysize(writes), NULL, 0,
    367              NULL, 0);
    368   AddAuthToCache();
    369 
    370   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    371                         &pool_, BoundNetLog());
    372   EXPECT_EQ(OK, rv);
    373   EXPECT_TRUE(handle_.is_initialized());
    374   ASSERT_TRUE(handle_.socket());
    375   HttpProxyClientSocket* tunnel_socket =
    376           static_cast<HttpProxyClientSocket*>(handle_.socket());
    377   EXPECT_TRUE(tunnel_socket->IsConnected());
    378 }
    379 
    380 TEST_P(HttpProxyClientSocketPoolTest, AsyncHaveAuth) {
    381   MockWrite writes[] = {
    382     MockWrite(ASYNC, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
    383               "Host: www.google.com\r\n"
    384               "Proxy-Connection: keep-alive\r\n"
    385               "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
    386   };
    387   MockRead reads[] = {
    388     MockRead(ASYNC, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
    389   };
    390 
    391   scoped_ptr<SpdyFrame> req(
    392       spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
    393   MockWrite spdy_writes[] = {
    394     CreateMockWrite(*req, 0, ASYNC)
    395   };
    396   scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
    397   MockRead spdy_reads[] = {
    398     CreateMockRead(*resp, 1, ASYNC),
    399     MockRead(ASYNC, 0, 2)
    400   };
    401 
    402   Initialize(reads, arraysize(reads), writes, arraysize(writes),
    403              spdy_reads, arraysize(spdy_reads), spdy_writes,
    404              arraysize(spdy_writes));
    405   AddAuthToCache();
    406 
    407   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    408                         &pool_, BoundNetLog());
    409   EXPECT_EQ(ERR_IO_PENDING, rv);
    410   EXPECT_FALSE(handle_.is_initialized());
    411   EXPECT_FALSE(handle_.socket());
    412 
    413   data_->RunFor(2);
    414   EXPECT_EQ(OK, callback_.WaitForResult());
    415   EXPECT_TRUE(handle_.is_initialized());
    416   ASSERT_TRUE(handle_.socket());
    417   HttpProxyClientSocket* tunnel_socket =
    418           static_cast<HttpProxyClientSocket*>(handle_.socket());
    419   EXPECT_TRUE(tunnel_socket->IsConnected());
    420 }
    421 
    422 // Make sure that HttpProxyConnectJob passes on its priority to its
    423 // SPDY session's socket request on Init (if applicable).
    424 TEST_P(HttpProxyClientSocketPoolTest,
    425        SetSpdySessionSocketRequestPriorityOnInit) {
    426   if (GetParam().proxy_type != SPDY)
    427     return;
    428 
    429   scoped_ptr<SpdyFrame> req(
    430       spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize,
    431                                       1, MEDIUM));
    432   MockWrite spdy_writes[] = {
    433     CreateMockWrite(*req, 0, ASYNC)
    434   };
    435   scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
    436   MockRead spdy_reads[] = {
    437     CreateMockRead(*resp, 1, ASYNC),
    438     MockRead(ASYNC, 0, 2)
    439   };
    440 
    441   Initialize(NULL, 0, NULL, 0,
    442              spdy_reads, arraysize(spdy_reads),
    443              spdy_writes, arraysize(spdy_writes));
    444   AddAuthToCache();
    445 
    446   EXPECT_EQ(ERR_IO_PENDING,
    447             handle_.Init("a", CreateTunnelParams(), MEDIUM,
    448                          callback_.callback(), &pool_, BoundNetLog()));
    449   EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority());
    450 
    451   data_->RunFor(2);
    452   EXPECT_EQ(OK, callback_.WaitForResult());
    453 }
    454 
    455 TEST_P(HttpProxyClientSocketPoolTest, TCPError) {
    456   if (GetParam().proxy_type == SPDY) return;
    457   data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0));
    458   data_->set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_CLOSED));
    459 
    460   socket_factory()->AddSocketDataProvider(data_.get());
    461 
    462   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    463                         &pool_, BoundNetLog());
    464   EXPECT_EQ(ERR_IO_PENDING, rv);
    465   EXPECT_FALSE(handle_.is_initialized());
    466   EXPECT_FALSE(handle_.socket());
    467 
    468   EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, callback_.WaitForResult());
    469 
    470   EXPECT_FALSE(handle_.is_initialized());
    471   EXPECT_FALSE(handle_.socket());
    472 }
    473 
    474 TEST_P(HttpProxyClientSocketPoolTest, SSLError) {
    475   if (GetParam().proxy_type == HTTP) return;
    476   data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0));
    477   data_->set_connect_data(MockConnect(ASYNC, OK));
    478   socket_factory()->AddSocketDataProvider(data_.get());
    479 
    480   ssl_data_.reset(new SSLSocketDataProvider(ASYNC,
    481                                             ERR_CERT_AUTHORITY_INVALID));
    482   if (GetParam().proxy_type == SPDY) {
    483     InitializeSpdySsl();
    484   }
    485   socket_factory()->AddSSLSocketDataProvider(ssl_data_.get());
    486 
    487   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    488                         &pool_, BoundNetLog());
    489   EXPECT_EQ(ERR_IO_PENDING, rv);
    490   EXPECT_FALSE(handle_.is_initialized());
    491   EXPECT_FALSE(handle_.socket());
    492 
    493   EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID, callback_.WaitForResult());
    494 
    495   EXPECT_FALSE(handle_.is_initialized());
    496   EXPECT_FALSE(handle_.socket());
    497 }
    498 
    499 TEST_P(HttpProxyClientSocketPoolTest, SslClientAuth) {
    500   if (GetParam().proxy_type == HTTP) return;
    501   data_.reset(new DeterministicSocketData(NULL, 0, NULL, 0));
    502   data_->set_connect_data(MockConnect(ASYNC, OK));
    503   socket_factory()->AddSocketDataProvider(data_.get());
    504 
    505   ssl_data_.reset(new SSLSocketDataProvider(ASYNC,
    506                                             ERR_SSL_CLIENT_AUTH_CERT_NEEDED));
    507   if (GetParam().proxy_type == SPDY) {
    508     InitializeSpdySsl();
    509   }
    510   socket_factory()->AddSSLSocketDataProvider(ssl_data_.get());
    511 
    512   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    513                         &pool_, BoundNetLog());
    514   EXPECT_EQ(ERR_IO_PENDING, rv);
    515   EXPECT_FALSE(handle_.is_initialized());
    516   EXPECT_FALSE(handle_.socket());
    517 
    518   EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, callback_.WaitForResult());
    519 
    520   EXPECT_FALSE(handle_.is_initialized());
    521   EXPECT_FALSE(handle_.socket());
    522 }
    523 
    524 TEST_P(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) {
    525   MockWrite writes[] = {
    526     MockWrite(ASYNC, 0,
    527               "CONNECT www.google.com:443 HTTP/1.1\r\n"
    528               "Host: www.google.com\r\n"
    529               "Proxy-Connection: keep-alive\r\n"
    530               "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
    531   };
    532   MockRead reads[] = {
    533     MockRead(ASYNC, 1, "HTTP/1.1 200 Conn"),
    534     MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2),
    535   };
    536   scoped_ptr<SpdyFrame> req(
    537       spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
    538   MockWrite spdy_writes[] = {
    539     CreateMockWrite(*req, 0, ASYNC)
    540   };
    541   MockRead spdy_reads[] = {
    542     MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1),
    543   };
    544 
    545   Initialize(reads, arraysize(reads), writes, arraysize(writes),
    546              spdy_reads, arraysize(spdy_reads), spdy_writes,
    547              arraysize(spdy_writes));
    548   AddAuthToCache();
    549 
    550   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    551                         &pool_, BoundNetLog());
    552   EXPECT_EQ(ERR_IO_PENDING, rv);
    553   EXPECT_FALSE(handle_.is_initialized());
    554   EXPECT_FALSE(handle_.socket());
    555 
    556   data_->RunFor(3);
    557   if (GetParam().proxy_type == SPDY) {
    558     // SPDY cannot process a headers block unless it's complete and so it
    559     // returns ERR_CONNECTION_CLOSED in this case.
    560     EXPECT_EQ(ERR_CONNECTION_CLOSED, callback_.WaitForResult());
    561   } else {
    562     EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, callback_.WaitForResult());
    563   }
    564   EXPECT_FALSE(handle_.is_initialized());
    565   EXPECT_FALSE(handle_.socket());
    566 }
    567 
    568 TEST_P(HttpProxyClientSocketPoolTest, Tunnel1xxResponse) {
    569   // Tests that 1xx responses are rejected for a CONNECT request.
    570   if (GetParam().proxy_type == SPDY) {
    571     // SPDY doesn't have 1xx responses.
    572     return;
    573   }
    574 
    575   MockWrite writes[] = {
    576     MockWrite(ASYNC, 0,
    577               "CONNECT www.google.com:443 HTTP/1.1\r\n"
    578               "Host: www.google.com\r\n"
    579               "Proxy-Connection: keep-alive\r\n\r\n"),
    580   };
    581   MockRead reads[] = {
    582     MockRead(ASYNC, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
    583     MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
    584   };
    585 
    586   Initialize(reads, arraysize(reads), writes, arraysize(writes),
    587              NULL, 0, NULL, 0);
    588 
    589   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    590                         &pool_, BoundNetLog());
    591   EXPECT_EQ(ERR_IO_PENDING, rv);
    592   EXPECT_FALSE(handle_.is_initialized());
    593   EXPECT_FALSE(handle_.socket());
    594 
    595   data_->RunFor(2);
    596   EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
    597 }
    598 
    599 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) {
    600   MockWrite writes[] = {
    601     MockWrite(ASYNC, 0,
    602               "CONNECT www.google.com:443 HTTP/1.1\r\n"
    603               "Host: www.google.com\r\n"
    604               "Proxy-Connection: keep-alive\r\n"
    605               "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
    606   };
    607   MockRead reads[] = {
    608     MockRead(ASYNC, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
    609   };
    610   scoped_ptr<SpdyFrame> req(
    611       spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
    612   scoped_ptr<SpdyFrame> rst(
    613       spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
    614   MockWrite spdy_writes[] = {
    615     CreateMockWrite(*req, 0, ASYNC),
    616     CreateMockWrite(*rst, 2, ASYNC),
    617   };
    618   scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdySynReplyError(1));
    619   MockRead spdy_reads[] = {
    620     CreateMockRead(*resp, 1, ASYNC),
    621     MockRead(ASYNC, 0, 3),
    622   };
    623 
    624   Initialize(reads, arraysize(reads), writes, arraysize(writes),
    625              spdy_reads, arraysize(spdy_reads), spdy_writes,
    626              arraysize(spdy_writes));
    627   AddAuthToCache();
    628 
    629   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    630                         &pool_, BoundNetLog());
    631   EXPECT_EQ(ERR_IO_PENDING, rv);
    632   EXPECT_FALSE(handle_.is_initialized());
    633   EXPECT_FALSE(handle_.socket());
    634 
    635   data_->RunFor(2);
    636 
    637   rv = callback_.WaitForResult();
    638   // All Proxy CONNECT responses are not trustworthy
    639   EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
    640   EXPECT_FALSE(handle_.is_initialized());
    641   EXPECT_FALSE(handle_.socket());
    642 }
    643 
    644 TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupRedirect) {
    645   const std::string redirectTarget = "https://foo.google.com/";
    646 
    647   const std::string responseText = "HTTP/1.1 302 Found\r\n"
    648                                    "Location: " + redirectTarget + "\r\n"
    649                                    "Set-Cookie: foo=bar\r\n"
    650                                    "\r\n";
    651   MockWrite writes[] = {
    652     MockWrite(ASYNC, 0,
    653               "CONNECT www.google.com:443 HTTP/1.1\r\n"
    654               "Host: www.google.com\r\n"
    655               "Proxy-Connection: keep-alive\r\n"
    656               "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
    657   };
    658   MockRead reads[] = {
    659     MockRead(ASYNC, 1, responseText.c_str()),
    660   };
    661   scoped_ptr<SpdyFrame> req(
    662       spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW));
    663   scoped_ptr<SpdyFrame> rst(
    664       spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
    665 
    666   MockWrite spdy_writes[] = {
    667     CreateMockWrite(*req, 0, ASYNC),
    668     CreateMockWrite(*rst, 3, ASYNC),
    669   };
    670 
    671   const char* const responseHeaders[] = {
    672     "location", redirectTarget.c_str(),
    673     "set-cookie", "foo=bar",
    674   };
    675   const int responseHeadersSize = arraysize(responseHeaders) / 2;
    676   scoped_ptr<SpdyFrame> resp(
    677       spdy_util_.ConstructSpdySynReplyError(
    678           "302 Found",
    679           responseHeaders, responseHeadersSize,
    680           1));
    681   MockRead spdy_reads[] = {
    682     CreateMockRead(*resp, 1, ASYNC),
    683     MockRead(ASYNC, 0, 2),
    684   };
    685 
    686   Initialize(reads, arraysize(reads), writes, arraysize(writes),
    687              spdy_reads, arraysize(spdy_reads), spdy_writes,
    688              arraysize(spdy_writes));
    689   AddAuthToCache();
    690 
    691   int rv = handle_.Init("a", CreateTunnelParams(), LOW, callback_.callback(),
    692                         &pool_, BoundNetLog());
    693   EXPECT_EQ(ERR_IO_PENDING, rv);
    694   EXPECT_FALSE(handle_.is_initialized());
    695   EXPECT_FALSE(handle_.socket());
    696 
    697   data_->RunFor(2);
    698 
    699   rv = callback_.WaitForResult();
    700 
    701   if (GetParam().proxy_type == HTTP) {
    702     // We don't trust 302 responses to CONNECT from HTTP proxies.
    703     EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
    704     EXPECT_FALSE(handle_.is_initialized());
    705     EXPECT_FALSE(handle_.socket());
    706   } else {
    707     // Expect ProxyClientSocket to return the proxy's response, sanitized.
    708     EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE, rv);
    709     EXPECT_TRUE(handle_.is_initialized());
    710     ASSERT_TRUE(handle_.socket());
    711 
    712     const ProxyClientSocket* tunnel_socket =
    713         static_cast<ProxyClientSocket*>(handle_.socket());
    714     const HttpResponseInfo* response = tunnel_socket->GetConnectResponseInfo();
    715     const HttpResponseHeaders* headers = response->headers.get();
    716 
    717     // Make sure Set-Cookie header was stripped.
    718     EXPECT_FALSE(headers->HasHeader("set-cookie"));
    719 
    720     // Make sure Content-Length: 0 header was added.
    721     EXPECT_TRUE(headers->HasHeaderValue("content-length", "0"));
    722 
    723     // Make sure Location header was included and correct.
    724     std::string location;
    725     EXPECT_TRUE(headers->IsRedirect(&location));
    726     EXPECT_EQ(location, redirectTarget);
    727   }
    728 }
    729 
    730 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.
    731 
    732 }  // namespace
    733 
    734 }  // namespace net
    735