• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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_controller.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "base/containers/contains.h"
11 #include "base/functional/bind.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "base/values.h"
18 #include "net/base/features.h"
19 #include "net/base/host_mapping_rules.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/privacy_mode.h"
23 #include "net/base/proxy_chain.h"
24 #include "net/base/proxy_string_util.h"
25 #include "net/base/session_usage.h"
26 #include "net/base/url_util.h"
27 #include "net/http/alternative_service.h"
28 #include "net/http/bidirectional_stream_impl.h"
29 #include "net/http/http_stream_key.h"
30 #include "net/http/http_stream_pool.h"
31 #include "net/http/http_stream_pool_request_info.h"
32 #include "net/http/transport_security_state.h"
33 #include "net/log/net_log.h"
34 #include "net/log/net_log_event_type.h"
35 #include "net/log/net_log_with_source.h"
36 #include "net/proxy_resolution/proxy_info.h"
37 #include "net/proxy_resolution/proxy_resolution_request.h"
38 #include "net/proxy_resolution/proxy_resolution_service.h"
39 #include "net/quic/quic_session_key.h"
40 #include "net/socket/next_proto.h"
41 #include "net/spdy/spdy_session.h"
42 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
43 #include "url/gurl.h"
44 #include "url/scheme_host_port.h"
45 #include "url/url_constants.h"
46 
47 namespace net {
48 
49 namespace {
50 
51 // Returns parameters associated with the proxy resolution.
NetLogHttpStreamJobProxyChainResolved(const ProxyChain & proxy_chain)52 base::Value::Dict NetLogHttpStreamJobProxyChainResolved(
53     const ProxyChain& proxy_chain) {
54   base::Value::Dict dict;
55 
56   dict.Set("proxy_chain",
57            proxy_chain.IsValid() ? proxy_chain.ToDebugString() : std::string());
58   return dict;
59 }
60 
CreateAltSvcUrl(const GURL & origin_url,const HostPortPair & alternative_destination)61 GURL CreateAltSvcUrl(const GURL& origin_url,
62                      const HostPortPair& alternative_destination) {
63   DCHECK(origin_url.is_valid());
64   DCHECK(origin_url.IsStandard());
65 
66   GURL::Replacements replacements;
67   std::string port_str = base::NumberToString(alternative_destination.port());
68   replacements.SetPortStr(port_str);
69   replacements.SetHostStr(alternative_destination.host());
70 
71   return origin_url.ReplaceComponents(replacements);
72 }
73 
ConvertWsToHttp(url::SchemeHostPort & input)74 void ConvertWsToHttp(url::SchemeHostPort& input) {
75   if (base::EqualsCaseInsensitiveASCII(input.scheme(), url::kHttpScheme) ||
76       base::EqualsCaseInsensitiveASCII(input.scheme(), url::kHttpsScheme)) {
77     return;
78   }
79 
80   if (base::EqualsCaseInsensitiveASCII(input.scheme(), url::kWsScheme)) {
81     input = url::SchemeHostPort(url::kHttpScheme, input.host(), input.port());
82     return;
83   }
84 
85   DCHECK(base::EqualsCaseInsensitiveASCII(input.scheme(), url::kWssScheme));
86   input = url::SchemeHostPort(url::kHttpsScheme, input.host(), input.port());
87 }
88 
HistogramProxyUsed(const ProxyInfo & proxy_info,bool success)89 void HistogramProxyUsed(const ProxyInfo& proxy_info, bool success) {
90   const ProxyServer::Scheme max_scheme = ProxyServer::Scheme::SCHEME_QUIC;
91   ProxyServer::Scheme proxy_scheme = ProxyServer::Scheme::SCHEME_INVALID;
92   if (!proxy_info.is_empty() && !proxy_info.is_direct()) {
93     if (proxy_info.proxy_chain().is_multi_proxy()) {
94       // TODO(crbug.com/40284947): Update this histogram to have a new
95       // bucket for multi-chain proxies. Until then, don't influence the
96       // existing metric counts which have historically been only for single-hop
97       // proxies.
98       return;
99     }
100     proxy_scheme = proxy_info.proxy_chain().is_direct()
101                        ? static_cast<ProxyServer::Scheme>(1)
102                        : proxy_info.proxy_chain().First().scheme();
103   }
104   if (success) {
105     UMA_HISTOGRAM_ENUMERATION("Net.HttpJob.ProxyTypeSuccess", proxy_scheme,
106                               max_scheme);
107   } else {
108     UMA_HISTOGRAM_ENUMERATION("Net.HttpJob.ProxyTypeFailed", proxy_scheme,
109                               max_scheme);
110   }
111 }
112 
113 // Generate a AlternativeService for DNS alt job. Note: Chrome does not yet
114 // support different port DNS alpn.
GetAlternativeServiceForDnsJob(const GURL & url)115 AlternativeService GetAlternativeServiceForDnsJob(const GURL& url) {
116   return AlternativeService(kProtoQUIC, HostPortPair::FromURL(url));
117 }
118 
NetLogAltSvcParams(const AlternativeServiceInfo * alt_svc_info,bool is_broken)119 base::Value::Dict NetLogAltSvcParams(const AlternativeServiceInfo* alt_svc_info,
120                                      bool is_broken) {
121   base::Value::Dict dict;
122   dict.Set("alt_svc", alt_svc_info->ToString());
123   dict.Set("is_broken", is_broken);
124   return dict;
125 }
126 
127 }  // namespace
128 
129 // The maximum time to wait for the alternate job to complete before resuming
130 // the main job.
131 const int kMaxDelayTimeForMainJobSecs = 3;
132 
JobController(HttpStreamFactory * factory,HttpStreamRequest::Delegate * delegate,HttpNetworkSession * session,JobFactory * job_factory,const HttpRequestInfo & http_request_info,bool is_preconnect,bool is_websocket,bool enable_ip_based_pooling,bool enable_alternative_services,bool delay_main_job_with_available_spdy_session,const std::vector<SSLConfig::CertAndStatus> & allowed_bad_certs)133 HttpStreamFactory::JobController::JobController(
134     HttpStreamFactory* factory,
135     HttpStreamRequest::Delegate* delegate,
136     HttpNetworkSession* session,
137     JobFactory* job_factory,
138     const HttpRequestInfo& http_request_info,
139     bool is_preconnect,
140     bool is_websocket,
141     bool enable_ip_based_pooling,
142     bool enable_alternative_services,
143     bool delay_main_job_with_available_spdy_session,
144     const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs)
145     : factory_(factory),
146       session_(session),
147       job_factory_(job_factory),
148       delegate_(delegate),
149       is_preconnect_(is_preconnect),
150       is_websocket_(is_websocket),
151       enable_ip_based_pooling_(enable_ip_based_pooling),
152       enable_alternative_services_(enable_alternative_services),
153       delay_main_job_with_available_spdy_session_(
154           delay_main_job_with_available_spdy_session),
155       http_request_info_url_(http_request_info.url),
156       origin_url_(DuplicateUrlWithHostMappingRules(http_request_info.url)),
157       request_info_(http_request_info),
158       allowed_bad_certs_(allowed_bad_certs),
159       net_log_(NetLogWithSource::Make(
160           session->net_log(),
161           NetLogSourceType::HTTP_STREAM_JOB_CONTROLLER)) {
162   DCHECK(factory_);
163   DCHECK(session_);
164   DCHECK(job_factory_);
165   DCHECK(base::EqualsCaseInsensitiveASCII(origin_url_.scheme_piece(),
166                                           url::kHttpScheme) ||
167          base::EqualsCaseInsensitiveASCII(origin_url_.scheme_piece(),
168                                           url::kHttpsScheme) ||
169          base::EqualsCaseInsensitiveASCII(origin_url_.scheme_piece(),
170                                           url::kWsScheme) ||
171          base::EqualsCaseInsensitiveASCII(origin_url_.scheme_piece(),
172                                           url::kWssScheme));
173 
174   net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER, [&] {
175     base::Value::Dict dict;
176     dict.Set("url", http_request_info.url.possibly_invalid_spec());
177     if (origin_url_ != http_request_info.url) {
178       dict.Set("url_after_host_mapping", origin_url_.possibly_invalid_spec());
179     }
180     dict.Set("is_preconnect", is_preconnect_);
181     dict.Set("privacy_mode",
182              PrivacyModeToDebugString(request_info_.privacy_mode));
183     base::Value::List allowed_bad_certs_list;
184     for (const auto& cert_and_status : allowed_bad_certs_) {
185       allowed_bad_certs_list.Append(
186           cert_and_status.cert->subject().GetDisplayName());
187     }
188     dict.Set("allowed_bad_certs", std::move(allowed_bad_certs_list));
189     return dict;
190   });
191 }
192 
~JobController()193 HttpStreamFactory::JobController::~JobController() {
194   bound_job_ = nullptr;
195   main_job_.reset();
196   alternative_job_.reset();
197   dns_alpn_h3_job_.reset();
198   if (proxy_resolve_request_) {
199     DCHECK_EQ(STATE_RESOLVE_PROXY_COMPLETE, next_state_);
200     proxy_resolve_request_.reset();
201   }
202   net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER);
203 }
204 
Start(HttpStreamRequest::Delegate * delegate,WebSocketHandshakeStreamBase::CreateHelper * websocket_handshake_stream_create_helper,const NetLogWithSource & source_net_log,HttpStreamRequest::StreamType stream_type,RequestPriority priority)205 std::unique_ptr<HttpStreamRequest> HttpStreamFactory::JobController::Start(
206     HttpStreamRequest::Delegate* delegate,
207     WebSocketHandshakeStreamBase::CreateHelper*
208         websocket_handshake_stream_create_helper,
209     const NetLogWithSource& source_net_log,
210     HttpStreamRequest::StreamType stream_type,
211     RequestPriority priority) {
212   DCHECK(!request_);
213 
214   stream_type_ = stream_type;
215   priority_ = priority;
216 
217   auto request = std::make_unique<HttpStreamRequest>(
218       this, websocket_handshake_stream_create_helper, source_net_log,
219       stream_type);
220   // Keep a raw pointer but release ownership of HttpStreamRequest instance.
221   request_ = request.get();
222 
223   // Associates |net_log_| with |source_net_log|.
224   source_net_log.AddEventReferencingSource(
225       NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND, net_log_.source());
226   net_log_.AddEventReferencingSource(
227       NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND,
228       source_net_log.source());
229 
230   RunLoop(OK);
231 
232   return request;
233 }
234 
Preconnect(int num_streams)235 void HttpStreamFactory::JobController::Preconnect(int num_streams) {
236   DCHECK(!main_job_);
237   DCHECK(!alternative_job_);
238   DCHECK(is_preconnect_);
239 
240   stream_type_ = HttpStreamRequest::HTTP_STREAM;
241   num_streams_ = num_streams;
242 
243   RunLoop(OK);
244 }
245 
GetLoadState() const246 LoadState HttpStreamFactory::JobController::GetLoadState() const {
247   DCHECK(request_);
248   if (next_state_ == STATE_RESOLVE_PROXY_COMPLETE) {
249     return proxy_resolve_request_->GetLoadState();
250   }
251   if (bound_job_) {
252     return bound_job_->GetLoadState();
253   }
254   if (main_job_) {
255     return main_job_->GetLoadState();
256   }
257   if (alternative_job_) {
258     return alternative_job_->GetLoadState();
259   }
260   if (dns_alpn_h3_job_) {
261     return dns_alpn_h3_job_->GetLoadState();
262   }
263 
264   // When proxy resolution fails, there is no job created and
265   // NotifyRequestFailed() is executed one message loop iteration later.
266   return LOAD_STATE_IDLE;
267 }
268 
OnRequestComplete()269 void HttpStreamFactory::JobController::OnRequestComplete() {
270   DCHECK(request_);
271   request_ = nullptr;
272   // This is called when the delegate is destroying its HttpStreamRequest, so
273   // it's no longer safe to call into it after this point.
274   delegate_ = nullptr;
275 
276   if (!job_bound_) {
277     alternative_job_.reset();
278     main_job_.reset();
279     dns_alpn_h3_job_.reset();
280   } else {
281     if (bound_job_->job_type() == MAIN) {
282       bound_job_ = nullptr;
283       main_job_.reset();
284     } else if (bound_job_->job_type() == ALTERNATIVE) {
285       bound_job_ = nullptr;
286       alternative_job_.reset();
287     } else {
288       DCHECK(bound_job_->job_type() == DNS_ALPN_H3);
289       bound_job_ = nullptr;
290       dns_alpn_h3_job_.reset();
291     }
292   }
293   MaybeNotifyFactoryOfCompletion();
294 }
295 
RestartTunnelWithProxyAuth()296 int HttpStreamFactory::JobController::RestartTunnelWithProxyAuth() {
297   DCHECK(bound_job_);
298   return bound_job_->RestartTunnelWithProxyAuth();
299 }
300 
SetPriority(RequestPriority priority)301 void HttpStreamFactory::JobController::SetPriority(RequestPriority priority) {
302   if (main_job_) {
303     main_job_->SetPriority(priority);
304   }
305   if (alternative_job_) {
306     alternative_job_->SetPriority(priority);
307   }
308   if (dns_alpn_h3_job_) {
309     dns_alpn_h3_job_->SetPriority(priority);
310   }
311   if (preconnect_backup_job_) {
312     preconnect_backup_job_->SetPriority(priority);
313   }
314 }
315 
OnStreamReady(Job * job)316 void HttpStreamFactory::JobController::OnStreamReady(Job* job) {
317   DCHECK(job);
318 
319   if (IsJobOrphaned(job)) {
320     // We have bound a job to the associated HttpStreamRequest, |job| has been
321     // orphaned.
322     OnOrphanedJobComplete(job);
323     return;
324   }
325   std::unique_ptr<HttpStream> stream = job->ReleaseStream();
326   DCHECK(stream);
327 
328   MarkRequestComplete(job);
329 
330   if (!request_) {
331     return;
332   }
333   DCHECK(!is_websocket_);
334   DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type());
335   OnJobSucceeded(job);
336 
337   // TODO(bnc): Remove when https://crbug.com/461981 is fixed.
338   CHECK(request_);
339 
340   DCHECK(request_->completed());
341 
342   HistogramProxyUsed(job->proxy_info(), /*success=*/true);
343   delegate_->OnStreamReady(job->proxy_info(), std::move(stream));
344 }
345 
OnBidirectionalStreamImplReady(Job * job,const ProxyInfo & used_proxy_info)346 void HttpStreamFactory::JobController::OnBidirectionalStreamImplReady(
347     Job* job,
348     const ProxyInfo& used_proxy_info) {
349   DCHECK(job);
350 
351   if (IsJobOrphaned(job)) {
352     // We have bound a job to the associated HttpStreamRequest, |job| has been
353     // orphaned.
354     OnOrphanedJobComplete(job);
355     return;
356   }
357 
358   MarkRequestComplete(job);
359 
360   if (!request_) {
361     return;
362   }
363   std::unique_ptr<BidirectionalStreamImpl> stream =
364       job->ReleaseBidirectionalStream();
365   DCHECK(stream);
366   DCHECK(!is_websocket_);
367   DCHECK_EQ(HttpStreamRequest::BIDIRECTIONAL_STREAM, request_->stream_type());
368 
369   OnJobSucceeded(job);
370   DCHECK(request_->completed());
371   delegate_->OnBidirectionalStreamImplReady(used_proxy_info, std::move(stream));
372 }
373 
OnWebSocketHandshakeStreamReady(Job * job,const ProxyInfo & used_proxy_info,std::unique_ptr<WebSocketHandshakeStreamBase> stream)374 void HttpStreamFactory::JobController::OnWebSocketHandshakeStreamReady(
375     Job* job,
376     const ProxyInfo& used_proxy_info,
377     std::unique_ptr<WebSocketHandshakeStreamBase> stream) {
378   DCHECK(job);
379   MarkRequestComplete(job);
380 
381   if (!request_) {
382     return;
383   }
384   DCHECK(is_websocket_);
385   DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type());
386   DCHECK(stream);
387 
388   OnJobSucceeded(job);
389   DCHECK(request_->completed());
390   delegate_->OnWebSocketHandshakeStreamReady(used_proxy_info,
391                                              std::move(stream));
392 }
393 
OnQuicHostResolution(const url::SchemeHostPort & destination,base::TimeTicks dns_resolution_start_time,base::TimeTicks dns_resolution_end_time)394 void HttpStreamFactory::JobController::OnQuicHostResolution(
395     const url::SchemeHostPort& destination,
396     base::TimeTicks dns_resolution_start_time,
397     base::TimeTicks dns_resolution_end_time) {
398   if (!request_) {
399     return;
400   }
401   if (destination != url::SchemeHostPort(origin_url_)) {
402     // Ignores different destination alternative job's DNS resolution time.
403     return;
404   }
405   // QUIC jobs (ALTERNATIVE, DNS_ALPN_H3) are started before the non-QUIC (MAIN)
406   // job. So we set the DNS resolution overrides to use the DNS timing of the
407   // QUIC jobs.
408   request_->SetDnsResolutionTimeOverrides(dns_resolution_start_time,
409                                           dns_resolution_end_time);
410 }
411 
OnStreamFailed(Job * job,int status)412 void HttpStreamFactory::JobController::OnStreamFailed(Job* job, int status) {
413   DCHECK_NE(OK, status);
414   if (job->job_type() == MAIN) {
415     DCHECK_EQ(main_job_.get(), job);
416     main_job_net_error_ = status;
417   } else if (job->job_type() == ALTERNATIVE) {
418     DCHECK_EQ(alternative_job_.get(), job);
419     DCHECK_NE(kProtoUnknown, alternative_service_info_.protocol());
420     alternative_job_net_error_ = status;
421   } else {
422     DCHECK_EQ(job->job_type(), DNS_ALPN_H3);
423     DCHECK_EQ(dns_alpn_h3_job_.get(), job);
424     dns_alpn_h3_job_net_error_ = status;
425   }
426 
427   MaybeResumeMainJob(job, base::TimeDelta());
428 
429   if (IsJobOrphaned(job)) {
430     // We have bound a job to the associated HttpStreamRequest, |job| has been
431     // orphaned.
432     OnOrphanedJobComplete(job);
433     return;
434   }
435 
436   if (!request_) {
437     return;
438   }
439   DCHECK_NE(OK, status);
440   DCHECK(job);
441 
442   if (!bound_job_) {
443     if (GetJobCount() >= 2) {
444       // Hey, we've got other jobs! Maybe one of them will succeed, let's just
445       // ignore this failure.
446       if (job->job_type() == MAIN) {
447         DCHECK_EQ(main_job_.get(), job);
448         main_job_.reset();
449       } else if (job->job_type() == ALTERNATIVE) {
450         DCHECK_EQ(alternative_job_.get(), job);
451         alternative_job_.reset();
452       } else {
453         DCHECK_EQ(job->job_type(), DNS_ALPN_H3);
454         DCHECK_EQ(dns_alpn_h3_job_.get(), job);
455         dns_alpn_h3_job_.reset();
456       }
457       return;
458     } else {
459       BindJob(job);
460     }
461   }
462 
463   status = ReconsiderProxyAfterError(job, status);
464   if (next_state_ == STATE_RESOLVE_PROXY_COMPLETE) {
465     if (status == ERR_IO_PENDING) {
466       return;
467     }
468     DCHECK_EQ(OK, status);
469     RunLoop(status);
470     return;
471   }
472 
473   HistogramProxyUsed(job->proxy_info(), /*success=*/false);
474   delegate_->OnStreamFailed(status, *job->net_error_details(),
475                             job->proxy_info(), job->resolve_error_info());
476 }
477 
OnFailedOnDefaultNetwork(Job * job)478 void HttpStreamFactory::JobController::OnFailedOnDefaultNetwork(Job* job) {
479   if (job->job_type() == ALTERNATIVE) {
480     DCHECK_EQ(alternative_job_.get(), job);
481     alternative_job_failed_on_default_network_ = true;
482   } else {
483     DCHECK_EQ(job->job_type(), DNS_ALPN_H3);
484     DCHECK_EQ(dns_alpn_h3_job_.get(), job);
485     dns_alpn_h3_job_failed_on_default_network_ = true;
486   }
487 }
488 
OnCertificateError(Job * job,int status,const SSLInfo & ssl_info)489 void HttpStreamFactory::JobController::OnCertificateError(
490     Job* job,
491     int status,
492     const SSLInfo& ssl_info) {
493   MaybeResumeMainJob(job, base::TimeDelta());
494 
495   if (IsJobOrphaned(job)) {
496     // We have bound a job to the associated HttpStreamRequest, |job| has been
497     // orphaned.
498     OnOrphanedJobComplete(job);
499     return;
500   }
501 
502   if (!request_) {
503     return;
504   }
505   DCHECK_NE(OK, status);
506   if (!bound_job_) {
507     BindJob(job);
508   }
509 
510   delegate_->OnCertificateError(status, ssl_info);
511 }
512 
OnNeedsClientAuth(Job * job,SSLCertRequestInfo * cert_info)513 void HttpStreamFactory::JobController::OnNeedsClientAuth(
514     Job* job,
515     SSLCertRequestInfo* cert_info) {
516   MaybeResumeMainJob(job, base::TimeDelta());
517 
518   if (IsJobOrphaned(job)) {
519     // We have bound a job to the associated HttpStreamRequest, |job| has been
520     // orphaned.
521     OnOrphanedJobComplete(job);
522     return;
523   }
524   if (!request_) {
525     return;
526   }
527   if (!bound_job_) {
528     BindJob(job);
529   }
530 
531   delegate_->OnNeedsClientAuth(cert_info);
532 }
533 
OnNeedsProxyAuth(Job * job,const HttpResponseInfo & proxy_response,const ProxyInfo & used_proxy_info,HttpAuthController * auth_controller)534 void HttpStreamFactory::JobController::OnNeedsProxyAuth(
535     Job* job,
536     const HttpResponseInfo& proxy_response,
537     const ProxyInfo& used_proxy_info,
538     HttpAuthController* auth_controller) {
539   MaybeResumeMainJob(job, base::TimeDelta());
540 
541   if (IsJobOrphaned(job)) {
542     // We have bound a job to the associated HttpStreamRequest, |job| has been
543     // orphaned.
544     OnOrphanedJobComplete(job);
545     return;
546   }
547 
548   if (!request_) {
549     return;
550   }
551   if (!bound_job_) {
552     BindJob(job);
553   }
554   delegate_->OnNeedsProxyAuth(proxy_response, used_proxy_info, auth_controller);
555 }
556 
OnPreconnectsComplete(Job * job,int result)557 void HttpStreamFactory::JobController::OnPreconnectsComplete(Job* job,
558                                                              int result) {
559   // Preconnects only run as `main_job_`, never `alternative_job_` or
560   // `dns_alpn_h3_job_`.
561   DCHECK_EQ(main_job_.get(), job);
562 
563   // If the job failed because there were no matching HTTPS records in DNS, run
564   // the backup job. A TCP-based protocol may work instead.
565   if (result == ERR_DNS_NO_MATCHING_SUPPORTED_ALPN && preconnect_backup_job_) {
566     DCHECK_EQ(job->job_type(), PRECONNECT_DNS_ALPN_H3);
567     main_job_ = std::move(preconnect_backup_job_);
568     main_job_->Preconnect(num_streams_);
569     return;
570   }
571 
572   main_job_.reset();
573   preconnect_backup_job_.reset();
574   ResetErrorStatusForJobs();
575   factory_->OnPreconnectsCompleteInternal();
576   MaybeNotifyFactoryOfCompletion();
577 }
578 
OnOrphanedJobComplete(const Job * job)579 void HttpStreamFactory::JobController::OnOrphanedJobComplete(const Job* job) {
580   if (job->job_type() == MAIN) {
581     DCHECK_EQ(main_job_.get(), job);
582     main_job_.reset();
583   } else if (job->job_type() == ALTERNATIVE) {
584     DCHECK_EQ(alternative_job_.get(), job);
585     alternative_job_.reset();
586   } else {
587     DCHECK_EQ(job->job_type(), DNS_ALPN_H3);
588     DCHECK_EQ(dns_alpn_h3_job_.get(), job);
589     dns_alpn_h3_job_.reset();
590   }
591 
592   MaybeNotifyFactoryOfCompletion();
593 }
594 
AddConnectionAttemptsToRequest(Job * job,const ConnectionAttempts & attempts)595 void HttpStreamFactory::JobController::AddConnectionAttemptsToRequest(
596     Job* job,
597     const ConnectionAttempts& attempts) {
598   if (is_preconnect_ || IsJobOrphaned(job)) {
599     return;
600   }
601 
602   request_->AddConnectionAttempts(attempts);
603 }
604 
ResumeMainJobLater(const base::TimeDelta & delay)605 void HttpStreamFactory::JobController::ResumeMainJobLater(
606     const base::TimeDelta& delay) {
607   net_log_.AddEventWithInt64Params(NetLogEventType::HTTP_STREAM_JOB_DELAYED,
608                                    "delay", delay.InMilliseconds());
609   resume_main_job_callback_.Reset(
610       base::BindOnce(&HttpStreamFactory::JobController::ResumeMainJob,
611                      ptr_factory_.GetWeakPtr()));
612   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
613       FROM_HERE, resume_main_job_callback_.callback(), delay);
614 }
615 
ResumeMainJob()616 void HttpStreamFactory::JobController::ResumeMainJob() {
617   DCHECK(main_job_);
618 
619   if (main_job_is_resumed_) {
620     return;
621   }
622   main_job_is_resumed_ = true;
623   main_job_->net_log().AddEventWithInt64Params(
624       NetLogEventType::HTTP_STREAM_JOB_RESUMED, "delay",
625       main_job_wait_time_.InMilliseconds());
626 
627   main_job_->Resume();
628   main_job_wait_time_ = base::TimeDelta();
629 }
630 
ResetErrorStatusForJobs()631 void HttpStreamFactory::JobController::ResetErrorStatusForJobs() {
632   main_job_net_error_ = OK;
633   alternative_job_net_error_ = OK;
634   alternative_job_failed_on_default_network_ = false;
635   dns_alpn_h3_job_net_error_ = OK;
636   dns_alpn_h3_job_failed_on_default_network_ = false;
637 }
638 
MaybeResumeMainJob(Job * job,const base::TimeDelta & delay)639 void HttpStreamFactory::JobController::MaybeResumeMainJob(
640     Job* job,
641     const base::TimeDelta& delay) {
642   DCHECK(delay == base::TimeDelta() || delay == main_job_wait_time_);
643   DCHECK(job == main_job_.get() || job == alternative_job_.get() ||
644          job == dns_alpn_h3_job_.get());
645 
646   if (job == main_job_.get()) {
647     return;
648   }
649   if (job == dns_alpn_h3_job_.get() && alternative_job_) {
650     return;
651   }
652   if (!main_job_) {
653     return;
654   }
655 
656   main_job_is_blocked_ = false;
657 
658   if (!main_job_->is_waiting()) {
659     // There are two cases where the main job is not in WAIT state:
660     //   1) The main job hasn't got to waiting state, do not yet post a task to
661     //      resume since that will happen in ShouldWait().
662     //   2) The main job has passed waiting state, so the main job does not need
663     //      to be resumed.
664     return;
665   }
666 
667   main_job_wait_time_ = delay;
668 
669   ResumeMainJobLater(main_job_wait_time_);
670 }
671 
OnConnectionInitialized(Job * job,int rv)672 void HttpStreamFactory::JobController::OnConnectionInitialized(Job* job,
673                                                                int rv) {
674   if (rv != OK) {
675     // Resume the main job as there's an error raised in connection
676     // initiation.
677     return MaybeResumeMainJob(job, main_job_wait_time_);
678   }
679 }
680 
ShouldWait(Job * job)681 bool HttpStreamFactory::JobController::ShouldWait(Job* job) {
682   // The alternative job never waits.
683   if (job == alternative_job_.get() || job == dns_alpn_h3_job_.get()) {
684     return false;
685   }
686   DCHECK_EQ(main_job_.get(), job);
687   if (main_job_is_blocked_) {
688     return true;
689   }
690 
691   if (main_job_wait_time_.is_zero()) {
692     return false;
693   }
694 
695   ResumeMainJobLater(main_job_wait_time_);
696   return true;
697 }
698 
GetNetLog() const699 const NetLogWithSource* HttpStreamFactory::JobController::GetNetLog() const {
700   return &net_log_;
701 }
702 
MaybeSetWaitTimeForMainJob(const base::TimeDelta & delay)703 void HttpStreamFactory::JobController::MaybeSetWaitTimeForMainJob(
704     const base::TimeDelta& delay) {
705   if (main_job_is_blocked_) {
706     const bool has_available_spdy_session =
707         main_job_->HasAvailableSpdySession();
708     if (!delay_main_job_with_available_spdy_session_ &&
709         has_available_spdy_session) {
710       main_job_wait_time_ = base::TimeDelta();
711     } else {
712       main_job_wait_time_ =
713           std::min(delay, base::Seconds(kMaxDelayTimeForMainJobSecs));
714     }
715     if (has_available_spdy_session) {
716       UMA_HISTOGRAM_TIMES("Net.HttpJob.MainJobWaitTimeWithAvailableSpdySession",
717                           main_job_wait_time_);
718     } else {
719       UMA_HISTOGRAM_TIMES(
720           "Net.HttpJob.MainJobWaitTimeWithoutAvailableSpdySession",
721           main_job_wait_time_);
722     }
723   }
724 }
725 
HasPendingMainJob() const726 bool HttpStreamFactory::JobController::HasPendingMainJob() const {
727   return main_job_.get() != nullptr;
728 }
729 
HasPendingAltJob() const730 bool HttpStreamFactory::JobController::HasPendingAltJob() const {
731   return alternative_job_.get() != nullptr;
732 }
733 
734 WebSocketHandshakeStreamBase::CreateHelper*
websocket_handshake_stream_create_helper()735 HttpStreamFactory::JobController::websocket_handshake_stream_create_helper() {
736   DCHECK(request_);
737   return request_->websocket_handshake_stream_create_helper();
738 }
739 
OnIOComplete(int result)740 void HttpStreamFactory::JobController::OnIOComplete(int result) {
741   RunLoop(result);
742 }
743 
RunLoop(int result)744 void HttpStreamFactory::JobController::RunLoop(int result) {
745   int rv = DoLoop(result);
746   if (rv == ERR_IO_PENDING) {
747     return;
748   }
749   if (rv != OK) {
750     // DoLoop can only fail during proxy resolution step which happens before
751     // any jobs are created. Notify |request_| of the failure one message loop
752     // iteration later to avoid re-entrancy.
753     DCHECK(!main_job_);
754     DCHECK(!alternative_job_);
755     DCHECK(!dns_alpn_h3_job_);
756     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
757         FROM_HERE,
758         base::BindOnce(&HttpStreamFactory::JobController::NotifyRequestFailed,
759                        ptr_factory_.GetWeakPtr(), rv));
760   }
761 }
762 
DoLoop(int rv)763 int HttpStreamFactory::JobController::DoLoop(int rv) {
764   DCHECK_NE(next_state_, STATE_NONE);
765   do {
766     State state = next_state_;
767     next_state_ = STATE_NONE;
768     switch (state) {
769       case STATE_RESOLVE_PROXY:
770         DCHECK_EQ(OK, rv);
771         rv = DoResolveProxy();
772         break;
773       case STATE_RESOLVE_PROXY_COMPLETE:
774         rv = DoResolveProxyComplete(rv);
775         break;
776       case STATE_CREATE_JOBS:
777         DCHECK_EQ(OK, rv);
778         rv = DoCreateJobs();
779         break;
780       default:
781         NOTREACHED() << "bad state";
782     }
783   } while (next_state_ != STATE_NONE && rv != ERR_IO_PENDING);
784   return rv;
785 }
786 
DoResolveProxy()787 int HttpStreamFactory::JobController::DoResolveProxy() {
788   DCHECK(!proxy_resolve_request_);
789 
790   next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
791 
792   if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
793     proxy_info_.UseDirect();
794     return OK;
795   }
796 
797   CompletionOnceCallback io_callback =
798       base::BindOnce(&JobController::OnIOComplete, base::Unretained(this));
799   return session_->proxy_resolution_service()->ResolveProxy(
800       origin_url_, request_info_.method,
801       request_info_.network_anonymization_key, &proxy_info_,
802       std::move(io_callback), &proxy_resolve_request_, net_log_);
803 }
804 
DoResolveProxyComplete(int rv)805 int HttpStreamFactory::JobController::DoResolveProxyComplete(int rv) {
806   DCHECK_NE(ERR_IO_PENDING, rv);
807 
808   proxy_resolve_request_ = nullptr;
809   net_log_.AddEvent(
810       NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_PROXY_SERVER_RESOLVED, [&] {
811         return NetLogHttpStreamJobProxyChainResolved(
812             proxy_info_.is_empty() ? ProxyChain() : proxy_info_.proxy_chain());
813       });
814 
815   if (rv != OK) {
816     return rv;
817   }
818   // Remove unsupported proxies from the list.
819   int supported_proxies = ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS |
820                           ProxyServer::SCHEME_SOCKS4 |
821                           ProxyServer::SCHEME_SOCKS5;
822   // WebSockets is not supported over QUIC.
823   if (session_->IsQuicEnabled() && !is_websocket_) {
824     supported_proxies |= ProxyServer::SCHEME_QUIC;
825   }
826   proxy_info_.RemoveProxiesWithoutScheme(supported_proxies);
827 
828   if (proxy_info_.is_empty()) {
829     // No proxies/direct to choose from.
830     return ERR_NO_SUPPORTED_PROXIES;
831   }
832 
833   next_state_ = STATE_CREATE_JOBS;
834   return rv;
835 }
836 
DoCreateJobs()837 int HttpStreamFactory::JobController::DoCreateJobs() {
838   DCHECK(!main_job_);
839   DCHECK(!alternative_job_);
840   DCHECK(origin_url_.is_valid());
841   DCHECK(origin_url_.IsStandard());
842 
843   url::SchemeHostPort destination(origin_url_);
844   DCHECK(destination.IsValid());
845   ConvertWsToHttp(destination);
846 
847   // Create an alternative job if alternative service is set up for this domain.
848   // This is applicable even if the connection will be made via a proxy.
849   alternative_service_info_ = GetAlternativeServiceInfoFor(
850       http_request_info_url_, request_info_, delegate_, stream_type_);
851 
852   if (base::FeatureList::IsEnabled(features::kHappyEyeballsV3) &&
853       proxy_info_.is_direct() && !is_websocket_) {
854     SwitchToHttpStreamPool();
855     return OK;
856   }
857 
858   quic::ParsedQuicVersion quic_version = quic::ParsedQuicVersion::Unsupported();
859   if (alternative_service_info_.protocol() == kProtoQUIC) {
860     quic_version =
861         SelectQuicVersion(alternative_service_info_.advertised_versions());
862     DCHECK_NE(quic_version, quic::ParsedQuicVersion::Unsupported());
863   }
864 
865   // Getting ALPN for H3 from DNS has a lot of preconditions. Among them:
866   // - proxied connections perform DNS on the proxy, so they can't get supported
867   //   ALPNs from DNS
868   const bool dns_alpn_h3_job_enabled =
869       !session_->ShouldForceQuic(destination, proxy_info_, is_websocket_) &&
870       enable_alternative_services_ &&
871       session_->params().use_dns_https_svcb_alpn &&
872       base::EqualsCaseInsensitiveASCII(origin_url_.scheme(),
873                                        url::kHttpsScheme) &&
874       session_->IsQuicEnabled() && proxy_info_.is_direct() &&
875       !session_->http_server_properties()->IsAlternativeServiceBroken(
876           GetAlternativeServiceForDnsJob(origin_url_),
877           request_info_.network_anonymization_key);
878 
879   if (is_preconnect_) {
880     // Due to how the socket pools handle priorities and idle sockets, only IDLE
881     // priority currently makes sense for preconnects. The priority for
882     // preconnects is currently ignored (see RequestSocketsForPool()), but could
883     // be used at some point for proxy resolution or something.
884     // Note: When `dns_alpn_h3_job_enabled` is true, we create a
885     // PRECONNECT_DNS_ALPN_H3 job. If no matching HTTPS DNS ALPN records are
886     // received, the PRECONNECT_DNS_ALPN_H3 job will fail with
887     // ERR_DNS_NO_MATCHING_SUPPORTED_ALPN, and `preconnect_backup_job_` will
888     // be started in OnPreconnectsComplete().
889     std::unique_ptr<Job> preconnect_job = job_factory_->CreateJob(
890         this, dns_alpn_h3_job_enabled ? PRECONNECT_DNS_ALPN_H3 : PRECONNECT,
891         session_, request_info_, IDLE, proxy_info_, allowed_bad_certs_,
892         destination, origin_url_, is_websocket_, enable_ip_based_pooling_,
893         net_log_.net_log(), NextProto::kProtoUnknown,
894         quic::ParsedQuicVersion::Unsupported());
895     // When there is an valid alternative service info, and `preconnect_job`
896     // has no existing QUIC session, create a job for the alternative service.
897     if (alternative_service_info_.protocol() != kProtoUnknown &&
898         !preconnect_job->HasAvailableQuicSession()) {
899       GURL alternative_url = CreateAltSvcUrl(
900           origin_url_, alternative_service_info_.GetHostPortPair());
901       RewriteUrlWithHostMappingRules(alternative_url);
902 
903       url::SchemeHostPort alternative_destination =
904           url::SchemeHostPort(alternative_url);
905       ConvertWsToHttp(alternative_destination);
906 
907       main_job_ = job_factory_->CreateJob(
908           this, PRECONNECT, session_, request_info_, IDLE, proxy_info_,
909           allowed_bad_certs_, std::move(alternative_destination), origin_url_,
910           is_websocket_, enable_ip_based_pooling_, session_->net_log(),
911           alternative_service_info_.protocol(), quic_version);
912     } else {
913       main_job_ = std::move(preconnect_job);
914 
915       if (dns_alpn_h3_job_enabled) {
916         preconnect_backup_job_ = job_factory_->CreateJob(
917             this, PRECONNECT, session_, request_info_, IDLE, proxy_info_,
918             allowed_bad_certs_, std::move(destination), origin_url_,
919             is_websocket_, enable_ip_based_pooling_, net_log_.net_log(),
920             NextProto::kProtoUnknown, quic::ParsedQuicVersion::Unsupported());
921       }
922     }
923     main_job_->Preconnect(num_streams_);
924     return OK;
925   }
926   main_job_ = job_factory_->CreateJob(
927       this, MAIN, session_, request_info_, priority_, proxy_info_,
928       allowed_bad_certs_, std::move(destination), origin_url_, is_websocket_,
929       enable_ip_based_pooling_, net_log_.net_log(), NextProto::kProtoUnknown,
930       quic::ParsedQuicVersion::Unsupported());
931 
932   // Alternative Service can only be set for HTTPS requests while Alternative
933   // Proxy is set for HTTP requests.
934   // The main job may use HTTP/3 if the origin is specified in
935   // `--origin-to-force-quic-on` switch. In that case, do not create
936   // `alternative_job_` and `dns_alpn_h3_job_`.
937   if ((alternative_service_info_.protocol() != kProtoUnknown) &&
938       !main_job_->using_quic()) {
939     DCHECK(origin_url_.SchemeIs(url::kHttpsScheme));
940     DCHECK(!is_websocket_);
941     DVLOG(1) << "Selected alternative service (host: "
942              << alternative_service_info_.GetHostPortPair().host()
943              << " port: " << alternative_service_info_.GetHostPortPair().port()
944              << " version: " << quic_version << ")";
945 
946     GURL alternative_url = CreateAltSvcUrl(
947         origin_url_, alternative_service_info_.GetHostPortPair());
948     RewriteUrlWithHostMappingRules(alternative_url);
949 
950     url::SchemeHostPort alternative_destination =
951         url::SchemeHostPort(alternative_url);
952     ConvertWsToHttp(alternative_destination);
953 
954     alternative_job_ = job_factory_->CreateJob(
955         this, ALTERNATIVE, session_, request_info_, priority_, proxy_info_,
956         allowed_bad_certs_, std::move(alternative_destination), origin_url_,
957         is_websocket_, enable_ip_based_pooling_, net_log_.net_log(),
958         alternative_service_info_.protocol(), quic_version);
959   }
960 
961   if (dns_alpn_h3_job_enabled && !main_job_->using_quic()) {
962     DCHECK(!is_websocket_);
963     url::SchemeHostPort dns_alpn_h3_destination =
964         url::SchemeHostPort(origin_url_);
965     dns_alpn_h3_job_ = job_factory_->CreateJob(
966         this, DNS_ALPN_H3, session_, request_info_, priority_, proxy_info_,
967         allowed_bad_certs_, std::move(dns_alpn_h3_destination), origin_url_,
968         is_websocket_, enable_ip_based_pooling_, net_log_.net_log(),
969         NextProto::kProtoUnknown, quic::ParsedQuicVersion::Unsupported());
970   }
971 
972   ClearInappropriateJobs();
973 
974   if (main_job_ && (alternative_job_ ||
975                     (dns_alpn_h3_job_ &&
976                      (!main_job_->TargettedSocketGroupHasActiveSocket() &&
977                       !main_job_->HasAvailableSpdySession())))) {
978     // We don't block |main_job_| when |alternative_job_| doesn't exists and
979     // |dns_alpn_h3_job_| exists and an active socket is available for
980     // |main_job_|. This is intended to make the fallback logic faster.
981     main_job_is_blocked_ = true;
982   }
983 
984   if (alternative_job_) {
985     alternative_job_->Start(request_->stream_type());
986   }
987 
988   if (dns_alpn_h3_job_) {
989     dns_alpn_h3_job_->Start(request_->stream_type());
990   }
991 
992   if (main_job_) {
993     main_job_->Start(request_->stream_type());
994   }
995   return OK;
996 }
997 
ClearInappropriateJobs()998 void HttpStreamFactory::JobController::ClearInappropriateJobs() {
999   if (dns_alpn_h3_job_ && dns_alpn_h3_job_->HasAvailableQuicSession()) {
1000     // Clear |main_job_| and |alternative_job_| here not to start them when
1001     // there is an active session available for |dns_alpn_h3_job_|.
1002     main_job_.reset();
1003     alternative_job_.reset();
1004   }
1005 
1006   if (alternative_job_ && dns_alpn_h3_job_ &&
1007       (alternative_job_->HasAvailableQuicSession() ||
1008        (alternative_service_info_.alternative_service() ==
1009         GetAlternativeServiceForDnsJob(http_request_info_url_)))) {
1010     // Clear |dns_alpn_h3_job_|, when there is an active session available for
1011     // |alternative_job_| or |alternative_job_| was created for the same
1012     // destination.
1013     dns_alpn_h3_job_.reset();
1014   }
1015 }
1016 
BindJob(Job * job)1017 void HttpStreamFactory::JobController::BindJob(Job* job) {
1018   DCHECK(request_);
1019   DCHECK(job);
1020   DCHECK(job == alternative_job_.get() || job == main_job_.get() ||
1021          job == dns_alpn_h3_job_.get());
1022   DCHECK(!job_bound_);
1023   DCHECK(!bound_job_);
1024 
1025   job_bound_ = true;
1026   bound_job_ = job;
1027 
1028   request_->net_log().AddEventReferencingSource(
1029       NetLogEventType::HTTP_STREAM_REQUEST_BOUND_TO_JOB,
1030       job->net_log().source());
1031   job->net_log().AddEventReferencingSource(
1032       NetLogEventType::HTTP_STREAM_JOB_BOUND_TO_REQUEST,
1033       request_->net_log().source());
1034 
1035   OrphanUnboundJob();
1036 }
1037 
OrphanUnboundJob()1038 void HttpStreamFactory::JobController::OrphanUnboundJob() {
1039   DCHECK(request_);
1040   DCHECK(bound_job_);
1041 
1042   if (bound_job_->job_type() == MAIN) {
1043     // Allow |alternative_job_| and |dns_alpn_h3_job_| to run to completion,
1044     // rather than resetting them to check if there is any broken alternative
1045     // service to report. OnOrphanedJobComplete() will clean up |this| when the
1046     // jobs complete.
1047     if (alternative_job_) {
1048       DCHECK(!is_websocket_);
1049       alternative_job_->Orphan();
1050     }
1051     if (dns_alpn_h3_job_) {
1052       DCHECK(!is_websocket_);
1053       dns_alpn_h3_job_->Orphan();
1054     }
1055     return;
1056   }
1057 
1058   if (bound_job_->job_type() == ALTERNATIVE) {
1059     if (!alternative_job_failed_on_default_network_ && !dns_alpn_h3_job_) {
1060       // |request_| is bound to the alternative job and the alternative job
1061       // succeeds on the default network, and there is no DNS alt job. This
1062       // means that the main job is no longer needed, so cancel it now. Pending
1063       // ConnectJobs will return established sockets to socket pools if
1064       // applicable.
1065       // https://crbug.com/757548.
1066       // The main job still needs to run if the alternative job succeeds on the
1067       // alternate network in order to figure out whether QUIC should be marked
1068       // as broken until the default network changes. And also the main job
1069       // still needs to run if the DNS alt job exists to figure out whether
1070       // the DNS alpn service is broken.
1071       DCHECK(!main_job_ || (alternative_job_net_error_ == OK));
1072       main_job_.reset();
1073     }
1074     // Allow |dns_alpn_h3_job_| to run to completion, rather than resetting
1075     // it to check if there is any broken alternative service to report.
1076     // OnOrphanedJobComplete() will clean up |this| when the job completes.
1077     if (dns_alpn_h3_job_) {
1078       DCHECK(!is_websocket_);
1079       dns_alpn_h3_job_->Orphan();
1080     }
1081   }
1082   if (bound_job_->job_type() == DNS_ALPN_H3) {
1083     if (!dns_alpn_h3_job_failed_on_default_network_ && !alternative_job_) {
1084       DCHECK(!main_job_ || (dns_alpn_h3_job_net_error_ == OK));
1085       main_job_.reset();
1086     }
1087     // Allow |alternative_job_| to run to completion, rather than resetting
1088     // it to check if there is any broken alternative service to report.
1089     // OnOrphanedJobComplete() will clean up |this| when the job completes.
1090     if (alternative_job_) {
1091       DCHECK(!is_websocket_);
1092       alternative_job_->Orphan();
1093     }
1094   }
1095 }
1096 
OnJobSucceeded(Job * job)1097 void HttpStreamFactory::JobController::OnJobSucceeded(Job* job) {
1098   DCHECK(job);
1099   if (!bound_job_) {
1100     BindJob(job);
1101     return;
1102   }
1103 }
1104 
MarkRequestComplete(Job * job)1105 void HttpStreamFactory::JobController::MarkRequestComplete(Job* job) {
1106   if (request_) {
1107     AlternateProtocolUsage alternate_protocol_usage =
1108         CalculateAlternateProtocolUsage(job);
1109     request_->Complete(job->negotiated_protocol(), alternate_protocol_usage);
1110     ReportAlternateProtocolUsage(alternate_protocol_usage,
1111                                  HasGoogleHost(job->origin_url()));
1112   }
1113 }
1114 
MaybeReportBrokenAlternativeService(const AlternativeService & alt_service,int alt_job_net_error,bool alt_job_failed_on_default_network,const std::string & histogram_name_for_failure)1115 void HttpStreamFactory::JobController::MaybeReportBrokenAlternativeService(
1116     const AlternativeService& alt_service,
1117     int alt_job_net_error,
1118     bool alt_job_failed_on_default_network,
1119     const std::string& histogram_name_for_failure) {
1120   // If alternative job succeeds on the default network, no brokenness to
1121   // report.
1122   if (alt_job_net_error == OK && !alt_job_failed_on_default_network) {
1123     return;
1124   }
1125 
1126   // No brokenness to report if the main job fails.
1127   if (main_job_net_error_ != OK) {
1128     return;
1129   }
1130 
1131   // No need to record DNS_NO_MATCHING_SUPPORTED_ALPN error.
1132   if (alt_job_net_error == ERR_DNS_NO_MATCHING_SUPPORTED_ALPN) {
1133     return;
1134   }
1135 
1136   if (alt_job_failed_on_default_network && alt_job_net_error == OK) {
1137     // Alternative job failed on the default network but succeeds on the
1138     // non-default network, mark alternative service broken until the default
1139     // network changes.
1140     session_->http_server_properties()
1141         ->MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
1142             alt_service, request_info_.network_anonymization_key);
1143     return;
1144   }
1145 
1146   if (alt_job_net_error == ERR_NETWORK_CHANGED ||
1147       alt_job_net_error == ERR_INTERNET_DISCONNECTED ||
1148       (alt_job_net_error == ERR_NAME_NOT_RESOLVED &&
1149        http_request_info_url_.host() == alt_service.host)) {
1150     // No need to mark alternative service as broken.
1151     return;
1152   }
1153 
1154   // Report brokenness if alternative job failed.
1155   base::UmaHistogramSparse(histogram_name_for_failure, -alt_job_net_error);
1156 
1157   HistogramBrokenAlternateProtocolLocation(
1158       BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_JOB_ALT);
1159   session_->http_server_properties()->MarkAlternativeServiceBroken(
1160       alt_service, request_info_.network_anonymization_key);
1161 }
1162 
MaybeNotifyFactoryOfCompletion()1163 void HttpStreamFactory::JobController::MaybeNotifyFactoryOfCompletion() {
1164   if (switched_to_http_stream_pool_) {
1165     factory_->OnJobControllerComplete(this);
1166     return;
1167   }
1168 
1169   if (main_job_ || alternative_job_ || dns_alpn_h3_job_) {
1170     return;
1171   }
1172 
1173   // All jobs are gone.
1174   // Report brokenness for the alternate jobs if apply.
1175   MaybeReportBrokenAlternativeService(
1176       alternative_service_info_.alternative_service(),
1177       alternative_job_net_error_, alternative_job_failed_on_default_network_,
1178       "Net.AlternateServiceFailed");
1179   // Report for the DNS alt job if apply.
1180   MaybeReportBrokenAlternativeService(
1181       GetAlternativeServiceForDnsJob(http_request_info_url_),
1182       dns_alpn_h3_job_net_error_, dns_alpn_h3_job_failed_on_default_network_,
1183       "Net.AlternateServiceForDnsAlpnH3Failed");
1184 
1185   // Reset error status for Jobs after reporting brokenness to avoid redundant
1186   // reporting.
1187   ResetErrorStatusForJobs();
1188 
1189   if (request_) {
1190     return;
1191   }
1192   DCHECK(!bound_job_);
1193   factory_->OnJobControllerComplete(this);
1194 }
1195 
NotifyRequestFailed(int rv)1196 void HttpStreamFactory::JobController::NotifyRequestFailed(int rv) {
1197   if (!request_) {
1198     return;
1199   }
1200   delegate_->OnStreamFailed(rv, NetErrorDetails(), ProxyInfo(),
1201                             ResolveErrorInfo());
1202 }
1203 
RewriteUrlWithHostMappingRules(GURL & url) const1204 void HttpStreamFactory::JobController::RewriteUrlWithHostMappingRules(
1205     GURL& url) const {
1206   session_->params().host_mapping_rules.RewriteUrl(url);
1207 }
1208 
DuplicateUrlWithHostMappingRules(const GURL & url) const1209 GURL HttpStreamFactory::JobController::DuplicateUrlWithHostMappingRules(
1210     const GURL& url) const {
1211   GURL copy = url;
1212   RewriteUrlWithHostMappingRules(copy);
1213   return copy;
1214 }
1215 
1216 AlternativeServiceInfo
GetAlternativeServiceInfoFor(const GURL & http_request_info_url,const StreamRequestInfo & request_info,HttpStreamRequest::Delegate * delegate,HttpStreamRequest::StreamType stream_type)1217 HttpStreamFactory::JobController::GetAlternativeServiceInfoFor(
1218     const GURL& http_request_info_url,
1219     const StreamRequestInfo& request_info,
1220     HttpStreamRequest::Delegate* delegate,
1221     HttpStreamRequest::StreamType stream_type) {
1222   if (!enable_alternative_services_) {
1223     return AlternativeServiceInfo();
1224   }
1225 
1226   AlternativeServiceInfo alternative_service_info =
1227       GetAlternativeServiceInfoInternal(http_request_info_url, request_info,
1228                                         delegate, stream_type);
1229   AlternativeServiceType type;
1230   if (alternative_service_info.protocol() == kProtoUnknown) {
1231     type = NO_ALTERNATIVE_SERVICE;
1232   } else if (alternative_service_info.protocol() == kProtoQUIC) {
1233     if (http_request_info_url.host_piece() ==
1234         alternative_service_info.alternative_service().host) {
1235       type = QUIC_SAME_DESTINATION;
1236     } else {
1237       type = QUIC_DIFFERENT_DESTINATION;
1238     }
1239   } else {
1240     if (http_request_info_url.host_piece() ==
1241         alternative_service_info.alternative_service().host) {
1242       type = NOT_QUIC_SAME_DESTINATION;
1243     } else {
1244       type = NOT_QUIC_DIFFERENT_DESTINATION;
1245     }
1246   }
1247   UMA_HISTOGRAM_ENUMERATION("Net.AlternativeServiceTypeForRequest", type,
1248                             MAX_ALTERNATIVE_SERVICE_TYPE);
1249   return alternative_service_info;
1250 }
1251 
1252 AlternativeServiceInfo
GetAlternativeServiceInfoInternal(const GURL & http_request_info_url,const StreamRequestInfo & request_info,HttpStreamRequest::Delegate * delegate,HttpStreamRequest::StreamType stream_type)1253 HttpStreamFactory::JobController::GetAlternativeServiceInfoInternal(
1254     const GURL& http_request_info_url,
1255     const StreamRequestInfo& request_info,
1256     HttpStreamRequest::Delegate* delegate,
1257     HttpStreamRequest::StreamType stream_type) {
1258   GURL original_url = http_request_info_url;
1259 
1260   if (!original_url.SchemeIs(url::kHttpsScheme)) {
1261     return AlternativeServiceInfo();
1262   }
1263 
1264   HttpServerProperties& http_server_properties =
1265       *session_->http_server_properties();
1266   const AlternativeServiceInfoVector alternative_service_info_vector =
1267       http_server_properties.GetAlternativeServiceInfos(
1268           url::SchemeHostPort(original_url),
1269           request_info.network_anonymization_key);
1270   if (alternative_service_info_vector.empty()) {
1271     return AlternativeServiceInfo();
1272   }
1273 
1274   bool quic_advertised = false;
1275   bool quic_all_broken = true;
1276 
1277   // First alternative service that is not marked as broken.
1278   AlternativeServiceInfo first_alternative_service_info;
1279 
1280   bool is_any_broken = false;
1281   for (const AlternativeServiceInfo& alternative_service_info :
1282        alternative_service_info_vector) {
1283     DCHECK(IsAlternateProtocolValid(alternative_service_info.protocol()));
1284     if (!quic_advertised && alternative_service_info.protocol() == kProtoQUIC) {
1285       quic_advertised = true;
1286     }
1287     const bool is_broken = http_server_properties.IsAlternativeServiceBroken(
1288         alternative_service_info.alternative_service(),
1289         request_info.network_anonymization_key);
1290     net_log_.AddEvent(
1291         NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_ALT_SVC_FOUND, [&] {
1292           return NetLogAltSvcParams(&alternative_service_info, is_broken);
1293         });
1294     if (is_broken) {
1295       if (!is_any_broken) {
1296         // Only log the broken alternative service once per request.
1297         is_any_broken = true;
1298         HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN,
1299                                         HasGoogleHost(original_url));
1300       }
1301       continue;
1302     }
1303 
1304     // Some shared unix systems may have user home directories (like
1305     // http://foo.com/~mike) which allow users to emit headers.  This is a bad
1306     // idea already, but with Alternate-Protocol, it provides the ability for a
1307     // single user on a multi-user system to hijack the alternate protocol.
1308     // These systems also enforce ports <1024 as restricted ports.  So don't
1309     // allow protocol upgrades to user-controllable ports.
1310     const int kUnrestrictedPort = 1024;
1311     if (!session_->params().enable_user_alternate_protocol_ports &&
1312         (alternative_service_info.alternative_service().port >=
1313              kUnrestrictedPort &&
1314          original_url.EffectiveIntPort() < kUnrestrictedPort)) {
1315       continue;
1316     }
1317 
1318     if (alternative_service_info.protocol() == kProtoHTTP2) {
1319       if (!session_->params().enable_http2_alternative_service) {
1320         continue;
1321       }
1322 
1323       // Cache this entry if we don't have a non-broken Alt-Svc yet.
1324       if (first_alternative_service_info.protocol() == kProtoUnknown) {
1325         first_alternative_service_info = alternative_service_info;
1326       }
1327       continue;
1328     }
1329 
1330     DCHECK_EQ(kProtoQUIC, alternative_service_info.protocol());
1331     quic_all_broken = false;
1332     if (!session_->IsQuicEnabled()) {
1333       continue;
1334     }
1335 
1336     if (!original_url.SchemeIs(url::kHttpsScheme)) {
1337       continue;
1338     }
1339 
1340     // If there is no QUIC version in the advertised versions that is
1341     // supported, ignore this entry.
1342     if (SelectQuicVersion(alternative_service_info.advertised_versions()) ==
1343         quic::ParsedQuicVersion::Unsupported()) {
1344       continue;
1345     }
1346 
1347     // Check whether there is an existing QUIC session to use for this origin.
1348     GURL mapped_origin = original_url;
1349     RewriteUrlWithHostMappingRules(mapped_origin);
1350     QuicSessionKey session_key(
1351         HostPortPair::FromURL(mapped_origin), request_info.privacy_mode,
1352         proxy_info_.proxy_chain(), SessionUsage::kDestination,
1353         request_info.socket_tag, request_info.network_anonymization_key,
1354         request_info.secure_dns_policy, /*require_dns_https_alpn=*/false);
1355 
1356     GURL destination = CreateAltSvcUrl(
1357         original_url, alternative_service_info.GetHostPortPair());
1358     if (session_key.host() != destination.host_piece() &&
1359         !session_->context().quic_context->params()->allow_remote_alt_svc) {
1360       continue;
1361     }
1362     RewriteUrlWithHostMappingRules(destination);
1363 
1364     if (session_->quic_session_pool()->CanUseExistingSession(
1365             session_key, url::SchemeHostPort(destination))) {
1366       return alternative_service_info;
1367     }
1368 
1369     if (!IsQuicAllowedForHost(destination.host())) {
1370       continue;
1371     }
1372 
1373     // Cache this entry if we don't have a non-broken Alt-Svc yet.
1374     if (first_alternative_service_info.protocol() == kProtoUnknown) {
1375       first_alternative_service_info = alternative_service_info;
1376     }
1377   }
1378 
1379   // Ask delegate to mark QUIC as broken for the origin.
1380   if (quic_advertised && quic_all_broken && delegate != nullptr) {
1381     delegate->OnQuicBroken();
1382   }
1383 
1384   return first_alternative_service_info;
1385 }
1386 
SelectQuicVersion(const quic::ParsedQuicVersionVector & advertised_versions)1387 quic::ParsedQuicVersion HttpStreamFactory::JobController::SelectQuicVersion(
1388     const quic::ParsedQuicVersionVector& advertised_versions) {
1389   return session_->context().quic_context->SelectQuicVersion(
1390       advertised_versions);
1391 }
1392 
ReportAlternateProtocolUsage(AlternateProtocolUsage alternate_protocol_usage,bool is_google_host) const1393 void HttpStreamFactory::JobController::ReportAlternateProtocolUsage(
1394     AlternateProtocolUsage alternate_protocol_usage,
1395     bool is_google_host) const {
1396   DCHECK_LT(alternate_protocol_usage, ALTERNATE_PROTOCOL_USAGE_MAX);
1397   HistogramAlternateProtocolUsage(alternate_protocol_usage, is_google_host);
1398 }
1399 
IsJobOrphaned(Job * job) const1400 bool HttpStreamFactory::JobController::IsJobOrphaned(Job* job) const {
1401   return !request_ || (job_bound_ && bound_job_ != job);
1402 }
1403 
1404 AlternateProtocolUsage
CalculateAlternateProtocolUsage(Job * job) const1405 HttpStreamFactory::JobController::CalculateAlternateProtocolUsage(
1406     Job* job) const {
1407   if ((main_job_ && alternative_job_) || dns_alpn_h3_job_) {
1408     if (job == main_job_.get()) {
1409       return ALTERNATE_PROTOCOL_USAGE_MAIN_JOB_WON_RACE;
1410     }
1411     if (job == alternative_job_.get()) {
1412       if (job->using_existing_quic_session()) {
1413         return ALTERNATE_PROTOCOL_USAGE_NO_RACE;
1414       }
1415       return ALTERNATE_PROTOCOL_USAGE_WON_RACE;
1416     }
1417     if (job == dns_alpn_h3_job_.get()) {
1418       if (job->using_existing_quic_session()) {
1419         return ALTERNATE_PROTOCOL_USAGE_DNS_ALPN_H3_JOB_WON_WITHOUT_RACE;
1420       }
1421       return ALTERNATE_PROTOCOL_USAGE_DNS_ALPN_H3_JOB_WON_RACE;
1422     }
1423   }
1424   // TODO(crbug.com/40232167): Implement better logic to support uncovered
1425   // cases.
1426   return ALTERNATE_PROTOCOL_USAGE_UNSPECIFIED_REASON;
1427 }
1428 
ReconsiderProxyAfterError(Job * job,int error)1429 int HttpStreamFactory::JobController::ReconsiderProxyAfterError(Job* job,
1430                                                                 int error) {
1431   // ReconsiderProxyAfterError() should only be called when the last job fails.
1432   DCHECK_EQ(1, GetJobCount());
1433   DCHECK(!proxy_resolve_request_);
1434 
1435   if (!job->should_reconsider_proxy()) {
1436     return error;
1437   }
1438 
1439   if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
1440     return error;
1441   }
1442 
1443   // Clear client certificates for all proxies in the chain.
1444   // TODO(crbug.com/40284947): client certificates for multi-proxy
1445   // chains are not yet supported, and this is only tested with single-proxy
1446   // chains.
1447   for (auto& proxy_server : proxy_info_.proxy_chain().proxy_servers()) {
1448     if (proxy_server.is_secure_http_like()) {
1449       session_->ssl_client_context()->ClearClientCertificate(
1450           proxy_server.host_port_pair());
1451     }
1452   }
1453 
1454   if (!proxy_info_.Fallback(error, net_log_)) {
1455     // If there is no more proxy to fallback to, fail the transaction
1456     // with the last connection error we got.
1457     return error;
1458   }
1459 
1460   // Abandon all Jobs and start over.
1461   job_bound_ = false;
1462   bound_job_ = nullptr;
1463   dns_alpn_h3_job_.reset();
1464   alternative_job_.reset();
1465   main_job_.reset();
1466   ResetErrorStatusForJobs();
1467   // Also resets states that related to the old main job. In particular,
1468   // cancels |resume_main_job_callback_| so there won't be any delayed
1469   // ResumeMainJob() left in the task queue.
1470   resume_main_job_callback_.Cancel();
1471   main_job_is_resumed_ = false;
1472   main_job_is_blocked_ = false;
1473 
1474   next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
1475   return OK;
1476 }
1477 
IsQuicAllowedForHost(const std::string & host)1478 bool HttpStreamFactory::JobController::IsQuicAllowedForHost(
1479     const std::string& host) {
1480   const base::flat_set<std::string>& host_allowlist =
1481       session_->params().quic_host_allowlist;
1482   if (host_allowlist.empty()) {
1483     return true;
1484   }
1485 
1486   std::string lowered_host = base::ToLowerASCII(host);
1487   return base::Contains(host_allowlist, lowered_host);
1488 }
1489 
SwitchToHttpStreamPool()1490 void HttpStreamFactory::JobController::SwitchToHttpStreamPool() {
1491   CHECK(request_info_.socket_tag == SocketTag());
1492   CHECK_EQ(stream_type_, HttpStreamRequest::HTTP_STREAM);
1493 
1494   switched_to_http_stream_pool_ = true;
1495 
1496   bool disable_cert_network_fetches =
1497       !!(request_info_.load_flags & LOAD_DISABLE_CERT_NETWORK_FETCHES);
1498   url::SchemeHostPort destination(origin_url_);
1499   session_->ApplyTestingFixedPort(destination);
1500   HttpStreamPoolRequestInfo pool_request_info(
1501       std::move(destination), request_info_.privacy_mode,
1502       request_info_.socket_tag, request_info_.network_anonymization_key,
1503       request_info_.secure_dns_policy, disable_cert_network_fetches,
1504       alternative_service_info_, request_info_.is_http1_allowed,
1505       request_info_.load_flags, proxy_info_);
1506   if (is_preconnect_) {
1507     int rv = session_->http_stream_pool()->Preconnect(
1508         std::move(pool_request_info), num_streams_,
1509         base::BindOnce(&JobController::OnPoolPreconnectsComplete,
1510                        ptr_factory_.GetWeakPtr()));
1511     if (rv != ERR_IO_PENDING) {
1512       base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1513           FROM_HERE, base::BindOnce(&JobController::OnPoolPreconnectsComplete,
1514                                     ptr_factory_.GetWeakPtr(), rv));
1515     }
1516     return;
1517   }
1518 
1519   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1520       FROM_HERE,
1521       base::BindOnce(&JobController::CallOnSwitchesToHttpStreamPool,
1522                      ptr_factory_.GetWeakPtr(), std::move(pool_request_info)));
1523 }
1524 
OnPoolPreconnectsComplete(int rv)1525 void HttpStreamFactory::JobController::OnPoolPreconnectsComplete(int rv) {
1526   CHECK(switched_to_http_stream_pool_);
1527   factory_->OnPreconnectsCompleteInternal();
1528   MaybeNotifyFactoryOfCompletion();
1529 }
1530 
CallOnSwitchesToHttpStreamPool(HttpStreamPoolRequestInfo request_info)1531 void HttpStreamFactory::JobController::CallOnSwitchesToHttpStreamPool(
1532     HttpStreamPoolRequestInfo request_info) {
1533   CHECK(request_);
1534   CHECK(delegate_);
1535 
1536   // `request_` and `delegate_` will be reset later.
1537 
1538   delegate_->OnSwitchesToHttpStreamPool(std::move(request_info));
1539 }
1540 
1541 }  // namespace net
1542