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