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