• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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/http/http_stream_pool_quic_task.h"
6 
7 #include <memory>
8 #include <vector>
9 
10 #include "base/task/sequenced_task_runner.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "net/base/connection_endpoint_metadata.h"
14 #include "net/base/ip_endpoint.h"
15 #include "net/base/net_error_details.h"
16 #include "net/base/net_errors.h"
17 #include "net/dns/host_resolver.h"
18 #include "net/dns/public/host_resolver_results.h"
19 #include "net/http/http_network_session.h"
20 #include "net/http/http_stream_pool.h"
21 #include "net/http/http_stream_pool_attempt_manager.h"
22 #include "net/http/http_stream_pool_group.h"
23 #include "net/log/net_log_source_type.h"
24 #include "net/log/net_log_with_source.h"
25 #include "net/quic/quic_session_alias_key.h"
26 #include "net/quic/quic_session_key.h"
27 #include "net/quic/quic_session_pool.h"
28 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
29 
30 namespace net {
31 
QuicTask(AttemptManager * manager,quic::ParsedQuicVersion quic_version)32 HttpStreamPool::QuicTask::QuicTask(AttemptManager* manager,
33                                    quic::ParsedQuicVersion quic_version)
34     : manager_(manager),
35       quic_version_(quic_version),
36       net_log_(NetLogWithSource::Make(
37           manager->net_log().net_log(),
38           NetLogSourceType::HTTP_STREAM_POOL_QUIC_TASK)) {
39   CHECK(manager_);
40   CHECK(service_endpoint_request());
41   CHECK(service_endpoint_request()->EndpointsCryptoReady());
42 
43   net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_POOL_QUIC_TASK_ALIVE, [&] {
44     base::Value::Dict dict;
45     dict.Set("quic_version", quic::ParsedQuicVersionToString(quic_version_));
46     manager_->net_log().source().AddToEventParameters(dict);
47     return dict;
48   });
49   manager_->net_log().AddEventReferencingSource(
50       NetLogEventType::HTTP_STREAM_POOL_ATTEMPT_MANAGER_QUIC_TASK_BOUND,
51       net_log_.source());
52 }
53 
~QuicTask()54 HttpStreamPool::QuicTask::~QuicTask() {
55   net_log_.EndEvent(NetLogEventType::HTTP_STREAM_POOL_QUIC_TASK_ALIVE);
56 }
57 
MaybeAttempt()58 void HttpStreamPool::QuicTask::MaybeAttempt() {
59   CHECK(!quic_session_pool()->CanUseExistingSession(GetKey().session_key(),
60                                                     GetKey().destination()));
61 
62   if (session_attempt_) {
63     // TODO(crbug.com/346835898): Support multiple attempts.
64     return;
65   }
66 
67   std::optional<QuicEndpoint> quic_endpoint = GetQuicEndpointToAttempt();
68   if (!quic_endpoint.has_value()) {
69     if (manager_->is_service_endpoint_request_finished()) {
70       if (!start_result_.has_value()) {
71         start_result_ = ERR_DNS_NO_MATCHING_SUPPORTED_ALPN;
72       }
73       base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
74           FROM_HERE, base::BindOnce(&QuicTask::OnSessionAttemptComplete,
75                                     weak_ptr_factory_.GetWeakPtr(),
76                                     ERR_DNS_NO_MATCHING_SUPPORTED_ALPN));
77     }
78     return;
79   }
80 
81   SSLConfig ssl_config;
82   ssl_config.disable_cert_verification_network_fetches =
83       stream_key().disable_cert_network_fetches();
84   int cert_verify_flags = ssl_config.GetCertVerifyFlags();
85 
86   base::TimeTicks dns_resolution_start_time =
87       manager_->dns_resolution_start_time();
88   // The DNS resolution end time could be null when the resolution is still
89   // ongoing. In that case, use the current time to make sure the connect
90   // start time is already greater than the DNS resolution end time.
91   base::TimeTicks dns_resolution_end_time =
92       manager_->dns_resolution_end_time().is_null()
93           ? base::TimeTicks::Now()
94           : manager_->dns_resolution_end_time();
95 
96   std::set<std::string> dns_aliases =
97       service_endpoint_request()->GetDnsAliasResults();
98 
99   net_log_.AddEvent(NetLogEventType::HTTP_STREAM_POOL_QUIC_ATTEMPT_START,
100                     [&] { return quic_endpoint->ToValue(); });
101 
102   session_attempt_ = quic_session_pool()->CreateSessionAttempt(
103       this, GetKey().session_key(), std::move(*quic_endpoint),
104       cert_verify_flags, dns_resolution_start_time, dns_resolution_end_time,
105       /*use_dns_aliases=*/true, std::move(dns_aliases),
106       manager_->CalculateMultiplexedSessionCreationInitiator());
107 
108   int rv = session_attempt_->Start(base::BindOnce(
109       &QuicTask::OnSessionAttemptComplete, weak_ptr_factory_.GetWeakPtr()));
110   if (rv != ERR_IO_PENDING) {
111     OnSessionAttemptComplete(rv);
112   }
113 }
114 
GetQuicSessionPool()115 QuicSessionPool* HttpStreamPool::QuicTask::GetQuicSessionPool() {
116   return manager_->group()->http_network_session()->quic_session_pool();
117 }
118 
GetKey()119 const QuicSessionAliasKey& HttpStreamPool::QuicTask::GetKey() {
120   return manager_->group()->quic_session_alias_key();
121 }
122 
GetNetLog()123 const NetLogWithSource& HttpStreamPool::QuicTask::GetNetLog() {
124   return net_log_;
125 }
126 
GetInfoAsValue() const127 base::Value::Dict HttpStreamPool::QuicTask::GetInfoAsValue() const {
128   base::Value::Dict dict;
129   dict.Set("has_session_attempt", session_attempt_ != nullptr);
130   if (start_result_.has_value()) {
131     dict.Set("start_result", ErrorToString(*start_result_));
132   }
133   return dict;
134 }
135 
stream_key() const136 const HttpStreamKey& HttpStreamPool::QuicTask::stream_key() const {
137   return manager_->group()->stream_key();
138 }
139 
quic_session_pool()140 QuicSessionPool* HttpStreamPool::QuicTask::quic_session_pool() {
141   return manager_->group()->http_network_session()->quic_session_pool();
142 }
143 
144 HostResolver::ServiceEndpointRequest*
service_endpoint_request()145 HttpStreamPool::QuicTask::service_endpoint_request() {
146   return manager_->service_endpoint_request();
147 }
148 
149 std::optional<QuicEndpoint>
GetQuicEndpointToAttempt()150 HttpStreamPool::QuicTask::GetQuicEndpointToAttempt() {
151   const bool svcb_optional = manager_->IsSvcbOptional();
152   for (auto& endpoint : service_endpoint_request()->GetEndpointResults()) {
153     std::optional<QuicEndpoint> quic_endpoint =
154         GetQuicEndpointFromServiceEndpoint(endpoint, svcb_optional);
155     if (quic_endpoint.has_value()) {
156       return quic_endpoint;
157     }
158   }
159 
160   return std::nullopt;
161 }
162 
163 std::optional<QuicEndpoint>
GetQuicEndpointFromServiceEndpoint(const ServiceEndpoint & service_endpoint,bool svcb_optional)164 HttpStreamPool::QuicTask::GetQuicEndpointFromServiceEndpoint(
165     const ServiceEndpoint& service_endpoint,
166     bool svcb_optional) {
167   quic::ParsedQuicVersion endpoint_quic_version =
168       quic_session_pool()->SelectQuicVersion(
169           quic_version_, service_endpoint.metadata, svcb_optional);
170   if (!endpoint_quic_version.IsKnown()) {
171     return std::nullopt;
172   }
173 
174   // TODO(crbug.com/346835898): Attempt more than one endpoints.
175   std::optional<IPEndPoint> ip_endpoint =
176       GetPreferredIPEndPoint(service_endpoint.ipv6_endpoints);
177   if (!ip_endpoint.has_value()) {
178     ip_endpoint = GetPreferredIPEndPoint(service_endpoint.ipv4_endpoints);
179   }
180 
181   if (!ip_endpoint.has_value()) {
182     return std::nullopt;
183   }
184 
185   return QuicEndpoint(endpoint_quic_version, *ip_endpoint,
186                       service_endpoint.metadata);
187 }
188 
GetPreferredIPEndPoint(const std::vector<IPEndPoint> & ip_endpoints)189 std::optional<IPEndPoint> HttpStreamPool::QuicTask::GetPreferredIPEndPoint(
190     const std::vector<IPEndPoint>& ip_endpoints) {
191   // TODO(crbug.com/346835898): Attempt more than one endpoints.
192   return ip_endpoints.empty() ? std::nullopt : std::optional(ip_endpoints[0]);
193 }
194 
OnSessionAttemptComplete(int rv)195 void HttpStreamPool::QuicTask::OnSessionAttemptComplete(int rv) {
196   if (rv == OK) {
197     QuicChromiumClientSession* session =
198         quic_session_pool()->FindExistingSession(GetKey().session_key(),
199                                                  GetKey().destination());
200     if (!session) {
201       // QUIC session is closed before stream can be created.
202       rv = ERR_CONNECTION_CLOSED;
203     }
204   }
205 
206   net_log_.AddEventWithNetErrorCode(
207       NetLogEventType::HTTP_STREAM_POOL_QUIC_ATTEMPT_END, rv);
208 
209   // TODO(crbug.com/346835898): Attempt other endpoints when failed.
210 
211   if (rv == OK &&
212       !quic_session_pool()->has_quic_ever_worked_on_current_network()) {
213     quic_session_pool()->set_has_quic_ever_worked_on_current_network(true);
214   }
215 
216   NetErrorDetails details;
217   if (session_attempt_) {
218     session_attempt_->PopulateNetErrorDetails(&details);
219   }
220   session_attempt_.reset();
221   manager_->OnQuicTaskComplete(rv, std::move(details));
222   // `this` is deleted.
223 }
224 
225 }  // namespace net
226