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