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