Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(347)

Side by Side Diff: net/http/http_stream_factory_impl_job_controller.cc

Issue 1941083002: JobController 1: Adding a new class HttpStreamFactoryImpl::JobController (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address nits Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
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_impl_job_controller.h"
6
7 #include "base/metrics/histogram_macros.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "net/base/host_mapping_rules.h"
11 #include "net/http/bidirectional_stream_impl.h"
12 #include "net/http/transport_security_state.h"
13 #include "net/spdy/spdy_session.h"
14
15 namespace net {
16
17 HttpStreamFactoryImpl::JobController::JobController(
18 HttpStreamFactoryImpl* factory,
19 HttpStreamRequest::Delegate* delegate,
20 HttpNetworkSession* session,
21 JobFactory* job_factory)
22 : factory_(factory),
23 session_(session),
24 job_factory_(job_factory),
25 request_(nullptr),
26 delegate_(delegate),
27 is_preconnect_(false),
28 job_bound_(false),
29 bound_job_(nullptr) {
30 DCHECK(factory);
31 }
32
33 HttpStreamFactoryImpl::JobController::~JobController() {
34 main_job_.reset();
35 alternative_job_.reset();
36 bound_job_ = nullptr;
37 }
38
39 bool HttpStreamFactoryImpl::JobController::for_websockets() {
40 return factory_->for_websockets_;
41 }
42
43 HttpStreamFactoryImpl::Request* HttpStreamFactoryImpl::JobController::Start(
44 const HttpRequestInfo& request_info,
45 HttpStreamRequest::Delegate* delegate,
46 WebSocketHandshakeStreamBase::CreateHelper*
47 websocket_handshake_stream_create_helper,
48 const BoundNetLog& net_log,
49 HttpStreamRequest::StreamType stream_type,
50 RequestPriority priority,
51 const SSLConfig& server_ssl_config,
52 const SSLConfig& proxy_ssl_config) {
53 DCHECK(factory_);
54 DCHECK(!request_);
55
56 request_ = new Request(request_info.url, this, delegate,
57 websocket_handshake_stream_create_helper, net_log,
58 stream_type);
59
60 CreateJobs(request_info, priority, server_ssl_config, proxy_ssl_config,
61 delegate, stream_type, net_log);
62
63 return request_;
64 }
65
66 void HttpStreamFactoryImpl::JobController::Preconnect(
67 int num_streams,
68 const HttpRequestInfo& request_info,
69 const SSLConfig& server_ssl_config,
70 const SSLConfig& proxy_ssl_config) {
71 DCHECK(!main_job_);
72 DCHECK(!alternative_job_);
73
74 is_preconnect_ = true;
75 HostPortPair destination(HostPortPair::FromURL(request_info.url));
76 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
77
78 const AlternativeService alternative_service = GetAlternativeServiceFor(
79 request_info, nullptr, HttpStreamRequest::HTTP_STREAM);
80
81 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
82 if (session_->params().quic_disable_preconnect_if_0rtt &&
83 alternative_service.protocol == QUIC &&
84 session_->quic_stream_factory()->ZeroRTTEnabledFor(QuicServerId(
85 alternative_service.host_port_pair(), request_info.privacy_mode))) {
86 MaybeNotifyFactoryOfCompletion();
87 return;
88 }
89 destination = alternative_service.host_port_pair();
90 ignore_result(ApplyHostMappingRules(request_info.url, &destination));
91 }
92
93 // Due to how the socket pools handle priorities and idle sockets, only IDLE
94 // priority currently makes sense for preconnects. The priority for
95 // preconnects is currently ignored (see RequestSocketsForPool()), but could
96 // be used at some point for proxy resolution or something.
97 main_job_.reset(job_factory_->CreateJob(
98 this, PRECONNECT, session_, request_info, IDLE, server_ssl_config,
99 proxy_ssl_config, destination, origin_url, alternative_service,
100 session_->net_log()));
101 main_job_->Preconnect(num_streams);
102 }
103
104 LoadState HttpStreamFactoryImpl::JobController::GetLoadState() const {
105 DCHECK(request_);
106 DCHECK(main_job_ || alternative_job_);
107 if (bound_job_)
108 return bound_job_->GetLoadState();
109
110 // Just pick the first one.
111 return main_job_ ? main_job_->GetLoadState()
112 : alternative_job_->GetLoadState();
113 }
114
115 void HttpStreamFactoryImpl::JobController::OnRequestComplete() {
116 CancelJobs();
117 DCHECK(request_);
118 request_ = nullptr;
119 if (bound_job_) {
120 if (bound_job_->job_type() == MAIN) {
121 main_job_.reset();
122 } else {
123 DCHECK(bound_job_->job_type() == ALTERNATIVE);
124 alternative_job_.reset();
125 }
126 bound_job_ = nullptr;
127 }
128 MaybeNotifyFactoryOfCompletion();
129 }
130
131 int HttpStreamFactoryImpl::JobController::RestartTunnelWithProxyAuth(
132 const AuthCredentials& credentials) {
133 DCHECK(bound_job_);
134 return bound_job_->RestartTunnelWithProxyAuth(credentials);
135 }
136
137 void HttpStreamFactoryImpl::JobController::SetPriority(
138 RequestPriority priority) {
139 if (main_job_) {
140 main_job_->SetPriority(priority);
141 }
142 if (alternative_job_) {
143 alternative_job_->SetPriority(priority);
144 }
145 }
146
147 void HttpStreamFactoryImpl::JobController::OnStreamReady(
148 Job* job,
149 const SSLConfig& used_ssl_config,
150 const ProxyInfo& used_proxy_info) {
151 DCHECK(job);
152
153 if (job_bound_ && bound_job_ != job) {
154 // We have bound a job to the associated Request, |job| has been orphaned.
155 OnOrphanedJobComplete(job);
156 return;
157 }
158 std::unique_ptr<HttpStream> stream = job->ReleaseStream();
159 DCHECK(stream);
160
161 MarkRequestComplete(job->was_npn_negotiated(), job->protocol_negotiated(),
162 job->using_spdy());
163
164 if (!request_)
165 return;
166 DCHECK(!factory_->for_websockets_);
167 DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type());
168 OnJobSucceeded(job);
169 request_->OnStreamReady(used_ssl_config, used_proxy_info, stream.release());
170 }
171
172 void HttpStreamFactoryImpl::JobController::OnBidirectionalStreamImplReady(
173 Job* job,
174 const SSLConfig& used_ssl_config,
175 const ProxyInfo& used_proxy_info) {
176 DCHECK(job);
177
178 if (job_bound_ && bound_job_ != job) {
179 // We have bound a job to the associated Request, |job| has been orphaned.
180 OnOrphanedJobComplete(job);
181 return;
182 }
183
184 MarkRequestComplete(job->was_npn_negotiated(), job->protocol_negotiated(),
185 job->using_spdy());
186
187 if (!request_)
188 return;
189 std::unique_ptr<BidirectionalStreamImpl> stream =
190 job->ReleaseBidirectionalStream();
191 DCHECK(stream);
192 DCHECK(!factory_->for_websockets_);
193 DCHECK_EQ(HttpStreamRequest::BIDIRECTIONAL_STREAM, request_->stream_type());
194
195 OnJobSucceeded(job);
196 request_->OnBidirectionalStreamImplReady(used_ssl_config, used_proxy_info,
197 stream.release());
198 }
199
200 void HttpStreamFactoryImpl::JobController::OnWebSocketHandshakeStreamReady(
201 Job* job,
202 const SSLConfig& used_ssl_config,
203 const ProxyInfo& used_proxy_info,
204 WebSocketHandshakeStreamBase* stream) {
205 DCHECK(job);
206
207 MarkRequestComplete(job->was_npn_negotiated(), job->protocol_negotiated(),
208 job->using_spdy());
209
210 if (!request_)
211 return;
212 DCHECK(factory_->for_websockets_);
213 DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type());
214 DCHECK(stream);
215
216 OnJobSucceeded(job);
217 request_->OnWebSocketHandshakeStreamReady(used_ssl_config, used_proxy_info,
218 stream);
219 }
220
221 void HttpStreamFactoryImpl::JobController::OnStreamFailed(
222 Job* job,
223 int status,
224 const SSLConfig& used_ssl_config,
225 SSLFailureState ssl_failure_state) {
226 if (job_bound_ && bound_job_ != job) {
227 // We have bound a job to the associated Request, |job| has been orphaned.
228 OnOrphanedJobComplete(job);
229 return;
230 }
231
232 if (!request_)
233 return;
234 DCHECK_NE(OK, status);
235 DCHECK(job);
236
237 if (!bound_job_) {
238 if (main_job_ && alternative_job_) {
239 // Hey, we've got other jobs! Maybe one of them will succeed, let's just
240 // ignore this failure.
241 factory_->request_map_.erase(job);
242 // Notify all the other jobs that this one failed.
243 if (job->job_type() == MAIN) {
244 alternative_job_->MarkOtherJobComplete(*job);
245 main_job_.reset();
246 } else {
247 DCHECK(job->job_type() == ALTERNATIVE);
248 main_job_->MarkOtherJobComplete(*job);
249 alternative_job_.reset();
250 }
251 return;
252 } else {
253 BindJob(job);
254 }
255 }
256
257 request_->OnStreamFailed(status, used_ssl_config, ssl_failure_state);
258 }
259
260 void HttpStreamFactoryImpl::JobController::OnCertificateError(
261 Job* job,
262 int status,
263 const SSLConfig& used_ssl_config,
264 const SSLInfo& ssl_info) {
265 if (job_bound_ && bound_job_ != job) {
266 // We have bound a job to the associated Request, |job| has been orphaned.
267 OnOrphanedJobComplete(job);
268 return;
269 }
270
271 if (!request_)
272 return;
273 DCHECK_NE(OK, status);
274 if (!bound_job_)
275 BindJob(job);
276
277 request_->OnCertificateError(status, used_ssl_config, ssl_info);
278 }
279
280 void HttpStreamFactoryImpl::JobController::OnHttpsProxyTunnelResponse(
281 Job* job,
282 const HttpResponseInfo& response_info,
283 const SSLConfig& used_ssl_config,
284 const ProxyInfo& used_proxy_info,
285 HttpStream* stream) {
286 if (job_bound_ && bound_job_ != job) {
287 // We have bound a job to the associated Request, |job| has been orphaned.
288 OnOrphanedJobComplete(job);
289 return;
290 }
291
292 if (!bound_job_)
293 BindJob(job);
294 if (!request_)
295 return;
296 request_->OnHttpsProxyTunnelResponse(response_info, used_ssl_config,
297 used_proxy_info, stream);
298 }
299
300 void HttpStreamFactoryImpl::JobController::OnNeedsClientAuth(
301 Job* job,
302 const SSLConfig& used_ssl_config,
303 SSLCertRequestInfo* cert_info) {
304 if (job_bound_ && bound_job_ != job) {
305 // We have bound a job to the associated Request, |job| has been orphaned.
306 OnOrphanedJobComplete(job);
307 return;
308 }
309 if (!request_)
310 return;
311 if (!bound_job_)
312 BindJob(job);
313
314 request_->OnNeedsClientAuth(used_ssl_config, cert_info);
315 }
316
317 void HttpStreamFactoryImpl::JobController::OnNeedsProxyAuth(
318 Job* job,
319 const HttpResponseInfo& proxy_response,
320 const SSLConfig& used_ssl_config,
321 const ProxyInfo& used_proxy_info,
322 HttpAuthController* auth_controller) {
323 if (job_bound_ && bound_job_ != job) {
324 // We have bound a job to the associated Request, |job| has been orphaned.
325 OnOrphanedJobComplete(job);
326 return;
327 }
328
329 if (!request_)
330 return;
331 if (!bound_job_)
332 BindJob(job);
333 request_->OnNeedsProxyAuth(proxy_response, used_ssl_config, used_proxy_info,
334 auth_controller);
335 }
336
337 void HttpStreamFactoryImpl::JobController::OnNewSpdySessionReady(
338 Job* job,
339 const base::WeakPtr<SpdySession>& spdy_session,
340 bool direct) {
341 DCHECK(job);
342 DCHECK(job->using_spdy());
343
344 bool is_job_orphaned = job_bound_ && bound_job_ != job;
345
346 // Cache these values in case the job gets deleted.
347 const SSLConfig used_ssl_config = job->server_ssl_config();
348 const ProxyInfo used_proxy_info = job->proxy_info();
349 const bool was_npn_negotiated = job->was_npn_negotiated();
350 const NextProto protocol_negotiated = job->protocol_negotiated();
351 const bool using_spdy = job->using_spdy();
352 const BoundNetLog net_log = job->net_log();
353
354 // Cache this so we can still use it if the JobController is deleted.
355 HttpStreamFactoryImpl* factory = factory_;
356
357 // Notify |request_|.
358 if (!is_preconnect_ && !is_job_orphaned) {
359 DCHECK(request_);
360
361 // The first case is the usual case.
362 if (!job_bound_) {
363 BindJob(job);
364 }
365
366 MarkRequestComplete(was_npn_negotiated, protocol_negotiated, using_spdy);
367
368 std::unique_ptr<HttpStream> stream;
369 std::unique_ptr<BidirectionalStreamImpl> bidirectional_stream_impl;
370
371 if (for_websockets()) {
372 // TODO(ricea): Re-instate this code when WebSockets over SPDY is
373 // implemented.
374 NOTREACHED();
375 } else if (job->stream_type() == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
376 bidirectional_stream_impl = job->ReleaseBidirectionalStream();
377 DCHECK(bidirectional_stream_impl);
378 delegate_->OnBidirectionalStreamImplReady(
379 used_ssl_config, used_proxy_info,
380 bidirectional_stream_impl.release());
381 } else {
382 stream = job->ReleaseStream();
383 DCHECK(stream);
384 delegate_->OnStreamReady(used_ssl_config, used_proxy_info,
385 stream.release());
386 }
387 }
388
389 // Notify |factory_|. |request_| and |bounded_job_| might be deleted already.
390 if (spdy_session && spdy_session->IsAvailable()) {
391 factory->OnNewSpdySessionReady(spdy_session, direct, used_ssl_config,
392 used_proxy_info, was_npn_negotiated,
393 protocol_negotiated, using_spdy, net_log);
394 }
395 if (is_job_orphaned) {
396 OnOrphanedJobComplete(job);
397 }
398 }
399
400 void HttpStreamFactoryImpl::JobController::OnPreconnectsComplete(Job* job) {
401 DCHECK_EQ(main_job_.get(), job);
402 main_job_.reset();
403 factory_->OnPreconnectsCompleteInternal();
404 MaybeNotifyFactoryOfCompletion();
405 }
406
407 void HttpStreamFactoryImpl::JobController::OnOrphanedJobComplete(
408 const Job* job) {
409 if (job->job_type() == MAIN) {
410 DCHECK_EQ(main_job_.get(), job);
411 main_job_.reset();
412 } else {
413 DCHECK_EQ(alternative_job_.get(), job);
414 alternative_job_.reset();
415 }
416
417 MaybeNotifyFactoryOfCompletion();
418 }
419
420 void HttpStreamFactoryImpl::JobController::AddConnectionAttemptsToRequest(
421 Job* job,
422 const ConnectionAttempts& attempts) {
423 if (is_preconnect_ || (job_bound_ && bound_job_ != job))
424 return;
425
426 DCHECK(request_);
427 request_->AddConnectionAttempts(attempts);
428 }
429
430 void HttpStreamFactoryImpl::JobController::SetSpdySessionKey(
431 Job* job,
432 const SpdySessionKey& spdy_session_key) {
433 if (is_preconnect_ || (job_bound_ && bound_job_ != job))
434 return;
435
436 DCHECK(request_);
437 if (!request_->HasSpdySessionKey()) {
438 RequestSet& request_set =
439 factory_->spdy_session_request_map_[spdy_session_key];
440 DCHECK(!ContainsKey(request_set, request_));
441 request_set.insert(request_);
442 request_->SetSpdySessionKey(spdy_session_key);
443 }
444 }
445
446 void HttpStreamFactoryImpl::JobController::
447 RemoveRequestFromSpdySessionRequestMapForJob(Job* job) {
448 if (is_preconnect_ || (job_bound_ && bound_job_ != job))
449 return;
450 DCHECK(request_);
451
452 RemoveRequestFromSpdySessionRequestMap();
453 }
454
455 void HttpStreamFactoryImpl::JobController::
456 RemoveRequestFromSpdySessionRequestMap() {
457 const SpdySessionKey* spdy_session_key = request_->spdy_session_key();
458 if (spdy_session_key) {
459 SpdySessionRequestMap& spdy_session_request_map =
460 factory_->spdy_session_request_map_;
461 DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key));
462 RequestSet& request_set = spdy_session_request_map[*spdy_session_key];
463 DCHECK(ContainsKey(request_set, request_));
464 request_set.erase(request_);
465 if (request_set.empty())
466 spdy_session_request_map.erase(*spdy_session_key);
467 request_->ResetSpdySessionKey();
468 }
469 }
470
471 const BoundNetLog* HttpStreamFactoryImpl::JobController::GetNetLog(
472 Job* job) const {
473 if (is_preconnect_ || (job_bound_ && bound_job_ != job))
474 return nullptr;
475 DCHECK(request_);
476 return &request_->net_log();
477 }
478
479 WebSocketHandshakeStreamBase::CreateHelper* HttpStreamFactoryImpl::
480 JobController::websocket_handshake_stream_create_helper() {
481 DCHECK(request_);
482 return request_->websocket_handshake_stream_create_helper();
483 }
484
485 void HttpStreamFactoryImpl::JobController::CreateJobs(
486 const HttpRequestInfo& request_info,
487 RequestPriority priority,
488 const SSLConfig& server_ssl_config,
489 const SSLConfig& proxy_ssl_config,
490 HttpStreamRequest::Delegate* delegate,
491 HttpStreamRequest::StreamType stream_type,
492 const BoundNetLog& net_log) {
493 DCHECK(!main_job_);
494 DCHECK(!alternative_job_);
495 HostPortPair destination(HostPortPair::FromURL(request_info.url));
496 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
497
498 main_job_.reset(job_factory_->CreateJob(
499 this, MAIN, session_, request_info, priority, server_ssl_config,
500 proxy_ssl_config, destination, origin_url, net_log.net_log()));
501 AttachJob(main_job_.get());
502
503 // Create an alternative job if alternative service is set up for this domain.
504 const AlternativeService alternative_service =
505 GetAlternativeServiceFor(request_info, delegate, stream_type);
506
507 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
508 // Never share connection with other jobs for FTP requests.
509 DVLOG(1) << "Selected alternative service (host: "
510 << alternative_service.host_port_pair().host()
511 << " port: " << alternative_service.host_port_pair().port() << ")";
512
513 DCHECK(!request_info.url.SchemeIs("ftp"));
514 HostPortPair alternative_destination(alternative_service.host_port_pair());
515 ignore_result(
516 ApplyHostMappingRules(request_info.url, &alternative_destination));
517
518 alternative_job_.reset(job_factory_->CreateJob(
519 this, ALTERNATIVE, session_, request_info, priority, server_ssl_config,
520 proxy_ssl_config, alternative_destination, origin_url,
521 alternative_service, net_log.net_log()));
522 AttachJob(alternative_job_.get());
523
524 main_job_->WaitFor(alternative_job_.get());
525 // Make sure to wait until we call WaitFor(), before starting
526 // |alternative_job|, otherwise |alternative_job| will not notify |job|
527 // appropriately.
528 alternative_job_->Start(request_->stream_type());
529 }
530 // Even if |alternative_job| has already finished, it will not have notified
531 // the request yet, since we defer that to the next iteration of the
532 // MessageLoop, so starting |main_job_| is always safe.
533 main_job_->Start(request_->stream_type());
534 }
535
536 void HttpStreamFactoryImpl::JobController::AttachJob(Job* job) {
537 DCHECK(job);
538 factory_->request_map_[job] = request_;
539 }
540
541 void HttpStreamFactoryImpl::JobController::BindJob(Job* job) {
542 DCHECK(request_);
543 DCHECK(job);
544 DCHECK(job == alternative_job_.get() || job == main_job_.get());
545 DCHECK(!job_bound_);
546 DCHECK(!bound_job_);
547
548 job_bound_ = true;
549 bound_job_ = job;
550 factory_->request_map_.erase(job);
551
552 request_->net_log().AddEvent(
553 NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB,
554 job->net_log().source().ToEventParametersCallback());
555 job->net_log().AddEvent(
556 NetLog::TYPE_HTTP_STREAM_JOB_BOUND_TO_REQUEST,
557 request_->net_log().source().ToEventParametersCallback());
558
559 OrphanUnboundJob();
560 }
561
562 void HttpStreamFactoryImpl::JobController::CancelJobs() {
563 DCHECK(request_);
564 RemoveRequestFromSpdySessionRequestMap();
565 if (job_bound_)
566 return;
567 if (alternative_job_) {
568 factory_->request_map_.erase(alternative_job_.get());
569 alternative_job_.reset();
570 }
571 if (main_job_) {
572 factory_->request_map_.erase(main_job_.get());
573 main_job_.reset();
574 }
575 }
576
577 void HttpStreamFactoryImpl::JobController::OrphanUnboundJob() {
578 DCHECK(request_);
579 RemoveRequestFromSpdySessionRequestMap();
580
581 DCHECK(bound_job_);
582 if (bound_job_->job_type() == MAIN && alternative_job_) {
583 factory_->request_map_.erase(alternative_job_.get());
584 alternative_job_->Orphan();
585 } else if (bound_job_->job_type() == ALTERNATIVE && main_job_) {
586 // Orphan main job.
587 factory_->request_map_.erase(main_job_.get());
588 main_job_->Orphan();
589 }
590 }
591
592 void HttpStreamFactoryImpl::JobController::OnJobSucceeded(Job* job) {
593 // |job| should only be nullptr if we're being serviced by a late bound
594 // SpdySession (one that was not created by a job in our |jobs_| set).
595 if (!job) {
596 DCHECK(!bound_job_);
597 // NOTE(willchan): We do *NOT* call OrphanUnboundJob() here. The reason is
598 // because we *WANT* to cancel the unnecessary Jobs from other requests if
599 // another Job completes first.
600 // TODO(mbelshe): Revisit this when we implement ip connection pooling of
601 // SpdySessions. Do we want to orphan the jobs for a different hostname so
602 // they complete? Or do we want to prevent connecting a new SpdySession if
603 // we've already got one available for a different hostname where the ip
604 // address matches up?
605 CancelJobs();
606 return;
607 }
608 if (!bound_job_) {
609 if (main_job_ && alternative_job_) {
610 job->ReportJobSucceededForRequest();
611 // Notify all the other jobs that this one succeeded.
612 if (job->job_type() == MAIN) {
613 alternative_job_->MarkOtherJobComplete(*job);
614 } else {
615 DCHECK(job->job_type() == ALTERNATIVE);
616 main_job_->MarkOtherJobComplete(*job);
617 }
618 }
619 BindJob(job);
620 return;
621 }
622 DCHECK(bound_job_);
623 }
624
625 void HttpStreamFactoryImpl::JobController::MarkRequestComplete(
626 bool was_npn_negotiated,
627 NextProto protocol_negotiated,
628 bool using_spdy) {
629 if (request_)
630 request_->Complete(was_npn_negotiated, protocol_negotiated, using_spdy);
631 }
632
633 void HttpStreamFactoryImpl::JobController::MaybeNotifyFactoryOfCompletion() {
634 if (!request_ && !main_job_ && !alternative_job_) {
635 DCHECK(!bound_job_);
636 factory_->OnJobControllerComplete(this);
637 }
638 }
639
640 GURL HttpStreamFactoryImpl::JobController::ApplyHostMappingRules(
641 const GURL& url,
642 HostPortPair* endpoint) {
643 const HostMappingRules* mapping_rules = session_->params().host_mapping_rules;
644 if (mapping_rules && mapping_rules->RewriteHost(endpoint)) {
645 url::Replacements<char> replacements;
646 const std::string port_str = base::UintToString(endpoint->port());
647 replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size()));
648 replacements.SetHost(endpoint->host().c_str(),
649 url::Component(0, endpoint->host().size()));
650 return url.ReplaceComponents(replacements);
651 }
652 return url;
653 }
654
655 bool HttpStreamFactoryImpl::JobController::IsQuicWhitelistedForHost(
656 const std::string& host) {
657 bool whitelist_needed = false;
658 for (QuicVersion version : session_->params().quic_supported_versions) {
659 if (version <= QUIC_VERSION_30) {
660 whitelist_needed = true;
661 break;
662 }
663 }
664
665 // The QUIC whitelist is not needed in QUIC versions after 30.
666 if (!whitelist_needed)
667 return true;
668
669 if (session_->params().transport_security_state->IsGooglePinnedHost(host))
670 return true;
671
672 return ContainsKey(session_->params().quic_host_whitelist,
673 base::ToLowerASCII(host));
674 }
675
676 AlternativeService
677 HttpStreamFactoryImpl::JobController::GetAlternativeServiceFor(
678 const HttpRequestInfo& request_info,
679 HttpStreamRequest::Delegate* delegate,
680 HttpStreamRequest::StreamType stream_type) {
681 AlternativeService alternative_service =
682 GetAlternativeServiceForInternal(request_info, delegate, stream_type);
683 AlternativeServiceType type;
684 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) {
685 type = NO_ALTERNATIVE_SERVICE;
686 } else if (alternative_service.protocol == QUIC) {
687 if (request_info.url.host() == alternative_service.host) {
688 type = QUIC_SAME_DESTINATION;
689 } else {
690 type = QUIC_DIFFERENT_DESTINATION;
691 }
692 } else {
693 if (request_info.url.host() == alternative_service.host) {
694 type = NOT_QUIC_SAME_DESTINATION;
695 } else {
696 type = NOT_QUIC_DIFFERENT_DESTINATION;
697 }
698 }
699 UMA_HISTOGRAM_ENUMERATION("Net.AlternativeServiceTypeForRequest", type,
700 MAX_ALTERNATIVE_SERVICE_TYPE);
701 return alternative_service;
702 }
703
704 AlternativeService
705 HttpStreamFactoryImpl::JobController::GetAlternativeServiceForInternal(
706 const HttpRequestInfo& request_info,
707 HttpStreamRequest::Delegate* delegate,
708 HttpStreamRequest::StreamType stream_type) {
709 GURL original_url = request_info.url;
710
711 if (original_url.SchemeIs("ftp"))
712 return AlternativeService();
713
714 if (!session_->params().enable_alternative_service_for_insecure_origins &&
715 !original_url.SchemeIs("https"))
716 return AlternativeService();
717
718 url::SchemeHostPort origin(original_url);
719 HttpServerProperties& http_server_properties =
720 *session_->http_server_properties();
721 const AlternativeServiceVector alternative_service_vector =
722 http_server_properties.GetAlternativeServices(origin);
723 if (alternative_service_vector.empty())
724 return AlternativeService();
725
726 bool quic_advertised = false;
727 bool quic_all_broken = true;
728
729 const bool enable_different_host =
730 session_->params().enable_alternative_service_with_different_host;
731
732 // First Alt-Svc that is not marked as broken.
733 AlternativeService first_alternative_service;
734
735 for (const AlternativeService& alternative_service :
736 alternative_service_vector) {
737 DCHECK(IsAlternateProtocolValid(alternative_service.protocol));
738 if (!quic_advertised && alternative_service.protocol == QUIC)
739 quic_advertised = true;
740 if (http_server_properties.IsAlternativeServiceBroken(
741 alternative_service)) {
742 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN);
743 continue;
744 }
745
746 if (origin.host() != alternative_service.host && !enable_different_host)
747 continue;
748
749 // Some shared unix systems may have user home directories (like
750 // http://foo.com/~mike) which allow users to emit headers. This is a bad
751 // idea already, but with Alternate-Protocol, it provides the ability for a
752 // single user on a multi-user system to hijack the alternate protocol.
753 // These systems also enforce ports <1024 as restricted ports. So don't
754 // allow protocol upgrades to user-controllable ports.
755 const int kUnrestrictedPort = 1024;
756 if (!session_->params().enable_user_alternate_protocol_ports &&
757 (alternative_service.port >= kUnrestrictedPort &&
758 origin.port() < kUnrestrictedPort))
759 continue;
760
761 if (alternative_service.protocol >= NPN_SPDY_MINIMUM_VERSION &&
762 alternative_service.protocol <= NPN_SPDY_MAXIMUM_VERSION) {
763 if (!HttpStreamFactory::spdy_enabled())
764 continue;
765
766 // TODO(bnc): Re-enable when https://crbug.com/615413 is fixed.
767 if (origin.host() != alternative_service.host)
768 continue;
769
770 // Cache this entry if we don't have a non-broken Alt-Svc yet.
771 if (first_alternative_service.protocol ==
772 UNINITIALIZED_ALTERNATE_PROTOCOL)
773 first_alternative_service = alternative_service;
774 continue;
775 }
776
777 DCHECK_EQ(QUIC, alternative_service.protocol);
778 quic_all_broken = false;
779 if (!session_->params().enable_quic)
780 continue;
781
782 if (!IsQuicWhitelistedForHost(origin.host()))
783 continue;
784
785 if (stream_type == HttpStreamRequest::BIDIRECTIONAL_STREAM &&
786 session_->params().quic_disable_bidirectional_streams) {
787 continue;
788 }
789
790 if (session_->quic_stream_factory()->IsQuicDisabled(
791 alternative_service.port))
792 continue;
793
794 if (!original_url.SchemeIs("https"))
795 continue;
796
797 // Check whether there is an existing QUIC session to use for this origin.
798 HostPortPair mapped_origin(origin.host(), origin.port());
799 ignore_result(ApplyHostMappingRules(original_url, &mapped_origin));
800 QuicServerId server_id(mapped_origin, request_info.privacy_mode);
801
802 HostPortPair destination(alternative_service.host_port_pair());
803 ignore_result(ApplyHostMappingRules(original_url, &destination));
804
805 if (session_->quic_stream_factory()->CanUseExistingSession(server_id,
806 destination)) {
807 return alternative_service;
808 }
809
810 // Cache this entry if we don't have a non-broken Alt-Svc yet.
811 if (first_alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
812 first_alternative_service = alternative_service;
813 }
814
815 // Ask delegate to mark QUIC as broken for the origin.
816 if (quic_advertised && quic_all_broken && delegate != nullptr)
817 delegate->OnQuicBroken();
818
819 return first_alternative_service;
820 }
821 }
OLDNEW
« no previous file with comments | « net/http/http_stream_factory_impl_job_controller.h ('k') | net/http/http_stream_factory_impl_job_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698