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