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