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

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: add JobFactory and rewrite test cases 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 JobFactory* job_factory)
24 : factory_(factory),
25 session_(session),
26 job_factory_(job_factory),
27 request_(nullptr),
28 delegate_(delegate),
29 is_preconnect_(false),
30 job_bound_(false),
31 bound_job_(nullptr) {
32 DCHECK(factory);
33 }
34
35 HttpStreamFactoryImpl::JobController::~JobController() {
36 main_job_.reset();
37 alternative_job_.reset();
38 bound_job_ = nullptr;
39 }
40
41 bool HttpStreamFactoryImpl::JobController::for_websockets() {
42 return factory_->for_websockets_;
43 }
44
45 HttpStreamFactoryImpl::Request* HttpStreamFactoryImpl::JobController::Start(
46 const HttpRequestInfo& request_info,
47 HttpStreamRequest::Delegate* delegate,
48 WebSocketHandshakeStreamBase::CreateHelper*
49 websocket_handshake_stream_create_helper,
50 const BoundNetLog& net_log,
51 HttpStreamRequest::StreamType stream_type,
52 RequestPriority priority,
53 const SSLConfig& server_ssl_config,
54 const SSLConfig& proxy_ssl_config) {
55 DCHECK(factory_);
56 DCHECK(!request_);
57
58 request_ = new Request(request_info.url, this, delegate,
59 websocket_handshake_stream_create_helper, net_log,
60 stream_type);
61
62 CreateJobs(request_info, priority, server_ssl_config, proxy_ssl_config,
63 delegate, stream_type, net_log);
64
65 return request_;
66 }
67
68 void HttpStreamFactoryImpl::JobController::Preconnect(
69 int num_streams,
70 const HttpRequestInfo& request_info,
71 const SSLConfig& server_ssl_config,
72 const SSLConfig& proxy_ssl_config) {
73 DCHECK(!main_job_);
74 DCHECK(!alternative_job_);
75
76 is_preconnect_ = true;
77 HostPortPair destination(HostPortPair::FromURL(request_info.url));
78 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
79
80 const AlternativeService alternative_service = GetAlternativeServiceFor(
81 request_info, nullptr, HttpStreamRequest::HTTP_STREAM);
82
83 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
84 if (session_->params().quic_disable_preconnect_if_0rtt &&
85 alternative_service.protocol == QUIC &&
86 session_->quic_stream_factory()->ZeroRTTEnabledFor(QuicServerId(
87 alternative_service.host_port_pair(), request_info.privacy_mode))) {
88 MaybeNotifyFactoryOfCompletion();
89 return;
90 }
91 destination = alternative_service.host_port_pair();
92 ignore_result(ApplyHostMappingRules(request_info.url, &destination));
93 }
94
95 // Due to how the socket pools handle priorities and idle sockets, only IDLE
96 // priority currently makes sense for preconnects. The priority for
97 // preconnects is currently ignored (see RequestSocketsForPool()), but could
98 // be used at some point for proxy resolution or something.
99 main_job_.reset(job_factory_->CreateJob(
100 this, PRECONNECT, session_, request_info, IDLE, server_ssl_config,
101 proxy_ssl_config, destination, origin_url, alternative_service,
102 session_->net_log()));
103 main_job_->Preconnect(num_streams);
104 }
105
106 LoadState HttpStreamFactoryImpl::JobController::GetLoadState() const {
107 DCHECK(request_);
108 if (bound_job_)
109 return bound_job_->GetLoadState();
110
111 // Just pick the first one.
112 return main_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->GetStream();
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->GetBidirectionalStream();
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->GetBidirectionalStream();
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->GetStream();
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 GURL original_url = request_info.url;
682
683 if (original_url.SchemeIs("ftp"))
684 return AlternativeService();
685
686 url::SchemeHostPort origin(original_url);
687 HttpServerProperties& http_server_properties =
688 *session_->http_server_properties();
689 const AlternativeServiceVector alternative_service_vector =
690 http_server_properties.GetAlternativeServices(origin);
691 if (alternative_service_vector.empty())
692 return AlternativeService();
693
694 bool quic_advertised = false;
695 bool quic_all_broken = true;
696
697 const bool enable_different_host =
698 session_->params().enable_alternative_service_with_different_host;
699
700 // First Alt-Svc that is not marked as broken.
701 AlternativeService first_alternative_service;
702
703 for (const AlternativeService& alternative_service :
704 alternative_service_vector) {
705 DCHECK(IsAlternateProtocolValid(alternative_service.protocol));
706 if (!quic_advertised && alternative_service.protocol == QUIC)
707 quic_advertised = true;
708 if (http_server_properties.IsAlternativeServiceBroken(
709 alternative_service)) {
710 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN);
711 continue;
712 }
713
714 if (origin.host() != alternative_service.host && !enable_different_host)
715 continue;
716
717 // Some shared unix systems may have user home directories (like
718 // http://foo.com/~mike) which allow users to emit headers. This is a bad
719 // idea already, but with Alternate-Protocol, it provides the ability for a
720 // single user on a multi-user system to hijack the alternate protocol.
721 // These systems also enforce ports <1024 as restricted ports. So don't
722 // allow protocol upgrades to user-controllable ports.
723 const int kUnrestrictedPort = 1024;
724 if (!session_->params().enable_user_alternate_protocol_ports &&
725 (alternative_service.port >= kUnrestrictedPort &&
726 origin.port() < kUnrestrictedPort))
727 continue;
728
729 if (alternative_service.protocol >= NPN_SPDY_MINIMUM_VERSION &&
730 alternative_service.protocol <= NPN_SPDY_MAXIMUM_VERSION) {
731 if (!HttpStreamFactory::spdy_enabled())
732 continue;
733
734 // Cache this entry if we don't have a non-broken Alt-Svc yet.
735 if (first_alternative_service.protocol ==
736 UNINITIALIZED_ALTERNATE_PROTOCOL)
737 first_alternative_service = alternative_service;
738 continue;
739 }
740
741 DCHECK_EQ(QUIC, alternative_service.protocol);
742 quic_all_broken = false;
743 if (!session_->params().enable_quic)
744 continue;
745
746 if (!IsQuicWhitelistedForHost(origin.host()))
747 continue;
748
749 if (stream_type == HttpStreamRequest::BIDIRECTIONAL_STREAM &&
750 session_->params().quic_disable_bidirectional_streams) {
751 continue;
752 }
753
754 if (session_->quic_stream_factory()->IsQuicDisabled(
755 alternative_service.port))
756 continue;
757
758 if (!original_url.SchemeIs("https"))
759 continue;
760
761 // Check whether there is an existing QUIC session to use for this origin.
762 HostPortPair destination(alternative_service.host_port_pair());
763 ignore_result(ApplyHostMappingRules(original_url, &destination));
764 QuicServerId server_id(destination, request_info.privacy_mode);
765
766 HostPortPair origin_copy(origin.host(), origin.port());
767 ignore_result(ApplyHostMappingRules(original_url, &origin_copy));
768
769 if (session_->quic_stream_factory()->CanUseExistingSession(
770 server_id, origin_copy.host())) {
771 return alternative_service;
772 }
773
774 // Cache this entry if we don't have a non-broken Alt-Svc yet.
775 if (first_alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
776 first_alternative_service = alternative_service;
777 }
778
779 // Ask delegate to mark QUIC as broken for the origin.
780 if (quic_advertised && quic_all_broken && delegate != nullptr)
781 delegate->OnQuicBroken();
782
783 return first_alternative_service;
784 }
785 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698