1 // Copyright 2012 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_factory_job.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/check_op.h"
11 #include "base/containers/contains.h"
12 #include "base/feature_list.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback.h"
15 #include "base/location.h"
16 #include "base/metrics/histogram_functions_internal_overloads.h"
17 #include "base/notreached.h"
18 #include "base/strings/string_util.h"
19 #include "base/task/single_thread_task_runner.h"
20 #include "base/values.h"
21 #include "net/base/host_port_pair.h"
22 #include "net/base/load_flags.h"
23 #include "net/base/port_util.h"
24 #include "net/base/proxy_chain.h"
25 #include "net/base/proxy_delegate.h"
26 #include "net/base/session_usage.h"
27 #include "net/base/url_util.h"
28 #include "net/cert/cert_verifier.h"
29 #include "net/dns/public/secure_dns_policy.h"
30 #include "net/http/bidirectional_stream_impl.h"
31 #include "net/http/http_basic_stream.h"
32 #include "net/http/http_network_session.h"
33 #include "net/http/http_server_properties.h"
34 #include "net/http/http_stream_factory.h"
35 #include "net/http/proxy_fallback.h"
36 #include "net/log/net_log.h"
37 #include "net/log/net_log_event_type.h"
38 #include "net/log/net_log_source.h"
39 #include "net/log/net_log_source_type.h"
40 #include "net/proxy_resolution/proxy_resolution_service.h"
41 #include "net/quic/bidirectional_stream_quic_impl.h"
42 #include "net/quic/quic_http_stream.h"
43 #include "net/quic/quic_session_key.h"
44 #include "net/socket/client_socket_handle.h"
45 #include "net/socket/client_socket_pool_manager.h"
46 #include "net/socket/connect_job.h"
47 #include "net/socket/next_proto.h"
48 #include "net/socket/ssl_client_socket.h"
49 #include "net/socket/stream_socket.h"
50 #include "net/spdy/bidirectional_stream_spdy_impl.h"
51 #include "net/spdy/multiplexed_session_creation_initiator.h"
52 #include "net/spdy/spdy_http_stream.h"
53 #include "net/spdy/spdy_session.h"
54 #include "net/ssl/ssl_cert_request_info.h"
55 #include "url/gurl.h"
56 #include "url/scheme_host_port.h"
57 #include "url/url_constants.h"
58
59 namespace net {
60
61 namespace {
62
63 // Experiment to preconnect only one connection if HttpServerProperties is
64 // not supported or initialized.
65 BASE_FEATURE(kLimitEarlyPreconnectsExperiment,
66 "LimitEarlyPreconnects",
67 base::FEATURE_ENABLED_BY_DEFAULT);
68
69 } // namespace
70
NetLogHttpStreamJobType(HttpStreamFactory::JobType job_type)71 const char* NetLogHttpStreamJobType(HttpStreamFactory::JobType job_type) {
72 switch (job_type) {
73 case HttpStreamFactory::MAIN:
74 return "main";
75 case HttpStreamFactory::ALTERNATIVE:
76 return "alternative";
77 case HttpStreamFactory::DNS_ALPN_H3:
78 return "dns_alpn_h3";
79 case HttpStreamFactory::PRECONNECT:
80 return "preconnect";
81 case HttpStreamFactory::PRECONNECT_DNS_ALPN_H3:
82 return "preconnect_dns_alpn_h3";
83 }
84 return "";
85 }
86
87 // Returns parameters associated with the ALPN protocol of a HTTP stream.
NetLogHttpStreamProtoParams(NextProto negotiated_protocol)88 base::Value::Dict NetLogHttpStreamProtoParams(NextProto negotiated_protocol) {
89 base::Value::Dict dict;
90
91 dict.Set("proto", NextProtoToString(negotiated_protocol));
92 return dict;
93 }
94
Job(Delegate * delegate,JobType job_type,HttpNetworkSession * session,const StreamRequestInfo & request_info,RequestPriority priority,const ProxyInfo & proxy_info,const std::vector<SSLConfig::CertAndStatus> & allowed_bad_certs,url::SchemeHostPort destination,GURL origin_url,NextProto alternative_protocol,quic::ParsedQuicVersion quic_version,bool is_websocket,bool enable_ip_based_pooling,NetLog * net_log)95 HttpStreamFactory::Job::Job(
96 Delegate* delegate,
97 JobType job_type,
98 HttpNetworkSession* session,
99 const StreamRequestInfo& request_info,
100 RequestPriority priority,
101 const ProxyInfo& proxy_info,
102 const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs,
103 url::SchemeHostPort destination,
104 GURL origin_url,
105 NextProto alternative_protocol,
106 quic::ParsedQuicVersion quic_version,
107 bool is_websocket,
108 bool enable_ip_based_pooling,
109 NetLog* net_log)
110 : request_info_(request_info),
111 priority_(priority),
112 proxy_info_(proxy_info),
113 allowed_bad_certs_(allowed_bad_certs),
114 net_log_(
115 NetLogWithSource::Make(net_log, NetLogSourceType::HTTP_STREAM_JOB)),
116 io_callback_(
117 base::BindRepeating(&Job::OnIOComplete, base::Unretained(this))),
118 connection_(std::make_unique<ClientSocketHandle>()),
119 session_(session),
120 destination_(std::move(destination)),
121 origin_url_(std::move(origin_url)),
122 is_websocket_(is_websocket),
123 try_websocket_over_http2_(is_websocket_ &&
124 origin_url_.SchemeIs(url::kWssScheme)),
125 // Only support IP-based pooling for non-proxied streams.
126 enable_ip_based_pooling_(enable_ip_based_pooling &&
127 proxy_info.is_direct()),
128 delegate_(delegate),
129 job_type_(job_type),
130 using_ssl_(origin_url_.SchemeIs(url::kHttpsScheme) ||
131 origin_url_.SchemeIs(url::kWssScheme)),
132 using_quic_(
133 alternative_protocol == kProtoQUIC ||
134 session->ShouldForceQuic(destination_, proxy_info, is_websocket_) ||
135 job_type == DNS_ALPN_H3 || job_type == PRECONNECT_DNS_ALPN_H3),
136 quic_version_(quic_version),
137 expect_spdy_(alternative_protocol == kProtoHTTP2 && !using_quic_),
138 quic_request_(session_->quic_session_pool()),
139 spdy_session_key_(using_quic_
140 ? SpdySessionKey()
141 : GetSpdySessionKey(proxy_info_.proxy_chain(),
142 origin_url_,
143 request_info_)) {
144 // Websocket `destination` schemes should be converted to HTTP(S).
145 DCHECK(base::EqualsCaseInsensitiveASCII(destination_.scheme(),
146 url::kHttpScheme) ||
147 base::EqualsCaseInsensitiveASCII(destination_.scheme(),
148 url::kHttpsScheme));
149
150 // This class is specific to a single `ProxyChain`, so `proxy_info_` must be
151 // non-empty. Entries beyond the first are ignored. It should simply take a
152 // `ProxyChain`, but the full `ProxyInfo` is passed back to
153 // `HttpNetworkTransaction`, which consumes additional fields.
154 DCHECK(!proxy_info_.is_empty());
155
156 // The Job is forced to use QUIC without a designated version, try the
157 // preferred QUIC version that is supported by default.
158 if (quic_version_ == quic::ParsedQuicVersion::Unsupported() &&
159 session->ShouldForceQuic(destination_, proxy_info, is_websocket_)) {
160 quic_version_ =
161 session->context().quic_context->params()->supported_versions[0];
162 }
163
164 if (using_quic_) {
165 DCHECK((quic_version_ != quic::ParsedQuicVersion::Unsupported()) ||
166 (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3));
167 }
168
169 DCHECK(session);
170 if (alternative_protocol != kProtoUnknown) {
171 // If the alternative service protocol is specified, then the job type must
172 // be either ALTERNATIVE or PRECONNECT.
173 DCHECK(job_type_ == ALTERNATIVE || job_type_ == PRECONNECT);
174 }
175
176 if (expect_spdy_) {
177 DCHECK(origin_url_.SchemeIs(url::kHttpsScheme));
178 }
179 if (using_quic_) {
180 DCHECK(session_->IsQuicEnabled());
181 }
182 if (job_type_ == PRECONNECT || is_websocket_) {
183 DCHECK(request_info_.socket_tag == SocketTag());
184 }
185 if (is_websocket_) {
186 DCHECK(origin_url_.SchemeIsWSOrWSS());
187 } else {
188 DCHECK(!origin_url_.SchemeIsWSOrWSS());
189 }
190 }
191
~Job()192 HttpStreamFactory::Job::~Job() {
193 if (started_) {
194 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB);
195 }
196
197 // When we're in a partially constructed state, waiting for the user to
198 // provide certificate handling information or authentication, we can't reuse
199 // this stream at all.
200 if (next_state_ == STATE_WAITING_USER_ACTION) {
201 connection_->socket()->Disconnect();
202 connection_.reset();
203 }
204
205 // The stream could be in a partial state. It is not reusable.
206 if (stream_.get() && next_state_ != STATE_DONE) {
207 stream_->Close(true /* not reusable */);
208 }
209 }
210
Start(HttpStreamRequest::StreamType stream_type)211 void HttpStreamFactory::Job::Start(HttpStreamRequest::StreamType stream_type) {
212 started_ = true;
213 stream_type_ = stream_type;
214
215 const NetLogWithSource* delegate_net_log = delegate_->GetNetLog();
216 if (delegate_net_log) {
217 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB, [&] {
218 base::Value::Dict dict;
219 const auto& source = delegate_net_log->source();
220 if (source.IsValid()) {
221 source.AddToEventParameters(dict);
222 }
223 dict.Set("logical_destination",
224 url::SchemeHostPort(origin_url_).Serialize());
225 dict.Set("destination", destination_.Serialize());
226 dict.Set("expect_spdy", expect_spdy_);
227 dict.Set("using_quic", using_quic_);
228 dict.Set("priority", RequestPriorityToString(priority_));
229 dict.Set("type", NetLogHttpStreamJobType(job_type_));
230 return dict;
231 });
232 delegate_net_log->AddEventReferencingSource(
233 NetLogEventType::HTTP_STREAM_REQUEST_STARTED_JOB, net_log_.source());
234 }
235
236 StartInternal();
237 }
238
Preconnect(int num_streams)239 int HttpStreamFactory::Job::Preconnect(int num_streams) {
240 DCHECK_GT(num_streams, 0);
241 HttpServerProperties* http_server_properties =
242 session_->http_server_properties();
243 DCHECK(http_server_properties);
244 // Preconnect one connection if either of the following is true:
245 // (1) kLimitEarlyPreconnectsStreamExperiment is turned on,
246 // HttpServerProperties is not initialized, and url scheme is cryptographic.
247 // (2) The server supports H2 or QUIC.
248 bool connect_one_stream =
249 base::FeatureList::IsEnabled(kLimitEarlyPreconnectsExperiment) &&
250 !http_server_properties->IsInitialized() &&
251 origin_url_.SchemeIsCryptographic();
252 if (connect_one_stream || http_server_properties->SupportsRequestPriority(
253 url::SchemeHostPort(origin_url_),
254 request_info_.network_anonymization_key)) {
255 num_streams_ = 1;
256 } else {
257 num_streams_ = num_streams;
258 }
259 return StartInternal();
260 }
261
RestartTunnelWithProxyAuth()262 int HttpStreamFactory::Job::RestartTunnelWithProxyAuth() {
263 DCHECK(establishing_tunnel_);
264 DCHECK(restart_with_auth_callback_);
265
266 std::move(restart_with_auth_callback_).Run();
267 return ERR_IO_PENDING;
268 }
269
GetLoadState() const270 LoadState HttpStreamFactory::Job::GetLoadState() const {
271 switch (next_state_) {
272 case STATE_INIT_CONNECTION_COMPLETE:
273 case STATE_CREATE_STREAM_COMPLETE:
274 return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState();
275 default:
276 return LOAD_STATE_IDLE;
277 }
278 }
279
Resume()280 void HttpStreamFactory::Job::Resume() {
281 DCHECK_EQ(job_type_, MAIN);
282 DCHECK_EQ(next_state_, STATE_WAIT_COMPLETE);
283 OnIOComplete(OK);
284 }
285
Orphan()286 void HttpStreamFactory::Job::Orphan() {
287 DCHECK(job_type_ == ALTERNATIVE || job_type_ == DNS_ALPN_H3);
288 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_ORPHANED);
289
290 // Watching for SPDY sessions isn't supported on orphaned jobs.
291 // TODO(mmenke): Fix that.
292 spdy_session_request_.reset();
293 }
294
SetPriority(RequestPriority priority)295 void HttpStreamFactory::Job::SetPriority(RequestPriority priority) {
296 priority_ = priority;
297 // Ownership of |connection_| is passed to the newly created stream
298 // or H2 session in DoCreateStream(), and the consumer is not
299 // notified immediately, so this call may occur when |connection_|
300 // is null.
301 //
302 // Note that streams are created without a priority associated with them,
303 // and it is up to the consumer to set their priority via
304 // HttpStream::InitializeStream(). So there is no need for this code
305 // to propagate priority changes to the newly created stream.
306 if (connection_ && connection_->is_initialized()) {
307 connection_->SetPriority(priority);
308 }
309 // TODO(akalin): Maybe Propagate this to the preconnect state.
310 }
311
HasAvailableSpdySession() const312 bool HttpStreamFactory::Job::HasAvailableSpdySession() const {
313 return !using_quic_ && CanUseExistingSpdySession() &&
314 session_->spdy_session_pool()->HasAvailableSession(spdy_session_key_,
315 is_websocket_);
316 }
317
HasAvailableQuicSession() const318 bool HttpStreamFactory::Job::HasAvailableQuicSession() const {
319 if (!using_quic_) {
320 return false;
321 }
322 bool require_dns_https_alpn =
323 (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3);
324
325 QuicSessionKey quic_session_key(
326 HostPortPair::FromURL(origin_url_), request_info_.privacy_mode,
327 proxy_info_.proxy_chain(), SessionUsage::kDestination,
328 request_info_.socket_tag, request_info_.network_anonymization_key,
329 request_info_.secure_dns_policy, require_dns_https_alpn);
330 return session_->quic_session_pool()->CanUseExistingSession(quic_session_key,
331 destination_);
332 }
333
TargettedSocketGroupHasActiveSocket() const334 bool HttpStreamFactory::Job::TargettedSocketGroupHasActiveSocket() const {
335 DCHECK(!using_quic_);
336 DCHECK(!is_websocket_);
337 ClientSocketPool* pool = session_->GetSocketPool(
338 HttpNetworkSession::NORMAL_SOCKET_POOL, proxy_info_.proxy_chain());
339 DCHECK(pool);
340 ClientSocketPool::GroupId connection_group(
341 destination_, request_info_.privacy_mode,
342 request_info_.network_anonymization_key, request_info_.secure_dns_policy,
343 disable_cert_verification_network_fetches());
344 return pool->HasActiveSocket(connection_group);
345 }
346
negotiated_protocol() const347 NextProto HttpStreamFactory::Job::negotiated_protocol() const {
348 return negotiated_protocol_;
349 }
350
using_spdy() const351 bool HttpStreamFactory::Job::using_spdy() const {
352 return negotiated_protocol_ == kProtoHTTP2;
353 }
354
disable_cert_verification_network_fetches() const355 bool HttpStreamFactory::Job::disable_cert_verification_network_fetches() const {
356 return !!(request_info_.load_flags & LOAD_DISABLE_CERT_NETWORK_FETCHES);
357 }
358
proxy_info() const359 const ProxyInfo& HttpStreamFactory::Job::proxy_info() const {
360 return proxy_info_;
361 }
362
resolve_error_info() const363 ResolveErrorInfo HttpStreamFactory::Job::resolve_error_info() const {
364 return resolve_error_info_;
365 }
366
GetSSLInfo(SSLInfo * ssl_info)367 void HttpStreamFactory::Job::GetSSLInfo(SSLInfo* ssl_info) {
368 DCHECK(using_ssl_);
369 DCHECK(!establishing_tunnel_);
370 DCHECK(connection_.get() && connection_->socket());
371 connection_->socket()->GetSSLInfo(ssl_info);
372 }
373
UsingHttpProxyWithoutTunnel() const374 bool HttpStreamFactory::Job::UsingHttpProxyWithoutTunnel() const {
375 return !using_quic_ && !using_ssl_ && !is_websocket_ &&
376 proxy_info_.proxy_chain().is_get_to_proxy_allowed();
377 }
378
CanUseExistingSpdySession() const379 bool HttpStreamFactory::Job::CanUseExistingSpdySession() const {
380 DCHECK(!using_quic_);
381
382 if (proxy_info_.is_direct() &&
383 session_->http_server_properties()->RequiresHTTP11(
384 url::SchemeHostPort(origin_url_),
385 request_info_.network_anonymization_key)) {
386 return false;
387 }
388
389 if (is_websocket_) {
390 return try_websocket_over_http2_;
391 }
392
393 DCHECK(origin_url_.SchemeIsHTTPOrHTTPS());
394
395 // We need to make sure that if a HTTP/2 session was created for
396 // https://somehost/ then we do not use that session for http://somehost:443/.
397 // The only time we can use an existing session is if the request URL is
398 // https (the normal case) or if we are connecting to an HTTPS proxy to make
399 // a GET request for an HTTP destination. https://crbug.com/133176
400 if (origin_url_.SchemeIs(url::kHttpsScheme)) {
401 return true;
402 }
403 if (!proxy_info_.is_empty()) {
404 const ProxyChain& proxy_chain = proxy_info_.proxy_chain();
405 if (!proxy_chain.is_direct() && proxy_chain.is_get_to_proxy_allowed() &&
406 proxy_chain.Last().is_https()) {
407 return true;
408 }
409 }
410 return false;
411 }
412
OnStreamReadyCallback()413 void HttpStreamFactory::Job::OnStreamReadyCallback() {
414 DCHECK(stream_.get());
415 DCHECK_NE(job_type_, PRECONNECT);
416 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
417 DCHECK(!is_websocket_ || try_websocket_over_http2_);
418
419 MaybeCopyConnectionAttemptsFromHandle();
420
421 delegate_->OnStreamReady(this);
422 // |this| may be deleted after this call.
423 }
424
OnWebSocketHandshakeStreamReadyCallback()425 void HttpStreamFactory::Job::OnWebSocketHandshakeStreamReadyCallback() {
426 DCHECK(websocket_stream_);
427 DCHECK_NE(job_type_, PRECONNECT);
428 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
429 DCHECK(is_websocket_);
430
431 MaybeCopyConnectionAttemptsFromHandle();
432
433 delegate_->OnWebSocketHandshakeStreamReady(this, proxy_info_,
434 std::move(websocket_stream_));
435 // |this| may be deleted after this call.
436 }
437
OnBidirectionalStreamImplReadyCallback()438 void HttpStreamFactory::Job::OnBidirectionalStreamImplReadyCallback() {
439 DCHECK(bidirectional_stream_impl_);
440
441 MaybeCopyConnectionAttemptsFromHandle();
442
443 delegate_->OnBidirectionalStreamImplReady(this, proxy_info_);
444 // |this| may be deleted after this call.
445 }
446
OnStreamFailedCallback(int result)447 void HttpStreamFactory::Job::OnStreamFailedCallback(int result) {
448 DCHECK_NE(job_type_, PRECONNECT);
449 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
450
451 MaybeCopyConnectionAttemptsFromHandle();
452
453 delegate_->OnStreamFailed(this, result);
454 // |this| may be deleted after this call.
455 }
456
OnCertificateErrorCallback(int result,const SSLInfo & ssl_info)457 void HttpStreamFactory::Job::OnCertificateErrorCallback(
458 int result,
459 const SSLInfo& ssl_info) {
460 DCHECK_NE(job_type_, PRECONNECT);
461 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
462 DCHECK(!spdy_session_request_);
463
464 MaybeCopyConnectionAttemptsFromHandle();
465
466 delegate_->OnCertificateError(this, result, ssl_info);
467 // |this| may be deleted after this call.
468 }
469
OnNeedsProxyAuthCallback(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback)470 void HttpStreamFactory::Job::OnNeedsProxyAuthCallback(
471 const HttpResponseInfo& response,
472 HttpAuthController* auth_controller,
473 base::OnceClosure restart_with_auth_callback) {
474 DCHECK_NE(job_type_, PRECONNECT);
475 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
476 DCHECK(establishing_tunnel_);
477 DCHECK(!restart_with_auth_callback_);
478
479 restart_with_auth_callback_ = std::move(restart_with_auth_callback);
480
481 // This is called out of band, so need to abort the SpdySessionRequest to
482 // prevent being passed a new session while waiting on proxy auth credentials.
483 spdy_session_request_.reset();
484
485 delegate_->OnNeedsProxyAuth(this, response, proxy_info_, auth_controller);
486 // |this| may be deleted after this call.
487 }
488
OnNeedsClientAuthCallback(SSLCertRequestInfo * cert_info)489 void HttpStreamFactory::Job::OnNeedsClientAuthCallback(
490 SSLCertRequestInfo* cert_info) {
491 DCHECK_NE(job_type_, PRECONNECT);
492 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
493 DCHECK(!spdy_session_request_);
494
495 delegate_->OnNeedsClientAuth(this, cert_info);
496 // |this| may be deleted after this call.
497 }
498
OnPreconnectsComplete(int result)499 void HttpStreamFactory::Job::OnPreconnectsComplete(int result) {
500 RecordPreconnectHistograms(result);
501 delegate_->OnPreconnectsComplete(this, result);
502 // |this| may be deleted after this call.
503 }
504
OnIOComplete(int result)505 void HttpStreamFactory::Job::OnIOComplete(int result) {
506 RunLoop(result);
507 }
508
RunLoop(int result)509 void HttpStreamFactory::Job::RunLoop(int result) {
510 result = DoLoop(result);
511
512 if (result == ERR_IO_PENDING) {
513 return;
514 }
515
516 // Stop watching for new SpdySessions, to avoid receiving a new SPDY session
517 // while doing anything other than waiting to establish a connection.
518 spdy_session_request_.reset();
519
520 if ((job_type_ == PRECONNECT) || (job_type_ == PRECONNECT_DNS_ALPN_H3)) {
521 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
522 FROM_HERE,
523 base::BindOnce(&HttpStreamFactory::Job::OnPreconnectsComplete,
524 ptr_factory_.GetWeakPtr(), result));
525 return;
526 }
527
528 if (IsCertificateError(result)) {
529 // Retrieve SSL information from the socket.
530 SSLInfo ssl_info;
531 GetSSLInfo(&ssl_info);
532
533 next_state_ = STATE_WAITING_USER_ACTION;
534 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
535 FROM_HERE,
536 base::BindOnce(&HttpStreamFactory::Job::OnCertificateErrorCallback,
537 ptr_factory_.GetWeakPtr(), result, ssl_info));
538 return;
539 }
540
541 switch (result) {
542 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
543 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
544 FROM_HERE,
545 base::BindOnce(
546 &Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
547 base::RetainedRef(connection_->ssl_cert_request_info())));
548 return;
549
550 case OK:
551 next_state_ = STATE_DONE;
552 if (is_websocket_) {
553 DCHECK(websocket_stream_);
554 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
555 FROM_HERE,
556 base::BindOnce(&Job::OnWebSocketHandshakeStreamReadyCallback,
557 ptr_factory_.GetWeakPtr()));
558 } else if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
559 if (!bidirectional_stream_impl_) {
560 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
561 FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
562 ptr_factory_.GetWeakPtr(), ERR_FAILED));
563 } else {
564 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
565 FROM_HERE,
566 base::BindOnce(&Job::OnBidirectionalStreamImplReadyCallback,
567 ptr_factory_.GetWeakPtr()));
568 }
569 } else {
570 DCHECK(stream_.get());
571 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
572 FROM_HERE, base::BindOnce(&Job::OnStreamReadyCallback,
573 ptr_factory_.GetWeakPtr()));
574 }
575 return;
576
577 default:
578 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
579 FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
580 ptr_factory_.GetWeakPtr(), result));
581 return;
582 }
583 }
584
DoLoop(int result)585 int HttpStreamFactory::Job::DoLoop(int result) {
586 DCHECK_NE(next_state_, STATE_NONE);
587 int rv = result;
588 do {
589 State state = next_state_;
590 next_state_ = STATE_NONE;
591 switch (state) {
592 case STATE_START:
593 DCHECK_EQ(OK, rv);
594 rv = DoStart();
595 break;
596 case STATE_WAIT:
597 DCHECK_EQ(OK, rv);
598 rv = DoWait();
599 break;
600 case STATE_WAIT_COMPLETE:
601 rv = DoWaitComplete(rv);
602 break;
603 case STATE_INIT_CONNECTION:
604 DCHECK_EQ(OK, rv);
605 rv = DoInitConnection();
606 break;
607 case STATE_INIT_CONNECTION_COMPLETE:
608 rv = DoInitConnectionComplete(rv);
609 break;
610 case STATE_WAITING_USER_ACTION:
611 rv = DoWaitingUserAction(rv);
612 break;
613 case STATE_CREATE_STREAM:
614 DCHECK_EQ(OK, rv);
615 rv = DoCreateStream();
616 break;
617 case STATE_CREATE_STREAM_COMPLETE:
618 rv = DoCreateStreamComplete(rv);
619 break;
620 default:
621 NOTREACHED() << "bad state";
622 }
623 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
624 return rv;
625 }
626
StartInternal()627 int HttpStreamFactory::Job::StartInternal() {
628 CHECK_EQ(STATE_NONE, next_state_);
629 next_state_ = STATE_START;
630 RunLoop(OK);
631 return ERR_IO_PENDING;
632 }
633
DoStart()634 int HttpStreamFactory::Job::DoStart() {
635 // Don't connect to restricted ports.
636 if (!IsPortAllowedForScheme(destination_.port(),
637 origin_url_.scheme_piece())) {
638 return ERR_UNSAFE_PORT;
639 }
640
641 next_state_ = STATE_WAIT;
642 return OK;
643 }
644
DoWait()645 int HttpStreamFactory::Job::DoWait() {
646 next_state_ = STATE_WAIT_COMPLETE;
647 bool should_wait = delegate_->ShouldWait(this);
648 net_log_.AddEntryWithBoolParams(NetLogEventType::HTTP_STREAM_JOB_WAITING,
649 NetLogEventPhase::BEGIN, "should_wait",
650 should_wait);
651 if (should_wait) {
652 return ERR_IO_PENDING;
653 }
654
655 return OK;
656 }
657
DoWaitComplete(int result)658 int HttpStreamFactory::Job::DoWaitComplete(int result) {
659 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_WAITING);
660 DCHECK_EQ(OK, result);
661 next_state_ = STATE_INIT_CONNECTION;
662 return OK;
663 }
664
ResumeInitConnection()665 void HttpStreamFactory::Job::ResumeInitConnection() {
666 if (init_connection_already_resumed_) {
667 return;
668 }
669 DCHECK_EQ(next_state_, STATE_INIT_CONNECTION);
670 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_RESUME_INIT_CONNECTION);
671 init_connection_already_resumed_ = true;
672 OnIOComplete(OK);
673 }
674
DoInitConnection()675 int HttpStreamFactory::Job::DoInitConnection() {
676 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
677 int result = DoInitConnectionImpl();
678 if (!expect_on_quic_session_created_ && !expect_on_quic_host_resolution_) {
679 delegate_->OnConnectionInitialized(this, result);
680 }
681 return result;
682 }
683
DoInitConnectionImpl()684 int HttpStreamFactory::Job::DoInitConnectionImpl() {
685 DCHECK(!connection_->is_initialized());
686
687 if (using_quic_ && !proxy_info_.is_direct() &&
688 !proxy_info_.proxy_chain().Last().is_quic()) {
689 // QUIC can not be spoken to non-QUIC proxies. This error should not be
690 // user visible, because the non-alternative Job should be resumed.
691 return ERR_NO_SUPPORTED_PROXIES;
692 }
693
694 DCHECK(proxy_info_.proxy_chain().IsValid());
695 next_state_ = STATE_INIT_CONNECTION_COMPLETE;
696
697 if (using_quic_) {
698 // TODO(mmenke): Clean this up. `disable_cert_verification_network_fetches`
699 // is enabled in ConnectJobFactory for H1/H2 connections. Also need to add
700 // it to the SpdySessionKey for H2 connections.
701 SSLConfig server_ssl_config;
702 server_ssl_config.disable_cert_verification_network_fetches =
703 disable_cert_verification_network_fetches();
704 return DoInitConnectionImplQuic(server_ssl_config.GetCertVerifyFlags());
705 }
706
707 // Check first if there is a pushed stream matching the request, or an HTTP/2
708 // connection this request can pool to. If so, then go straight to using
709 // that.
710 if (CanUseExistingSpdySession()) {
711 if (!existing_spdy_session_) {
712 if (!spdy_session_request_) {
713 // If not currently watching for an H2 session, use
714 // SpdySessionPool::RequestSession() to check for a session, and start
715 // watching for one.
716 bool should_throttle_connect = ShouldThrottleConnectForSpdy();
717 base::RepeatingClosure resume_callback =
718 should_throttle_connect
719 ? base::BindRepeating(
720 &HttpStreamFactory::Job::ResumeInitConnection,
721 ptr_factory_.GetWeakPtr())
722 : base::RepeatingClosure();
723
724 bool is_blocking_request_for_session;
725 existing_spdy_session_ = session_->spdy_session_pool()->RequestSession(
726 spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
727 net_log_, resume_callback, this, &spdy_session_request_,
728 &is_blocking_request_for_session);
729 if (!existing_spdy_session_ && should_throttle_connect &&
730 !is_blocking_request_for_session) {
731 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_THROTTLED);
732 next_state_ = STATE_INIT_CONNECTION;
733 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
734 FROM_HERE, resume_callback, base::Milliseconds(kHTTP2ThrottleMs));
735 return ERR_IO_PENDING;
736 }
737 } else if (enable_ip_based_pooling_) {
738 // If already watching for an H2 session, still need to check for an
739 // existing connection that can be reused through IP pooling, as those
740 // don't post session available notifications.
741 //
742 // TODO(mmenke): Make sessions created through IP pooling invoke the
743 // callback.
744 existing_spdy_session_ =
745 session_->spdy_session_pool()->FindAvailableSession(
746 spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
747 net_log_);
748 }
749 }
750 if (existing_spdy_session_) {
751 // Stop watching for SpdySessions.
752 spdy_session_request_.reset();
753
754 // If we're preconnecting, but we already have a SpdySession, we don't
755 // actually need to preconnect any sockets, so we're done.
756 if (job_type_ == PRECONNECT) {
757 return OK;
758 }
759 negotiated_protocol_ = kProtoHTTP2;
760 next_state_ = STATE_CREATE_STREAM;
761 return OK;
762 }
763 }
764
765 establishing_tunnel_ = !UsingHttpProxyWithoutTunnel();
766
767 if (job_type_ == PRECONNECT) {
768 DCHECK(!is_websocket_);
769 DCHECK(request_info_.socket_tag == SocketTag());
770
771 // The lifeime of the preconnect tasks is not controlled by |connection_|.
772 // It may outlives |this|. So we can't use |io_callback_| which holds
773 // base::Unretained(this).
774 auto callback =
775 base::BindOnce(&Job::OnIOComplete, ptr_factory_.GetWeakPtr());
776
777 return PreconnectSocketsForHttpRequest(
778 destination_, request_info_.load_flags, priority_, session_,
779 proxy_info_, allowed_bad_certs_, request_info_.privacy_mode,
780 request_info_.network_anonymization_key,
781 request_info_.secure_dns_policy, net_log_, num_streams_,
782 std::move(callback));
783 }
784
785 ClientSocketPool::ProxyAuthCallback proxy_auth_callback =
786 base::BindRepeating(&HttpStreamFactory::Job::OnNeedsProxyAuthCallback,
787 base::Unretained(this));
788 if (is_websocket_) {
789 DCHECK(request_info_.socket_tag == SocketTag());
790 DCHECK_EQ(SecureDnsPolicy::kAllow, request_info_.secure_dns_policy);
791 return InitSocketHandleForWebSocketRequest(
792 destination_, request_info_.load_flags, priority_, session_,
793 proxy_info_, allowed_bad_certs_, request_info_.privacy_mode,
794 request_info_.network_anonymization_key, net_log_, connection_.get(),
795 io_callback_, proxy_auth_callback);
796 }
797
798 return InitSocketHandleForHttpRequest(
799 destination_, request_info_.load_flags, priority_, session_, proxy_info_,
800 allowed_bad_certs_, request_info_.privacy_mode,
801 request_info_.network_anonymization_key, request_info_.secure_dns_policy,
802 request_info_.socket_tag, net_log_, connection_.get(), io_callback_,
803 proxy_auth_callback);
804 }
805
DoInitConnectionImplQuic(int server_cert_verifier_flags)806 int HttpStreamFactory::Job::DoInitConnectionImplQuic(
807 int server_cert_verifier_flags) {
808 url::SchemeHostPort destination;
809
810 bool require_dns_https_alpn =
811 (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3);
812
813 ProxyChain proxy_chain = proxy_info_.proxy_chain();
814 if (!proxy_chain.is_direct()) {
815 // We only support proxying QUIC over QUIC. While MASQUE defines mechanisms
816 // to carry QUIC traffic over non-QUIC proxies, the performance of these
817 // mechanisms would be worse than simply using H/1 or H/2 to reach the
818 // destination. The error for an invalid condition should not be user
819 // visible, because the non-alternative Job should be resumed.
820 if (proxy_chain.AnyProxy(
821 [](const ProxyServer& s) { return !s.is_quic(); })) {
822 return ERR_NO_SUPPORTED_PROXIES;
823 }
824 }
825
826 std::optional<NetworkTrafficAnnotationTag> traffic_annotation =
827 proxy_info_.traffic_annotation().is_valid()
828 ? std::make_optional<NetworkTrafficAnnotationTag>(
829 proxy_info_.traffic_annotation())
830 : std::nullopt;
831
832 auto initiator =
833 (job_type_ == PRECONNECT || job_type_ == PRECONNECT_DNS_ALPN_H3)
834 ? MultiplexedSessionCreationInitiator::kPreconnect
835 : MultiplexedSessionCreationInitiator::kUnknown;
836
837 // The QuicSessionRequest will take care of connecting to any proxies in the
838 // proxy chain.
839 int rv = quic_request_.Request(
840 destination_, quic_version_, proxy_chain, std::move(traffic_annotation),
841 session_->context().http_user_agent_settings.get(),
842 SessionUsage::kDestination, request_info_.privacy_mode, priority_,
843 request_info_.socket_tag, request_info_.network_anonymization_key,
844 request_info_.secure_dns_policy, require_dns_https_alpn,
845 server_cert_verifier_flags, origin_url_, net_log_, &net_error_details_,
846 initiator,
847 base::BindOnce(&Job::OnFailedOnDefaultNetwork, ptr_factory_.GetWeakPtr()),
848 io_callback_);
849 if (rv == OK) {
850 using_existing_quic_session_ = true;
851 } else if (rv == ERR_IO_PENDING) {
852 // There's no available QUIC session. Inform the delegate how long to
853 // delay the main job.
854 delegate_->MaybeSetWaitTimeForMainJob(
855 quic_request_.GetTimeDelayForWaitingJob());
856 // Set up to get notified of either host resolution completion or session
857 // creation, in order to call the delegate's `OnConnectionInitialized`
858 // callback.
859 expect_on_quic_host_resolution_ = quic_request_.WaitForHostResolution(
860 base::BindOnce(&Job::OnQuicHostResolution, base::Unretained(this)));
861 expect_on_quic_session_created_ = quic_request_.WaitForQuicSessionCreation(
862 base::BindOnce(&Job::OnQuicSessionCreated, ptr_factory_.GetWeakPtr()));
863 }
864 return rv;
865 }
866
OnQuicHostResolution(int result)867 void HttpStreamFactory::Job::OnQuicHostResolution(int result) {
868 DCHECK(expect_on_quic_host_resolution_);
869 expect_on_quic_host_resolution_ = false;
870
871 delegate_->OnQuicHostResolution(destination_,
872 quic_request_.dns_resolution_start_time(),
873 quic_request_.dns_resolution_end_time());
874
875 // If no `OnQuicSessionCreated` call is expected, then consider the
876 // connection "initialized" and inform the delegate. Note that
877 // `OnQuicHostResolution` is actually called somewhat _after_ host resolution
878 // is complete -- the `Job` has already run to the point where it can make no
879 // further progress.
880 if (!expect_on_quic_session_created_) {
881 delegate_->OnConnectionInitialized(this, result);
882 }
883 }
884
OnQuicSessionCreated(int result)885 void HttpStreamFactory::Job::OnQuicSessionCreated(int result) {
886 DCHECK(expect_on_quic_session_created_);
887 expect_on_quic_session_created_ = false;
888 delegate_->OnConnectionInitialized(this, result);
889 }
890
OnFailedOnDefaultNetwork(int result)891 void HttpStreamFactory::Job::OnFailedOnDefaultNetwork(int result) {
892 DCHECK(job_type_ == ALTERNATIVE || job_type_ == DNS_ALPN_H3);
893 DCHECK(using_quic_);
894 delegate_->OnFailedOnDefaultNetwork(this);
895 }
896
DoInitConnectionComplete(int result)897 int HttpStreamFactory::Job::DoInitConnectionComplete(int result) {
898 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
899
900 establishing_tunnel_ = false;
901
902 // No need to continue waiting for a session, once a connection is
903 // established.
904 spdy_session_request_.reset();
905
906 if ((job_type_ == PRECONNECT) || (job_type_ == PRECONNECT_DNS_ALPN_H3)) {
907 if (using_quic_) {
908 return result;
909 }
910 DCHECK_EQ(OK, result);
911 return OK;
912 }
913
914 resolve_error_info_ = connection_->resolve_error_info();
915
916 // Determine the protocol (HTTP/1.1, HTTP/2, or HTTP/3). This covers both the
917 // origin and some proxy cases. First, if the URL is HTTPS (or WSS), we may
918 // negotiate HTTP/2 or HTTP/3 with the origin. Second, non-tunneled requests
919 // (i.e. HTTP URLs) through an HTTPS or QUIC proxy work by sending the request
920 // to the proxy directly. In that case, this logic also handles the proxy's
921 // negotiated protocol. HTTPS requests are always tunneled, so at most one of
922 // these applies.
923 //
924 // Tunneled requests may also negotiate ALPN at the proxy, but
925 // HttpProxyConnectJob handles ALPN. The resulting StreamSocket will not
926 // report an ALPN protocol.
927 if (result == OK) {
928 if (using_quic_) {
929 // TODO(davidben): Record these values consistently between QUIC and TCP
930 // below. In the QUIC case, we only record it for origin connections. In
931 // the TCP case, we also record it for non-tunneled, proxied requests.
932 if (using_ssl_) {
933 negotiated_protocol_ = kProtoQUIC;
934 }
935 } else if (connection_->socket()->GetNegotiatedProtocol() !=
936 kProtoUnknown) {
937 // Only connections that use TLS (either to the origin or via a GET to a
938 // secure proxy) can negotiate ALPN.
939 bool get_to_secure_proxy =
940 IsGetToProxy(proxy_info_.proxy_chain(), origin_url_) &&
941 proxy_info_.proxy_chain().Last().is_secure_http_like();
942 DCHECK(using_ssl_ || get_to_secure_proxy);
943 negotiated_protocol_ = connection_->socket()->GetNegotiatedProtocol();
944 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_REQUEST_PROTO, [&] {
945 return NetLogHttpStreamProtoParams(negotiated_protocol_);
946 });
947 if (using_spdy()) {
948 if (is_websocket_) {
949 // WebSocket is not supported over a fresh HTTP/2 connection. This
950 // should not be reachable. For the origin, we do not request HTTP/2
951 // on fresh WebSockets connections, because not all HTTP/2 servers
952 // implement RFC 8441. For proxies, WebSockets are always tunneled.
953 //
954 // TODO(davidben): This isn't a CHECK() because, previously, it was
955 // reachable in https://crbug.com/828865. However, if reachable, it
956 // means a bug in the socket pools. The socket pools have since been
957 // cleaned up, so this may no longer be reachable. Restore the CHECK
958 // and see if this is still needed.
959 return ERR_NOT_IMPLEMENTED;
960 }
961 }
962 }
963 }
964
965 if (using_quic_ && result < 0 && !proxy_info_.is_direct() &&
966 proxy_info_.proxy_chain().Last().is_quic()) {
967 return ReconsiderProxyAfterError(result);
968 }
969
970 if (expect_spdy_ && !using_spdy()) {
971 return ERR_ALPN_NEGOTIATION_FAILED;
972 }
973
974 // |result| may be the result of any of the stacked protocols. The following
975 // logic is used when determining how to interpret an error.
976 // If |result| < 0:
977 // and connection_->socket() != NULL, then the SSL handshake ran and it
978 // is a potentially recoverable error.
979 // and connection_->socket == NULL and connection_->is_ssl_error() is true,
980 // then the SSL handshake ran with an unrecoverable error.
981 // otherwise, the error came from one of the other protocols.
982 bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
983 connection_->is_ssl_error());
984 if (!ssl_started && result < 0 && (expect_spdy_ || using_quic_)) {
985 return result;
986 }
987
988 if (using_quic_) {
989 if (result < 0) {
990 return result;
991 }
992
993 if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
994 std::unique_ptr<QuicChromiumClientSession::Handle> session =
995 quic_request_.ReleaseSessionHandle();
996 if (!session) {
997 // Quic session is closed before stream can be created.
998 return ERR_CONNECTION_CLOSED;
999 }
1000 bidirectional_stream_impl_ =
1001 std::make_unique<BidirectionalStreamQuicImpl>(std::move(session));
1002 } else {
1003 std::unique_ptr<QuicChromiumClientSession::Handle> session =
1004 quic_request_.ReleaseSessionHandle();
1005 if (!session) {
1006 // Quic session is closed before stream can be created.
1007 return ERR_CONNECTION_CLOSED;
1008 }
1009 auto dns_aliases =
1010 session->GetDnsAliasesForSessionKey(quic_request_.session_key());
1011 stream_ = std::make_unique<QuicHttpStream>(std::move(session),
1012 std::move(dns_aliases));
1013 }
1014 next_state_ = STATE_CREATE_STREAM_COMPLETE;
1015 return OK;
1016 }
1017
1018 if (result < 0) {
1019 if (!ssl_started) {
1020 return ReconsiderProxyAfterError(result);
1021 }
1022 return result;
1023 }
1024
1025 next_state_ = STATE_CREATE_STREAM;
1026 return OK;
1027 }
1028
DoWaitingUserAction(int result)1029 int HttpStreamFactory::Job::DoWaitingUserAction(int result) {
1030 // This state indicates that the stream request is in a partially
1031 // completed state, and we've called back to the delegate for more
1032 // information.
1033
1034 // We're always waiting here for the delegate to call us back.
1035 return ERR_IO_PENDING;
1036 }
1037
SetSpdyHttpStreamOrBidirectionalStreamImpl(base::WeakPtr<SpdySession> session)1038 int HttpStreamFactory::Job::SetSpdyHttpStreamOrBidirectionalStreamImpl(
1039 base::WeakPtr<SpdySession> session) {
1040 DCHECK(using_spdy());
1041 auto dns_aliases = session_->spdy_session_pool()->GetDnsAliasesForSessionKey(
1042 spdy_session_key_);
1043
1044 if (is_websocket_) {
1045 DCHECK_NE(job_type_, PRECONNECT);
1046 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
1047 DCHECK(delegate_->websocket_handshake_stream_create_helper());
1048
1049 if (!try_websocket_over_http2_) {
1050 // TODO(davidben): Is this reachable? We shouldn't receive a SpdySession
1051 // if not requested.
1052 return ERR_NOT_IMPLEMENTED;
1053 }
1054
1055 websocket_stream_ =
1056 delegate_->websocket_handshake_stream_create_helper()
1057 ->CreateHttp2Stream(session, std::move(dns_aliases));
1058 return OK;
1059 }
1060 if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
1061 bidirectional_stream_impl_ = std::make_unique<BidirectionalStreamSpdyImpl>(
1062 session, net_log_.source());
1063 return OK;
1064 }
1065
1066 // TODO(willchan): Delete this code, because eventually, the HttpStreamFactory
1067 // will be creating all the SpdyHttpStreams, since it will know when
1068 // SpdySessions become available.
1069
1070 stream_ = std::make_unique<SpdyHttpStream>(session, net_log_.source(),
1071 std::move(dns_aliases));
1072 return OK;
1073 }
1074
DoCreateStream()1075 int HttpStreamFactory::Job::DoCreateStream() {
1076 DCHECK(connection_->socket() || existing_spdy_session_.get());
1077 DCHECK(!using_quic_);
1078
1079 next_state_ = STATE_CREATE_STREAM_COMPLETE;
1080
1081 if (!using_spdy()) {
1082 DCHECK(!expect_spdy_);
1083 bool is_for_get_to_http_proxy = UsingHttpProxyWithoutTunnel();
1084 if (is_websocket_) {
1085 DCHECK_NE(job_type_, PRECONNECT);
1086 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
1087 DCHECK(delegate_->websocket_handshake_stream_create_helper());
1088 websocket_stream_ =
1089 delegate_->websocket_handshake_stream_create_helper()
1090 ->CreateBasicStream(std::move(connection_),
1091 is_for_get_to_http_proxy,
1092 session_->websocket_endpoint_lock_manager());
1093 } else {
1094 if (!request_info_.is_http1_allowed) {
1095 return ERR_H2_OR_QUIC_REQUIRED;
1096 }
1097 stream_ = std::make_unique<HttpBasicStream>(std::move(connection_),
1098 is_for_get_to_http_proxy);
1099 }
1100 return OK;
1101 }
1102
1103 CHECK(!stream_.get());
1104
1105 // It is also possible that an HTTP/2 connection has been established since
1106 // last time Job checked above.
1107 if (!existing_spdy_session_) {
1108 // WebSocket over HTTP/2 is only allowed to use existing HTTP/2 connections.
1109 // Therefore `using_spdy()` could not have been set unless a connection had
1110 // already been found.
1111 DCHECK(!is_websocket_);
1112
1113 existing_spdy_session_ =
1114 session_->spdy_session_pool()->FindAvailableSession(
1115 spdy_session_key_, enable_ip_based_pooling_,
1116 /* is_websocket = */ false, net_log_);
1117 }
1118 if (existing_spdy_session_) {
1119 // We picked up an existing session, so we don't need our socket.
1120 if (connection_->socket()) {
1121 connection_->socket()->Disconnect();
1122 }
1123 connection_->Reset();
1124
1125 int set_result =
1126 SetSpdyHttpStreamOrBidirectionalStreamImpl(existing_spdy_session_);
1127 existing_spdy_session_.reset();
1128 return set_result;
1129 }
1130
1131 // Close idle sockets in this group, since subsequent requests will go over
1132 // |spdy_session|.
1133 if (connection_->socket()->IsConnected()) {
1134 connection_->CloseIdleSocketsInGroup("Switching to HTTP2 session");
1135 }
1136
1137 auto initiator =
1138 (job_type_ == PRECONNECT || job_type_ == PRECONNECT_DNS_ALPN_H3)
1139 ? MultiplexedSessionCreationInitiator::kPreconnect
1140 : MultiplexedSessionCreationInitiator::kUnknown;
1141
1142 base::WeakPtr<SpdySession> spdy_session;
1143 int rv =
1144 session_->spdy_session_pool()->CreateAvailableSessionFromSocketHandle(
1145 spdy_session_key_, std::move(connection_), net_log_, initiator,
1146 &spdy_session);
1147
1148 if (rv != OK) {
1149 return rv;
1150 }
1151
1152 url::SchemeHostPort scheme_host_port(
1153 using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
1154 spdy_session_key_.host_port_pair().host(),
1155 spdy_session_key_.host_port_pair().port());
1156
1157 HttpServerProperties* http_server_properties =
1158 session_->http_server_properties();
1159 if (http_server_properties) {
1160 http_server_properties->SetSupportsSpdy(
1161 scheme_host_port, request_info_.network_anonymization_key,
1162 true /* supports_spdy */);
1163 }
1164
1165 // Create a SpdyHttpStream or a BidirectionalStreamImpl attached to the
1166 // session.
1167 return SetSpdyHttpStreamOrBidirectionalStreamImpl(spdy_session);
1168 }
1169
DoCreateStreamComplete(int result)1170 int HttpStreamFactory::Job::DoCreateStreamComplete(int result) {
1171 if (result < 0) {
1172 return result;
1173 }
1174
1175 session_->proxy_resolution_service()->ReportSuccess(proxy_info_);
1176 next_state_ = STATE_NONE;
1177 return OK;
1178 }
1179
OnSpdySessionAvailable(base::WeakPtr<SpdySession> spdy_session)1180 void HttpStreamFactory::Job::OnSpdySessionAvailable(
1181 base::WeakPtr<SpdySession> spdy_session) {
1182 DCHECK(spdy_session);
1183
1184 // No need for the connection any more, since |spdy_session| can be used
1185 // instead, and there's no benefit from keeping the old ConnectJob in the
1186 // socket pool.
1187 if (connection_) {
1188 connection_->ResetAndCloseSocket();
1189 }
1190
1191 // Once a connection is initialized, or if there's any out-of-band callback,
1192 // like proxy auth challenge, the SpdySessionRequest is cancelled.
1193 DCHECK(next_state_ == STATE_INIT_CONNECTION ||
1194 next_state_ == STATE_INIT_CONNECTION_COMPLETE);
1195
1196 // Ignore calls to ResumeInitConnection() from either the timer or the
1197 // SpdySessionPool.
1198 init_connection_already_resumed_ = true;
1199
1200 // If this is a preconnect, nothing left do to.
1201 if (job_type_ == PRECONNECT) {
1202 OnPreconnectsComplete(OK);
1203 return;
1204 }
1205
1206 negotiated_protocol_ = kProtoHTTP2;
1207 existing_spdy_session_ = spdy_session;
1208 next_state_ = STATE_CREATE_STREAM;
1209
1210 // This will synchronously close |connection_|, so no need to worry about it
1211 // calling back into |this|.
1212 RunLoop(OK);
1213 }
1214
ReconsiderProxyAfterError(int error)1215 int HttpStreamFactory::Job::ReconsiderProxyAfterError(int error) {
1216 // Check if the error was a proxy failure.
1217 if (!CanFalloverToNextProxy(proxy_info_.proxy_chain(), error, &error,
1218 proxy_info_.is_for_ip_protection())) {
1219 return error;
1220 }
1221
1222 should_reconsider_proxy_ = true;
1223 return error;
1224 }
1225
MaybeCopyConnectionAttemptsFromHandle()1226 void HttpStreamFactory::Job::MaybeCopyConnectionAttemptsFromHandle() {
1227 if (!connection_) {
1228 return;
1229 }
1230
1231 delegate_->AddConnectionAttemptsToRequest(this,
1232 connection_->connection_attempts());
1233 }
1234
1235 HttpStreamFactory::JobFactory::JobFactory() = default;
1236
1237 HttpStreamFactory::JobFactory::~JobFactory() = default;
1238
1239 std::unique_ptr<HttpStreamFactory::Job>
CreateJob(HttpStreamFactory::Job::Delegate * delegate,HttpStreamFactory::JobType job_type,HttpNetworkSession * session,const StreamRequestInfo & request_info,RequestPriority priority,const ProxyInfo & proxy_info,const std::vector<SSLConfig::CertAndStatus> & allowed_bad_certs,url::SchemeHostPort destination,GURL origin_url,bool is_websocket,bool enable_ip_based_pooling,NetLog * net_log,NextProto alternative_protocol,quic::ParsedQuicVersion quic_version)1240 HttpStreamFactory::JobFactory::CreateJob(
1241 HttpStreamFactory::Job::Delegate* delegate,
1242 HttpStreamFactory::JobType job_type,
1243 HttpNetworkSession* session,
1244 const StreamRequestInfo& request_info,
1245 RequestPriority priority,
1246 const ProxyInfo& proxy_info,
1247 const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs,
1248 url::SchemeHostPort destination,
1249 GURL origin_url,
1250 bool is_websocket,
1251 bool enable_ip_based_pooling,
1252 NetLog* net_log,
1253 NextProto alternative_protocol,
1254 quic::ParsedQuicVersion quic_version) {
1255 return std::make_unique<HttpStreamFactory::Job>(
1256 delegate, job_type, session, request_info, priority, proxy_info,
1257 allowed_bad_certs, std::move(destination), origin_url,
1258 alternative_protocol, quic_version, is_websocket, enable_ip_based_pooling,
1259 net_log);
1260 }
1261
ShouldThrottleConnectForSpdy() const1262 bool HttpStreamFactory::Job::ShouldThrottleConnectForSpdy() const {
1263 DCHECK(!using_quic_);
1264 DCHECK(!spdy_session_request_);
1265
1266 // If the job has previously been throttled, don't throttle it again.
1267 if (init_connection_already_resumed_) {
1268 return false;
1269 }
1270
1271 url::SchemeHostPort scheme_host_port(
1272 using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
1273 spdy_session_key_.host_port_pair().host(),
1274 spdy_session_key_.host_port_pair().port());
1275 // Only throttle the request if the server is believed to support H2.
1276 return session_->http_server_properties()->GetSupportsSpdy(
1277 scheme_host_port, request_info_.network_anonymization_key);
1278 }
1279
RecordPreconnectHistograms(int result)1280 void HttpStreamFactory::Job::RecordPreconnectHistograms(int result) {
1281 if (!IsGoogleHost(destination_.host())) {
1282 return;
1283 }
1284 if (using_quic_) {
1285 // TODO(crbug.com/376304027): Expand this to non-Quic as well. Currently,
1286 // H1 and H2 does not return precise failure reason.
1287 base::UmaHistogramSparse(
1288 "Net.SessionCreate.GoogleSearch.Preconnect.Quic.CompletionResult",
1289 -result);
1290 }
1291 }
1292
1293 } // namespace net
1294