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