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