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

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

Powered by Google App Engine
This is Rietveld 408576698