transport_connect_job.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/socket/transport_connect_job.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/check_op.h"
  9. #include "base/containers/contains.h"
  10. #include "base/feature_list.h"
  11. #include "base/location.h"
  12. #include "base/logging.h"
  13. #include "base/metrics/histogram_macros.h"
  14. #include "base/notreached.h"
  15. #include "base/stl_util.h"
  16. #include "base/threading/thread_task_runner_handle.h"
  17. #include "base/time/time.h"
  18. #include "base/trace_event/trace_event.h"
  19. #include "base/values.h"
  20. #include "net/base/features.h"
  21. #include "net/base/host_port_pair.h"
  22. #include "net/base/ip_endpoint.h"
  23. #include "net/base/net_errors.h"
  24. #include "net/base/trace_constants.h"
  25. #include "net/dns/host_resolver_results.h"
  26. #include "net/dns/public/secure_dns_policy.h"
  27. #include "net/log/net_log_event_type.h"
  28. #include "net/log/net_log_source.h"
  29. #include "net/socket/socket_tag.h"
  30. #include "net/socket/transport_connect_sub_job.h"
  31. #include "third_party/abseil-cpp/absl/types/variant.h"
  32. #include "url/scheme_host_port.h"
  33. #include "url/url_constants.h"
  34. namespace net {
  35. namespace {
  36. // TODO(crbug.com/1206799): Delete once endpoint usage is converted to using
  37. // url::SchemeHostPort when available.
  38. HostPortPair ToLegacyDestinationEndpoint(
  39. const TransportSocketParams::Endpoint& endpoint) {
  40. if (absl::holds_alternative<url::SchemeHostPort>(endpoint)) {
  41. return HostPortPair::FromSchemeHostPort(
  42. absl::get<url::SchemeHostPort>(endpoint));
  43. }
  44. DCHECK(absl::holds_alternative<HostPortPair>(endpoint));
  45. return absl::get<HostPortPair>(endpoint);
  46. }
  47. } // namespace
  48. TransportSocketParams::TransportSocketParams(
  49. Endpoint destination,
  50. NetworkIsolationKey network_isolation_key,
  51. SecureDnsPolicy secure_dns_policy,
  52. OnHostResolutionCallback host_resolution_callback,
  53. base::flat_set<std::string> supported_alpns)
  54. : destination_(std::move(destination)),
  55. network_isolation_key_(std::move(network_isolation_key)),
  56. secure_dns_policy_(secure_dns_policy),
  57. host_resolution_callback_(std::move(host_resolution_callback)),
  58. supported_alpns_(std::move(supported_alpns)) {
  59. #if DCHECK_IS_ON()
  60. auto* scheme_host_port = absl::get_if<url::SchemeHostPort>(&destination_);
  61. if (scheme_host_port) {
  62. if (scheme_host_port->scheme() == url::kHttpsScheme) {
  63. // HTTPS destinations will, when passed to the DNS resolver, return
  64. // SVCB/HTTPS-based routes. Those routes require ALPN protocols to
  65. // evaluate. If there are none, `IsEndpointResultUsable` will correctly
  66. // skip each route, but it doesn't make sense to make a DNS query if we
  67. // can't handle the result.
  68. DCHECK(!supported_alpns_.empty());
  69. } else if (scheme_host_port->scheme() == url::kHttpScheme) {
  70. // HTTP (not HTTPS) does not currently define ALPN protocols, so the list
  71. // should be empty. This means `IsEndpointResultUsable` will skip any
  72. // SVCB-based routes. HTTP also has no SVCB mapping, so `HostResolver`
  73. // will never return them anyway.
  74. //
  75. // `HostResolver` will still query SVCB (rather, HTTPS) records for the
  76. // corresponding HTTPS URL to implement an upgrade flow (section 9.5 of
  77. // draft-ietf-dnsop-svcb-https-08), but this will result in DNS resolution
  78. // failing with `ERR_DNS_NAME_HTTPS_ONLY`, not SVCB-based routes.
  79. DCHECK(supported_alpns_.empty());
  80. }
  81. }
  82. #endif
  83. }
  84. TransportSocketParams::~TransportSocketParams() = default;
  85. std::unique_ptr<TransportConnectJob> TransportConnectJob::Factory::Create(
  86. RequestPriority priority,
  87. const SocketTag& socket_tag,
  88. const CommonConnectJobParams* common_connect_job_params,
  89. const scoped_refptr<TransportSocketParams>& params,
  90. Delegate* delegate,
  91. const NetLogWithSource* net_log) {
  92. return std::make_unique<TransportConnectJob>(priority, socket_tag,
  93. common_connect_job_params,
  94. params, delegate, net_log);
  95. }
  96. TransportConnectJob::EndpointResultOverride::EndpointResultOverride(
  97. HostResolverEndpointResult result,
  98. std::set<std::string> dns_aliases)
  99. : result(std::move(result)), dns_aliases(std::move(dns_aliases)) {}
  100. TransportConnectJob::EndpointResultOverride::EndpointResultOverride(
  101. EndpointResultOverride&&) = default;
  102. TransportConnectJob::EndpointResultOverride::EndpointResultOverride(
  103. const EndpointResultOverride&) = default;
  104. TransportConnectJob::EndpointResultOverride::~EndpointResultOverride() =
  105. default;
  106. TransportConnectJob::TransportConnectJob(
  107. RequestPriority priority,
  108. const SocketTag& socket_tag,
  109. const CommonConnectJobParams* common_connect_job_params,
  110. const scoped_refptr<TransportSocketParams>& params,
  111. Delegate* delegate,
  112. const NetLogWithSource* net_log,
  113. absl::optional<EndpointResultOverride> endpoint_result_override)
  114. : ConnectJob(priority,
  115. socket_tag,
  116. ConnectionTimeout(),
  117. common_connect_job_params,
  118. delegate,
  119. net_log,
  120. NetLogSourceType::TRANSPORT_CONNECT_JOB,
  121. NetLogEventType::TRANSPORT_CONNECT_JOB_CONNECT),
  122. params_(params) {
  123. if (endpoint_result_override) {
  124. has_dns_override_ = true;
  125. endpoint_results_ = {std::move(endpoint_result_override->result)};
  126. dns_aliases_ = std::move(endpoint_result_override->dns_aliases);
  127. DCHECK(!endpoint_results_.front().ip_endpoints.empty());
  128. DCHECK(IsEndpointResultUsable(endpoint_results_.front(),
  129. IsSvcbOptional(endpoint_results_)));
  130. }
  131. }
  132. // We don't worry about cancelling the host resolution and TCP connect, since
  133. // ~HostResolver::Request and ~TransportConnectSubJob will take care of it.
  134. TransportConnectJob::~TransportConnectJob() = default;
  135. LoadState TransportConnectJob::GetLoadState() const {
  136. switch (next_state_) {
  137. case STATE_RESOLVE_HOST:
  138. case STATE_RESOLVE_HOST_COMPLETE:
  139. case STATE_RESOLVE_HOST_CALLBACK_COMPLETE:
  140. return LOAD_STATE_RESOLVING_HOST;
  141. case STATE_TRANSPORT_CONNECT:
  142. case STATE_TRANSPORT_CONNECT_COMPLETE: {
  143. LoadState load_state = LOAD_STATE_IDLE;
  144. if (ipv6_job_ && ipv6_job_->started()) {
  145. load_state = ipv6_job_->GetLoadState();
  146. }
  147. // This method should return LOAD_STATE_CONNECTING in preference to
  148. // LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET when possible because "waiting
  149. // for available socket" implies that nothing is happening.
  150. if (ipv4_job_ && ipv4_job_->started() &&
  151. load_state != LOAD_STATE_CONNECTING) {
  152. load_state = ipv4_job_->GetLoadState();
  153. }
  154. return load_state;
  155. }
  156. case STATE_NONE:
  157. return LOAD_STATE_IDLE;
  158. }
  159. }
  160. bool TransportConnectJob::HasEstablishedConnection() const {
  161. // No need to ever return true, since NotifyComplete() is called as soon as a
  162. // connection is established.
  163. return false;
  164. }
  165. ConnectionAttempts TransportConnectJob::GetConnectionAttempts() const {
  166. return connection_attempts_;
  167. }
  168. ResolveErrorInfo TransportConnectJob::GetResolveErrorInfo() const {
  169. return resolve_error_info_;
  170. }
  171. absl::optional<HostResolverEndpointResult>
  172. TransportConnectJob::GetHostResolverEndpointResult() const {
  173. CHECK_LT(current_endpoint_result_, endpoint_results_.size());
  174. return endpoint_results_[current_endpoint_result_];
  175. }
  176. base::TimeDelta TransportConnectJob::ConnectionTimeout() {
  177. // TODO(eroman): The use of this constant needs to be re-evaluated. The time
  178. // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
  179. // the address list may contain many alternatives, and most of those may
  180. // timeout. Even worse, the per-connect timeout threshold varies greatly
  181. // between systems (anywhere from 20 seconds to 190 seconds).
  182. // See comment #12 at http://crbug.com/23364 for specifics.
  183. return base::Minutes(4);
  184. }
  185. void TransportConnectJob::OnIOComplete(int result) {
  186. result = DoLoop(result);
  187. if (result != ERR_IO_PENDING)
  188. NotifyDelegateOfCompletion(result); // Deletes |this|
  189. }
  190. int TransportConnectJob::DoLoop(int result) {
  191. DCHECK_NE(next_state_, STATE_NONE);
  192. int rv = result;
  193. do {
  194. State state = next_state_;
  195. next_state_ = STATE_NONE;
  196. switch (state) {
  197. case STATE_RESOLVE_HOST:
  198. DCHECK_EQ(OK, rv);
  199. rv = DoResolveHost();
  200. break;
  201. case STATE_RESOLVE_HOST_COMPLETE:
  202. rv = DoResolveHostComplete(rv);
  203. break;
  204. case STATE_RESOLVE_HOST_CALLBACK_COMPLETE:
  205. DCHECK_EQ(OK, rv);
  206. rv = DoResolveHostCallbackComplete();
  207. break;
  208. case STATE_TRANSPORT_CONNECT:
  209. DCHECK_EQ(OK, rv);
  210. rv = DoTransportConnect();
  211. break;
  212. case STATE_TRANSPORT_CONNECT_COMPLETE:
  213. rv = DoTransportConnectComplete(rv);
  214. break;
  215. default:
  216. NOTREACHED();
  217. rv = ERR_FAILED;
  218. break;
  219. }
  220. } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
  221. return rv;
  222. }
  223. int TransportConnectJob::DoResolveHost() {
  224. connect_timing_.dns_start = base::TimeTicks::Now();
  225. if (has_dns_override_) {
  226. DCHECK_EQ(1u, endpoint_results_.size());
  227. connect_timing_.dns_end = connect_timing_.dns_start;
  228. next_state_ = STATE_TRANSPORT_CONNECT;
  229. return OK;
  230. }
  231. next_state_ = STATE_RESOLVE_HOST_COMPLETE;
  232. HostResolver::ResolveHostParameters parameters;
  233. parameters.initial_priority = priority();
  234. parameters.secure_dns_policy = params_->secure_dns_policy();
  235. if (absl::holds_alternative<url::SchemeHostPort>(params_->destination())) {
  236. request_ = host_resolver()->CreateRequest(
  237. absl::get<url::SchemeHostPort>(params_->destination()),
  238. params_->network_isolation_key(), net_log(), parameters);
  239. } else {
  240. request_ = host_resolver()->CreateRequest(
  241. absl::get<HostPortPair>(params_->destination()),
  242. params_->network_isolation_key(), net_log(), parameters);
  243. }
  244. return request_->Start(base::BindOnce(&TransportConnectJob::OnIOComplete,
  245. base::Unretained(this)));
  246. }
  247. int TransportConnectJob::DoResolveHostComplete(int result) {
  248. TRACE_EVENT0(NetTracingCategory(),
  249. "TransportConnectJob::DoResolveHostComplete");
  250. connect_timing_.dns_end = base::TimeTicks::Now();
  251. // Overwrite connection start time, since for connections that do not go
  252. // through proxies, |connect_start| should not include dns lookup time.
  253. connect_timing_.connect_start = connect_timing_.dns_end;
  254. resolve_error_info_ = request_->GetResolveErrorInfo();
  255. if (result != OK) {
  256. // If hostname resolution failed, record an empty endpoint and the result.
  257. connection_attempts_.push_back(ConnectionAttempt(IPEndPoint(), result));
  258. return result;
  259. }
  260. DCHECK(request_->GetAddressResults());
  261. DCHECK(request_->GetDnsAliasResults());
  262. DCHECK(request_->GetEndpointResults());
  263. // Invoke callback. If it indicates |this| may be slated for deletion, then
  264. // only continue after a PostTask.
  265. next_state_ = STATE_RESOLVE_HOST_CALLBACK_COMPLETE;
  266. if (!params_->host_resolution_callback().is_null()) {
  267. // TODO(https://crbug.com/1287240): Switch `OnHostResolutionCallbackResult`
  268. // to `request_->GetEndpointResults()` and `request_->GetDnsAliasResults()`.
  269. OnHostResolutionCallbackResult callback_result =
  270. params_->host_resolution_callback().Run(
  271. ToLegacyDestinationEndpoint(params_->destination()),
  272. *request_->GetAddressResults());
  273. if (callback_result == OnHostResolutionCallbackResult::kMayBeDeletedAsync) {
  274. base::ThreadTaskRunnerHandle::Get()->PostTask(
  275. FROM_HERE, base::BindOnce(&TransportConnectJob::OnIOComplete,
  276. weak_ptr_factory_.GetWeakPtr(), OK));
  277. return ERR_IO_PENDING;
  278. }
  279. }
  280. return result;
  281. }
  282. int TransportConnectJob::DoResolveHostCallbackComplete() {
  283. const auto& unfiltered_results = *request_->GetEndpointResults();
  284. bool svcb_optional = IsSvcbOptional(unfiltered_results);
  285. std::set<IPEndPoint> ip_endpoints_seen;
  286. for (const auto& result : unfiltered_results) {
  287. if (!IsEndpointResultUsable(result, svcb_optional)) {
  288. continue;
  289. }
  290. // The TCP connect itself does not depend on any metadata, so we can dedup
  291. // by IP endpoint. In particular, the fallback A/AAAA route will often use
  292. // the same IP endpoints as the HTTPS route. If they do not work for one
  293. // route, there is no use in trying a second time.
  294. std::vector<IPEndPoint> ip_endpoints;
  295. for (const auto& ip_endpoint : result.ip_endpoints) {
  296. auto [iter, inserted] = ip_endpoints_seen.insert(ip_endpoint);
  297. if (inserted) {
  298. ip_endpoints.push_back(ip_endpoint);
  299. }
  300. }
  301. if (!ip_endpoints.empty()) {
  302. HostResolverEndpointResult new_result;
  303. new_result.ip_endpoints = std::move(ip_endpoints);
  304. new_result.metadata = result.metadata;
  305. endpoint_results_.push_back(std::move(new_result));
  306. }
  307. }
  308. dns_aliases_ = *request_->GetDnsAliasResults();
  309. // No need to retain `request_` beyond this point.
  310. request_.reset();
  311. if (endpoint_results_.empty()) {
  312. // In the general case, DNS may successfully return routes, but none are
  313. // compatible with this `ConnectJob`. This should not happen for HTTPS
  314. // because `HostResolver` will reject SVCB/HTTPS sets that do not cover the
  315. // default "http/1.1" ALPN.
  316. return ERR_NAME_NOT_RESOLVED;
  317. }
  318. next_state_ = STATE_TRANSPORT_CONNECT;
  319. return OK;
  320. }
  321. int TransportConnectJob::DoTransportConnect() {
  322. next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
  323. const HostResolverEndpointResult& endpoint =
  324. GetEndpointResultForCurrentSubJobs();
  325. std::vector<IPEndPoint> ipv4_addresses, ipv6_addresses;
  326. for (const auto& ip_endpoint : endpoint.ip_endpoints) {
  327. switch (ip_endpoint.GetFamily()) {
  328. case ADDRESS_FAMILY_IPV4:
  329. ipv4_addresses.push_back(ip_endpoint);
  330. break;
  331. case ADDRESS_FAMILY_IPV6:
  332. ipv6_addresses.push_back(ip_endpoint);
  333. break;
  334. default:
  335. DVLOG(1) << "Unexpected ADDRESS_FAMILY: " << ip_endpoint.GetFamily();
  336. break;
  337. }
  338. }
  339. if (!ipv4_addresses.empty()) {
  340. ipv4_job_ = std::make_unique<TransportConnectSubJob>(
  341. std::move(ipv4_addresses), this, SUB_JOB_IPV4);
  342. }
  343. if (!ipv6_addresses.empty()) {
  344. ipv6_job_ = std::make_unique<TransportConnectSubJob>(
  345. std::move(ipv6_addresses), this, SUB_JOB_IPV6);
  346. int result = ipv6_job_->Start();
  347. if (result != ERR_IO_PENDING)
  348. return HandleSubJobComplete(result, ipv6_job_.get());
  349. if (ipv4_job_) {
  350. // This use of base::Unretained is safe because |fallback_timer_| is
  351. // owned by this object.
  352. fallback_timer_.Start(
  353. FROM_HERE, kIPv6FallbackTime,
  354. base::BindOnce(&TransportConnectJob::StartIPv4JobAsync,
  355. base::Unretained(this)));
  356. }
  357. return ERR_IO_PENDING;
  358. }
  359. DCHECK(!ipv6_job_);
  360. DCHECK(ipv4_job_);
  361. int result = ipv4_job_->Start();
  362. if (result != ERR_IO_PENDING)
  363. return HandleSubJobComplete(result, ipv4_job_.get());
  364. return ERR_IO_PENDING;
  365. }
  366. int TransportConnectJob::DoTransportConnectComplete(int result) {
  367. // Make sure nothing else calls back into this object.
  368. ipv4_job_.reset();
  369. ipv6_job_.reset();
  370. fallback_timer_.Stop();
  371. if (result == OK) {
  372. DCHECK(!connect_timing_.connect_start.is_null());
  373. DCHECK(!connect_timing_.dns_start.is_null());
  374. // `HandleSubJobComplete` should have called `SetSocket`.
  375. DCHECK(socket());
  376. base::TimeTicks now = base::TimeTicks::Now();
  377. base::TimeDelta total_duration = now - connect_timing_.dns_start;
  378. UMA_HISTOGRAM_CUSTOM_TIMES("Net.DNS_Resolution_And_TCP_Connection_Latency2",
  379. total_duration, base::Milliseconds(1),
  380. base::Minutes(10), 100);
  381. base::TimeDelta connect_duration = now - connect_timing_.connect_start;
  382. UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", connect_duration,
  383. base::Milliseconds(1), base::Minutes(10), 100);
  384. } else {
  385. // Don't try the next route if entering suspend mode.
  386. if (result != ERR_NETWORK_IO_SUSPENDED) {
  387. // If there is another endpoint available, try it.
  388. current_endpoint_result_++;
  389. if (current_endpoint_result_ < endpoint_results_.size()) {
  390. next_state_ = STATE_TRANSPORT_CONNECT;
  391. result = OK;
  392. }
  393. }
  394. }
  395. return result;
  396. }
  397. int TransportConnectJob::HandleSubJobComplete(int result,
  398. TransportConnectSubJob* job) {
  399. DCHECK_NE(result, ERR_IO_PENDING);
  400. if (result == OK) {
  401. SetSocket(job->PassSocket(), dns_aliases_);
  402. return result;
  403. }
  404. if (result == ERR_NETWORK_IO_SUSPENDED) {
  405. // Don't try other jobs if entering suspend mode.
  406. return result;
  407. }
  408. switch (job->type()) {
  409. case SUB_JOB_IPV4:
  410. ipv4_job_.reset();
  411. break;
  412. case SUB_JOB_IPV6:
  413. ipv6_job_.reset();
  414. // Start the other job, rather than wait for the fallback timer.
  415. if (ipv4_job_ && !ipv4_job_->started()) {
  416. fallback_timer_.Stop();
  417. result = ipv4_job_->Start();
  418. if (result != ERR_IO_PENDING) {
  419. return HandleSubJobComplete(result, ipv4_job_.get());
  420. }
  421. }
  422. break;
  423. }
  424. if (ipv4_job_ || ipv6_job_) {
  425. // Wait for the other job to complete, rather than reporting |result|.
  426. return ERR_IO_PENDING;
  427. }
  428. return result;
  429. }
  430. void TransportConnectJob::OnSubJobComplete(int result,
  431. TransportConnectSubJob* job) {
  432. result = HandleSubJobComplete(result, job);
  433. if (result != ERR_IO_PENDING) {
  434. OnIOComplete(result);
  435. }
  436. }
  437. void TransportConnectJob::StartIPv4JobAsync() {
  438. DCHECK(ipv4_job_);
  439. int result = ipv4_job_->Start();
  440. if (result != ERR_IO_PENDING)
  441. OnSubJobComplete(result, ipv4_job_.get());
  442. }
  443. int TransportConnectJob::ConnectInternal() {
  444. next_state_ = STATE_RESOLVE_HOST;
  445. return DoLoop(OK);
  446. }
  447. void TransportConnectJob::ChangePriorityInternal(RequestPriority priority) {
  448. if (next_state_ == STATE_RESOLVE_HOST_COMPLETE) {
  449. DCHECK(request_);
  450. // Change the request priority in the host resolver.
  451. request_->ChangeRequestPriority(priority);
  452. }
  453. }
  454. bool TransportConnectJob::IsSvcbOptional(
  455. base::span<const HostResolverEndpointResult> results) const {
  456. // If SVCB/HTTPS resolution succeeded, the client supports ECH, and all routes
  457. // support ECH, disable the A/AAAA fallback. See Section 10.1 of
  458. // draft-ietf-dnsop-svcb-https-08.
  459. auto* scheme_host_port =
  460. absl::get_if<url::SchemeHostPort>(&params_->destination());
  461. if (!base::FeatureList::IsEnabled(features::kEncryptedClientHello) ||
  462. !scheme_host_port || scheme_host_port->scheme() != url::kHttpsScheme) {
  463. return true; // ECH is not supported for this request.
  464. }
  465. bool has_svcb = false;
  466. for (const auto& result : results) {
  467. if (!result.metadata.supported_protocol_alpns.empty()) {
  468. has_svcb = true;
  469. if (result.metadata.ech_config_list.empty()) {
  470. return true; // There is a non-ECH SVCB/HTTPS route.
  471. }
  472. }
  473. }
  474. // Either there were no SVCB/HTTPS records (should be SVCB-optional), or there
  475. // were and all supported ECH (should be SVCB-reliant).
  476. return !has_svcb;
  477. }
  478. bool TransportConnectJob::IsEndpointResultUsable(
  479. const HostResolverEndpointResult& result,
  480. bool svcb_optional) const {
  481. // A `HostResolverEndpointResult` with no ALPN protocols is the fallback
  482. // A/AAAA route. This is always compatible. We assume the ALPN-less option is
  483. // TCP-based.
  484. if (result.metadata.supported_protocol_alpns.empty()) {
  485. // See draft-ietf-dnsop-svcb-https-08, Section 3.
  486. return svcb_optional;
  487. }
  488. // See draft-ietf-dnsop-svcb-https-08, Section 7.1.2. Routes are usable if
  489. // there is an overlap between the route's ALPN protocols and the configured
  490. // ones. This ensures we do not, e.g., connect to a QUIC-only route with TCP.
  491. // Note that, if `params_` did not specify any ALPN protocols, no
  492. // SVCB/HTTPS-based routes will match and we will effectively ignore all but
  493. // plain A/AAAA routes.
  494. for (const auto& alpn : result.metadata.supported_protocol_alpns) {
  495. if (params_->supported_alpns().contains(alpn)) {
  496. return true;
  497. }
  498. }
  499. return false;
  500. }
  501. const HostResolverEndpointResult&
  502. TransportConnectJob::GetEndpointResultForCurrentSubJobs() const {
  503. CHECK_LT(current_endpoint_result_, endpoint_results_.size());
  504. return endpoint_results_[current_endpoint_result_];
  505. }
  506. } // namespace net