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