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

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

Powered by Google App Engine
This is Rietveld 408576698