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