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