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

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

Powered by Google App Engine
This is Rietveld 408576698