• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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