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

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

Powered by Google App Engine
This is Rietveld 408576698