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