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