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