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

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: 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 "net/http/bidirectional_stream_impl.h"
8 #include "net/http/http_stream_factory_impl_job.h"
9 #include "net/http/http_stream_factory_impl_request.h"
10 #include "net/spdy/spdy_session.h"
11
12 namespace net {
13
14 HttpStreamFactoryImpl::JobController::JobController(
15 HttpStreamFactoryImpl* factory)
16 : factory_(factory),
17 request_(NULL),
18 main_job_(NULL),
19 alternative_job_(NULL) {
20 DCHECK(factory);
21 }
22
23 HttpStreamFactoryImpl::JobController::~JobController() {
24 std::set<const Job*> tmp_job_set;
25 tmp_job_set.swap(orphaned_job_set_);
26 STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end());
Ryan Hamilton 2016/05/06 20:49:01 Would this work: STLDeleteContainerPointers(orpha
Zhongyi Shi 2016/05/12 07:26:23 Acknowledged.
27 DCHECK(orphaned_job_set_.empty());
28 }
29
30 HttpStreamFactoryImpl::Request*
31 HttpStreamFactoryImpl::JobController::CreatRequest(
32 const HttpRequestInfo& request_info,
33 HttpStreamRequest::Delegate* delegate,
34 WebSocketHandshakeStreamBase::CreateHelper*
35 websocket_handshake_stream_create_helper,
36 const BoundNetLog& net_log,
37 HttpStreamRequest::StreamType stream_type) {
38 DCHECK(factory_);
39 DCHECK(!request_);
40
41 request_ = new Request(request_info.url, this, delegate,
42 websocket_handshake_stream_create_helper, net_log,
43 stream_type);
44 return request_;
45 }
46
47 void HttpStreamFactoryImpl::JobController::Start(
48 HttpNetworkSession* session,
49 const HttpRequestInfo& request_info,
50 RequestPriority priority,
51 const SSLConfig& server_ssl_config,
52 const SSLConfig& proxy_ssl_config,
53 HttpStreamRequest::Delegate* delegate,
54 HttpStreamRequest::StreamType stream_type,
55 const BoundNetLog& net_log) {
56 DCHECK(!main_job_);
57 DCHECK(!alternative_job_);
58 HostPortPair destination(HostPortPair::FromURL(request_info.url));
59 GURL origin_url =
60 factory_->ApplyHostMappingRules(request_info.url, &destination);
61
62 main_job_ =
63 new Job(this, session, request_info, priority, server_ssl_config,
64 proxy_ssl_config, destination, origin_url, net_log.net_log());
65 jobs_.insert(main_job_);
66 AttachJob(main_job_);
67
68 // Create an alternative job if alternative service is set up for this domain.
69 const AlternativeService alternative_service =
70 factory_->GetAlternativeServiceFor(request_info, delegate, stream_type);
71
72 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
73 // Never share connection with other jobs for FTP requests.
74 DVLOG(1) << "Selected alternative service (host: "
75 << alternative_service.host_port_pair().host()
76 << " port: " << alternative_service.host_port_pair().port() << ")";
77
78 DCHECK(!request_info.url.SchemeIs("ftp"));
79 HostPortPair alternative_destination(alternative_service.host_port_pair());
80 ignore_result(factory_->ApplyHostMappingRules(request_info.url,
Ryan Hamilton 2016/05/06 20:49:02 I wonder if GetAlternativeServiceFor and ApplyHost
Zhongyi Shi 2016/05/12 07:26:23 Done! They are all moved in the new patch.
81 &alternative_destination));
82
83 alternative_job_ =
84 new Job(this, session, request_info, priority, server_ssl_config,
85 proxy_ssl_config, alternative_destination, origin_url,
86 alternative_service, net_log.net_log());
87 jobs_.insert(alternative_job_);
88 AttachJob(alternative_job_);
89
90 main_job_->WaitFor(alternative_job_);
91 // Make sure to wait until we call WaitFor(), before starting
92 // |alternative_job|, otherwise |alternative_job| will not notify |job|
93 // appropriately.
94 alternative_job_->Start(request_);
95 }
96 // Even if |alternative_job| has already finished, it will not have notified
97 // the request yet, since we defer that to the next iteration of the
98 // MessageLoop, so starting |job| is always safe.
99 main_job_->Start(request_);
100 }
101
102 void HttpStreamFactoryImpl::JobController::Preconnect(
103 int num_streams,
104 HttpNetworkSession* session,
105 const HttpRequestInfo& request_info,
106 const SSLConfig& server_ssl_config,
107 const SSLConfig& proxy_ssl_config) {
108 DCHECK(!main_job_);
109 DCHECK(!alternative_job_);
110
111 HostPortPair destination(HostPortPair::FromURL(request_info.url));
112 GURL origin_url =
113 factory_->ApplyHostMappingRules(request_info.url, &destination);
114
115 const AlternativeService alternative_service =
116 factory_->GetAlternativeServiceFor(request_info, nullptr,
117 HttpStreamRequest::HTTP_STREAM);
118
119 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
120 if (session->params().quic_disable_preconnect_if_0rtt &&
121 alternative_service.protocol == QUIC &&
122 session->quic_stream_factory()->ZeroRTTEnabledFor(QuicServerId(
123 alternative_service.host_port_pair(), request_info.privacy_mode))) {
124 MaybeNotifyFactoryOfCompletion();
125 return;
126 }
127 destination = alternative_service.host_port_pair();
128 ignore_result(
129 factory_->ApplyHostMappingRules(request_info.url, &destination));
130 }
131
132 // Due to how the socket pools handle priorities and idle sockets, only IDLE
133 // priority currently makes sense for preconnects. The priority for
134 // preconnects is currently ignored (see RequestSocketsForPool()), but could
135 // be used at some point for proxy resolution or something.
136 main_job_ = new Job(this, session, request_info, IDLE, server_ssl_config,
137 proxy_ssl_config, destination, origin_url,
138 alternative_service, session->net_log());
139 jobs_.insert(main_job_);
140 main_job_->Preconnect(num_streams);
141 }
142
143 LoadState HttpStreamFactoryImpl::JobController::GetLoadState() const {
144 DCHECK(request_);
145 if (bound_job_.get())
146 return bound_job_->GetLoadState();
147 DCHECK(!jobs_.empty());
148
149 // Just pick the first one.
150 return (*jobs_.begin())->GetLoadState();
151 }
152
153 void HttpStreamFactoryImpl::JobController::SetPriority(
154 RequestPriority priority) {
155 for (std::set<HttpStreamFactoryImpl::Job*>::const_iterator it = jobs_.begin();
156 it != jobs_.end(); ++it) {
Ryan Hamilton 2016/05/06 20:49:02 nit: I think you can do a C++ loop here: for (aut
Zhongyi Shi 2016/05/12 07:26:23 Acknowledged.
157 (*it)->SetPriority(priority);
158 }
159 if (bound_job_)
160 bound_job_->SetPriority(priority);
161 }
162
163 int HttpStreamFactoryImpl::JobController::RestartTunnelWithProxyAuth(
164 const AuthCredentials& credentials) {
165 DCHECK(bound_job_.get());
166 return bound_job_->RestartTunnelWithProxyAuth(credentials);
167 }
168
169 void HttpStreamFactoryImpl::JobController::AttachJob(Job* job) {
170 DCHECK(job);
171 jobs_.insert(job);
172 factory_->request_map_[job] = request_;
Ryan Hamilton 2016/05/06 20:49:02 Is request_map_ still used in your new world? If s
Zhongyi Shi 2016/05/12 07:26:23 This is still used as it is in the old code to mak
173 }
174
175 void HttpStreamFactoryImpl::JobController::BindJob(Job* job) {
176 DCHECK(request_);
177 DCHECK(job);
178 DCHECK(!bound_job_.get());
179 DCHECK(ContainsKey(jobs_, job));
180 bound_job_.reset(job);
181 jobs_.erase(job);
182 factory_->request_map_.erase(job);
183
184 request_->net_log().AddEvent(
185 NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB,
186 job->net_log().source().ToEventParametersCallback());
187 job->net_log().AddEvent(
188 NetLog::TYPE_HTTP_STREAM_JOB_BOUND_TO_REQUEST,
189 request_->net_log().source().ToEventParametersCallback());
190
191 OrphanJobs();
192 }
193
194 void HttpStreamFactoryImpl::JobController::OrphanJobs() {
195 DCHECK(request_);
196 request_->RemoveRequestFromSpdySessionRequestMap();
197
198 std::set<Job*> tmp;
199 tmp.swap(jobs_);
200
201 for (Job* job : tmp) {
202 DCHECK(ContainsKey(factory_->request_map_, job));
203 DCHECK_EQ(factory_->request_map_[job], request_);
204 DCHECK(!ContainsKey(orphaned_job_set_, job));
205
206 factory_->request_map_.erase(job);
207
208 orphaned_job_set_.insert(job);
209 job->Orphan(request_);
210 }
211 }
212
213 void HttpStreamFactoryImpl::JobController::CancelJobs() {
214 DCHECK(request_);
215 request_->RemoveRequestFromSpdySessionRequestMap();
216 std::set<Job*> tmp;
217 tmp.swap(jobs_);
218
219 for (Job* job : tmp) {
220 factory_->request_map_.erase(job);
221 delete job;
222 }
223 }
224
225 void HttpStreamFactoryImpl::JobController::OnJobSucceeded(Job* job) {
226 // |job| should only be NULL if we're being serviced by a late bound
227 // SpdySession (one that was not created by a job in our |jobs_| set).
228 if (!job) {
229 DCHECK(!bound_job_.get());
230 DCHECK(!jobs_.empty());
231 // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because
232 // we *WANT* to cancel the unnecessary Jobs from other requests if another
233 // Job completes first.
234 // TODO(mbelshe): Revisit this when we implement ip connection pooling of
235 // SpdySessions. Do we want to orphan the jobs for a different hostname so
236 // they complete? Or do we want to prevent connecting a new SpdySession if
237 // we've already got one available for a different hostname where the ip
238 // address matches up?
239 CancelJobs();
240 return;
241 }
242 if (!bound_job_.get()) {
243 if (jobs_.size() > 1)
244 job->ReportJobSucceededForRequest();
245 // Notify all the other jobs that this one succeeded.
246 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) {
247 if (*it != job) {
248 (*it)->MarkOtherJobComplete(*job);
249 }
250 }
251 // We may have other jobs in |jobs_|. For example, if we start multiple jobs
252 // for Alternate-Protocol.
253 BindJob(job);
254 return;
255 }
256 DCHECK(jobs_.empty());
257 }
258
259 void HttpStreamFactoryImpl::JobController::OnRequestFinish() {
260 DCHECK(request_);
261 if (bound_job_.get()) {
262 DCHECK(jobs_.empty());
263 }
264 request_ = NULL;
265 bound_job_.reset();
266 MaybeNotifyFactoryOfCompletion();
267 }
268
269 void HttpStreamFactoryImpl::JobController::MarkRequestComplete(
270 bool was_npn_negotiated,
271 NextProto protocol_negotiated,
272 bool using_spdy) {
273 if (request_)
274 request_->Complete(was_npn_negotiated, protocol_negotiated, using_spdy);
275 }
276
277 void HttpStreamFactoryImpl::JobController::OnStreamReady(
278 Job* job,
279 const SSLConfig& used_ssl_config,
280 const ProxyInfo& used_proxy_info,
281 HttpStream* stream) {
282 if (!request_)
283 return;
284 DCHECK(!factory_->for_websockets_);
285 DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type());
286 DCHECK(stream);
287
288 if (job)
289 OnJobSucceeded(job);
290 request_->OnStreamReady(used_ssl_config, used_proxy_info, stream);
291 }
292
293 void HttpStreamFactoryImpl::JobController::OnBidirectionalStreamImplReady(
294 Job* job,
295 const SSLConfig& used_ssl_config,
296 const ProxyInfo& used_proxy_info,
297 BidirectionalStreamImpl* stream) {
298 if (!request_)
299 return;
300 DCHECK(!factory_->for_websockets_);
301 DCHECK_EQ(HttpStreamRequest::BIDIRECTIONAL_STREAM, request_->stream_type());
302 DCHECK(stream);
303
304 if (job)
305 OnJobSucceeded(job);
306 request_->OnBidirectionalStreamImplReady(used_ssl_config, used_proxy_info,
307 stream);
308 }
309
310 void HttpStreamFactoryImpl::JobController::OnWebSocketHandshakeStreamReady(
311 Job* job,
312 const SSLConfig& used_ssl_config,
313 const ProxyInfo& used_proxy_info,
314 WebSocketHandshakeStreamBase* stream) {
315 if (!request_)
316 return;
317 DCHECK(factory_->for_websockets_);
318 DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type());
319 DCHECK(stream);
320
321 if (job)
322 OnJobSucceeded(job);
323 request_->OnWebSocketHandshakeStreamReady(used_ssl_config, used_proxy_info,
324 stream);
325 }
326
327 void HttpStreamFactoryImpl::JobController::OnStreamFailed(
328 Job* job,
329 int status,
330 const SSLConfig& used_ssl_config,
331 SSLFailureState ssl_failure_state) {
332 if (!request_)
333 return;
334 DCHECK_NE(OK, status);
335 DCHECK(job);
336
337 if (!bound_job_.get()) {
338 if (jobs_.size() > 1) {
339 // Hey, we've got other jobs! Maybe one of them will succeed, let's just
340 // ignore this failure.
341 jobs_.erase(job);
342 factory_->request_map_.erase(job);
343 // Notify all the other jobs that this one failed.
344 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
345 (*it)->MarkOtherJobComplete(*job);
346 delete job;
347 return;
348 } else {
349 BindJob(job);
350 }
351 } else {
352 DCHECK(jobs_.empty());
353 }
354
355 request_->OnStreamFailed(status, used_ssl_config, ssl_failure_state);
356 }
357
358 void HttpStreamFactoryImpl::JobController::OnCertificateError(
359 Job* job,
360 int status,
361 const SSLConfig& used_ssl_config,
362 const SSLInfo& ssl_info) {
363 if (!request_)
364 return;
365 DCHECK_NE(OK, status);
366 if (!bound_job_.get())
367 BindJob(job);
368 else
369 DCHECK(jobs_.empty());
370
371 request_->OnCertificateError(status, used_ssl_config, ssl_info);
372 }
373
374 void HttpStreamFactoryImpl::JobController::OnNeedsProxyAuth(
375 Job* job,
376 const HttpResponseInfo& proxy_response,
377 const SSLConfig& used_ssl_config,
378 const ProxyInfo& used_proxy_info,
379 HttpAuthController* auth_controller) {
380 if (!request_)
381 return;
382 if (!bound_job_.get())
383 BindJob(job);
384 else
385 DCHECK(jobs_.empty());
386 request_->OnNeedsProxyAuth(proxy_response, used_ssl_config, used_proxy_info,
387 auth_controller);
388 }
389
390 void HttpStreamFactoryImpl::JobController::OnNeedsClientAuth(
391 Job* job,
392 const SSLConfig& used_ssl_config,
393 SSLCertRequestInfo* cert_info) {
394 if (!request_)
395 return;
396 if (!bound_job_.get())
397 BindJob(job);
398 else
399 DCHECK(jobs_.empty());
400
401 request_->OnNeedsClientAuth(used_ssl_config, cert_info);
402 }
403
404 void HttpStreamFactoryImpl::JobController::OnHttpsProxyTunnelResponse(
405 Job* job,
406 const HttpResponseInfo& response_info,
407 const SSLConfig& used_ssl_config,
408 const ProxyInfo& used_proxy_info,
409 HttpStream* stream) {
410 if (!bound_job_.get())
411 BindJob(job);
412 else
413 DCHECK(jobs_.empty());
414 if (!request_)
415 return;
416 request_->OnHttpsProxyTunnelResponse(response_info, used_ssl_config,
417 used_proxy_info, stream);
418 }
419
420 void HttpStreamFactoryImpl::JobController::OnNewSpdySessionReady(
421 Job* job,
422 std::unique_ptr<HttpStream> stream,
423 std::unique_ptr<BidirectionalStreamImpl> bidirectional_stream_impl,
424 const base::WeakPtr<SpdySession>& spdy_session,
425 bool direct) {
426 DCHECK(job);
427 DCHECK(job->using_spdy());
428
429 // Cache these values in case the job gets deleted.
430 const SSLConfig used_ssl_config = job->server_ssl_config();
431 const ProxyInfo used_proxy_info = job->proxy_info();
432 const bool was_npn_negotiated = job->was_npn_negotiated();
433 const NextProto protocol_negotiated = job->protocol_negotiated();
434 const bool using_spdy = job->using_spdy();
435 const BoundNetLog net_log = job->net_log();
436
437 // Cache this so we can still use it if the JobController is deleted.
438 HttpStreamFactoryImpl* factory = factory_;
439
440 // Notify |request_|.
441 if (!job->IsPreconnecting() && !job->IsOrphaned()) {
442 DCHECK(request_);
443
444 // The first case is the usual case.
445 if (!bound_job_.get()) {
446 BindJob(job);
447 } else { // This is the case for HTTPS proxy tunneling.
448 DCHECK_EQ(bound_job_.get(), job);
449 DCHECK(jobs_.empty());
450 }
451
452 request_->Complete(was_npn_negotiated, protocol_negotiated, using_spdy);
453
454 request_->OnNewSpdySessionReady(
455 std::move(stream), std::move(bidirectional_stream_impl), spdy_session,
456 used_ssl_config, used_proxy_info, was_npn_negotiated,
457 protocol_negotiated, using_spdy, net_log, direct);
458 }
459
460 // Notify |factory_|. |request_| might be deleted already.
461 if (spdy_session && spdy_session->IsAvailable()) {
462 factory->OnNewSpdySessionReady(spdy_session, direct, used_ssl_config,
463 used_proxy_info, was_npn_negotiated,
464 protocol_negotiated, using_spdy, net_log);
465 }
466 }
467
468 void HttpStreamFactoryImpl::JobController::OnPreconnectsComplete(Job* job) {
469 jobs_.erase(job);
470 delete job;
471 factory_->OnPreconnectsCompleteInternal();
472 MaybeNotifyFactoryOfCompletion();
473 }
474
475 void HttpStreamFactoryImpl::JobController::OnOrphanedJobComplete(
476 const Job* job) {
477 orphaned_job_set_.erase(job);
478 delete job;
479 MaybeNotifyFactoryOfCompletion();
480 }
481
482 void HttpStreamFactoryImpl::JobController::AddConnectionAttemptsToRequest(
483 const ConnectionAttempts& attempts) {
484 DCHECK(request_);
485 request_->AddConnectionAttempts(attempts);
486 }
487
488 void HttpStreamFactoryImpl::JobController::SetSpdySessionKey(
489 const SpdySessionKey& spdy_session_key) {
490 DCHECK(request_);
491 if (!request_->HasSpdySessionKey()) {
492 request_->SetSpdySessionKey(spdy_session_key);
493 }
494 }
495
496 void HttpStreamFactoryImpl::JobController::
497 RemoveRequestFromSpdySessionRequestMap() {
498 DCHECK(request_);
499 request_->RemoveRequestFromSpdySessionRequestMap();
500 }
501
502 void HttpStreamFactoryImpl::JobController::MaybeNotifyFactoryOfCompletion() {
503 if (!request_ && jobs_.empty() && orphaned_job_set_.empty()) {
504 DCHECK(jobs_.empty());
505 factory_->OnJobControllerComplete(this);
506 }
507 }
508
509 const BoundNetLog& HttpStreamFactoryImpl::JobController::GetNetLogFromRequest()
510 const {
511 DCHECK(request_);
512 return request_->net_log();
513 }
514
515 WebSocketHandshakeStreamBase::CreateHelper* HttpStreamFactoryImpl::
516 JobController::websocket_handshake_stream_create_helper() {
517 DCHECK(request_);
518 return request_->websocket_handshake_stream_create_helper();
519 }
520 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698