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