xref: /aosp_15_r20/external/cronet/net/quic/quic_session_pool_proxy_job.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/quic/quic_session_pool_proxy_job.h"
6 
7 #include "base/memory/weak_ptr.h"
8 #include "net/base/completion_once_callback.h"
9 #include "net/base/network_change_notifier.h"
10 #include "net/base/network_handle.h"
11 #include "net/base/request_priority.h"
12 #include "net/base/trace_constants.h"
13 #include "net/base/tracing.h"
14 #include "net/log/net_log_with_source.h"
15 #include "net/quic/address_utils.h"
16 #include "net/quic/quic_crypto_client_config_handle.h"
17 #include "net/quic/quic_http_stream.h"
18 #include "net/quic/quic_session_pool.h"
19 #include "net/third_party/quiche/src/quiche/quic/core/quic_packet_writer.h"
20 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
21 
22 namespace net {
23 
ProxyJob(QuicSessionPool * pool,quic::ParsedQuicVersion quic_version,const QuicSessionAliasKey & key,NetworkTrafficAnnotationTag proxy_annotation_tag,const HttpUserAgentSettings * http_user_agent_settings,std::unique_ptr<CryptoClientConfigHandle> client_config_handle,RequestPriority priority,int cert_verify_flags,const NetLogWithSource & net_log)24 QuicSessionPool::ProxyJob::ProxyJob(
25     QuicSessionPool* pool,
26     quic::ParsedQuicVersion quic_version,
27     const QuicSessionAliasKey& key,
28     NetworkTrafficAnnotationTag proxy_annotation_tag,
29     const HttpUserAgentSettings* http_user_agent_settings,
30     std::unique_ptr<CryptoClientConfigHandle> client_config_handle,
31     RequestPriority priority,
32     int cert_verify_flags,
33     const NetLogWithSource& net_log)
34     : QuicSessionPool::Job::Job(
35           pool,
36           key,
37           std::move(client_config_handle),
38           priority,
39           NetLogWithSource::Make(
40               net_log.net_log(),
41               NetLogSourceType::QUIC_SESSION_POOL_PROXY_JOB)),
42       io_callback_(base::BindRepeating(&QuicSessionPool::ProxyJob::OnIOComplete,
43                                        base::Unretained(this))),
44       quic_version_(quic_version),
45       proxy_annotation_tag_(proxy_annotation_tag),
46       cert_verify_flags_(cert_verify_flags),
47       http_user_agent_settings_(http_user_agent_settings) {
48   DCHECK(!Job::key().session_key().proxy_chain().is_direct());
49 }
50 
51 QuicSessionPool::ProxyJob::~ProxyJob() = default;
52 
Run(CompletionOnceCallback callback)53 int QuicSessionPool::ProxyJob::Run(CompletionOnceCallback callback) {
54   int rv = DoLoop(OK);
55   if (rv == ERR_IO_PENDING) {
56     callback_ = std::move(callback);
57   }
58 
59   return rv > 0 ? OK : rv;
60 }
61 
SetRequestExpectations(QuicSessionRequest * request)62 void QuicSessionPool::ProxyJob::SetRequestExpectations(
63     QuicSessionRequest* request) {
64   // This Job does not do host resolution, but can notify when the session
65   // creation is finished.
66   const bool session_creation_finished =
67       session_attempt_ && session_attempt_->session_creation_finished();
68   if (!session_creation_finished) {
69     request->ExpectQuicSessionCreation();
70   }
71 }
72 
PopulateNetErrorDetails(NetErrorDetails * details) const73 void QuicSessionPool::ProxyJob::PopulateNetErrorDetails(
74     NetErrorDetails* details) const {
75   // First, prefer any error details reported from creating the session over
76   // which this job is carried.
77   if (net_error_details_.quic_connection_error != quic::QUIC_NO_ERROR) {
78     *details = net_error_details_;
79     return;
80   }
81 
82   // Second, prefer to include error details from the session over which this
83   // job is carried, as any error in that session is "closer to" the client.
84   if (proxy_session_) {
85     proxy_session_->PopulateNetErrorDetails(details);
86     if (details->quic_connection_error != quic::QUIC_NO_ERROR) {
87       return;
88     }
89   }
90 
91   // Finally, return the error from the session attempt.
92   if (!session_attempt_ || !session_attempt_->session()) {
93     return;
94   }
95   details->connection_info = QuicHttpStream::ConnectionInfoFromQuicVersion(
96       session_attempt_->session()->connection()->version());
97   details->quic_connection_error = session_attempt_->session()->error();
98 }
99 
DoLoop(int rv)100 int QuicSessionPool::ProxyJob::DoLoop(int rv) {
101   do {
102     IoState state = io_state_;
103     io_state_ = STATE_NONE;
104     switch (state) {
105       case STATE_CREATE_PROXY_SESSION:
106         CHECK_EQ(OK, rv);
107         rv = DoCreateProxySession();
108         break;
109       case STATE_CREATE_PROXY_SESSION_COMPLETE:
110         rv = DoCreateProxySessionComplete(rv);
111         break;
112       case STATE_CREATE_PROXY_STREAM:
113         CHECK_EQ(OK, rv);
114         rv = DoCreateProxyStream();
115         break;
116       case STATE_CREATE_PROXY_STREAM_COMPLETE:
117         rv = DoCreateProxyStreamComplete(rv);
118         break;
119       case STATE_ATTEMPT_SESSION:
120         rv = DoAttemptSession();
121         break;
122       default:
123         NOTREACHED() << "io_state_: " << io_state_;
124         break;
125     }
126   } while (io_state_ != STATE_NONE && rv != ERR_IO_PENDING);
127   return rv;
128 }
129 
OnSessionAttemptComplete(int rv)130 void QuicSessionPool::ProxyJob::OnSessionAttemptComplete(int rv) {
131   CHECK_NE(rv, ERR_IO_PENDING);
132   if (!callback_.is_null()) {
133     std::move(callback_).Run(rv);
134   }
135 }
136 
OnIOComplete(int rv)137 void QuicSessionPool::ProxyJob::OnIOComplete(int rv) {
138   rv = DoLoop(rv);
139   if (rv != ERR_IO_PENDING && !callback_.is_null()) {
140     std::move(callback_).Run(rv);
141   }
142 }
143 
DoCreateProxySession()144 int QuicSessionPool::ProxyJob::DoCreateProxySession() {
145   io_state_ = STATE_CREATE_PROXY_SESSION_COMPLETE;
146 
147   net_log().BeginEvent(NetLogEventType::QUIC_SESSION_POOL_PROXY_JOB_CONNECT);
148 
149   const QuicSessionKey& session_key = key_.session_key();
150   auto [proxy_chain_prefix, last_proxy_server] =
151       session_key.proxy_chain().SplitLast();
152   auto last_server = last_proxy_server.host_port_pair();
153   url::SchemeHostPort destination(url::kHttpsScheme, last_server.host(),
154                                   last_server.port());
155 
156   net_log_.BeginEventWithStringParams(
157       NetLogEventType::QUIC_SESSION_POOL_PROXY_JOB_CREATE_PROXY_SESSION,
158       "destination", destination.Serialize());
159 
160   proxy_session_request_ = std::make_unique<QuicSessionRequest>(pool_);
161   return proxy_session_request_->Request(
162       destination, quic_version_, proxy_chain_prefix, proxy_annotation_tag_,
163       http_user_agent_settings_.get(), SessionUsage::kProxy,
164       session_key.privacy_mode(), priority(), session_key.socket_tag(),
165       session_key.network_anonymization_key(), session_key.secure_dns_policy(),
166       session_key.require_dns_https_alpn(), cert_verify_flags_,
167       GURL("https://" + last_server.ToString()), net_log(), &net_error_details_,
168       /*failed_on_default_network_callback=*/CompletionOnceCallback(),
169       io_callback_);
170 }
171 
DoCreateProxySessionComplete(int rv)172 int QuicSessionPool::ProxyJob::DoCreateProxySessionComplete(int rv) {
173   net_log().EndEventWithNetErrorCode(
174       NetLogEventType::QUIC_SESSION_POOL_PROXY_JOB_CREATE_PROXY_SESSION, rv);
175   if (rv != 0) {
176     proxy_session_request_.reset();
177     return rv;
178   }
179   io_state_ = STATE_CREATE_PROXY_STREAM;
180   proxy_session_ = proxy_session_request_->ReleaseSessionHandle();
181   proxy_session_request_.reset();
182 
183   return OK;
184 }
185 
DoCreateProxyStream()186 int QuicSessionPool::ProxyJob::DoCreateProxyStream() {
187   // Requiring confirmation here means more confidence that the underlying
188   // connection is working before building the proxy tunnel, at the cost of one
189   // more round-trip.
190   io_state_ = STATE_CREATE_PROXY_STREAM_COMPLETE;
191   return proxy_session_->RequestStream(/*requires_confirmation=*/true,
192                                        io_callback_, proxy_annotation_tag_);
193 }
194 
DoCreateProxyStreamComplete(int rv)195 int QuicSessionPool::ProxyJob::DoCreateProxyStreamComplete(int rv) {
196   if (rv != 0) {
197     return rv;
198   }
199   proxy_stream_ = proxy_session_->ReleaseStream();
200 
201   DCHECK(proxy_stream_);
202   if (!proxy_stream_->IsOpen()) {
203     return ERR_CONNECTION_CLOSED;
204   }
205 
206   io_state_ = STATE_ATTEMPT_SESSION;
207   return OK;
208 }
209 
DoAttemptSession()210 int QuicSessionPool::ProxyJob::DoAttemptSession() {
211   IPEndPoint local_address;
212   int rv = proxy_session_->GetSelfAddress(&local_address);
213   if (rv != 0) {
214     return rv;
215   }
216 
217   IPEndPoint peer_address;
218   rv = proxy_session_->GetPeerAddress(&peer_address);
219   if (rv != 0) {
220     return rv;
221   }
222 
223   session_attempt_ = std::make_unique<SessionAttempt>(
224       this, std::move(local_address), std::move(peer_address), quic_version_,
225       cert_verify_flags_, std::move(proxy_stream_), http_user_agent_settings_);
226 
227   return session_attempt_->Start(
228       base::BindOnce(&ProxyJob::OnSessionAttemptComplete, GetWeakPtr()));
229 }
230 
231 }  // namespace net
232