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