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