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