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

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

Powered by Google App Engine
This is Rietveld 408576698