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

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

Powered by Google App Engine
This is Rietveld 408576698