1 // Copyright 2018 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/socket/transport_connect_job.h"
6
7 #include <memory>
8 #include <string>
9 #include <vector>
10
11 #include "base/memory/ref_counted.h"
12 #include "base/test/scoped_feature_list.h"
13 #include "base/test/task_environment.h"
14 #include "net/base/address_family.h"
15 #include "net/base/features.h"
16 #include "net/base/host_port_pair.h"
17 #include "net/base/ip_address.h"
18 #include "net/base/ip_endpoint.h"
19 #include "net/base/net_errors.h"
20 #include "net/cert/ct_policy_enforcer.h"
21 #include "net/cert/mock_cert_verifier.h"
22 #include "net/dns/mock_host_resolver.h"
23 #include "net/dns/public/secure_dns_policy.h"
24 #include "net/http/transport_security_state.h"
25 #include "net/log/net_log.h"
26 #include "net/socket/connect_job_test_util.h"
27 #include "net/socket/connection_attempts.h"
28 #include "net/socket/ssl_client_socket.h"
29 #include "net/socket/stream_socket.h"
30 #include "net/socket/transport_client_socket_pool_test_util.h"
31 #include "net/ssl/ssl_config_service.h"
32 #include "net/ssl/test_ssl_config_service.h"
33 #include "net/test/gtest_util.h"
34 #include "net/test/test_with_task_environment.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 #include "url/scheme_host_port.h"
37 #include "url/url_constants.h"
38
39 namespace net {
40 namespace {
41
42 const char kHostName[] = "unresolvable.host.name";
43
ParseIP(const std::string & ip)44 IPAddress ParseIP(const std::string& ip) {
45 IPAddress address;
46 CHECK(address.AssignFromIPLiteral(ip));
47 return address;
48 }
49
50 class TransportConnectJobTest : public WithTaskEnvironment,
51 public testing::Test {
52 public:
TransportConnectJobTest()53 TransportConnectJobTest()
54 : WithTaskEnvironment(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
55 client_socket_factory_(NetLog::Get()),
56 common_connect_job_params_(
57 &client_socket_factory_,
58 &host_resolver_,
59 /*http_auth_cache=*/nullptr,
60 /*http_auth_handler_factory=*/nullptr,
61 /*spdy_session_pool=*/nullptr,
62 /*quic_supported_versions=*/nullptr,
63 /*quic_stream_factory=*/nullptr,
64 /*proxy_delegate=*/nullptr,
65 /*http_user_agent_settings=*/nullptr,
66 &ssl_client_context_,
67 /*socket_performance_watcher_factory=*/nullptr,
68 /*network_quality_estimator=*/nullptr,
69 NetLog::Get(),
70 /*websocket_endpoint_lock_manager=*/nullptr,
71 /*http_server_properties=*/nullptr,
72 /*alpn_protos=*/nullptr,
73 /*application_settings=*/nullptr,
74 /*ignore_certificate_errors=*/nullptr) {}
75
76 ~TransportConnectJobTest() override = default;
77
DefaultParams()78 static scoped_refptr<TransportSocketParams> DefaultParams() {
79 return base::MakeRefCounted<TransportSocketParams>(
80 url::SchemeHostPort(url::kHttpScheme, kHostName, 80),
81 NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
82 OnHostResolutionCallback(),
83 /*supported_alpns=*/base::flat_set<std::string>());
84 }
85
DefaultHttpsParams()86 static scoped_refptr<TransportSocketParams> DefaultHttpsParams() {
87 return base::MakeRefCounted<TransportSocketParams>(
88 url::SchemeHostPort(url::kHttpsScheme, kHostName, 443),
89 NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
90 OnHostResolutionCallback(),
91 /*supported_alpns=*/base::flat_set<std::string>{"h2", "http/1.1"});
92 }
93
94 protected:
95 MockHostResolver host_resolver_{/*default_result=*/MockHostResolverBase::
96 RuleResolver::GetLocalhostResult()};
97 MockTransportClientSocketFactory client_socket_factory_;
98 TestSSLConfigService ssl_config_service_{SSLContextConfig{}};
99 MockCertVerifier cert_verifier_;
100 TransportSecurityState transport_security_state_;
101 DefaultCTPolicyEnforcer ct_policy_enforcer_;
102 SSLClientContext ssl_client_context_{&ssl_config_service_,
103 &cert_verifier_,
104 &transport_security_state_,
105 &ct_policy_enforcer_,
106 /*ssl_client_session_cache=*/nullptr,
107 /*sct_auditing_delegate=*/nullptr};
108 const CommonConnectJobParams common_connect_job_params_;
109 };
110
TEST_F(TransportConnectJobTest,HostResolutionFailure)111 TEST_F(TransportConnectJobTest, HostResolutionFailure) {
112 host_resolver_.rules()->AddSimulatedTimeoutFailure(kHostName);
113
114 // Check sync and async failures.
115 for (bool host_resolution_synchronous : {false, true}) {
116 host_resolver_.set_synchronous_mode(host_resolution_synchronous);
117 TestConnectJobDelegate test_delegate;
118 TransportConnectJob transport_connect_job(
119 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
120 DefaultParams(), &test_delegate, nullptr /* net_log */);
121 test_delegate.StartJobExpectingResult(&transport_connect_job,
122 ERR_NAME_NOT_RESOLVED,
123 host_resolution_synchronous);
124 EXPECT_THAT(transport_connect_job.GetResolveErrorInfo().error,
125 test::IsError(ERR_DNS_TIMED_OUT));
126 }
127 }
128
TEST_F(TransportConnectJobTest,ConnectionFailure)129 TEST_F(TransportConnectJobTest, ConnectionFailure) {
130 for (bool host_resolution_synchronous : {false, true}) {
131 for (bool connection_synchronous : {false, true}) {
132 host_resolver_.set_synchronous_mode(host_resolution_synchronous);
133 client_socket_factory_.set_default_client_socket_type(
134 connection_synchronous
135 ? MockTransportClientSocketFactory::Type::kFailing
136 : MockTransportClientSocketFactory::Type::kPendingFailing);
137 TestConnectJobDelegate test_delegate;
138 TransportConnectJob transport_connect_job(
139 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
140 DefaultParams(), &test_delegate, nullptr /* net_log */);
141 test_delegate.StartJobExpectingResult(
142 &transport_connect_job, ERR_CONNECTION_FAILED,
143 host_resolution_synchronous && connection_synchronous);
144 }
145 }
146 }
147
TEST_F(TransportConnectJobTest,HostResolutionTimeout)148 TEST_F(TransportConnectJobTest, HostResolutionTimeout) {
149 const base::TimeDelta kTinyTime = base::Microseconds(1);
150
151 // Make request hang.
152 host_resolver_.set_ondemand_mode(true);
153
154 TestConnectJobDelegate test_delegate;
155 TransportConnectJob transport_connect_job(
156 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
157 DefaultParams(), &test_delegate, nullptr /* net_log */);
158 ASSERT_THAT(transport_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
159
160 // Right up until just before expiration, the job does not time out.
161 FastForwardBy(TransportConnectJob::ConnectionTimeout() - kTinyTime);
162 EXPECT_FALSE(test_delegate.has_result());
163
164 // But at the exact time of expiration, the job fails.
165 FastForwardBy(kTinyTime);
166 EXPECT_TRUE(test_delegate.has_result());
167 EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
168 }
169
TEST_F(TransportConnectJobTest,ConnectionTimeout)170 TEST_F(TransportConnectJobTest, ConnectionTimeout) {
171 const base::TimeDelta kTinyTime = base::Microseconds(1);
172
173 // Half the timeout time. In the async case, spend half the time waiting on
174 // host resolution, half on connecting.
175 const base::TimeDelta kFirstHalfOfTimeout =
176 TransportConnectJob::ConnectionTimeout() / 2;
177
178 const base::TimeDelta kSecondHalfOfTimeout =
179 TransportConnectJob::ConnectionTimeout() - kFirstHalfOfTimeout;
180 ASSERT_LE(kTinyTime, kSecondHalfOfTimeout);
181
182 // Make connection attempts hang.
183 client_socket_factory_.set_default_client_socket_type(
184 MockTransportClientSocketFactory::Type::kStalled);
185
186 for (bool host_resolution_synchronous : {false, true}) {
187 host_resolver_.set_ondemand_mode(!host_resolution_synchronous);
188 TestConnectJobDelegate test_delegate;
189 TransportConnectJob transport_connect_job(
190 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
191 DefaultParams(), &test_delegate, nullptr /* net_log */);
192 EXPECT_THAT(transport_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
193
194 // After half the timeout, connection does not timeout.
195 FastForwardBy(kFirstHalfOfTimeout);
196 EXPECT_FALSE(test_delegate.has_result());
197
198 // In the async case, the host resolution completes now.
199 if (!host_resolution_synchronous)
200 host_resolver_.ResolveOnlyRequestNow();
201
202 // After (almost) the second half of timeout, just before the full timeout
203 // period, the ConnectJob is still live.
204 FastForwardBy(kSecondHalfOfTimeout - kTinyTime);
205 EXPECT_FALSE(test_delegate.has_result());
206
207 // But at the exact timeout time, the job fails.
208 FastForwardBy(kTinyTime);
209 EXPECT_TRUE(test_delegate.has_result());
210 EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
211 }
212 }
213
TEST_F(TransportConnectJobTest,ConnectionSuccess)214 TEST_F(TransportConnectJobTest, ConnectionSuccess) {
215 for (bool host_resolution_synchronous : {false, true}) {
216 for (bool connection_synchronous : {false, true}) {
217 host_resolver_.set_synchronous_mode(host_resolution_synchronous);
218 client_socket_factory_.set_default_client_socket_type(
219 connection_synchronous
220 ? MockTransportClientSocketFactory::Type::kSynchronous
221 : MockTransportClientSocketFactory::Type::kPending);
222 TestConnectJobDelegate test_delegate;
223 TransportConnectJob transport_connect_job(
224 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
225 DefaultParams(), &test_delegate, nullptr /* net_log */);
226 test_delegate.StartJobExpectingResult(
227 &transport_connect_job, OK,
228 host_resolution_synchronous && connection_synchronous);
229 }
230 }
231 }
232
TEST_F(TransportConnectJobTest,LoadState)233 TEST_F(TransportConnectJobTest, LoadState) {
234 client_socket_factory_.set_default_client_socket_type(
235 MockTransportClientSocketFactory::Type::kStalled);
236 host_resolver_.set_ondemand_mode(true);
237 host_resolver_.rules()->AddIPLiteralRule(kHostName, "1:abcd::3:4:ff,1.1.1.1",
238 std::string());
239
240 TestConnectJobDelegate test_delegate;
241 TransportConnectJob transport_connect_job(
242 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
243 DefaultParams(), &test_delegate, /*net_log=*/nullptr);
244 EXPECT_THAT(transport_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
245
246 // The job is initially waiting on DNS.
247 EXPECT_EQ(transport_connect_job.GetLoadState(), LOAD_STATE_RESOLVING_HOST);
248
249 // Complete DNS. It is now waiting on a TCP connection.
250 host_resolver_.ResolveOnlyRequestNow();
251 RunUntilIdle();
252 EXPECT_EQ(transport_connect_job.GetLoadState(), LOAD_STATE_CONNECTING);
253
254 // Wait for the IPv4 job to start. The job is still waiting on a TCP
255 // connection.
256 FastForwardBy(TransportConnectJob::kIPv6FallbackTime +
257 base::Milliseconds(50));
258 EXPECT_EQ(transport_connect_job.GetLoadState(), LOAD_STATE_CONNECTING);
259 }
260
261 // TODO(crbug.com/1206799): Set up `host_resolver_` to require the expected
262 // scheme.
TEST_F(TransportConnectJobTest,HandlesHttpsEndpoint)263 TEST_F(TransportConnectJobTest, HandlesHttpsEndpoint) {
264 TestConnectJobDelegate test_delegate;
265 TransportConnectJob transport_connect_job(
266 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
267 base::MakeRefCounted<TransportSocketParams>(
268 url::SchemeHostPort(url::kHttpsScheme, kHostName, 80),
269 NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
270 OnHostResolutionCallback(),
271 /*supported_alpns=*/base::flat_set<std::string>{"h2", "http/1.1"}),
272 &test_delegate, nullptr /* net_log */);
273 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
274 false /* expect_sync_result */);
275 }
276
277 // TODO(crbug.com/1206799): Set up `host_resolver_` to require the expected
278 // lack of scheme.
TEST_F(TransportConnectJobTest,HandlesNonStandardEndpoint)279 TEST_F(TransportConnectJobTest, HandlesNonStandardEndpoint) {
280 TestConnectJobDelegate test_delegate;
281 TransportConnectJob transport_connect_job(
282 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
283 base::MakeRefCounted<TransportSocketParams>(
284 HostPortPair(kHostName, 80), NetworkAnonymizationKey(),
285 SecureDnsPolicy::kAllow, OnHostResolutionCallback(),
286 /*supported_alpns=*/base::flat_set<std::string>()),
287 &test_delegate, nullptr /* net_log */);
288 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
289 false /* expect_sync_result */);
290 }
291
TEST_F(TransportConnectJobTest,SecureDnsPolicy)292 TEST_F(TransportConnectJobTest, SecureDnsPolicy) {
293 for (auto secure_dns_policy :
294 {SecureDnsPolicy::kAllow, SecureDnsPolicy::kDisable}) {
295 TestConnectJobDelegate test_delegate;
296 TransportConnectJob transport_connect_job(
297 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
298 base::MakeRefCounted<TransportSocketParams>(
299 url::SchemeHostPort(url::kHttpScheme, kHostName, 80),
300 NetworkAnonymizationKey(), secure_dns_policy,
301 OnHostResolutionCallback(),
302 /*supported_alpns=*/base::flat_set<std::string>{}),
303 &test_delegate, nullptr /* net_log */);
304 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
305 false /* expect_sync_result */);
306 EXPECT_EQ(secure_dns_policy, host_resolver_.last_secure_dns_policy());
307 }
308 }
309
310 // Test the case of the IPv6 address stalling, and falling back to the IPv4
311 // socket which finishes first.
TEST_F(TransportConnectJobTest,IPv6FallbackSocketIPv4FinishesFirst)312 TEST_F(TransportConnectJobTest, IPv6FallbackSocketIPv4FinishesFirst) {
313 MockTransportClientSocketFactory::Rule rules[] = {
314 // The first IPv6 attempt fails.
315 MockTransportClientSocketFactory::Rule(
316 MockTransportClientSocketFactory::Type::kFailing,
317 std::vector{IPEndPoint(ParseIP("1:abcd::3:4:ff"), 80)}),
318 // The second IPv6 attempt stalls.
319 MockTransportClientSocketFactory::Rule(
320 MockTransportClientSocketFactory::Type::kStalled,
321 std::vector{IPEndPoint(ParseIP("2:abcd::3:4:ff"), 80)}),
322 // After a timeout, we try the IPv4 address.
323 MockTransportClientSocketFactory::Rule(
324 MockTransportClientSocketFactory::Type::kPending,
325 std::vector{IPEndPoint(ParseIP("2.2.2.2"), 80)})};
326
327 client_socket_factory_.SetRules(rules);
328
329 // Resolve an AddressList with two IPv6 addresses and then a IPv4 address.
330 host_resolver_.rules()->AddIPLiteralRule(
331 kHostName, "1:abcd::3:4:ff,2:abcd::3:4:ff,2.2.2.2", std::string());
332
333 TestConnectJobDelegate test_delegate;
334 TransportConnectJob transport_connect_job(
335 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
336 DefaultParams(), &test_delegate, nullptr /* net_log */);
337 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
338 false /* expect_sync_result */);
339
340 IPEndPoint endpoint;
341 test_delegate.socket()->GetLocalAddress(&endpoint);
342 EXPECT_TRUE(endpoint.address().IsIPv4());
343
344 // Check that the failed connection attempt is collected.
345 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
346 ASSERT_EQ(1u, attempts.size());
347 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
348 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1:abcd::3:4:ff"), 80));
349
350 EXPECT_EQ(3, client_socket_factory_.allocation_count());
351 }
352
353 // Test the case of the IPv6 address being slow, thus falling back to trying to
354 // connect to the IPv4 address, but having the connect to the IPv6 address
355 // finish first.
TEST_F(TransportConnectJobTest,IPv6FallbackSocketIPv6FinishesFirst)356 TEST_F(TransportConnectJobTest, IPv6FallbackSocketIPv6FinishesFirst) {
357 MockTransportClientSocketFactory::Rule rules[] = {
358 // The first IPv6 attempt ultimately succeeds, but is delayed.
359 MockTransportClientSocketFactory::Rule(
360 MockTransportClientSocketFactory::Type::kDelayed,
361 std::vector{IPEndPoint(ParseIP("2:abcd::3:4:ff"), 80)}),
362 // The first IPv4 attempt fails.
363 MockTransportClientSocketFactory::Rule(
364 MockTransportClientSocketFactory::Type::kFailing,
365 std::vector{IPEndPoint(ParseIP("2.2.2.2"), 80)}),
366 // The second IPv4 attempt stalls.
367 MockTransportClientSocketFactory::Rule(
368 MockTransportClientSocketFactory::Type::kStalled,
369 std::vector{IPEndPoint(ParseIP("3.3.3.3"), 80)})};
370
371 client_socket_factory_.SetRules(rules);
372 client_socket_factory_.set_delay(TransportConnectJob::kIPv6FallbackTime +
373 base::Milliseconds(50));
374
375 // Resolve an AddressList with a IPv6 address first and then a IPv4 address.
376 host_resolver_.rules()->AddIPLiteralRule(
377 kHostName, "2:abcd::3:4:ff,2.2.2.2,3.3.3.3", std::string());
378
379 TestConnectJobDelegate test_delegate;
380 TransportConnectJob transport_connect_job(
381 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
382 DefaultParams(), &test_delegate, nullptr /* net_log */);
383 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
384 false /* expect_sync_result */);
385
386 IPEndPoint endpoint;
387 test_delegate.socket()->GetLocalAddress(&endpoint);
388 EXPECT_TRUE(endpoint.address().IsIPv6());
389
390 // Check that the failed connection attempt on the fallback socket is
391 // collected.
392 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
393 ASSERT_EQ(1u, attempts.size());
394 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
395 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("2.2.2.2"), 80));
396
397 EXPECT_EQ(3, client_socket_factory_.allocation_count());
398 }
399
TEST_F(TransportConnectJobTest,IPv6NoIPv4AddressesToFallbackTo)400 TEST_F(TransportConnectJobTest, IPv6NoIPv4AddressesToFallbackTo) {
401 client_socket_factory_.set_default_client_socket_type(
402 MockTransportClientSocketFactory::Type::kDelayed);
403
404 // Resolve an AddressList with only IPv6 addresses.
405 host_resolver_.rules()->AddIPLiteralRule(
406 kHostName, "2:abcd::3:4:ff,3:abcd::3:4:ff", std::string());
407
408 TestConnectJobDelegate test_delegate;
409 TransportConnectJob transport_connect_job(
410 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
411 DefaultParams(), &test_delegate, nullptr /* net_log */);
412 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
413 false /* expect_sync_result */);
414
415 IPEndPoint endpoint;
416 test_delegate.socket()->GetLocalAddress(&endpoint);
417 EXPECT_TRUE(endpoint.address().IsIPv6());
418 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
419 EXPECT_EQ(0u, attempts.size());
420 EXPECT_EQ(1, client_socket_factory_.allocation_count());
421 }
422
TEST_F(TransportConnectJobTest,IPv4HasNoFallback)423 TEST_F(TransportConnectJobTest, IPv4HasNoFallback) {
424 client_socket_factory_.set_default_client_socket_type(
425 MockTransportClientSocketFactory::Type::kDelayed);
426
427 // Resolve an AddressList with only IPv4 addresses.
428 host_resolver_.rules()->AddIPLiteralRule(kHostName, "1.1.1.1", std::string());
429
430 TestConnectJobDelegate test_delegate;
431 TransportConnectJob transport_connect_job(
432 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
433 DefaultParams(), &test_delegate, nullptr /* net_log */);
434 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
435 false /* expect_sync_result */);
436
437 IPEndPoint endpoint;
438 test_delegate.socket()->GetLocalAddress(&endpoint);
439 EXPECT_TRUE(endpoint.address().IsIPv4());
440 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
441 EXPECT_EQ(0u, attempts.size());
442 EXPECT_EQ(1, client_socket_factory_.allocation_count());
443 }
444
TEST_F(TransportConnectJobTest,DnsAliases)445 TEST_F(TransportConnectJobTest, DnsAliases) {
446 host_resolver_.set_synchronous_mode(true);
447 client_socket_factory_.set_default_client_socket_type(
448 MockTransportClientSocketFactory::Type::kSynchronous);
449
450 // Resolve an AddressList with DNS aliases.
451 std::vector<std::string> aliases({"alias1", "alias2", kHostName});
452 host_resolver_.rules()->AddIPLiteralRuleWithDnsAliases(kHostName, "2.2.2.2",
453 std::move(aliases));
454
455 TestConnectJobDelegate test_delegate;
456 TransportConnectJob transport_connect_job(
457 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
458 DefaultParams(), &test_delegate, nullptr /* net_log */);
459
460 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
461 true /* expect_sync_result */);
462
463 // Verify that the elements of the alias list are those from the
464 // parameter vector.
465 EXPECT_THAT(test_delegate.socket()->GetDnsAliases(),
466 testing::ElementsAre("alias1", "alias2", kHostName));
467 }
468
TEST_F(TransportConnectJobTest,NoAdditionalDnsAliases)469 TEST_F(TransportConnectJobTest, NoAdditionalDnsAliases) {
470 host_resolver_.set_synchronous_mode(true);
471 client_socket_factory_.set_default_client_socket_type(
472 MockTransportClientSocketFactory::Type::kSynchronous);
473
474 // Resolve an AddressList without additional DNS aliases. (The parameter
475 // is an empty vector.)
476 std::vector<std::string> aliases;
477 host_resolver_.rules()->AddIPLiteralRuleWithDnsAliases(kHostName, "2.2.2.2",
478 std::move(aliases));
479
480 TestConnectJobDelegate test_delegate;
481 TransportConnectJob transport_connect_job(
482 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
483 DefaultParams(), &test_delegate, nullptr /* net_log */);
484
485 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
486 true /* expect_sync_result */);
487
488 // Verify that the alias list only contains kHostName.
489 EXPECT_THAT(test_delegate.socket()->GetDnsAliases(),
490 testing::ElementsAre(kHostName));
491 }
492
493 // Test that `TransportConnectJob` will pick up options from
494 // `HostResolverEndpointResult`.
TEST_F(TransportConnectJobTest,EndpointResult)495 TEST_F(TransportConnectJobTest, EndpointResult) {
496 HostResolverEndpointResult endpoint;
497 endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8443),
498 IPEndPoint(ParseIP("1.1.1.1"), 8443)};
499 endpoint.metadata.supported_protocol_alpns = {"h2"};
500 host_resolver_.rules()->AddRule(
501 kHostName,
502 MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));
503
504 // The first access succeeds.
505 MockTransportClientSocketFactory::Rule rule(
506 MockTransportClientSocketFactory::Type::kSynchronous,
507 std::vector{IPEndPoint(ParseIP("1::"), 8443)});
508 client_socket_factory_.SetRules(base::make_span(&rule, 1u));
509
510 TestConnectJobDelegate test_delegate;
511 TransportConnectJob transport_connect_job(
512 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
513 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
514 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
515 /*expect_sync_result=*/false);
516
517 IPEndPoint peer_address;
518 test_delegate.socket()->GetPeerAddress(&peer_address);
519 EXPECT_EQ(peer_address, IPEndPoint(ParseIP("1::"), 8443));
520
521 EXPECT_EQ(1, client_socket_factory_.allocation_count());
522
523 // There were no failed connection attempts to report.
524 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
525 EXPECT_EQ(0u, attempts.size());
526 }
527
528 // Test that, given multiple `HostResolverEndpointResult` results,
529 // `TransportConnectJob` tries each in succession.
TEST_F(TransportConnectJobTest,MultipleRoutesFallback)530 TEST_F(TransportConnectJobTest, MultipleRoutesFallback) {
531 std::vector<HostResolverEndpointResult> endpoints(3);
532 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441),
533 IPEndPoint(ParseIP("1.1.1.1"), 8441)};
534 endpoints[0].metadata.supported_protocol_alpns = {"h3", "h2", "http/1.1"};
535 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442),
536 IPEndPoint(ParseIP("2.2.2.2"), 8442)};
537 endpoints[1].metadata.supported_protocol_alpns = {"h3"};
538 endpoints[2].ip_endpoints = {IPEndPoint(ParseIP("4::"), 443),
539 IPEndPoint(ParseIP("4.4.4.4"), 443)};
540 host_resolver_.rules()->AddRule(
541 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
542
543 MockTransportClientSocketFactory::Rule rules[] = {
544 // `endpoints[0]`'s addresses each fail.
545 MockTransportClientSocketFactory::Rule(
546 MockTransportClientSocketFactory::Type::kFailing,
547 std::vector{endpoints[0].ip_endpoints[0]}),
548 MockTransportClientSocketFactory::Rule(
549 MockTransportClientSocketFactory::Type::kFailing,
550 std::vector{endpoints[0].ip_endpoints[1]}),
551 // `endpoints[1]` is skipped because the ALPN is not compatible.
552 // `endpoints[2]`'s first address succeeds.
553 MockTransportClientSocketFactory::Rule(
554 MockTransportClientSocketFactory::Type::kSynchronous,
555 std::vector{endpoints[2].ip_endpoints[0]}),
556 };
557
558 client_socket_factory_.SetRules(rules);
559
560 TestConnectJobDelegate test_delegate;
561 TransportConnectJob transport_connect_job(
562 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
563 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
564 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
565 /*expect_sync_result=*/false);
566
567 IPEndPoint peer_address;
568 test_delegate.socket()->GetPeerAddress(&peer_address);
569 EXPECT_EQ(peer_address, IPEndPoint(ParseIP("4::"), 443));
570
571 // Check that failed connection attempts are reported.
572 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
573 ASSERT_EQ(2u, attempts.size());
574 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
575 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1::"), 8441));
576 EXPECT_THAT(attempts[1].result, test::IsError(ERR_CONNECTION_FAILED));
577 EXPECT_EQ(attempts[1].endpoint, IPEndPoint(ParseIP("1.1.1.1"), 8441));
578 }
579
580 // Test that the `HostResolverEndpointResult` fallback works in combination with
581 // the IPv4 fallback.
TEST_F(TransportConnectJobTest,MultipleRoutesIPV4Fallback)582 TEST_F(TransportConnectJobTest, MultipleRoutesIPV4Fallback) {
583 HostResolverEndpointResult endpoint1, endpoint2, endpoint3;
584 endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441),
585 IPEndPoint(ParseIP("1.1.1.1"), 8441)};
586 endpoint1.metadata.supported_protocol_alpns = {"h3", "h2", "http/1.1"};
587 endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442),
588 IPEndPoint(ParseIP("2.2.2.2"), 8442)};
589 endpoint2.metadata.supported_protocol_alpns = {"h3"};
590 endpoint3.ip_endpoints = {IPEndPoint(ParseIP("3::"), 443),
591 IPEndPoint(ParseIP("3.3.3.3"), 443)};
592 host_resolver_.rules()->AddRule(
593 kHostName, MockHostResolverBase::RuleResolver::RuleResult(
594 std::vector{endpoint1, endpoint2, endpoint3}));
595
596 MockTransportClientSocketFactory::Rule rules[] = {
597 // `endpoint1`'s IPv6 address fails, but takes long enough that the IPv4
598 // fallback runs.
599 //
600 // TODO(davidben): If the network is such that IPv6 connection attempts
601 // always stall, we will never try `endpoint2`. Should Happy Eyeballs
602 // logic happen before HTTPS RR. Or perhaps we should implement a more
603 // Happy-Eyeballs-v2-like strategy.
604 MockTransportClientSocketFactory::Rule(
605 MockTransportClientSocketFactory::Type::kDelayedFailing,
606 std::vector{IPEndPoint(ParseIP("1::"), 8441)}),
607
608 // `endpoint1`'s IPv4 address fails immediately.
609 MockTransportClientSocketFactory::Rule(
610 MockTransportClientSocketFactory::Type::kFailing,
611 std::vector{IPEndPoint(ParseIP("1.1.1.1"), 8441)}),
612
613 // `endpoint2` is skipped because the ALPN is not compatible.
614
615 // `endpoint3`'s IPv6 address never completes.
616 MockTransportClientSocketFactory::Rule(
617 MockTransportClientSocketFactory::Type::kStalled,
618 std::vector{IPEndPoint(ParseIP("3::"), 443)}),
619 // `endpoint3`'s IPv4 address succeeds.
620 MockTransportClientSocketFactory::Rule(
621 MockTransportClientSocketFactory::Type::kSynchronous,
622 std::vector{IPEndPoint(ParseIP("3.3.3.3"), 443)}),
623 };
624 client_socket_factory_.SetRules(rules);
625 client_socket_factory_.set_delay(TransportConnectJob::kIPv6FallbackTime +
626 base::Milliseconds(50));
627
628 TestConnectJobDelegate test_delegate;
629 TransportConnectJob transport_connect_job(
630 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
631 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
632 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
633 /*expect_sync_result=*/false);
634
635 IPEndPoint peer_address;
636 test_delegate.socket()->GetPeerAddress(&peer_address);
637 EXPECT_EQ(peer_address, IPEndPoint(ParseIP("3.3.3.3"), 443));
638
639 // Check that failed connection attempts are reported.
640 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
641 ASSERT_EQ(2u, attempts.size());
642 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
643 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1.1.1.1"), 8441));
644 EXPECT_THAT(attempts[1].result, test::IsError(ERR_CONNECTION_FAILED));
645 EXPECT_EQ(attempts[1].endpoint, IPEndPoint(ParseIP("1::"), 8441));
646 }
647
648 // Test that `TransportConnectJob` will not continue trying routes given
649 // ERR_NETWORK_IO_SUSPENDED.
TEST_F(TransportConnectJobTest,MultipleRoutesSuspended)650 TEST_F(TransportConnectJobTest, MultipleRoutesSuspended) {
651 std::vector<HostResolverEndpointResult> endpoints(2);
652 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8443)};
653 endpoints[0].metadata.supported_protocol_alpns = {"h3", "h2", "http/1.1"};
654 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 443)};
655 host_resolver_.rules()->AddRule(
656 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
657
658 // The first connect attempt will fail with `ERR_NETWORK_IO_SUSPENDED`.
659 // `TransportConnectJob` should not attempt routes after receiving this error.
660 MockTransportClientSocketFactory::Rule rule(
661 MockTransportClientSocketFactory::Type::kFailing,
662 endpoints[0].ip_endpoints, ERR_NETWORK_IO_SUSPENDED);
663 client_socket_factory_.SetRules(base::make_span(&rule, 1u));
664
665 TestConnectJobDelegate test_delegate;
666 TransportConnectJob transport_connect_job(
667 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
668 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
669 test_delegate.StartJobExpectingResult(&transport_connect_job,
670 ERR_NETWORK_IO_SUSPENDED,
671 /*expect_sync_result=*/false);
672
673 // Check that failed connection attempts are reported.
674 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
675 ASSERT_EQ(1u, attempts.size());
676 EXPECT_THAT(attempts[0].result, test::IsError(ERR_NETWORK_IO_SUSPENDED));
677 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1::"), 8443));
678 }
679
680 // Test that, if `HostResolver` supports SVCB for a scheme but the caller didn't
681 // pass in any ALPN protocols, `TransportConnectJob` ignores all protocol
682 // endpoints.
TEST_F(TransportConnectJobTest,NoAlpnProtocols)683 TEST_F(TransportConnectJobTest, NoAlpnProtocols) {
684 std::vector<HostResolverEndpointResult> endpoints(3);
685 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8081),
686 IPEndPoint(ParseIP("1.1.1.1"), 8081)};
687 endpoints[0].metadata.supported_protocol_alpns = {"foo", "bar"};
688 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 8082),
689 IPEndPoint(ParseIP("2.2.2.2"), 8082)};
690 endpoints[1].metadata.supported_protocol_alpns = {"baz"};
691 endpoints[2].ip_endpoints = {IPEndPoint(ParseIP("3::"), 80),
692 IPEndPoint(ParseIP("3.3.3.3"), 80)};
693 host_resolver_.rules()->AddRule(
694 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
695
696 // `endpoints[2]`'s first address succeeds.
697 MockTransportClientSocketFactory::Rule rule(
698 MockTransportClientSocketFactory::Type::kSynchronous,
699 std::vector{endpoints[2].ip_endpoints[0]});
700 client_socket_factory_.SetRules(base::make_span(&rule, 1u));
701
702 // Use `DefaultParams()`, an http scheme. That it is http is not very
703 // important, but `url::SchemeHostPort` is difficult to use with unknown
704 // schemes. See https://crbug.com/869291.
705 scoped_refptr<TransportSocketParams> params = DefaultParams();
706 ASSERT_TRUE(params->supported_alpns().empty());
707
708 TestConnectJobDelegate test_delegate;
709 TransportConnectJob transport_connect_job(
710 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
711 std::move(params), &test_delegate, /*net_log=*/nullptr);
712 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
713 /*expect_sync_result=*/false);
714
715 IPEndPoint peer_address;
716 test_delegate.socket()->GetPeerAddress(&peer_address);
717 EXPECT_EQ(peer_address, IPEndPoint(ParseIP("3::"), 80));
718 }
719
720 // Test that, given multiple `HostResolverEndpointResult` results,
721 // `TransportConnectJob` reports failure if each one fails.
TEST_F(TransportConnectJobTest,MultipleRoutesAllFailed)722 TEST_F(TransportConnectJobTest, MultipleRoutesAllFailed) {
723 std::vector<HostResolverEndpointResult> endpoints(3);
724 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441),
725 IPEndPoint(ParseIP("1.1.1.1"), 8441)};
726 endpoints[0].metadata.supported_protocol_alpns = {"h3", "h2", "http/1.1"};
727 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442),
728 IPEndPoint(ParseIP("2.2.2.2"), 8442)};
729 endpoints[1].metadata.supported_protocol_alpns = {"h3"};
730 endpoints[2].ip_endpoints = {IPEndPoint(ParseIP("3::"), 443),
731 IPEndPoint(ParseIP("3.3.3.3"), 443)};
732 host_resolver_.rules()->AddRule(
733 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
734
735 MockTransportClientSocketFactory::Rule rules[] = {
736 // `endpoints[0]`'s addresses each fail.
737 MockTransportClientSocketFactory::Rule(
738 MockTransportClientSocketFactory::Type::kFailing,
739 std::vector{endpoints[0].ip_endpoints[0]}),
740 MockTransportClientSocketFactory::Rule(
741 MockTransportClientSocketFactory::Type::kFailing,
742 std::vector{endpoints[0].ip_endpoints[1]}),
743 // `endpoints[1]` is skipped because the ALPN is not compatible.
744 // `endpoints[2]`'s addresses each fail.
745 MockTransportClientSocketFactory::Rule(
746 MockTransportClientSocketFactory::Type::kFailing,
747 std::vector{endpoints[2].ip_endpoints[0]}),
748 MockTransportClientSocketFactory::Rule(
749 MockTransportClientSocketFactory::Type::kFailing,
750 std::vector{endpoints[2].ip_endpoints[1]}),
751 };
752
753 client_socket_factory_.SetRules(rules);
754
755 TestConnectJobDelegate test_delegate;
756 TransportConnectJob transport_connect_job(
757 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
758 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
759 test_delegate.StartJobExpectingResult(&transport_connect_job,
760 ERR_CONNECTION_FAILED,
761 /*expect_sync_result=*/false);
762
763 // Check that failed connection attempts are reported.
764 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
765 ASSERT_EQ(4u, attempts.size());
766 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
767 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1::"), 8441));
768 EXPECT_THAT(attempts[1].result, test::IsError(ERR_CONNECTION_FAILED));
769 EXPECT_EQ(attempts[1].endpoint, IPEndPoint(ParseIP("1.1.1.1"), 8441));
770 EXPECT_THAT(attempts[2].result, test::IsError(ERR_CONNECTION_FAILED));
771 EXPECT_EQ(attempts[2].endpoint, IPEndPoint(ParseIP("3::"), 443));
772 EXPECT_THAT(attempts[3].result, test::IsError(ERR_CONNECTION_FAILED));
773 EXPECT_EQ(attempts[3].endpoint, IPEndPoint(ParseIP("3.3.3.3"), 443));
774 }
775
776 // Test that `TransportConnectJob` reports failure if all provided routes were
777 // unusable.
TEST_F(TransportConnectJobTest,NoUsableRoutes)778 TEST_F(TransportConnectJobTest, NoUsableRoutes) {
779 std::vector<HostResolverEndpointResult> endpoints(2);
780 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441),
781 IPEndPoint(ParseIP("1.1.1.1"), 8441)};
782 endpoints[0].metadata.supported_protocol_alpns = {"h3"};
783 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442),
784 IPEndPoint(ParseIP("2.2.2.2"), 8442)};
785 endpoints[1].metadata.supported_protocol_alpns = {"unrecognized-protocol"};
786 host_resolver_.rules()->AddRule(
787 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
788
789 // `TransportConnectJob` should not create any sockets.
790 client_socket_factory_.set_default_client_socket_type(
791 MockTransportClientSocketFactory::Type::kUnexpected);
792
793 TestConnectJobDelegate test_delegate;
794 TransportConnectJob transport_connect_job(
795 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
796 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
797 test_delegate.StartJobExpectingResult(&transport_connect_job,
798 ERR_NAME_NOT_RESOLVED,
799 /*expect_sync_result=*/false);
800 }
801
802 // Test that, if the last route is unusable, the error from the
803 // previously-attempted route is preserved.
TEST_F(TransportConnectJobTest,LastRouteUnusable)804 TEST_F(TransportConnectJobTest, LastRouteUnusable) {
805 std::vector<HostResolverEndpointResult> endpoints(2);
806 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441),
807 IPEndPoint(ParseIP("1.1.1.1"), 8441)};
808 endpoints[0].metadata.supported_protocol_alpns = {"h3", "h2", "http/1.1"};
809 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442),
810 IPEndPoint(ParseIP("2.2.2.2"), 8442)};
811 endpoints[1].metadata.supported_protocol_alpns = {"h3"};
812 host_resolver_.rules()->AddRule(
813 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
814
815 MockTransportClientSocketFactory::Rule rules[] = {
816 // `endpoints[0]`'s addresses each fail.
817 MockTransportClientSocketFactory::Rule(
818 MockTransportClientSocketFactory::Type::kFailing,
819 std::vector{endpoints[0].ip_endpoints[0]}),
820 MockTransportClientSocketFactory::Rule(
821 MockTransportClientSocketFactory::Type::kFailing,
822 std::vector{endpoints[0].ip_endpoints[1]}),
823 // `endpoints[1]` is skipped because the ALPN is not compatible.
824 };
825
826 client_socket_factory_.SetRules(rules);
827
828 TestConnectJobDelegate test_delegate;
829 TransportConnectJob transport_connect_job(
830 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
831 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
832 test_delegate.StartJobExpectingResult(&transport_connect_job,
833 ERR_CONNECTION_FAILED,
834 /*expect_sync_result=*/false);
835
836 // Check that failed connection attempts are reported.
837 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
838 ASSERT_EQ(2u, attempts.size());
839 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
840 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1::"), 8441));
841 EXPECT_THAT(attempts[1].result, test::IsError(ERR_CONNECTION_FAILED));
842 EXPECT_EQ(attempts[1].endpoint, IPEndPoint(ParseIP("1.1.1.1"), 8441));
843 }
844
845 // `GetHostResolverEndpointResult` should surface information about the endpoint
846 // that was actually used.
TEST_F(TransportConnectJobTest,GetHostResolverEndpointResult)847 TEST_F(TransportConnectJobTest, GetHostResolverEndpointResult) {
848 std::vector<HostResolverEndpointResult> endpoints(4);
849 // `endpoints[0]` will be skipped due to ALPN mismatch.
850 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
851 endpoints[0].metadata.supported_protocol_alpns = {"h3"};
852 endpoints[0].metadata.ech_config_list = {1, 2, 3, 4};
853 // `endpoints[1]` will be skipped due to connection failure.
854 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
855 endpoints[1].metadata.supported_protocol_alpns = {"http/1.1"};
856 endpoints[1].metadata.ech_config_list = {5, 6, 7, 8};
857 // `endpoints[2]` will succeed.
858 endpoints[2].ip_endpoints = {IPEndPoint(ParseIP("3::"), 8443)};
859 endpoints[2].metadata.supported_protocol_alpns = {"http/1.1"};
860 endpoints[2].metadata.ech_config_list = {9, 10, 11, 12};
861 // `endpoints[3]` will be not be tried because `endpoints[2]` will already
862 // have succeeded.
863 endpoints[3].ip_endpoints = {IPEndPoint(ParseIP("4::"), 8444)};
864 endpoints[3].metadata.supported_protocol_alpns = {"http/1.1"};
865 endpoints[3].metadata.ech_config_list = {13, 14, 15, 16};
866 host_resolver_.rules()->AddRule(
867 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
868
869 MockTransportClientSocketFactory::Rule rules[] = {
870 MockTransportClientSocketFactory::Rule(
871 MockTransportClientSocketFactory::Type::kFailing,
872 std::vector{IPEndPoint(ParseIP("2::"), 8442)}),
873 MockTransportClientSocketFactory::Rule(
874 MockTransportClientSocketFactory::Type::kSynchronous,
875 std::vector{IPEndPoint(ParseIP("3::"), 8443)}),
876 };
877 client_socket_factory_.SetRules(rules);
878
879 TestConnectJobDelegate test_delegate;
880 TransportConnectJob transport_connect_job(
881 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
882 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
883 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
884 /*expect_sync_result=*/false);
885
886 EXPECT_EQ(transport_connect_job.GetHostResolverEndpointResult(),
887 endpoints[2]);
888 }
889
890 // If the client and server both support ECH, TransportConnectJob should switch
891 // to SVCB-reliant mode and disable the A/AAAA fallback.
TEST_F(TransportConnectJobTest,SvcbReliantIfEch)892 TEST_F(TransportConnectJobTest, SvcbReliantIfEch) {
893 base::test::ScopedFeatureList feature_list;
894 feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
895
896 HostResolverEndpointResult endpoint1, endpoint2, endpoint3;
897 endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
898 endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
899 endpoint1.metadata.ech_config_list = {1, 2, 3, 4};
900 endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
901 endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
902 endpoint2.metadata.ech_config_list = {1, 2, 3, 4};
903 endpoint3.ip_endpoints = {IPEndPoint(ParseIP("3::"), 443)};
904 // `endpoint3` has no `supported_protocol_alpns` and is thus a fallback route.
905 host_resolver_.rules()->AddRule(
906 kHostName, MockHostResolverBase::RuleResolver::RuleResult(
907 std::vector{endpoint1, endpoint2, endpoint3}));
908
909 // `TransportConnectJob` should not try `endpoint3`.
910 MockTransportClientSocketFactory::Rule rules[] = {
911 MockTransportClientSocketFactory::Rule(
912 MockTransportClientSocketFactory::Type::kFailing,
913 std::vector{IPEndPoint(ParseIP("1::"), 8441)}),
914 MockTransportClientSocketFactory::Rule(
915 MockTransportClientSocketFactory::Type::kFailing,
916 std::vector{IPEndPoint(ParseIP("2::"), 8442)}),
917 };
918 client_socket_factory_.SetRules(rules);
919
920 TestConnectJobDelegate test_delegate;
921 TransportConnectJob transport_connect_job(
922 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
923 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
924 test_delegate.StartJobExpectingResult(&transport_connect_job,
925 ERR_CONNECTION_FAILED,
926 /*expect_sync_result=*/false);
927
928 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
929 ASSERT_EQ(2u, attempts.size());
930 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
931 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1::"), 8441));
932 EXPECT_THAT(attempts[1].result, test::IsError(ERR_CONNECTION_FAILED));
933 EXPECT_EQ(attempts[1].endpoint, IPEndPoint(ParseIP("2::"), 8442));
934 }
935
936 // SVCB-reliant mode should be disabled for ECH servers when ECH is disabled via
937 // `base::Feature`.
TEST_F(TransportConnectJobTest,SvcbOptionalIfEchDisabledFeature)938 TEST_F(TransportConnectJobTest, SvcbOptionalIfEchDisabledFeature) {
939 base::test::ScopedFeatureList feature_list;
940 feature_list.InitAndDisableFeature(features::kEncryptedClientHello);
941
942 HostResolverEndpointResult endpoint1, endpoint2, endpoint3;
943 endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
944 endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
945 endpoint1.metadata.ech_config_list = {1, 2, 3, 4};
946 endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
947 endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
948 endpoint2.metadata.ech_config_list = {1, 2, 3, 4};
949 endpoint3.ip_endpoints = {IPEndPoint(ParseIP("3::"), 443)};
950 // `endpoint3` has no `supported_protocol_alpns` and is thus a fallback route.
951 host_resolver_.rules()->AddRule(
952 kHostName, MockHostResolverBase::RuleResolver::RuleResult(
953 std::vector{endpoint1, endpoint2, endpoint3}));
954
955 // `TransportConnectJob` should try `endpoint3`.
956 MockTransportClientSocketFactory::Rule rules[] = {
957 MockTransportClientSocketFactory::Rule(
958 MockTransportClientSocketFactory::Type::kFailing,
959 std::vector{IPEndPoint(ParseIP("1::"), 8441)}),
960 MockTransportClientSocketFactory::Rule(
961 MockTransportClientSocketFactory::Type::kFailing,
962 std::vector{IPEndPoint(ParseIP("2::"), 8442)}),
963 MockTransportClientSocketFactory::Rule(
964 MockTransportClientSocketFactory::Type::kSynchronous,
965 std::vector{IPEndPoint(ParseIP("3::"), 443)}),
966 };
967 client_socket_factory_.SetRules(rules);
968
969 TestConnectJobDelegate test_delegate;
970 TransportConnectJob transport_connect_job(
971 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
972 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
973 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
974 /*expect_sync_result=*/false);
975 }
976
977 // SVCB-reliant mode should be disabled for ECH servers when ECH is disabled via
978 // config.
TEST_F(TransportConnectJobTest,SvcbOptionalIfEchDisabledConfig)979 TEST_F(TransportConnectJobTest, SvcbOptionalIfEchDisabledConfig) {
980 base::test::ScopedFeatureList feature_list;
981 feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
982
983 SSLContextConfig config;
984 config.ech_enabled = false;
985 ssl_config_service_.UpdateSSLConfigAndNotify(config);
986
987 HostResolverEndpointResult endpoint1, endpoint2, endpoint3;
988 endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
989 endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
990 endpoint1.metadata.ech_config_list = {1, 2, 3, 4};
991 endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
992 endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
993 endpoint2.metadata.ech_config_list = {1, 2, 3, 4};
994 endpoint3.ip_endpoints = {IPEndPoint(ParseIP("3::"), 443)};
995 // `endpoint3` has no `supported_protocol_alpns` and is thus a fallback route.
996 host_resolver_.rules()->AddRule(
997 kHostName, MockHostResolverBase::RuleResolver::RuleResult(
998 std::vector{endpoint1, endpoint2, endpoint3}));
999
1000 // `TransportConnectJob` should try `endpoint3`.
1001 MockTransportClientSocketFactory::Rule rules[] = {
1002 MockTransportClientSocketFactory::Rule(
1003 MockTransportClientSocketFactory::Type::kFailing,
1004 std::vector{IPEndPoint(ParseIP("1::"), 8441)}),
1005 MockTransportClientSocketFactory::Rule(
1006 MockTransportClientSocketFactory::Type::kFailing,
1007 std::vector{IPEndPoint(ParseIP("2::"), 8442)}),
1008 MockTransportClientSocketFactory::Rule(
1009 MockTransportClientSocketFactory::Type::kSynchronous,
1010 std::vector{IPEndPoint(ParseIP("3::"), 443)}),
1011 };
1012 client_socket_factory_.SetRules(rules);
1013
1014 TestConnectJobDelegate test_delegate;
1015 TransportConnectJob transport_connect_job(
1016 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
1017 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
1018 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
1019 /*expect_sync_result=*/false);
1020 }
1021
1022 // SVCB-reliant mode should be disabled if not all SVCB/HTTPS records include
1023 // ECH.
TEST_F(TransportConnectJobTest,SvcbOptionalIfEchInconsistent)1024 TEST_F(TransportConnectJobTest, SvcbOptionalIfEchInconsistent) {
1025 base::test::ScopedFeatureList feature_list;
1026 feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
1027
1028 HostResolverEndpointResult endpoint1, endpoint2, endpoint3;
1029 endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
1030 endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
1031 endpoint1.metadata.ech_config_list = {1, 2, 3, 4};
1032 endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
1033 endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
1034 endpoint2.metadata.ech_config_list = {};
1035 endpoint3.ip_endpoints = {IPEndPoint(ParseIP("3::"), 443)};
1036 // `endpoint3` has no `supported_protocol_alpns` and is thus a fallback route.
1037 host_resolver_.rules()->AddRule(
1038 kHostName, MockHostResolverBase::RuleResolver::RuleResult(
1039 std::vector{endpoint1, endpoint2, endpoint3}));
1040
1041 // `TransportConnectJob` should try `endpoint3`.
1042 MockTransportClientSocketFactory::Rule rules[] = {
1043 MockTransportClientSocketFactory::Rule(
1044 MockTransportClientSocketFactory::Type::kFailing,
1045 std::vector{IPEndPoint(ParseIP("1::"), 8441)}),
1046 MockTransportClientSocketFactory::Rule(
1047 MockTransportClientSocketFactory::Type::kFailing,
1048 std::vector{IPEndPoint(ParseIP("2::"), 8442)}),
1049 MockTransportClientSocketFactory::Rule(
1050 MockTransportClientSocketFactory::Type::kSynchronous,
1051 std::vector{IPEndPoint(ParseIP("3::"), 443)}),
1052 };
1053 client_socket_factory_.SetRules(rules);
1054
1055 TestConnectJobDelegate test_delegate;
1056 TransportConnectJob transport_connect_job(
1057 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
1058 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
1059 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
1060 /*expect_sync_result=*/false);
1061 }
1062
1063 // Overriding the endpoint results should skip DNS resolution.
TEST_F(TransportConnectJobTest,EndpointResultOverride)1064 TEST_F(TransportConnectJobTest, EndpointResultOverride) {
1065 // Make DNS resolution fail, to confirm we don't use the result.
1066 host_resolver_.rules()->AddRule(kHostName, ERR_FAILED);
1067
1068 // `TransportConnectJob` should try `endpoint`.
1069 HostResolverEndpointResult endpoint;
1070 endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
1071 endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
1072 MockTransportClientSocketFactory::Rule rules[] = {
1073 MockTransportClientSocketFactory::Rule(
1074 MockTransportClientSocketFactory::Type::kSynchronous,
1075 endpoint.ip_endpoints),
1076 };
1077 client_socket_factory_.SetRules(rules);
1078
1079 TransportConnectJob::EndpointResultOverride override(
1080 endpoint, {"alias.example", kHostName});
1081 TestConnectJobDelegate test_delegate;
1082 TransportConnectJob transport_connect_job(
1083 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
1084 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr, override);
1085 test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
1086 /*expect_sync_result=*/true);
1087
1088 // Verify information is reported from the override.
1089 EXPECT_EQ(transport_connect_job.GetHostResolverEndpointResult(), endpoint);
1090 EXPECT_THAT(test_delegate.socket()->GetDnsAliases(),
1091 testing::ElementsAre("alias.example", kHostName));
1092 }
1093
1094 // If two `HostResolverEndpointResult`s share an IP endpoint,
1095 // `TransportConnectJob` should not try to connect a second time.
TEST_F(TransportConnectJobTest,DedupIPEndPoints)1096 TEST_F(TransportConnectJobTest, DedupIPEndPoints) {
1097 std::vector<HostResolverEndpointResult> endpoints(4);
1098 // Some initial IPEndPoints.
1099 endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 443),
1100 IPEndPoint(ParseIP("1.1.1.1"), 443)};
1101 endpoints[0].metadata.supported_protocol_alpns = {"h2", "http/1.1"};
1102 // Contains a new IPEndPoint, but no common protocols.
1103 endpoints[1].ip_endpoints = {IPEndPoint(ParseIP("2::"), 443)};
1104 endpoints[1].metadata.supported_protocol_alpns = {"h3"};
1105 // Contains mixture of previously seen and new IPEndPoints, so we should only
1106 // try a subset of them.
1107 endpoints[2].ip_endpoints = {
1108 // Duplicate from `endpoints[0]`, should be filtered out.
1109 IPEndPoint(ParseIP("1::"), 443),
1110 // Same IP but new port. Should be used.
1111 IPEndPoint(ParseIP("1::"), 444),
1112 // Duplicate from `endpoints[1]`, but `endpoints[1]` was dropped, so this
1113 // should be used.
1114 IPEndPoint(ParseIP("2::"), 443),
1115 // Duplicate from `endpoints[0]`, should be filtered out.
1116 IPEndPoint(ParseIP("1.1.1.1"), 443),
1117 // New endpoint. Should be used.
1118 IPEndPoint(ParseIP("2.2.2.2"), 443)};
1119 endpoints[2].metadata.supported_protocol_alpns = {"h2", "http/1.1"};
1120 // Contains only previously seen IPEndPoints, so should be filtered out
1121 // entirely.
1122 endpoints[3].ip_endpoints = {IPEndPoint(ParseIP("1::"), 443),
1123 IPEndPoint(ParseIP("1::"), 444),
1124 IPEndPoint(ParseIP("2.2.2.2"), 443)};
1125 endpoints[3].metadata.supported_protocol_alpns = {"h2", "http/1.1"};
1126 host_resolver_.rules()->AddRule(
1127 kHostName, MockHostResolverBase::RuleResolver::RuleResult(endpoints));
1128
1129 MockTransportClientSocketFactory::Rule rules[] = {
1130 // First, try `endpoints[0]`'s addresses.
1131 MockTransportClientSocketFactory::Rule(
1132 MockTransportClientSocketFactory::Type::kFailing,
1133 std::vector{IPEndPoint(ParseIP("1::"), 443)}),
1134 MockTransportClientSocketFactory::Rule(
1135 MockTransportClientSocketFactory::Type::kFailing,
1136 std::vector{IPEndPoint(ParseIP("1.1.1.1"), 443)}),
1137
1138 // `endpoints[1]` is unusable, so it is ignored, including for purposes of
1139 // duplicate endpoints.
1140
1141 // Only new IP endpoints from `endpoints[2]` should be considered. Note
1142 // different ports count as different endpoints.
1143 MockTransportClientSocketFactory::Rule(
1144 MockTransportClientSocketFactory::Type::kFailing,
1145 std::vector{IPEndPoint(ParseIP("1::"), 444)}),
1146 MockTransportClientSocketFactory::Rule(
1147 MockTransportClientSocketFactory::Type::kFailing,
1148 std::vector{IPEndPoint(ParseIP("2::"), 443)}),
1149 MockTransportClientSocketFactory::Rule(
1150 MockTransportClientSocketFactory::Type::kFailing,
1151 std::vector{IPEndPoint(ParseIP("2.2.2.2"), 443)}),
1152
1153 // `endpoints[3]` only contains duplicate IP endpoints and should be
1154 // skipped.
1155 };
1156
1157 client_socket_factory_.SetRules(rules);
1158
1159 TestConnectJobDelegate test_delegate;
1160 TransportConnectJob transport_connect_job(
1161 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
1162 DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr);
1163 test_delegate.StartJobExpectingResult(&transport_connect_job,
1164 ERR_CONNECTION_FAILED,
1165 /*expect_sync_result=*/false);
1166
1167 // Check that failed connection attempts are reported.
1168 ConnectionAttempts attempts = transport_connect_job.GetConnectionAttempts();
1169 ASSERT_EQ(5u, attempts.size());
1170 EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
1171 EXPECT_EQ(attempts[0].endpoint, IPEndPoint(ParseIP("1::"), 443));
1172 EXPECT_THAT(attempts[1].result, test::IsError(ERR_CONNECTION_FAILED));
1173 EXPECT_EQ(attempts[1].endpoint, IPEndPoint(ParseIP("1.1.1.1"), 443));
1174 EXPECT_THAT(attempts[2].result, test::IsError(ERR_CONNECTION_FAILED));
1175 EXPECT_EQ(attempts[2].endpoint, IPEndPoint(ParseIP("1::"), 444));
1176 EXPECT_THAT(attempts[3].result, test::IsError(ERR_CONNECTION_FAILED));
1177 EXPECT_EQ(attempts[3].endpoint, IPEndPoint(ParseIP("2::"), 443));
1178 EXPECT_THAT(attempts[4].result, test::IsError(ERR_CONNECTION_FAILED));
1179 EXPECT_EQ(attempts[4].endpoint, IPEndPoint(ParseIP("2.2.2.2"), 443));
1180 }
1181
1182 } // namespace
1183 } // namespace net
1184