• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
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_connect_job.h"
6 
7 #include <algorithm>
8 #include <map>
9 #include <string>
10 #include <utility>
11 
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/field_trial_param_associator.h"
14 #include "base/metrics/field_trial_params.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/test/metrics/histogram_tester.h"
20 #include "base/test/task_environment.h"
21 #include "base/time/time.h"
22 #include "build/build_config.h"
23 #include "net/base/host_port_pair.h"
24 #include "net/base/network_anonymization_key.h"
25 #include "net/base/proxy_chain.h"
26 #include "net/base/proxy_string_util.h"
27 #include "net/base/test_proxy_delegate.h"
28 #include "net/dns/mock_host_resolver.h"
29 #include "net/dns/public/secure_dns_policy.h"
30 #include "net/http/http_network_session.h"
31 #include "net/http/http_response_headers.h"
32 #include "net/nqe/network_quality_estimator_test_util.h"
33 #include "net/socket/client_socket_handle.h"
34 #include "net/socket/connect_job_test_util.h"
35 #include "net/socket/socket_test_util.h"
36 #include "net/socket/socks_connect_job.h"
37 #include "net/socket/ssl_client_socket.h"
38 #include "net/socket/ssl_connect_job.h"
39 #include "net/socket/transport_connect_job.h"
40 #include "net/spdy/spdy_test_util_common.h"
41 #include "net/test/cert_test_util.h"
42 #include "net/test/gtest_util.h"
43 #include "net/test/test_data_directory.h"
44 #include "net/test/test_with_task_environment.h"
45 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "url/gurl.h"
48 #include "url/scheme_host_port.h"
49 
50 namespace net {
51 
52 namespace {
53 
54 const char kEndpointHost[] = "www.endpoint.test";
55 
56 enum HttpProxyType { HTTP, HTTPS, SPDY };
57 
58 const char kHttpProxyHost[] = "httpproxy.example.test";
59 const char kHttpsProxyHost[] = "httpsproxy.example.test";
60 const char kHttpsNestedProxyHost[] = "last-hop-https-proxy.example.test";
61 
62 const ProxyServer kHttpProxyServer{ProxyServer::SCHEME_HTTP,
63                                    HostPortPair(kHttpProxyHost, 80)};
64 const ProxyServer kHttpsProxyServer{ProxyServer::SCHEME_HTTPS,
65                                     HostPortPair(kHttpsProxyHost, 443)};
66 const ProxyServer kHttpsNestedProxyServer{
67     ProxyServer::SCHEME_HTTPS, HostPortPair(kHttpsNestedProxyHost, 443)};
68 
69 const ProxyChain kHttpProxyChain{kHttpProxyServer};
70 const ProxyChain kHttpsProxyChain{kHttpsProxyServer};
71 const ProxyChain kHttpsNestedProxyChain{
72     {kHttpsProxyServer, kHttpsNestedProxyServer}};
73 
74 }  // namespace
75 
76 class HttpProxyConnectJobTest : public ::testing::TestWithParam<HttpProxyType>,
77                                 public WithTaskEnvironment {
78  protected:
HttpProxyConnectJobTest()79   HttpProxyConnectJobTest()
80       : WithTaskEnvironment(
81             base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
82     // Used a mock HostResolver that does not have a cache.
83     session_deps_.host_resolver = std::make_unique<MockHostResolver>(
84         /*default_result=*/MockHostResolverBase::RuleResolver::
85             GetLocalhostResult());
86 
87     network_quality_estimator_ =
88         std::make_unique<TestNetworkQualityEstimator>();
89     session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
90     InitCommonConnectJobParams();
91   }
92 
~HttpProxyConnectJobTest()93   ~HttpProxyConnectJobTest() override {
94     // Reset global field trial parameters to defaults values.
95     base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
96     HttpProxyConnectJob::UpdateFieldTrialParametersForTesting();
97   }
98 
99   // Initializes the field trial parameters for the field trial that determines
100   // connection timeout based on the network quality.
InitAdaptiveTimeoutFieldTrialWithParams(bool use_default_params,int ssl_http_rtt_multiplier,int non_ssl_http_rtt_multiplier,base::TimeDelta min_proxy_connection_timeout,base::TimeDelta max_proxy_connection_timeout)101   void InitAdaptiveTimeoutFieldTrialWithParams(
102       bool use_default_params,
103       int ssl_http_rtt_multiplier,
104       int non_ssl_http_rtt_multiplier,
105       base::TimeDelta min_proxy_connection_timeout,
106       base::TimeDelta max_proxy_connection_timeout) {
107     std::string trial_name = "NetAdaptiveProxyConnectionTimeout";
108     std::string group_name = "GroupName";
109 
110     std::map<std::string, std::string> params;
111     if (!use_default_params) {
112       params["ssl_http_rtt_multiplier"] =
113           base::NumberToString(ssl_http_rtt_multiplier);
114       params["non_ssl_http_rtt_multiplier"] =
115           base::NumberToString(non_ssl_http_rtt_multiplier);
116       params["min_proxy_connection_timeout_seconds"] =
117           base::NumberToString(min_proxy_connection_timeout.InSeconds());
118       params["max_proxy_connection_timeout_seconds"] =
119           base::NumberToString(max_proxy_connection_timeout.InSeconds());
120     }
121     base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
122     EXPECT_TRUE(
123         base::AssociateFieldTrialParams(trial_name, group_name, params));
124     EXPECT_TRUE(base::FieldTrialList::CreateFieldTrial(trial_name, group_name));
125 
126     // Force static global that reads the field trials to update.
127     HttpProxyConnectJob::UpdateFieldTrialParametersForTesting();
128   }
129 
CreateHttpProxyParams(SecureDnsPolicy secure_dns_policy) const130   scoped_refptr<TransportSocketParams> CreateHttpProxyParams(
131       SecureDnsPolicy secure_dns_policy) const {
132     if (GetParam() != HTTP) {
133       return nullptr;
134     }
135     return base::MakeRefCounted<TransportSocketParams>(
136         kHttpProxyServer.host_port_pair(), NetworkAnonymizationKey(),
137         secure_dns_policy, OnHostResolutionCallback(),
138         /*supported_alpns=*/base::flat_set<std::string>());
139   }
140 
CreateHttpsProxyParams(SecureDnsPolicy secure_dns_policy) const141   scoped_refptr<SSLSocketParams> CreateHttpsProxyParams(
142       SecureDnsPolicy secure_dns_policy) const {
143     if (GetParam() == HTTP) {
144       return nullptr;
145     }
146     return base::MakeRefCounted<SSLSocketParams>(
147         base::MakeRefCounted<TransportSocketParams>(
148             kHttpsProxyServer.host_port_pair(), NetworkAnonymizationKey(),
149             secure_dns_policy, OnHostResolutionCallback(),
150             /*supported_alpns=*/base::flat_set<std::string>()),
151         nullptr, nullptr, HostPortPair(kHttpsProxyHost, 443), SSLConfig(),
152         PRIVACY_MODE_DISABLED, NetworkAnonymizationKey());
153   }
154 
155   // Returns a correctly constructed HttpProxyParams for a single HTTP or HTTPS
156   // proxy.
CreateParams(bool tunnel,SecureDnsPolicy secure_dns_policy)157   scoped_refptr<HttpProxySocketParams> CreateParams(
158       bool tunnel,
159       SecureDnsPolicy secure_dns_policy) {
160     return base::MakeRefCounted<HttpProxySocketParams>(
161         CreateHttpProxyParams(secure_dns_policy),
162         CreateHttpsProxyParams(secure_dns_policy),
163         HostPortPair(kEndpointHost, tunnel ? 443 : 80),
164         GetParam() == HTTP ? kHttpProxyChain : kHttpsProxyChain,
165         /*proxy_chain_index=*/0, tunnel, TRAFFIC_ANNOTATION_FOR_TESTS,
166         NetworkAnonymizationKey(), secure_dns_policy);
167   }
168 
169   // Creates a correctly constructed `SSLSocketParams()` corresponding to the
170   // proxy server in `proxy_chain` at index `proxy_chain_index`.
CreateNestedHttpsProxyParams(bool tunnel,SecureDnsPolicy secure_dns_policy,const ProxyChain & proxy_chain,size_t proxy_chain_index) const171   scoped_refptr<SSLSocketParams> CreateNestedHttpsProxyParams(
172       bool tunnel,
173       SecureDnsPolicy secure_dns_policy,
174       const ProxyChain& proxy_chain,
175       size_t proxy_chain_index) const {
176     DCHECK_NE(GetParam(), HTTP);
177 
178     scoped_refptr<TransportSocketParams> transport_params;
179     scoped_refptr<HttpProxySocketParams> http_proxy_params;
180 
181     const ProxyServer& proxy_server =
182         proxy_chain.GetProxyServer(proxy_chain_index);
183 
184     if (proxy_chain_index != 0) {
185       // For all but the first hop in a multi-hop proxy, the SSLSocketParams
186       // should be created such that it tunnels over a direct encrypted
187       // connection made to the first hop (possibly via intermediate tunnels
188       // through other hops)... Build an HttpProxySocketParams for the
189       // previous hop that will establish this.
190       size_t previous_hop_proxy_chain_index = proxy_chain_index - 1;
191 
192       transport_params = nullptr;
193       http_proxy_params =
194           CreateNestedParams(tunnel, secure_dns_policy, proxy_chain,
195                              previous_hop_proxy_chain_index);
196     } else {
197       // If we are creating the SSLSocketParams for the first hop, establish a
198       // direct encrypted connection to it.
199       transport_params = base::MakeRefCounted<TransportSocketParams>(
200           proxy_server.host_port_pair(), NetworkAnonymizationKey(),
201           secure_dns_policy, OnHostResolutionCallback(),
202           /*supported_alpns=*/base::flat_set<std::string>());
203       http_proxy_params = nullptr;
204     }
205     return base::MakeRefCounted<SSLSocketParams>(
206         std::move(transport_params),
207         /*socks_proxy_params=*/nullptr, std::move(http_proxy_params),
208         proxy_server.host_port_pair(), SSLConfig(), PRIVACY_MODE_DISABLED,
209         NetworkAnonymizationKey());
210   }
211 
212   // Creates a correctly constructed `HttpProxySocketParams()` corresponding to
213   // the proxy server in `proxy_chain` at index `proxy_chain_index` (and set to
214   // create a CONNECT for either the next hop in the proxy or to
215   // `kEndpointHost`).
CreateNestedParams(bool tunnel,SecureDnsPolicy secure_dns_policy,const ProxyChain & proxy_chain,size_t proxy_chain_index) const216   scoped_refptr<HttpProxySocketParams> CreateNestedParams(
217       bool tunnel,
218       SecureDnsPolicy secure_dns_policy,
219       const ProxyChain& proxy_chain,
220       size_t proxy_chain_index) const {
221     DCHECK_NE(GetParam(), HTTP);
222     HostPortPair connect_host_port_pair;
223     scoped_refptr<SSLSocketParams> ssl_params = CreateNestedHttpsProxyParams(
224         tunnel, secure_dns_policy, proxy_chain, proxy_chain_index);
225     if (proxy_chain_index + 1 != proxy_chain.length()) {
226       // For all but the last hop in the proxy, what we CONNECT to is the next
227       // hop in the proxy.
228       size_t next_hop_proxy_chain_index = proxy_chain_index + 1;
229       const ProxyServer& next_hop_proxy_server =
230           proxy_chain.GetProxyServer(next_hop_proxy_chain_index);
231       connect_host_port_pair = next_hop_proxy_server.host_port_pair();
232     } else {
233       // If we aren't testing multi-hop proxies or this HttpProxySocketParams
234       // corresponds to the last hop, then we need to CONNECT to the
235       // destination site.
236       connect_host_port_pair = HostPortPair(kEndpointHost, tunnel ? 443 : 80);
237     }
238     return base::MakeRefCounted<HttpProxySocketParams>(
239         nullptr, std::move(ssl_params), connect_host_port_pair, proxy_chain,
240         proxy_chain_index, tunnel, TRAFFIC_ANNOTATION_FOR_TESTS,
241         NetworkAnonymizationKey(), secure_dns_policy);
242   }
243 
CreateConnectJobForHttpRequest(ConnectJob::Delegate * delegate,RequestPriority priority=DEFAULT_PRIORITY,SecureDnsPolicy secure_dns_policy=SecureDnsPolicy::kAllow)244   std::unique_ptr<HttpProxyConnectJob> CreateConnectJobForHttpRequest(
245       ConnectJob::Delegate* delegate,
246       RequestPriority priority = DEFAULT_PRIORITY,
247       SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
248     return CreateConnectJob(CreateParams(false /* tunnel */, secure_dns_policy),
249                             delegate, priority);
250   }
251 
CreateConnectJobForTunnel(ConnectJob::Delegate * delegate,RequestPriority priority=DEFAULT_PRIORITY,SecureDnsPolicy secure_dns_policy=SecureDnsPolicy::kAllow)252   std::unique_ptr<HttpProxyConnectJob> CreateConnectJobForTunnel(
253       ConnectJob::Delegate* delegate,
254       RequestPriority priority = DEFAULT_PRIORITY,
255       SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
256     return CreateConnectJob(CreateParams(true /* tunnel */, secure_dns_policy),
257                             delegate, priority);
258   }
259 
260   // Creates an HttpProxyConnectJob corresponding to `kHttpsNestedProxyChain`.
261   // This is done by working backwards through the proxy chain and creating
262   // socket params such that connect jobs will be created recursively with
263   // dependencies in the correct order (in other words, the inner-most connect
264   // job will establish a connection to the first proxy, and then that
265   // connection will get used to establish a connection to the second proxy, and
266   // finally a connection will be established to the destination).
CreateConnectJobForNestedProxyTunnel(ConnectJob::Delegate * delegate,RequestPriority priority=DEFAULT_PRIORITY,SecureDnsPolicy secure_dns_policy=SecureDnsPolicy::kAllow)267   std::unique_ptr<HttpProxyConnectJob> CreateConnectJobForNestedProxyTunnel(
268       ConnectJob::Delegate* delegate,
269       RequestPriority priority = DEFAULT_PRIORITY,
270       SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
271     size_t last_hop_proxy_server_index = kHttpsNestedProxyChain.length() - 1;
272     return CreateConnectJob(
273         CreateNestedParams(/*tunnel=*/true, secure_dns_policy,
274                            kHttpsNestedProxyChain, last_hop_proxy_server_index),
275         delegate, priority);
276   }
277 
CreateConnectJob(scoped_refptr<HttpProxySocketParams> http_proxy_socket_params,ConnectJob::Delegate * delegate,RequestPriority priority)278   std::unique_ptr<HttpProxyConnectJob> CreateConnectJob(
279       scoped_refptr<HttpProxySocketParams> http_proxy_socket_params,
280       ConnectJob::Delegate* delegate,
281       RequestPriority priority) {
282     return std::make_unique<HttpProxyConnectJob>(
283         priority, SocketTag(), common_connect_job_params_.get(),
284         std::move(http_proxy_socket_params), delegate, nullptr /* net_log */);
285   }
286 
287   // This may only be called at the start of the test, before any ConnectJobs
288   // have been created.
InitProxyDelegate()289   void InitProxyDelegate() {
290     proxy_delegate_ = std::make_unique<TestProxyDelegate>();
291     InitCommonConnectJobParams();
292   }
293 
294   // This may only be called at the start of the test, before any ConnectJobs
295   // have been created.
InitCommonConnectJobParams()296   void InitCommonConnectJobParams() {
297     common_connect_job_params_ = std::make_unique<CommonConnectJobParams>(
298         session_->CreateCommonConnectJobParams());
299     // TODO(mmenke): Consider reworking this so it can be done through
300     // |session_deps_|.
301     common_connect_job_params_->proxy_delegate = proxy_delegate_.get();
302     common_connect_job_params_->network_quality_estimator =
303         network_quality_estimator_.get();
304   }
305 
Initialize(base::span<const MockRead> reads,base::span<const MockWrite> writes,base::span<const MockRead> spdy_reads,base::span<const MockWrite> spdy_writes,IoMode connect_and_ssl_io_mode,bool two_ssl_proxies=false)306   void Initialize(base::span<const MockRead> reads,
307                   base::span<const MockWrite> writes,
308                   base::span<const MockRead> spdy_reads,
309                   base::span<const MockWrite> spdy_writes,
310                   IoMode connect_and_ssl_io_mode,
311                   bool two_ssl_proxies = false) {
312     if (GetParam() == SPDY) {
313       data_ = std::make_unique<SequencedSocketData>(spdy_reads, spdy_writes);
314     } else {
315       data_ = std::make_unique<SequencedSocketData>(reads, writes);
316     }
317 
318     data_->set_connect_data(MockConnect(connect_and_ssl_io_mode, OK));
319 
320     session_deps_.socket_factory->AddSocketDataProvider(data_.get());
321 
322     if (GetParam() != HTTP) {
323       // Keep the old ssl_data in case there is a draining socket.
324       old_ssl_data_.swap(ssl_data_);
325       ssl_data_ =
326           std::make_unique<SSLSocketDataProvider>(connect_and_ssl_io_mode, OK);
327       if (GetParam() == SPDY) {
328         InitializeSpdySsl(ssl_data_.get());
329       }
330       session_deps_.socket_factory->AddSSLSocketDataProvider(ssl_data_.get());
331     }
332 
333     if (two_ssl_proxies) {
334       // For testing nested proxies we need another SSLSocketDataProvider
335       // corresponding to the SSL connection established to the second hop in
336       // the proxy.
337       nested_second_proxy_ssl_data_ =
338           std::make_unique<SSLSocketDataProvider>(connect_and_ssl_io_mode, OK);
339       if (GetParam() == SPDY) {
340         InitializeSpdySsl(nested_second_proxy_ssl_data_.get());
341       }
342       session_deps_.socket_factory->AddSSLSocketDataProvider(
343           nested_second_proxy_ssl_data_.get());
344     }
345   }
346 
InitializeSpdySsl(SSLSocketDataProvider * ssl_data)347   void InitializeSpdySsl(SSLSocketDataProvider* ssl_data) {
348     ssl_data->next_proto = kProtoHTTP2;
349   }
350 
351   // Return the timeout for establishing the lower layer connection. i.e., for
352   // an HTTP proxy, the TCP connection timeout, and for an HTTPS proxy, the
353   // TCP+SSL connection timeout. In many cases, this will return the return
354   // value of the "AlternateNestedConnectionTimeout()".
GetNestedConnectionTimeout()355   base::TimeDelta GetNestedConnectionTimeout() {
356     base::TimeDelta normal_nested_connection_timeout =
357         TransportConnectJob::ConnectionTimeout();
358     if (GetParam() != HTTP) {
359       normal_nested_connection_timeout +=
360           SSLConnectJob::HandshakeTimeoutForTesting();
361     }
362 
363     // Doesn't actually matter whether or not this is for a tunnel - the
364     // connection timeout is the same, though it probably shouldn't be the
365     // same, since tunnels need an extra round trip.
366     base::TimeDelta alternate_connection_timeout =
367         HttpProxyConnectJob::AlternateNestedConnectionTimeout(
368             *CreateParams(true /* tunnel */, SecureDnsPolicy::kAllow),
369             network_quality_estimator_.get());
370 
371     // If there's an alternate connection timeout, and it's less than the
372     // standard TCP+SSL timeout (Which is also applied by the nested connect
373     // jobs), return the alternate connection timeout. Otherwise, return the
374     // normal timeout.
375     if (!alternate_connection_timeout.is_zero() &&
376         alternate_connection_timeout < normal_nested_connection_timeout) {
377       return alternate_connection_timeout;
378     }
379 
380     return normal_nested_connection_timeout;
381   }
382 
383  protected:
384   std::unique_ptr<TestProxyDelegate> proxy_delegate_;
385 
386   std::unique_ptr<SSLSocketDataProvider> ssl_data_;
387   std::unique_ptr<SSLSocketDataProvider> old_ssl_data_;
388   std::unique_ptr<SSLSocketDataProvider> nested_second_proxy_ssl_data_;
389   std::unique_ptr<SequencedSocketData> data_;
390   SpdySessionDependencies session_deps_;
391 
392   std::unique_ptr<TestNetworkQualityEstimator> network_quality_estimator_;
393 
394   std::unique_ptr<HttpNetworkSession> session_;
395 
396   SpdyTestUtil spdy_util_;
397 
398   TestCompletionCallback callback_;
399 
400   std::unique_ptr<CommonConnectJobParams> common_connect_job_params_;
401 };
402 
403 // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
404 // and SPDY.
405 INSTANTIATE_TEST_SUITE_P(HttpProxyType,
406                          HttpProxyConnectJobTest,
407                          ::testing::Values(HTTP, HTTPS, SPDY));
408 
TEST_P(HttpProxyConnectJobTest,NoTunnel)409 TEST_P(HttpProxyConnectJobTest, NoTunnel) {
410   InitProxyDelegate();
411   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
412     SCOPED_TRACE(io_mode);
413     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
414     base::HistogramTester histogram_tester;
415 
416     Initialize(base::span<MockRead>(), base::span<MockWrite>(),
417                base::span<MockRead>(), base::span<MockWrite>(), io_mode);
418 
419     TestConnectJobDelegate test_delegate;
420     std::unique_ptr<ConnectJob> connect_job =
421         CreateConnectJobForHttpRequest(&test_delegate);
422     test_delegate.StartJobExpectingResult(connect_job.get(), OK,
423                                           io_mode == SYNCHRONOUS);
424     EXPECT_FALSE(proxy_delegate_->on_before_tunnel_request_called());
425 
426     // Proxies should not set any DNS aliases.
427     EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
428 
429     bool is_secure_proxy = GetParam() == HTTPS || GetParam() == SPDY;
430     histogram_tester.ExpectTotalCount(
431         "Net.HttpProxy.ConnectLatency.Insecure.Success",
432         is_secure_proxy ? 0 : 1);
433     histogram_tester.ExpectTotalCount(
434         "Net.HttpProxy.ConnectLatency.Secure.Success", is_secure_proxy ? 1 : 0);
435   }
436 }
437 
438 // Pauses an HttpProxyConnectJob at various states, and check the value of
439 // HasEstablishedConnection().
TEST_P(HttpProxyConnectJobTest,HasEstablishedConnectionNoTunnel)440 TEST_P(HttpProxyConnectJobTest, HasEstablishedConnectionNoTunnel) {
441   session_deps_.host_resolver->set_ondemand_mode(true);
442 
443   SequencedSocketData data;
444   data.set_connect_data(MockConnect(ASYNC, OK));
445   session_deps_.socket_factory->AddSocketDataProvider(&data);
446 
447   // Set up SSL, if needed.
448   SSLSocketDataProvider ssl_data(ASYNC, OK);
449   switch (GetParam()) {
450     case HTTP:
451       // No SSL needed.
452       break;
453     case HTTPS:
454       // SSL negotiation is the last step in non-tunnel connections over HTTPS
455       // proxies, so pause there, to check the final state before completion.
456       ssl_data = SSLSocketDataProvider(SYNCHRONOUS, ERR_IO_PENDING);
457       session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
458       break;
459     case SPDY:
460       InitializeSpdySsl(&ssl_data);
461       session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
462       break;
463   }
464 
465   TestConnectJobDelegate test_delegate;
466   std::unique_ptr<ConnectJob> connect_job =
467       CreateConnectJobForHttpRequest(&test_delegate);
468 
469   // Connecting should run until the request hits the HostResolver.
470   EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
471   EXPECT_FALSE(test_delegate.has_result());
472   EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
473   EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
474   EXPECT_FALSE(connect_job->HasEstablishedConnection());
475 
476   // Once the HostResolver completes, the job should start establishing a
477   // connection, which will complete asynchronously.
478   session_deps_.host_resolver->ResolveOnlyRequestNow();
479   EXPECT_FALSE(test_delegate.has_result());
480   EXPECT_EQ(LOAD_STATE_CONNECTING, connect_job->GetLoadState());
481   EXPECT_FALSE(connect_job->HasEstablishedConnection());
482 
483   switch (GetParam()) {
484     case HTTP:
485     case SPDY:
486       // Connection completes. Since no tunnel is established, the socket is
487       // returned immediately, and HasEstablishedConnection() is only specified
488       // to work before the ConnectJob completes.
489       EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
490       break;
491     case HTTPS:
492       base::RunLoop().RunUntilIdle();
493       EXPECT_FALSE(test_delegate.has_result());
494       EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, connect_job->GetLoadState());
495       EXPECT_TRUE(connect_job->HasEstablishedConnection());
496 
497       // Unfortunately, there's no API to advance the paused SSL negotiation,
498       // so just end the test here.
499   }
500 }
501 
502 // Pauses an HttpProxyConnectJob at various states, and check the value of
503 // HasEstablishedConnection().
TEST_P(HttpProxyConnectJobTest,HasEstablishedConnectionTunnel)504 TEST_P(HttpProxyConnectJobTest, HasEstablishedConnectionTunnel) {
505   session_deps_.host_resolver->set_ondemand_mode(true);
506 
507   // HTTP proxy CONNECT request / response, with a pause during the read.
508   MockWrite http1_writes[] = {
509       MockWrite(ASYNC, 0,
510                 "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
511                 "Host: www.endpoint.test:443\r\n"
512                 "Proxy-Connection: keep-alive\r\n\r\n"),
513   };
514   MockRead http1_reads[] = {
515       // Pause at first read.
516       MockRead(ASYNC, ERR_IO_PENDING, 1),
517       MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
518   };
519   SequencedSocketData http1_data(http1_reads, http1_writes);
520   http1_data.set_connect_data(MockConnect(ASYNC, OK));
521 
522   // SPDY proxy CONNECT request / response, with a pause during the read.
523   spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
524       nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
525       HostPortPair(kEndpointHost, 443)));
526   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
527   spdy::SpdySerializedFrame resp(
528       spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
529   MockRead spdy_reads[] = {
530       // Pause at first read.
531       MockRead(ASYNC, ERR_IO_PENDING, 1),
532       CreateMockRead(resp, 2, ASYNC),
533       MockRead(ASYNC, 0, 3),
534   };
535   SequencedSocketData spdy_data(spdy_reads, spdy_writes);
536   spdy_data.set_connect_data(MockConnect(ASYNC, OK));
537 
538   // Will point to either the HTTP/1.x or SPDY data, depending on GetParam().
539   SequencedSocketData* sequenced_data = nullptr;
540 
541   SSLSocketDataProvider ssl_data(ASYNC, OK);
542   ssl_data.ssl_info.cert =
543       ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
544   ASSERT_TRUE(ssl_data.ssl_info.cert);
545 
546   switch (GetParam()) {
547     case HTTP:
548       sequenced_data = &http1_data;
549       break;
550     case HTTPS:
551       sequenced_data = &http1_data;
552       ssl_data.next_proto = NextProto::kProtoHTTP11;
553       session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
554       break;
555     case SPDY:
556       sequenced_data = &spdy_data;
557       InitializeSpdySsl(&ssl_data);
558       session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
559       break;
560   }
561 
562   session_deps_.socket_factory->AddSocketDataProvider(sequenced_data);
563 
564   TestConnectJobDelegate test_delegate;
565   std::unique_ptr<ConnectJob> connect_job =
566       CreateConnectJobForTunnel(&test_delegate);
567 
568   // Connecting should run until the request hits the HostResolver.
569   EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
570   EXPECT_FALSE(test_delegate.has_result());
571   EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
572   EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
573   EXPECT_FALSE(connect_job->HasEstablishedConnection());
574 
575   // Once the HostResolver completes, the job should start establishing a
576   // connection, which will complete asynchronously.
577   session_deps_.host_resolver->ResolveOnlyRequestNow();
578   EXPECT_FALSE(test_delegate.has_result());
579   EXPECT_EQ(LOAD_STATE_CONNECTING, connect_job->GetLoadState());
580   EXPECT_FALSE(connect_job->HasEstablishedConnection());
581 
582   // Run until the socket starts reading the proxy's handshake response.
583   sequenced_data->RunUntilPaused();
584   EXPECT_FALSE(test_delegate.has_result());
585   EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL, connect_job->GetLoadState());
586   EXPECT_TRUE(connect_job->HasEstablishedConnection());
587 
588   // Finish the read, and run the job until it's complete.
589   sequenced_data->Resume();
590   EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
591 
592   // Proxies should not set any DNS aliases.
593   EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
594 
595   // Although the underlying proxy connection may use TLS or negotiate ALPN, the
596   // tunnel itself is a TCP connection to the origin and should not report these
597   // values.
598   SSLInfo ssl_info;
599   EXPECT_FALSE(test_delegate.socket()->GetSSLInfo(&ssl_info));
600   EXPECT_EQ(test_delegate.socket()->GetNegotiatedProtocol(),
601             NextProto::kProtoUnknown);
602 }
603 
TEST_P(HttpProxyConnectJobTest,ProxyDelegateExtraHeaders)604 TEST_P(HttpProxyConnectJobTest, ProxyDelegateExtraHeaders) {
605   InitProxyDelegate();
606 
607   ProxyServer proxy_server(
608       GetParam() == HTTP ? ProxyServer::SCHEME_HTTP : ProxyServer::SCHEME_HTTPS,
609       HostPortPair(GetParam() == HTTP ? kHttpProxyHost : kHttpsProxyHost,
610                    GetParam() == HTTP ? 80 : 443));
611   std::string proxy_server_uri = ProxyServerToProxyUri(proxy_server);
612 
613   std::string http1_request = base::StringPrintf(
614       "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
615       "Host: www.endpoint.test:443\r\n"
616       "Proxy-Connection: keep-alive\r\n"
617       "%s: %s\r\n\r\n",
618       TestProxyDelegate::kTestHeaderName, proxy_server_uri.c_str());
619   MockWrite writes[] = {
620       MockWrite(ASYNC, 0, http1_request.c_str()),
621   };
622 
623   const char kResponseHeaderName[] = "bar";
624   const char kResponseHeaderValue[] = "Response";
625   std::string http1_response = base::StringPrintf(
626       "HTTP/1.1 200 Connection Established\r\n"
627       "%s: %s\r\n\r\n",
628       kResponseHeaderName, kResponseHeaderValue);
629   MockRead reads[] = {
630       MockRead(ASYNC, 1, http1_response.c_str()),
631   };
632 
633   const char* const kExtraRequestHeaders[] = {
634       TestProxyDelegate::kTestSpdyHeaderName,
635       proxy_server_uri.c_str(),
636   };
637   const char* const kExtraResponseHeaders[] = {
638       kResponseHeaderName,
639       kResponseHeaderValue,
640   };
641   spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
642       kExtraRequestHeaders, std::size(kExtraRequestHeaders) / 2, 1,
643       HttpProxyConnectJob::kH2QuicTunnelPriority,
644       HostPortPair(kEndpointHost, 443)));
645   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
646   spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(
647       kExtraResponseHeaders, std::size(kExtraResponseHeaders) / 2, 1));
648   MockRead spdy_reads[] = {
649       CreateMockRead(resp, 1, ASYNC),
650       MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2),
651   };
652 
653   Initialize(reads, writes, spdy_reads, spdy_writes, ASYNC);
654 
655   TestConnectJobDelegate test_delegate;
656   std::unique_ptr<ConnectJob> connect_job =
657       CreateConnectJobForTunnel(&test_delegate);
658   test_delegate.StartJobExpectingResult(connect_job.get(), OK,
659                                         false /* expect_sync_result */);
660 
661   ASSERT_EQ(proxy_delegate_->on_tunnel_headers_received_call_count(), 1u);
662   proxy_delegate_->VerifyOnTunnelHeadersReceived(
663       ProxyChain(proxy_server), 0, kResponseHeaderName, kResponseHeaderValue);
664 }
665 
666 // Test HTTP CONNECTs and SPDY CONNECTs through two proxies
667 // (HTTPS -> HTTPS -> HTTPS and SPDY -> SPDY -> HTTPS).
TEST_P(HttpProxyConnectJobTest,NestedProxyProxyDelegateExtraHeaders)668 TEST_P(HttpProxyConnectJobTest, NestedProxyProxyDelegateExtraHeaders) {
669   if (GetParam() == HTTP) {
670     return;
671   }
672   InitProxyDelegate();
673 
674   const ProxyServer& first_hop_proxy_server =
675       kHttpsNestedProxyChain.GetProxyServer(/*chain_index=*/0);
676   const ProxyServer& second_hop_proxy_server =
677       kHttpsNestedProxyChain.GetProxyServer(/*chain_index=*/1);
678 
679   std::string first_hop_proxy_server_uri =
680       ProxyServerToProxyUri(first_hop_proxy_server);
681   std::string second_hop_proxy_server_uri =
682       ProxyServerToProxyUri(second_hop_proxy_server);
683 
684   std::string first_hop_http1_request = base::StringPrintf(
685       "CONNECT last-hop-https-proxy.example.test:443 HTTP/1.1\r\n"
686       "Host: last-hop-https-proxy.example.test:443\r\n"
687       "Proxy-Connection: keep-alive\r\n"
688       "%s: %s\r\n\r\n",
689       TestProxyDelegate::kTestHeaderName, first_hop_proxy_server_uri.c_str());
690   std::string second_hop_http1_request = base::StringPrintf(
691       "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
692       "Host: www.endpoint.test:443\r\n"
693       "Proxy-Connection: keep-alive\r\n"
694       "%s: %s\r\n\r\n",
695       TestProxyDelegate::kTestHeaderName, second_hop_proxy_server_uri.c_str());
696 
697   const char kResponseHeaderName[] = "bar";
698   std::string first_hop_http1_response = base::StringPrintf(
699       "HTTP/1.1 200 Connection Established\r\n"
700       "%s: %s\r\n\r\n",
701       kResponseHeaderName, first_hop_proxy_server_uri.c_str());
702 
703   std::string second_hop_http1_response = base::StringPrintf(
704       "HTTP/1.1 200 Connection Established\r\n"
705       "%s: %s\r\n\r\n",
706       kResponseHeaderName, second_hop_proxy_server_uri.c_str());
707 
708   MockWrite writes[] = {
709       MockWrite(ASYNC, 0, first_hop_http1_request.c_str()),
710       MockWrite(ASYNC, 2, second_hop_http1_request.c_str()),
711   };
712 
713   MockRead reads[] = {
714       MockRead(ASYNC, 1, first_hop_http1_response.c_str()),
715       MockRead(ASYNC, 3, second_hop_http1_response.c_str()),
716   };
717 
718   const char* const kFirstHopExtraRequestHeaders[] = {
719       TestProxyDelegate::kTestSpdyHeaderName,
720       first_hop_proxy_server_uri.c_str(),
721   };
722   const char* const kSecondHopExtraRequestHeaders[] = {
723       TestProxyDelegate::kTestSpdyHeaderName,
724       second_hop_proxy_server_uri.c_str(),
725   };
726   const char* const kFirstHopExtraResponseHeaders[] = {
727       kResponseHeaderName,
728       first_hop_proxy_server_uri.c_str(),
729   };
730   const char* const kSecondHopExtraResponseHeaders[] = {
731       kResponseHeaderName,
732       second_hop_proxy_server_uri.c_str(),
733   };
734 
735   spdy::SpdySerializedFrame first_hop_req(spdy_util_.ConstructSpdyConnect(
736       kFirstHopExtraRequestHeaders, std::size(kFirstHopExtraRequestHeaders) / 2,
737       1, HttpProxyConnectJob::kH2QuicTunnelPriority,
738       second_hop_proxy_server.host_port_pair()));
739 
740   spdy::SpdySerializedFrame first_hop_resp(spdy_util_.ConstructSpdyGetReply(
741       kFirstHopExtraResponseHeaders,
742       std::size(kFirstHopExtraResponseHeaders) / 2, 1));
743 
744   // Use a new `SpdyTestUtil()` instance for the second hop response and request
745   // because otherwise, the serialized frames that get generated for these will
746   // use header compression and won't match what actually gets sent on the wire
747   // (where header compression doesn't affect these requests because they are
748   // associated with different streams).
749   SpdyTestUtil new_spdy_util;
750 
751   spdy::SpdySerializedFrame second_hop_req(new_spdy_util.ConstructSpdyConnect(
752       kSecondHopExtraRequestHeaders,
753       std::size(kSecondHopExtraRequestHeaders) / 2, 1,
754       HttpProxyConnectJob::kH2QuicTunnelPriority,
755       HostPortPair(kEndpointHost, 443)));
756 
757   // Since the second request and response are sent over the tunnel established
758   // previously, from a socket-perspective these need to be wrapped as data
759   // frames.
760   spdy::SpdySerializedFrame wrapped_second_hop_req(
761       new_spdy_util.ConstructWrappedSpdyFrame(second_hop_req, 1));
762 
763   spdy::SpdySerializedFrame second_hop_resp(new_spdy_util.ConstructSpdyGetReply(
764       kSecondHopExtraResponseHeaders,
765       std::size(kSecondHopExtraResponseHeaders) / 2, 1));
766 
767   spdy::SpdySerializedFrame wrapped_second_hop_resp(
768       new_spdy_util.ConstructWrappedSpdyFrame(second_hop_resp, 1));
769 
770   MockWrite spdy_writes[] = {
771       CreateMockWrite(first_hop_req, 0),
772       CreateMockWrite(wrapped_second_hop_req, 2),
773   };
774   MockRead spdy_reads[] = {
775       CreateMockRead(first_hop_resp, 1, ASYNC),
776       // TODO(https://crbug.com/497228): We have to manually delay this read so
777       // that the higher-level SPDY stream doesn't get notified of an available
778       // read before the write it initiated (the second CONNECT) finishes,
779       // triggering a DCHECK.
780       MockRead(ASYNC, ERR_IO_PENDING, 3),
781       CreateMockRead(wrapped_second_hop_resp, 4, ASYNC),
782       MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5),
783   };
784 
785   Initialize(reads, writes, spdy_reads, spdy_writes, ASYNC,
786              /*two_ssl_proxies=*/true);
787 
788   TestConnectJobDelegate test_delegate;
789   std::unique_ptr<ConnectJob> connect_job =
790       CreateConnectJobForNestedProxyTunnel(&test_delegate);
791 
792   if (GetParam() != SPDY) {
793     test_delegate.StartJobExpectingResult(connect_job.get(), OK,
794                                           /*expect_sync_result=*/false);
795   } else {
796     EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
797 
798     data_->RunUntilPaused();
799     base::RunLoop().RunUntilIdle();
800     data_->Resume();
801 
802     EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
803   }
804   ASSERT_EQ(proxy_delegate_->on_tunnel_headers_received_call_count(), 2u);
805   proxy_delegate_->VerifyOnTunnelHeadersReceived(
806       kHttpsNestedProxyChain, /*chain_index=*/0, kResponseHeaderName,
807       first_hop_proxy_server_uri, /*call_index=*/0);
808   proxy_delegate_->VerifyOnTunnelHeadersReceived(
809       kHttpsNestedProxyChain, /*chain_index=*/1, kResponseHeaderName,
810       second_hop_proxy_server_uri, /*call_index=*/1);
811 }
812 
813 // Test the case where auth credentials are not cached.
TEST_P(HttpProxyConnectJobTest,NeedAuth)814 TEST_P(HttpProxyConnectJobTest, NeedAuth) {
815   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
816     SCOPED_TRACE(io_mode);
817 
818     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
819 
820     MockWrite writes[] = {
821         MockWrite(io_mode, 0,
822                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
823                   "Host: www.endpoint.test:443\r\n"
824                   "Proxy-Connection: keep-alive\r\n\r\n"),
825         MockWrite(io_mode, 5,
826                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
827                   "Host: www.endpoint.test:443\r\n"
828                   "Proxy-Connection: keep-alive\r\n"
829                   "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
830     };
831     MockRead reads[] = {
832         // No credentials.
833         MockRead(io_mode, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
834         MockRead(io_mode, 2,
835                  "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
836         MockRead(io_mode, 3, "Content-Length: 10\r\n\r\n"),
837         MockRead(io_mode, 4, "0123456789"),
838         MockRead(io_mode, 6, "HTTP/1.1 200 Connection Established\r\n\r\n"),
839     };
840 
841     SpdyTestUtil spdy_util;
842     spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
843         nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
844         HostPortPair(kEndpointHost, 443)));
845     spdy::SpdySerializedFrame rst(
846         spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
847     spdy_util.UpdateWithStreamDestruction(1);
848 
849     // After calling trans.RestartWithAuth(), this is the request we should
850     // be issuing -- the final header line contains the credentials.
851     const char* const kSpdyAuthCredentials[] = {
852         "proxy-authorization",
853         "Basic Zm9vOmJhcg==",
854     };
855     spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect(
856         kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3,
857         HttpProxyConnectJob::kH2QuicTunnelPriority,
858         HostPortPair(kEndpointHost, 443)));
859 
860     MockWrite spdy_writes[] = {
861         CreateMockWrite(connect, 0, io_mode),
862         CreateMockWrite(rst, 2, io_mode),
863         CreateMockWrite(connect2, 3, io_mode),
864     };
865 
866     // The proxy responds to the connect with a 407, using a persistent
867     // connection.
868     const char kAuthStatus[] = "407";
869     const char* const kAuthChallenge[] = {
870         "proxy-authenticate",
871         "Basic realm=\"MyRealm1\"",
872     };
873     spdy::SpdySerializedFrame connect_auth_resp(
874         spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge,
875                                           std::size(kAuthChallenge) / 2, 1));
876 
877     spdy::SpdySerializedFrame connect2_resp(
878         spdy_util.ConstructSpdyGetReply(nullptr, 0, 3));
879     MockRead spdy_reads[] = {
880         CreateMockRead(connect_auth_resp, 1, ASYNC),
881         CreateMockRead(connect2_resp, 4, ASYNC),
882         MockRead(ASYNC, OK, 5),
883     };
884 
885     Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
886 
887     TestConnectJobDelegate test_delegate;
888     std::unique_ptr<ConnectJob> connect_job =
889         CreateConnectJobForTunnel(&test_delegate);
890     ASSERT_EQ(ERR_IO_PENDING, connect_job->Connect());
891     // Auth callback is always invoked asynchronously when a challenge is
892     // observed.
893     EXPECT_EQ(0, test_delegate.num_auth_challenges());
894 
895     test_delegate.WaitForAuthChallenge(1);
896     ASSERT_TRUE(test_delegate.auth_response_info().headers);
897     EXPECT_EQ(407, test_delegate.auth_response_info().headers->response_code());
898     std::string proxy_authenticate;
899     ASSERT_TRUE(test_delegate.auth_response_info().headers->EnumerateHeader(
900         nullptr, "Proxy-Authenticate", &proxy_authenticate));
901     EXPECT_EQ(proxy_authenticate, "Basic realm=\"MyRealm1\"");
902     ASSERT_TRUE(test_delegate.auth_controller());
903     EXPECT_FALSE(test_delegate.has_result());
904 
905     test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
906     test_delegate.RunAuthCallback();
907     // Per API contract, the request can not complete synchronously.
908     EXPECT_FALSE(test_delegate.has_result());
909 
910     EXPECT_EQ(net::OK, test_delegate.WaitForResult());
911     EXPECT_EQ(1, test_delegate.num_auth_challenges());
912 
913     // Close the H2 session to prevent reuse.
914     if (GetParam() == SPDY)
915       session_->CloseAllConnections(ERR_FAILED, "Very good reason");
916     // Also need to clear the auth cache before re-running the test.
917     session_->http_auth_cache()->ClearAllEntries();
918   }
919 }
920 
921 // Test the case where auth credentials are not cached and the first time
922 // credentials are sent, they are rejected.
TEST_P(HttpProxyConnectJobTest,NeedAuthTwice)923 TEST_P(HttpProxyConnectJobTest, NeedAuthTwice) {
924   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
925     SCOPED_TRACE(io_mode);
926 
927     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
928 
929     MockWrite writes[] = {
930         MockWrite(io_mode, 0,
931                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
932                   "Host: www.endpoint.test:443\r\n"
933                   "Proxy-Connection: keep-alive\r\n\r\n"),
934         MockWrite(io_mode, 2,
935                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
936                   "Host: www.endpoint.test:443\r\n"
937                   "Proxy-Connection: keep-alive\r\n"
938                   "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
939         MockWrite(io_mode, 4,
940                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
941                   "Host: www.endpoint.test:443\r\n"
942                   "Proxy-Connection: keep-alive\r\n"
943                   "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
944     };
945     MockRead reads[] = {
946         // No credentials.
947         MockRead(io_mode, 1,
948                  "HTTP/1.1 407 Proxy Authentication Required\r\n"
949                  "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
950                  "Content-Length: 0\r\n\r\n"),
951         MockRead(io_mode, 3,
952                  "HTTP/1.1 407 Proxy Authentication Required\r\n"
953                  "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
954                  "Content-Length: 0\r\n\r\n"),
955         MockRead(io_mode, 5, "HTTP/1.1 200 Connection Established\r\n\r\n"),
956     };
957 
958     SpdyTestUtil spdy_util;
959     spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
960         nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
961         HostPortPair(kEndpointHost, 443)));
962     spdy::SpdySerializedFrame rst(
963         spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
964     spdy_util.UpdateWithStreamDestruction(1);
965 
966     // After calling trans.RestartWithAuth(), this is the request we should
967     // be issuing -- the final header line contains the credentials.
968     const char* const kSpdyAuthCredentials[] = {
969         "proxy-authorization",
970         "Basic Zm9vOmJhcg==",
971     };
972     spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect(
973         kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3,
974         HttpProxyConnectJob::kH2QuicTunnelPriority,
975         HostPortPair(kEndpointHost, 443)));
976     spdy::SpdySerializedFrame rst2(
977         spdy_util.ConstructSpdyRstStream(3, spdy::ERROR_CODE_CANCEL));
978     spdy_util.UpdateWithStreamDestruction(3);
979 
980     spdy::SpdySerializedFrame connect3(spdy_util.ConstructSpdyConnect(
981         kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 5,
982         HttpProxyConnectJob::kH2QuicTunnelPriority,
983         HostPortPair(kEndpointHost, 443)));
984     MockWrite spdy_writes[] = {
985         CreateMockWrite(connect, 0, io_mode),
986         CreateMockWrite(rst, 2, io_mode),
987         CreateMockWrite(connect2, 3, io_mode),
988         CreateMockWrite(rst2, 5, io_mode),
989         CreateMockWrite(connect3, 6, io_mode),
990     };
991 
992     // The proxy responds to the connect with a 407, using a persistent
993     // connection.
994     const char kAuthStatus[] = "407";
995     const char* const kAuthChallenge[] = {
996         "proxy-authenticate",
997         "Basic realm=\"MyRealm1\"",
998     };
999     spdy::SpdySerializedFrame connect_auth_resp(
1000         spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge,
1001                                           std::size(kAuthChallenge) / 2, 1));
1002     spdy::SpdySerializedFrame connect2_auth_resp(
1003         spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge,
1004                                           std::size(kAuthChallenge) / 2, 3));
1005     spdy::SpdySerializedFrame connect3_resp(
1006         spdy_util.ConstructSpdyGetReply(nullptr, 0, 5));
1007     MockRead spdy_reads[] = {
1008         CreateMockRead(connect_auth_resp, 1, ASYNC),
1009         CreateMockRead(connect2_auth_resp, 4, ASYNC),
1010         CreateMockRead(connect3_resp, 7, ASYNC),
1011         MockRead(ASYNC, OK, 8),
1012     };
1013 
1014     Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
1015 
1016     TestConnectJobDelegate test_delegate;
1017     std::unique_ptr<ConnectJob> connect_job =
1018         CreateConnectJobForTunnel(&test_delegate);
1019     ASSERT_EQ(ERR_IO_PENDING, connect_job->Connect());
1020     // Auth callback is always invoked asynchronously when a challenge is
1021     // observed.
1022     EXPECT_EQ(0, test_delegate.num_auth_challenges());
1023 
1024     test_delegate.WaitForAuthChallenge(1);
1025     ASSERT_TRUE(test_delegate.auth_response_info().headers);
1026     EXPECT_EQ(407, test_delegate.auth_response_info().headers->response_code());
1027     std::string proxy_authenticate;
1028     ASSERT_TRUE(test_delegate.auth_response_info().headers->EnumerateHeader(
1029         nullptr, "Proxy-Authenticate", &proxy_authenticate));
1030     EXPECT_EQ(proxy_authenticate, "Basic realm=\"MyRealm1\"");
1031     EXPECT_FALSE(test_delegate.has_result());
1032 
1033     test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
1034     test_delegate.RunAuthCallback();
1035     // Per API contract, the auth callback can't be invoked synchronously.
1036     EXPECT_FALSE(test_delegate.auth_controller());
1037     EXPECT_FALSE(test_delegate.has_result());
1038 
1039     test_delegate.WaitForAuthChallenge(2);
1040     ASSERT_TRUE(test_delegate.auth_response_info().headers);
1041     EXPECT_EQ(407, test_delegate.auth_response_info().headers->response_code());
1042     ASSERT_TRUE(test_delegate.auth_response_info().headers->EnumerateHeader(
1043         nullptr, "Proxy-Authenticate", &proxy_authenticate));
1044     EXPECT_EQ(proxy_authenticate, "Basic realm=\"MyRealm1\"");
1045     EXPECT_FALSE(test_delegate.has_result());
1046 
1047     test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
1048     test_delegate.RunAuthCallback();
1049     // Per API contract, the request can't complete synchronously.
1050     EXPECT_FALSE(test_delegate.has_result());
1051 
1052     EXPECT_EQ(net::OK, test_delegate.WaitForResult());
1053     EXPECT_EQ(2, test_delegate.num_auth_challenges());
1054 
1055     // Close the H2 session to prevent reuse.
1056     if (GetParam() == SPDY)
1057       session_->CloseAllConnections(ERR_FAILED, "Very good reason");
1058     // Also need to clear the auth cache before re-running the test.
1059     session_->http_auth_cache()->ClearAllEntries();
1060   }
1061 }
1062 
1063 // Test the case where auth credentials are cached.
TEST_P(HttpProxyConnectJobTest,HaveAuth)1064 TEST_P(HttpProxyConnectJobTest, HaveAuth) {
1065   // Prepopulate auth cache.
1066   const std::u16string kFoo(u"foo");
1067   const std::u16string kBar(u"bar");
1068   url::SchemeHostPort proxy_scheme_host_port(
1069       GetParam() == HTTP ? GURL(std::string("http://") + kHttpProxyHost)
1070                          : GURL(std::string("https://") + kHttpsProxyHost));
1071   session_->http_auth_cache()->Add(
1072       proxy_scheme_host_port, HttpAuth::AUTH_PROXY, "MyRealm1",
1073       HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(),
1074       "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
1075 
1076   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1077     SCOPED_TRACE(io_mode);
1078 
1079     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1080 
1081     MockWrite writes[] = {
1082         MockWrite(io_mode, 0,
1083                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1084                   "Host: www.endpoint.test:443\r\n"
1085                   "Proxy-Connection: keep-alive\r\n"
1086                   "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
1087     };
1088     MockRead reads[] = {
1089         MockRead(io_mode, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
1090     };
1091 
1092     const char* const kSpdyAuthCredentials[] = {
1093         "proxy-authorization",
1094         "Basic Zm9vOmJhcg==",
1095     };
1096     SpdyTestUtil spdy_util;
1097     spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
1098         kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 1,
1099         HttpProxyConnectJob::kH2QuicTunnelPriority,
1100         HostPortPair(kEndpointHost, 443)));
1101 
1102     MockWrite spdy_writes[] = {
1103         CreateMockWrite(connect, 0, ASYNC),
1104     };
1105 
1106     spdy::SpdySerializedFrame connect_resp(
1107         spdy_util.ConstructSpdyGetReply(nullptr, 0, 1));
1108     MockRead spdy_reads[] = {
1109         // SpdySession starts trying to read from the socket as soon as it's
1110         // created, so this cannot be SYNCHRONOUS.
1111         CreateMockRead(connect_resp, 1, ASYNC),
1112         MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2),
1113     };
1114 
1115     Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
1116 
1117     TestConnectJobDelegate test_delegate;
1118     std::unique_ptr<ConnectJob> connect_job =
1119         CreateConnectJobForTunnel(&test_delegate);
1120     // SPDY operations always complete asynchronously.
1121     test_delegate.StartJobExpectingResult(
1122         connect_job.get(), OK, io_mode == SYNCHRONOUS && GetParam() != SPDY);
1123 
1124     // Close the H2 session to prevent reuse.
1125     if (GetParam() == SPDY)
1126       session_->CloseAllConnections(ERR_FAILED, "Very good reason");
1127   }
1128 }
1129 
TEST_P(HttpProxyConnectJobTest,HostResolutionFailure)1130 TEST_P(HttpProxyConnectJobTest, HostResolutionFailure) {
1131   session_deps_.host_resolver->rules()->AddSimulatedTimeoutFailure(
1132       kHttpProxyHost);
1133   session_deps_.host_resolver->rules()->AddSimulatedTimeoutFailure(
1134       kHttpsProxyHost);
1135 
1136   TestConnectJobDelegate test_delegate;
1137   std::unique_ptr<ConnectJob> connect_job =
1138       CreateConnectJobForHttpRequest(&test_delegate, DEFAULT_PRIORITY);
1139   test_delegate.StartJobExpectingResult(connect_job.get(),
1140                                         ERR_PROXY_CONNECTION_FAILED,
1141                                         false /* expect_sync_result */);
1142   EXPECT_THAT(connect_job->GetResolveErrorInfo().error,
1143               test::IsError(ERR_DNS_TIMED_OUT));
1144 }
1145 
TEST_P(HttpProxyConnectJobTest,RequestPriority)1146 TEST_P(HttpProxyConnectJobTest, RequestPriority) {
1147   // Make request hang during host resolution, so can observe priority there.
1148   session_deps_.host_resolver->set_ondemand_mode(true);
1149 
1150   for (int initial_priority = MINIMUM_PRIORITY;
1151        initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
1152     SCOPED_TRACE(initial_priority);
1153     for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
1154          ++new_priority) {
1155       SCOPED_TRACE(new_priority);
1156       if (initial_priority == new_priority)
1157         continue;
1158       TestConnectJobDelegate test_delegate;
1159       std::unique_ptr<ConnectJob> connect_job = CreateConnectJobForHttpRequest(
1160           &test_delegate, static_cast<RequestPriority>(initial_priority));
1161       EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
1162       EXPECT_FALSE(test_delegate.has_result());
1163 
1164       MockHostResolverBase* host_resolver = session_deps_.host_resolver.get();
1165       size_t request_id = host_resolver->last_id();
1166       EXPECT_EQ(initial_priority, host_resolver->request_priority(request_id));
1167 
1168       connect_job->ChangePriority(static_cast<RequestPriority>(new_priority));
1169       EXPECT_EQ(new_priority, host_resolver->request_priority(request_id));
1170 
1171       connect_job->ChangePriority(
1172           static_cast<RequestPriority>(initial_priority));
1173       EXPECT_EQ(initial_priority, host_resolver->request_priority(request_id));
1174     }
1175   }
1176 }
1177 
TEST_P(HttpProxyConnectJobTest,SecureDnsPolicy)1178 TEST_P(HttpProxyConnectJobTest, SecureDnsPolicy) {
1179   for (auto secure_dns_policy :
1180        {SecureDnsPolicy::kAllow, SecureDnsPolicy::kDisable}) {
1181     TestConnectJobDelegate test_delegate;
1182     std::unique_ptr<ConnectJob> connect_job = CreateConnectJobForHttpRequest(
1183         &test_delegate, DEFAULT_PRIORITY, secure_dns_policy);
1184 
1185     EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
1186     EXPECT_EQ(secure_dns_policy,
1187               session_deps_.host_resolver->last_secure_dns_policy());
1188   }
1189 }
1190 
TEST_P(HttpProxyConnectJobTest,SpdySessionKeyDisableSecureDns)1191 TEST_P(HttpProxyConnectJobTest, SpdySessionKeyDisableSecureDns) {
1192   if (GetParam() != SPDY)
1193     return;
1194 
1195   SSLSocketDataProvider ssl_data(ASYNC, OK);
1196   InitializeSpdySsl(&ssl_data);
1197   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
1198 
1199   // SPDY proxy CONNECT request / response, with a pause during the read.
1200   spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
1201       nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
1202       HostPortPair(kEndpointHost, 443)));
1203   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
1204   spdy::SpdySerializedFrame resp(
1205       spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
1206   MockRead spdy_reads[] = {CreateMockRead(resp, 1), MockRead(ASYNC, 0, 2)};
1207   SequencedSocketData spdy_data(spdy_reads, spdy_writes);
1208   spdy_data.set_connect_data(MockConnect(ASYNC, OK));
1209   SequencedSocketData* sequenced_data = &spdy_data;
1210   session_deps_.socket_factory->AddSocketDataProvider(sequenced_data);
1211 
1212   TestConnectJobDelegate test_delegate;
1213   std::unique_ptr<ConnectJob> connect_job = CreateConnectJobForTunnel(
1214       &test_delegate, DEFAULT_PRIORITY, SecureDnsPolicy::kDisable);
1215 
1216   EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
1217   EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
1218   EXPECT_TRUE(
1219       common_connect_job_params_->spdy_session_pool->FindAvailableSession(
1220           SpdySessionKey(kHttpsProxyServer.host_port_pair(),
1221                          ProxyChain::Direct(), PRIVACY_MODE_DISABLED,
1222                          SpdySessionKey::IsProxySession::kTrue, SocketTag(),
1223                          NetworkAnonymizationKey(), SecureDnsPolicy::kDisable),
1224           /* enable_ip_based_pooling = */ false,
1225           /* is_websocket = */ false, NetLogWithSource()));
1226   EXPECT_FALSE(
1227       common_connect_job_params_->spdy_session_pool->FindAvailableSession(
1228           SpdySessionKey(kHttpsProxyServer.host_port_pair(),
1229                          ProxyChain::Direct(), PRIVACY_MODE_DISABLED,
1230                          SpdySessionKey::IsProxySession::kTrue, SocketTag(),
1231                          NetworkAnonymizationKey(), SecureDnsPolicy::kAllow),
1232           /* enable_ip_based_pooling = */ false,
1233           /* is_websocket = */ false, NetLogWithSource()));
1234 }
1235 
1236 // Make sure that HttpProxyConnectJob does not pass on its priority to its
1237 // SPDY session's socket request on Init, or on SetPriority.
TEST_P(HttpProxyConnectJobTest,SetSpdySessionSocketRequestPriority)1238 TEST_P(HttpProxyConnectJobTest, SetSpdySessionSocketRequestPriority) {
1239   if (GetParam() != SPDY)
1240     return;
1241   session_deps_.host_resolver->set_synchronous_mode(true);
1242 
1243   // The SPDY CONNECT request should have a priority of kH2QuicTunnelPriority,
1244   // even though the ConnectJob's priority is set to HIGHEST after connection
1245   // establishment.
1246   spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
1247       nullptr /* extra_headers */, 0 /* extra_header_count */,
1248       1 /* stream_id */, HttpProxyConnectJob::kH2QuicTunnelPriority,
1249       HostPortPair(kEndpointHost, 443)));
1250   MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)};
1251   spdy::SpdySerializedFrame resp(
1252       spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
1253   MockRead spdy_reads[] = {CreateMockRead(resp, 1, ASYNC),
1254                            MockRead(ASYNC, 0, 2)};
1255 
1256   Initialize(base::span<MockRead>(), base::span<MockWrite>(), spdy_reads,
1257              spdy_writes, SYNCHRONOUS);
1258 
1259   TestConnectJobDelegate test_delegate;
1260   std::unique_ptr<ConnectJob> connect_job =
1261       CreateConnectJobForTunnel(&test_delegate, IDLE);
1262   EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
1263   EXPECT_FALSE(test_delegate.has_result());
1264 
1265   connect_job->ChangePriority(HIGHEST);
1266 
1267   // Wait for tunnel to be established. If the frame has a MEDIUM priority
1268   // instead of highest, the written data will not match what is expected, and
1269   // the test will fail.
1270   EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
1271 }
1272 
TEST_P(HttpProxyConnectJobTest,TCPError)1273 TEST_P(HttpProxyConnectJobTest, TCPError) {
1274   // SPDY and HTTPS are identical, as they only differ once a connection is
1275   // established.
1276   if (GetParam() == SPDY)
1277     return;
1278   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1279     SCOPED_TRACE(io_mode);
1280     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1281     base::HistogramTester histogram_tester;
1282 
1283     SequencedSocketData data;
1284     data.set_connect_data(MockConnect(io_mode, ERR_CONNECTION_CLOSED));
1285     session_deps_.socket_factory->AddSocketDataProvider(&data);
1286 
1287     TestConnectJobDelegate test_delegate;
1288     std::unique_ptr<ConnectJob> connect_job =
1289         CreateConnectJobForHttpRequest(&test_delegate);
1290     test_delegate.StartJobExpectingResult(
1291         connect_job.get(), ERR_PROXY_CONNECTION_FAILED, io_mode == SYNCHRONOUS);
1292 
1293     bool is_secure_proxy = GetParam() == HTTPS;
1294     histogram_tester.ExpectTotalCount(
1295         "Net.HttpProxy.ConnectLatency.Insecure.Error", is_secure_proxy ? 0 : 1);
1296     histogram_tester.ExpectTotalCount(
1297         "Net.HttpProxy.ConnectLatency.Secure.Error", is_secure_proxy ? 1 : 0);
1298   }
1299 }
1300 
TEST_P(HttpProxyConnectJobTest,SSLError)1301 TEST_P(HttpProxyConnectJobTest, SSLError) {
1302   if (GetParam() == HTTP)
1303     return;
1304 
1305   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1306     SCOPED_TRACE(io_mode);
1307     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1308     base::HistogramTester histogram_tester;
1309 
1310     SequencedSocketData data;
1311     data.set_connect_data(MockConnect(io_mode, OK));
1312     session_deps_.socket_factory->AddSocketDataProvider(&data);
1313 
1314     SSLSocketDataProvider ssl_data(io_mode, ERR_CERT_AUTHORITY_INVALID);
1315     if (GetParam() == SPDY) {
1316       InitializeSpdySsl(&ssl_data);
1317     }
1318     session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
1319 
1320     TestConnectJobDelegate test_delegate;
1321     std::unique_ptr<ConnectJob> connect_job =
1322         CreateConnectJobForTunnel(&test_delegate);
1323     test_delegate.StartJobExpectingResult(connect_job.get(),
1324                                           ERR_PROXY_CERTIFICATE_INVALID,
1325                                           io_mode == SYNCHRONOUS);
1326 
1327     histogram_tester.ExpectTotalCount(
1328         "Net.HttpProxy.ConnectLatency.Secure.Error", 1);
1329     histogram_tester.ExpectTotalCount(
1330         "Net.HttpProxy.ConnectLatency.Insecure.Error", 0);
1331   }
1332 }
1333 
TEST_P(HttpProxyConnectJobTest,TunnelUnexpectedClose)1334 TEST_P(HttpProxyConnectJobTest, TunnelUnexpectedClose) {
1335   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1336     SCOPED_TRACE(io_mode);
1337     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1338 
1339     MockWrite writes[] = {
1340         MockWrite(io_mode, 0,
1341                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1342                   "Host: www.endpoint.test:443\r\n"
1343                   "Proxy-Connection: keep-alive\r\n\r\n"),
1344     };
1345     MockRead reads[] = {
1346         MockRead(io_mode, 1, "HTTP/1.1 200 Conn"),
1347         MockRead(io_mode, ERR_CONNECTION_CLOSED, 2),
1348     };
1349     spdy::SpdySerializedFrame req(SpdyTestUtil().ConstructSpdyConnect(
1350         nullptr /*extra_headers */, 0 /*extra_header_count */,
1351         1 /* stream_id */, HttpProxyConnectJob::kH2QuicTunnelPriority,
1352         HostPortPair(kEndpointHost, 443)));
1353     MockWrite spdy_writes[] = {CreateMockWrite(req, 0, io_mode)};
1354     // Sync reads don't really work with SPDY, since it constantly reads from
1355     // the socket.
1356     MockRead spdy_reads[] = {
1357         MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1),
1358     };
1359 
1360     Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
1361 
1362     TestConnectJobDelegate test_delegate;
1363     std::unique_ptr<ConnectJob> connect_job =
1364         CreateConnectJobForTunnel(&test_delegate);
1365 
1366     if (GetParam() == SPDY) {
1367       // SPDY cannot process a headers block unless it's complete and so it
1368       // returns ERR_CONNECTION_CLOSED in this case. SPDY also doesn't return
1369       // this failure synchronously.
1370       test_delegate.StartJobExpectingResult(connect_job.get(),
1371                                             ERR_CONNECTION_CLOSED,
1372                                             false /* expect_sync_result */);
1373     } else {
1374       test_delegate.StartJobExpectingResult(connect_job.get(),
1375                                             ERR_RESPONSE_HEADERS_TRUNCATED,
1376                                             io_mode == SYNCHRONOUS);
1377     }
1378   }
1379 }
1380 
TEST_P(HttpProxyConnectJobTest,Tunnel1xxResponse)1381 TEST_P(HttpProxyConnectJobTest, Tunnel1xxResponse) {
1382   // Tests that 1xx responses are rejected for a CONNECT request.
1383   if (GetParam() == SPDY) {
1384     // SPDY doesn't have 1xx responses.
1385     return;
1386   }
1387 
1388   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1389     SCOPED_TRACE(io_mode);
1390     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1391 
1392     MockWrite writes[] = {
1393         MockWrite(io_mode, 0,
1394                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1395                   "Host: www.endpoint.test:443\r\n"
1396                   "Proxy-Connection: keep-alive\r\n\r\n"),
1397     };
1398     MockRead reads[] = {
1399         MockRead(io_mode, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
1400         MockRead(io_mode, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
1401     };
1402 
1403     Initialize(reads, writes, base::span<MockRead>(), base::span<MockWrite>(),
1404                io_mode);
1405 
1406     TestConnectJobDelegate test_delegate;
1407     std::unique_ptr<ConnectJob> connect_job =
1408         CreateConnectJobForTunnel(&test_delegate);
1409     test_delegate.StartJobExpectingResult(connect_job.get(),
1410                                           ERR_TUNNEL_CONNECTION_FAILED,
1411                                           io_mode == SYNCHRONOUS);
1412   }
1413 }
1414 
TEST_P(HttpProxyConnectJobTest,TunnelSetupError)1415 TEST_P(HttpProxyConnectJobTest, TunnelSetupError) {
1416   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1417     SCOPED_TRACE(io_mode);
1418     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1419 
1420     MockWrite writes[] = {
1421         MockWrite(io_mode, 0,
1422                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1423                   "Host: www.endpoint.test:443\r\n"
1424                   "Proxy-Connection: keep-alive\r\n\r\n"),
1425     };
1426     MockRead reads[] = {
1427         MockRead(io_mode, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
1428     };
1429     SpdyTestUtil spdy_util;
1430     spdy::SpdySerializedFrame req(spdy_util.ConstructSpdyConnect(
1431         nullptr /* extra_headers */, 0 /* extra_header_count */,
1432         1 /* stream_id */, HttpProxyConnectJob::kH2QuicTunnelPriority,
1433         HostPortPair("www.endpoint.test", 443)));
1434     spdy::SpdySerializedFrame rst(
1435         spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
1436     MockWrite spdy_writes[] = {
1437         CreateMockWrite(req, 0, io_mode),
1438         CreateMockWrite(rst, 2, io_mode),
1439     };
1440     spdy::SpdySerializedFrame resp(spdy_util.ConstructSpdyReplyError(1));
1441     // Sync reads don't really work with SPDY, since it constantly reads from
1442     // the socket.
1443     MockRead spdy_reads[] = {
1444         CreateMockRead(resp, 1, ASYNC),
1445         MockRead(ASYNC, OK, 3),
1446     };
1447 
1448     Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
1449 
1450     TestConnectJobDelegate test_delegate;
1451     std::unique_ptr<ConnectJob> connect_job =
1452         CreateConnectJobForTunnel(&test_delegate, LOW);
1453     test_delegate.StartJobExpectingResult(
1454         connect_job.get(), ERR_TUNNEL_CONNECTION_FAILED,
1455         io_mode == SYNCHRONOUS && GetParam() != SPDY);
1456     // Need to close the session to prevent reuse in the next loop iteration.
1457     session_->spdy_session_pool()->CloseAllSessions();
1458   }
1459 }
1460 
TEST_P(HttpProxyConnectJobTest,SslClientAuth)1461 TEST_P(HttpProxyConnectJobTest, SslClientAuth) {
1462   if (GetParam() == HTTP)
1463     return;
1464   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1465     SCOPED_TRACE(io_mode);
1466     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1467     base::HistogramTester histogram_tester;
1468 
1469     SequencedSocketData socket_data(MockConnect(io_mode, OK),
1470                                     base::span<const MockRead>(),
1471                                     base::span<const MockWrite>());
1472     session_deps_.socket_factory->AddSocketDataProvider(&socket_data);
1473     SSLSocketDataProvider ssl_data(io_mode, ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
1474     if (GetParam() == SPDY)
1475       InitializeSpdySsl(&ssl_data);
1476     session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
1477 
1478     // Redirects in the HTTPS case return errors, but also return sockets.
1479     TestConnectJobDelegate test_delegate;
1480     std::unique_ptr<ConnectJob> connect_job =
1481         CreateConnectJobForTunnel(&test_delegate);
1482     test_delegate.StartJobExpectingResult(connect_job.get(),
1483                                           ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
1484                                           io_mode == SYNCHRONOUS);
1485 
1486     histogram_tester.ExpectTotalCount(
1487         "Net.HttpProxy.ConnectLatency.Secure.Error", 1);
1488     histogram_tester.ExpectTotalCount(
1489         "Net.HttpProxy.ConnectLatency.Insecure.Error", 0);
1490   }
1491 }
1492 
TEST_P(HttpProxyConnectJobTest,TunnelSetupRedirect)1493 TEST_P(HttpProxyConnectJobTest, TunnelSetupRedirect) {
1494   const std::string kRedirectTarget = "https://foo.google.com/";
1495 
1496   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
1497     SCOPED_TRACE(io_mode);
1498     session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
1499 
1500     const std::string kResponseText =
1501         "HTTP/1.1 302 Found\r\n"
1502         "Location: " +
1503         kRedirectTarget +
1504         "\r\n"
1505         "Set-Cookie: foo=bar\r\n"
1506         "\r\n";
1507 
1508     MockWrite writes[] = {
1509         MockWrite(io_mode, 0,
1510                   "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1511                   "Host: www.endpoint.test:443\r\n"
1512                   "Proxy-Connection: keep-alive\r\n\r\n"),
1513     };
1514     MockRead reads[] = {
1515         MockRead(io_mode, 1, kResponseText.c_str()),
1516     };
1517     SpdyTestUtil spdy_util;
1518     spdy::SpdySerializedFrame req(spdy_util.ConstructSpdyConnect(
1519         nullptr /* extra_headers */, 0 /* extra_header_count */, 1,
1520         DEFAULT_PRIORITY, HostPortPair(kEndpointHost, 443)));
1521     spdy::SpdySerializedFrame rst(
1522         spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
1523 
1524     MockWrite spdy_writes[] = {
1525         CreateMockWrite(req, 0, io_mode),
1526         CreateMockWrite(rst, 3, io_mode),
1527     };
1528 
1529     const char* const responseHeaders[] = {
1530         "location",
1531         kRedirectTarget.c_str(),
1532         "set-cookie",
1533         "foo=bar",
1534     };
1535     const int responseHeadersSize = std::size(responseHeaders) / 2;
1536     spdy::SpdySerializedFrame resp(spdy_util.ConstructSpdyReplyError(
1537         "302", responseHeaders, responseHeadersSize, 1));
1538     MockRead spdy_reads[] = {
1539         CreateMockRead(resp, 1, ASYNC),
1540         MockRead(ASYNC, 0, 2),
1541     };
1542 
1543     Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
1544 
1545     // Redirects during CONNECT returns an error.
1546     TestConnectJobDelegate test_delegate(
1547         TestConnectJobDelegate::SocketExpected::ON_SUCCESS_ONLY);
1548     std::unique_ptr<ConnectJob> connect_job =
1549         CreateConnectJobForTunnel(&test_delegate);
1550 
1551     // H2 never completes synchronously.
1552     bool expect_sync_result = (io_mode == SYNCHRONOUS && GetParam() != SPDY);
1553 
1554     // We don't trust 302 responses to CONNECT from proxies.
1555     test_delegate.StartJobExpectingResult(
1556         connect_job.get(), ERR_TUNNEL_CONNECTION_FAILED, expect_sync_result);
1557     EXPECT_FALSE(test_delegate.socket());
1558 
1559     // Need to close the session to prevent reuse in the next loop iteration.
1560     session_->spdy_session_pool()->CloseAllSessions();
1561   }
1562 }
1563 
1564 // Test timeouts in the case of an auth challenge and response.
TEST_P(HttpProxyConnectJobTest,TestTimeoutsAuthChallenge)1565 TEST_P(HttpProxyConnectJobTest, TestTimeoutsAuthChallenge) {
1566   // Wait until this amount of time before something times out.
1567   const base::TimeDelta kTinyTime = base::Microseconds(1);
1568 
1569   enum class TimeoutPhase {
1570     CONNECT,
1571     PROXY_HANDSHAKE,
1572     SECOND_PROXY_HANDSHAKE,
1573 
1574     NONE,
1575   };
1576 
1577   const TimeoutPhase kTimeoutPhases[] = {
1578       TimeoutPhase::CONNECT,
1579       TimeoutPhase::PROXY_HANDSHAKE,
1580       TimeoutPhase::SECOND_PROXY_HANDSHAKE,
1581       TimeoutPhase::NONE,
1582   };
1583 
1584   session_deps_.host_resolver->set_ondemand_mode(true);
1585 
1586   MockWrite writes[] = {
1587       MockWrite(ASYNC, 0,
1588                 "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1589                 "Host: www.endpoint.test:443\r\n"
1590                 "Proxy-Connection: keep-alive\r\n\r\n"),
1591       MockWrite(ASYNC, 3,
1592                 "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1593                 "Host: www.endpoint.test:443\r\n"
1594                 "Proxy-Connection: keep-alive\r\n"
1595                 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
1596   };
1597   MockRead reads[] = {
1598       // Pause before first response is read.
1599       MockRead(ASYNC, ERR_IO_PENDING, 1),
1600       MockRead(ASYNC, 2,
1601                "HTTP/1.1 407 Proxy Authentication Required\r\n"
1602                "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
1603                "Content-Length: 0\r\n\r\n"),
1604 
1605       // Pause again before second response is read.
1606       MockRead(ASYNC, ERR_IO_PENDING, 4),
1607       MockRead(ASYNC, 5, "HTTP/1.1 200 Connection Established\r\n\r\n"),
1608   };
1609 
1610   SpdyTestUtil spdy_util;
1611   spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
1612       nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
1613       HostPortPair(kEndpointHost, 443)));
1614   spdy::SpdySerializedFrame rst(
1615       spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
1616   spdy_util.UpdateWithStreamDestruction(1);
1617 
1618   // After calling trans.RestartWithAuth(), this is the request we should
1619   // be issuing -- the final header line contains the credentials.
1620   const char* const kSpdyAuthCredentials[] = {
1621       "proxy-authorization",
1622       "Basic Zm9vOmJhcg==",
1623   };
1624   spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect(
1625       kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3,
1626       HttpProxyConnectJob::kH2QuicTunnelPriority,
1627       HostPortPair(kEndpointHost, 443)));
1628   // This may be sent in some tests, either when tearing down a successful
1629   // connection, or on timeout.
1630   spdy::SpdySerializedFrame rst2(
1631       spdy_util.ConstructSpdyRstStream(3, spdy::ERROR_CODE_CANCEL));
1632   MockWrite spdy_writes[] = {
1633       CreateMockWrite(connect, 0, ASYNC),
1634       CreateMockWrite(rst, 3, ASYNC),
1635       CreateMockWrite(connect2, 4, ASYNC),
1636       CreateMockWrite(rst2, 8, ASYNC),
1637   };
1638 
1639   // The proxy responds to the connect with a 407, using a persistent
1640   // connection.
1641   const char kAuthStatus[] = "407";
1642   const char* const kAuthChallenge[] = {
1643       "proxy-authenticate",
1644       "Basic realm=\"MyRealm1\"",
1645   };
1646   spdy::SpdySerializedFrame connect_auth_resp(spdy_util.ConstructSpdyReplyError(
1647       kAuthStatus, kAuthChallenge, std::size(kAuthChallenge) / 2, 1));
1648   spdy::SpdySerializedFrame connect2_resp(
1649       spdy_util.ConstructSpdyGetReply(nullptr, 0, 3));
1650   MockRead spdy_reads[] = {
1651       // Pause before first response is read.
1652       MockRead(ASYNC, ERR_IO_PENDING, 1),
1653       CreateMockRead(connect_auth_resp, 2, ASYNC),
1654       // Pause again before second response is read.
1655       MockRead(ASYNC, ERR_IO_PENDING, 5),
1656       CreateMockRead(connect2_resp, 6, ASYNC),
1657       MockRead(ASYNC, OK, 7),
1658   };
1659 
1660   for (TimeoutPhase timeout_phase : kTimeoutPhases) {
1661     SCOPED_TRACE(static_cast<int>(timeout_phase));
1662 
1663     // Need to close the session to prevent reuse of a session from the last
1664     // loop iteration.
1665     session_->spdy_session_pool()->CloseAllSessions();
1666     // And clear the auth cache to prevent reusing cache entries.
1667     session_->http_auth_cache()->ClearAllEntries();
1668 
1669     TestConnectJobDelegate test_delegate;
1670     std::unique_ptr<ConnectJob> connect_job =
1671         CreateConnectJobForTunnel(&test_delegate);
1672 
1673     // Connecting should run until the request hits the HostResolver.
1674     EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
1675     EXPECT_FALSE(test_delegate.has_result());
1676     EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
1677     EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
1678 
1679     // Run until just before timeout.
1680     FastForwardBy(GetNestedConnectionTimeout() - kTinyTime);
1681     EXPECT_FALSE(test_delegate.has_result());
1682 
1683     // Wait until timeout, if appropriate.
1684     if (timeout_phase == TimeoutPhase::CONNECT) {
1685       FastForwardBy(kTinyTime);
1686       ASSERT_TRUE(test_delegate.has_result());
1687       EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
1688       continue;
1689     }
1690 
1691     // Add mock reads for socket needed in next step. Connect phase is timed out
1692     // before establishing a connection, so don't need them for
1693     // TimeoutPhase::CONNECT.
1694     Initialize(reads, writes, spdy_reads, spdy_writes, SYNCHRONOUS);
1695 
1696     // Finish resolution.
1697     session_deps_.host_resolver->ResolveOnlyRequestNow();
1698     EXPECT_FALSE(test_delegate.has_result());
1699     EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
1700               connect_job->GetLoadState());
1701 
1702     // Wait until just before negotiation with the tunnel should time out.
1703     FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
1704     EXPECT_FALSE(test_delegate.has_result());
1705 
1706     if (timeout_phase == TimeoutPhase::PROXY_HANDSHAKE) {
1707       FastForwardBy(kTinyTime);
1708       ASSERT_TRUE(test_delegate.has_result());
1709       EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
1710       continue;
1711     }
1712 
1713     data_->Resume();
1714     test_delegate.WaitForAuthChallenge(1);
1715     EXPECT_FALSE(test_delegate.has_result());
1716 
1717     // ConnectJobs cannot timeout while showing an auth dialog.
1718     FastForwardBy(base::Days(1));
1719     EXPECT_FALSE(test_delegate.has_result());
1720 
1721     // Send credentials
1722     test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
1723     test_delegate.RunAuthCallback();
1724     EXPECT_FALSE(test_delegate.has_result());
1725 
1726     FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
1727     EXPECT_FALSE(test_delegate.has_result());
1728 
1729     if (timeout_phase == TimeoutPhase::SECOND_PROXY_HANDSHAKE) {
1730       FastForwardBy(kTinyTime);
1731       ASSERT_TRUE(test_delegate.has_result());
1732       EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
1733       continue;
1734     }
1735 
1736     data_->Resume();
1737     EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
1738   }
1739 }
1740 
1741 // Same as above, except test the case the first connection cannot be reused
1742 // once credentials are received.
TEST_P(HttpProxyConnectJobTest,TestTimeoutsAuthChallengeNewConnection)1743 TEST_P(HttpProxyConnectJobTest, TestTimeoutsAuthChallengeNewConnection) {
1744   // Proxy-Connection: Close doesn't make sense with H2.
1745   if (GetParam() == SPDY)
1746     return;
1747 
1748   enum class TimeoutPhase {
1749     CONNECT,
1750     PROXY_HANDSHAKE,
1751     SECOND_CONNECT,
1752     SECOND_PROXY_HANDSHAKE,
1753 
1754     // This has to be last for the H2 proxy case, since success will populate
1755     // the H2 session pool.
1756     NONE,
1757   };
1758 
1759   const TimeoutPhase kTimeoutPhases[] = {
1760       TimeoutPhase::CONNECT,        TimeoutPhase::PROXY_HANDSHAKE,
1761       TimeoutPhase::SECOND_CONNECT, TimeoutPhase::SECOND_PROXY_HANDSHAKE,
1762       TimeoutPhase::NONE,
1763   };
1764 
1765   // Wait until this amount of time before something times out.
1766   const base::TimeDelta kTinyTime = base::Microseconds(1);
1767 
1768   session_deps_.host_resolver->set_ondemand_mode(true);
1769 
1770   MockWrite writes[] = {
1771       MockWrite(ASYNC, 0,
1772                 "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1773                 "Host: www.endpoint.test:443\r\n"
1774                 "Proxy-Connection: keep-alive\r\n\r\n"),
1775   };
1776   MockRead reads[] = {
1777       // Pause at read.
1778       MockRead(ASYNC, ERR_IO_PENDING, 1),
1779       MockRead(ASYNC, 2,
1780                "HTTP/1.1 407 Proxy Authentication Required\r\n"
1781                "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
1782                "Proxy-Connection: Close\r\n"
1783                "Content-Length: 0\r\n\r\n"),
1784   };
1785 
1786   MockWrite writes2[] = {
1787       MockWrite(ASYNC, 0,
1788                 "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
1789                 "Host: www.endpoint.test:443\r\n"
1790                 "Proxy-Connection: keep-alive\r\n"
1791                 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
1792   };
1793   MockRead reads2[] = {
1794       // Pause at read.
1795       MockRead(ASYNC, ERR_IO_PENDING, 1),
1796       MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
1797   };
1798 
1799   for (TimeoutPhase timeout_phase : kTimeoutPhases) {
1800     SCOPED_TRACE(static_cast<int>(timeout_phase));
1801 
1802     // Need to clear the auth cache to prevent reusing cache entries.
1803     session_->http_auth_cache()->ClearAllEntries();
1804 
1805     TestConnectJobDelegate test_delegate;
1806     std::unique_ptr<ConnectJob> connect_job =
1807         CreateConnectJobForTunnel(&test_delegate);
1808 
1809     // Connecting should run until the request hits the HostResolver.
1810     EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
1811     EXPECT_FALSE(test_delegate.has_result());
1812     EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
1813     EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
1814 
1815     // Run until just before timeout.
1816     FastForwardBy(GetNestedConnectionTimeout() - kTinyTime);
1817     EXPECT_FALSE(test_delegate.has_result());
1818 
1819     // Wait until timeout, if appropriate.
1820     if (timeout_phase == TimeoutPhase::CONNECT) {
1821       FastForwardBy(kTinyTime);
1822       ASSERT_TRUE(test_delegate.has_result());
1823       EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
1824       continue;
1825     }
1826 
1827     // Add mock reads for socket needed in next step. Connect phase is timed out
1828     // before establishing a connection, so don't need them for
1829     // TimeoutPhase::CONNECT.
1830     Initialize(reads, writes, base::span<MockRead>(), base::span<MockWrite>(),
1831                SYNCHRONOUS);
1832 
1833     // Finish resolution.
1834     session_deps_.host_resolver->ResolveOnlyRequestNow();
1835     EXPECT_FALSE(test_delegate.has_result());
1836     EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
1837               connect_job->GetLoadState());
1838 
1839     // Wait until just before negotiation with the tunnel should time out.
1840     FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
1841     EXPECT_FALSE(test_delegate.has_result());
1842 
1843     if (timeout_phase == TimeoutPhase::PROXY_HANDSHAKE) {
1844       FastForwardBy(kTinyTime);
1845       ASSERT_TRUE(test_delegate.has_result());
1846       EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
1847       continue;
1848     }
1849 
1850     data_->Resume();
1851     test_delegate.WaitForAuthChallenge(1);
1852     EXPECT_FALSE(test_delegate.has_result());
1853 
1854     // ConnectJobs cannot timeout while showing an auth dialog.
1855     FastForwardBy(base::Days(1));
1856     EXPECT_FALSE(test_delegate.has_result());
1857 
1858     // Send credentials
1859     test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
1860     test_delegate.RunAuthCallback();
1861     EXPECT_FALSE(test_delegate.has_result());
1862 
1863     // Since the connection was not reusable, a new connection needs to be
1864     // established.
1865     base::RunLoop().RunUntilIdle();
1866     EXPECT_FALSE(test_delegate.has_result());
1867     EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
1868     EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
1869 
1870     // Run until just before timeout.
1871     FastForwardBy(GetNestedConnectionTimeout() - kTinyTime);
1872     EXPECT_FALSE(test_delegate.has_result());
1873 
1874     // Wait until timeout, if appropriate.
1875     if (timeout_phase == TimeoutPhase::SECOND_CONNECT) {
1876       FastForwardBy(kTinyTime);
1877       ASSERT_TRUE(test_delegate.has_result());
1878       EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
1879       continue;
1880     }
1881 
1882     // Add mock reads for socket needed in next step. Connect phase is timed out
1883     // before establishing a connection, so don't need them for
1884     // TimeoutPhase::SECOND_CONNECT.
1885     Initialize(reads2, writes2, base::span<MockRead>(), base::span<MockWrite>(),
1886                SYNCHRONOUS);
1887 
1888     // Finish resolution.
1889     session_deps_.host_resolver->ResolveOnlyRequestNow();
1890     EXPECT_FALSE(test_delegate.has_result());
1891     EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
1892               connect_job->GetLoadState());
1893 
1894     // Wait until just before negotiation with the tunnel should time out.
1895     FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
1896     EXPECT_FALSE(test_delegate.has_result());
1897 
1898     if (timeout_phase == TimeoutPhase::SECOND_PROXY_HANDSHAKE) {
1899       FastForwardBy(kTinyTime);
1900       ASSERT_TRUE(test_delegate.has_result());
1901       EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
1902       continue;
1903     }
1904 
1905     data_->Resume();
1906     ASSERT_TRUE(test_delegate.has_result());
1907     EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
1908   }
1909 }
1910 
TEST_P(HttpProxyConnectJobTest,ConnectionTimeoutNoNQE)1911 TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutNoNQE) {
1912   // Doesn't actually matter whether or not this is for a tunnel - the
1913   // connection timeout is the same, though it probably shouldn't be the same,
1914   // since tunnels need an extra round trip.
1915   base::TimeDelta alternate_connection_timeout =
1916       HttpProxyConnectJob::AlternateNestedConnectionTimeout(
1917           *CreateParams(true /* tunnel */, SecureDnsPolicy::kAllow),
1918           nullptr /* network_quality_estimator */);
1919 
1920 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
1921   // On Android and iOS, when there's no NQE, there's a hard-coded alternate
1922   // proxy timeout.
1923   EXPECT_EQ(base::Seconds(10), alternate_connection_timeout);
1924 #else
1925   // On other platforms, there is not.
1926   EXPECT_EQ(base::TimeDelta(), alternate_connection_timeout);
1927 #endif
1928 }
1929 
TEST_P(HttpProxyConnectJobTest,ConnectionTimeoutMin)1930 TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutMin) {
1931   // Set RTT estimate to a low value.
1932   base::TimeDelta rtt_estimate = base::Milliseconds(1);
1933   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
1934 
1935   EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
1936 
1937   // Test against a large value.
1938   EXPECT_GE(base::Minutes(10), GetNestedConnectionTimeout());
1939 
1940   EXPECT_EQ(base::Seconds(8), GetNestedConnectionTimeout());
1941 }
1942 
TEST_P(HttpProxyConnectJobTest,ConnectionTimeoutMax)1943 TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutMax) {
1944   // Set RTT estimate to a high value.
1945   base::TimeDelta rtt_estimate = base::Seconds(100);
1946   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
1947 
1948   EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
1949 
1950   // Test against a large value.
1951   EXPECT_GE(base::Minutes(10), GetNestedConnectionTimeout());
1952 
1953   EXPECT_EQ(base::Seconds(30), GetNestedConnectionTimeout());
1954 }
1955 
1956 // Tests the connection timeout values when the field trial parameters are
1957 // specified.
TEST_P(HttpProxyConnectJobTest,ConnectionTimeoutWithExperiment)1958 TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutWithExperiment) {
1959   // Timeout should be kMultiplier times the HTTP RTT estimate.
1960   const int kMultiplier = 4;
1961   const base::TimeDelta kMinTimeout = base::Seconds(8);
1962   const base::TimeDelta kMaxTimeout = base::Seconds(20);
1963 
1964   InitAdaptiveTimeoutFieldTrialWithParams(false, kMultiplier, kMultiplier,
1965                                           kMinTimeout, kMaxTimeout);
1966   EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
1967 
1968   base::TimeDelta rtt_estimate = base::Seconds(4);
1969   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
1970   base::TimeDelta expected_connection_timeout = kMultiplier * rtt_estimate;
1971   EXPECT_EQ(expected_connection_timeout, GetNestedConnectionTimeout());
1972 
1973   // Connection timeout should not exceed kMaxTimeout.
1974   rtt_estimate = base::Seconds(25);
1975   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
1976   EXPECT_EQ(kMaxTimeout, GetNestedConnectionTimeout());
1977 
1978   // Connection timeout should not be less than kMinTimeout.
1979   rtt_estimate = base::Seconds(0);
1980   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
1981   EXPECT_EQ(kMinTimeout, GetNestedConnectionTimeout());
1982 }
1983 
1984 // Tests the connection timeout values when the field trial parameters are
1985 // specified.
TEST_P(HttpProxyConnectJobTest,ConnectionTimeoutExperimentDifferentParams)1986 TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutExperimentDifferentParams) {
1987   // Timeout should be kMultiplier times the HTTP RTT estimate.
1988   const int kMultiplier = 3;
1989   const base::TimeDelta kMinTimeout = base::Seconds(2);
1990   const base::TimeDelta kMaxTimeout = base::Seconds(30);
1991 
1992   InitAdaptiveTimeoutFieldTrialWithParams(false, kMultiplier, kMultiplier,
1993                                           kMinTimeout, kMaxTimeout);
1994   EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
1995 
1996   base::TimeDelta rtt_estimate = base::Seconds(2);
1997   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
1998   EXPECT_EQ(kMultiplier * rtt_estimate, GetNestedConnectionTimeout());
1999 
2000   // A change in RTT estimate should also change the connection timeout.
2001   rtt_estimate = base::Seconds(7);
2002   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
2003   EXPECT_EQ(kMultiplier * rtt_estimate, GetNestedConnectionTimeout());
2004 
2005   // Connection timeout should not exceed kMaxTimeout.
2006   rtt_estimate = base::Seconds(35);
2007   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
2008   EXPECT_EQ(kMaxTimeout, GetNestedConnectionTimeout());
2009 
2010   // Connection timeout should not be less than kMinTimeout.
2011   rtt_estimate = base::Seconds(0);
2012   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
2013   EXPECT_EQ(kMinTimeout, GetNestedConnectionTimeout());
2014 }
2015 
TEST_P(HttpProxyConnectJobTest,ConnectionTimeoutWithConnectionProperty)2016 TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutWithConnectionProperty) {
2017   const int kSecureMultiplier = 3;
2018   const int kNonSecureMultiplier = 5;
2019   const base::TimeDelta kMinTimeout = base::Seconds(2);
2020   const base::TimeDelta kMaxTimeout = base::Seconds(30);
2021 
2022   InitAdaptiveTimeoutFieldTrialWithParams(
2023       false, kSecureMultiplier, kNonSecureMultiplier, kMinTimeout, kMaxTimeout);
2024 
2025   const base::TimeDelta kRttEstimate = base::Seconds(2);
2026   network_quality_estimator_->SetStartTimeNullHttpRtt(kRttEstimate);
2027   // By default, connection timeout should return the timeout for secure
2028   // proxies.
2029   if (GetParam() != HTTP) {
2030     EXPECT_EQ(kSecureMultiplier * kRttEstimate, GetNestedConnectionTimeout());
2031   } else {
2032     EXPECT_EQ(kNonSecureMultiplier * kRttEstimate,
2033               GetNestedConnectionTimeout());
2034   }
2035 }
2036 
2037 // Tests the connection timeout values when the field trial parameters are not
2038 // specified.
TEST_P(HttpProxyConnectJobTest,ProxyPoolTimeoutWithExperimentDefaultParams)2039 TEST_P(HttpProxyConnectJobTest, ProxyPoolTimeoutWithExperimentDefaultParams) {
2040   InitAdaptiveTimeoutFieldTrialWithParams(true, 0, 0, base::TimeDelta(),
2041                                           base::TimeDelta());
2042   EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
2043 
2044   // Timeout should be |http_rtt_multiplier| times the HTTP RTT
2045   // estimate.
2046   base::TimeDelta rtt_estimate = base::Milliseconds(10);
2047   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
2048   // Connection timeout should not be less than the HTTP RTT estimate.
2049   EXPECT_LE(rtt_estimate, GetNestedConnectionTimeout());
2050 
2051   // A change in RTT estimate should also change the connection timeout.
2052   rtt_estimate = base::Seconds(10);
2053   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
2054   // Connection timeout should not be less than the HTTP RTT estimate.
2055   EXPECT_LE(rtt_estimate, GetNestedConnectionTimeout());
2056 
2057   // Set RTT to a very large value.
2058   rtt_estimate = base::Minutes(60);
2059   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
2060   EXPECT_GT(rtt_estimate, GetNestedConnectionTimeout());
2061 
2062   // Set RTT to a very small value.
2063   rtt_estimate = base::Seconds(0);
2064   network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
2065   EXPECT_LT(rtt_estimate, GetNestedConnectionTimeout());
2066 }
2067 
2068 }  // namespace net
2069