Move AsyncPolicyProvider, etc. to components/policy/.
[chromium-blink-merge.git] / net / http / http_stream_factory_impl_request.cc
blobd731d414b7150158ba3c963ec230bde31e561760
1 // Copyright (c) 2012 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.
5 #include "net/http/http_stream_factory_impl_request.h"
7 #include "base/callback.h"
8 #include "base/logging.h"
9 #include "base/stl_util.h"
10 #include "net/http/http_stream_factory_impl_job.h"
11 #include "net/spdy/spdy_http_stream.h"
12 #include "net/spdy/spdy_session.h"
14 namespace net {
16 HttpStreamFactoryImpl::Request::Request(
17 const GURL& url,
18 HttpStreamFactoryImpl* factory,
19 HttpStreamRequest::Delegate* delegate,
20 WebSocketHandshakeStreamBase::CreateHelper*
21 websocket_handshake_stream_create_helper,
22 const BoundNetLog& net_log)
23 : url_(url),
24 factory_(factory),
25 websocket_handshake_stream_create_helper_(
26 websocket_handshake_stream_create_helper),
27 delegate_(delegate),
28 net_log_(net_log),
29 completed_(false),
30 was_npn_negotiated_(false),
31 protocol_negotiated_(kProtoUnknown),
32 using_spdy_(false) {
33 DCHECK(factory_);
34 DCHECK(delegate_);
36 net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_REQUEST);
39 HttpStreamFactoryImpl::Request::~Request() {
40 if (bound_job_.get())
41 DCHECK(jobs_.empty());
42 else
43 DCHECK(!jobs_.empty());
45 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_REQUEST);
47 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
48 factory_->request_map_.erase(*it);
50 RemoveRequestFromSpdySessionRequestMap();
51 RemoveRequestFromHttpPipeliningRequestMap();
53 STLDeleteElements(&jobs_);
56 void HttpStreamFactoryImpl::Request::SetSpdySessionKey(
57 const SpdySessionKey& spdy_session_key) {
58 DCHECK(!spdy_session_key_.get());
59 spdy_session_key_.reset(new SpdySessionKey(spdy_session_key));
60 RequestSet& request_set =
61 factory_->spdy_session_request_map_[spdy_session_key];
62 DCHECK(!ContainsKey(request_set, this));
63 request_set.insert(this);
66 bool HttpStreamFactoryImpl::Request::SetHttpPipeliningKey(
67 const HttpPipelinedHost::Key& http_pipelining_key) {
68 CHECK(!http_pipelining_key_.get());
69 http_pipelining_key_.reset(new HttpPipelinedHost::Key(http_pipelining_key));
70 bool was_new_key = !ContainsKey(factory_->http_pipelining_request_map_,
71 http_pipelining_key);
72 RequestVector& request_vector =
73 factory_->http_pipelining_request_map_[http_pipelining_key];
74 request_vector.push_back(this);
75 return was_new_key;
78 void HttpStreamFactoryImpl::Request::AttachJob(Job* job) {
79 DCHECK(job);
80 jobs_.insert(job);
81 factory_->request_map_[job] = this;
84 void HttpStreamFactoryImpl::Request::Complete(
85 bool was_npn_negotiated,
86 NextProto protocol_negotiated,
87 bool using_spdy,
88 const BoundNetLog& job_net_log) {
89 DCHECK(!completed_);
90 completed_ = true;
91 was_npn_negotiated_ = was_npn_negotiated;
92 protocol_negotiated_ = protocol_negotiated;
93 using_spdy_ = using_spdy;
94 net_log_.AddEvent(
95 NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB,
96 job_net_log.source().ToEventParametersCallback());
97 job_net_log.AddEvent(
98 NetLog::TYPE_HTTP_STREAM_JOB_BOUND_TO_REQUEST,
99 net_log_.source().ToEventParametersCallback());
102 void HttpStreamFactoryImpl::Request::OnStreamReady(
103 Job* job,
104 const SSLConfig& used_ssl_config,
105 const ProxyInfo& used_proxy_info,
106 HttpStreamBase* stream) {
107 DCHECK(!factory_->for_websockets_);
108 DCHECK(stream);
109 DCHECK(completed_);
111 OnJobSucceeded(job);
112 delegate_->OnStreamReady(used_ssl_config, used_proxy_info, stream);
115 void HttpStreamFactoryImpl::Request::OnWebSocketHandshakeStreamReady(
116 Job* job,
117 const SSLConfig& used_ssl_config,
118 const ProxyInfo& used_proxy_info,
119 WebSocketHandshakeStreamBase* stream) {
120 DCHECK(factory_->for_websockets_);
121 DCHECK(stream);
122 DCHECK(completed_);
124 OnJobSucceeded(job);
125 delegate_->OnWebSocketHandshakeStreamReady(
126 used_ssl_config, used_proxy_info, stream);
129 void HttpStreamFactoryImpl::Request::OnStreamFailed(
130 Job* job,
131 int status,
132 const SSLConfig& used_ssl_config) {
133 DCHECK_NE(OK, status);
134 // |job| should only be NULL if we're being canceled by a late bound
135 // HttpPipelinedConnection (one that was not created by a job in our |jobs_|
136 // set).
137 if (!job) {
138 DCHECK(!bound_job_.get());
139 DCHECK(!jobs_.empty());
140 // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because
141 // we *WANT* to cancel the unnecessary Jobs from other requests if another
142 // Job completes first.
143 } else if (!bound_job_.get()) {
144 // Hey, we've got other jobs! Maybe one of them will succeed, let's just
145 // ignore this failure.
146 if (jobs_.size() > 1) {
147 jobs_.erase(job);
148 factory_->request_map_.erase(job);
149 delete job;
150 return;
151 } else {
152 bound_job_.reset(job);
153 jobs_.erase(job);
154 DCHECK(jobs_.empty());
155 factory_->request_map_.erase(job);
157 } else {
158 DCHECK(jobs_.empty());
160 delegate_->OnStreamFailed(status, used_ssl_config);
163 void HttpStreamFactoryImpl::Request::OnCertificateError(
164 Job* job,
165 int status,
166 const SSLConfig& used_ssl_config,
167 const SSLInfo& ssl_info) {
168 DCHECK_NE(OK, status);
169 if (!bound_job_.get())
170 OrphanJobsExcept(job);
171 else
172 DCHECK(jobs_.empty());
173 delegate_->OnCertificateError(status, used_ssl_config, ssl_info);
176 void HttpStreamFactoryImpl::Request::OnNeedsProxyAuth(
177 Job* job,
178 const HttpResponseInfo& proxy_response,
179 const SSLConfig& used_ssl_config,
180 const ProxyInfo& used_proxy_info,
181 HttpAuthController* auth_controller) {
182 if (!bound_job_.get())
183 OrphanJobsExcept(job);
184 else
185 DCHECK(jobs_.empty());
186 delegate_->OnNeedsProxyAuth(
187 proxy_response, used_ssl_config, used_proxy_info, auth_controller);
190 void HttpStreamFactoryImpl::Request::OnNeedsClientAuth(
191 Job* job,
192 const SSLConfig& used_ssl_config,
193 SSLCertRequestInfo* cert_info) {
194 if (!bound_job_.get())
195 OrphanJobsExcept(job);
196 else
197 DCHECK(jobs_.empty());
198 delegate_->OnNeedsClientAuth(used_ssl_config, cert_info);
201 void HttpStreamFactoryImpl::Request::OnHttpsProxyTunnelResponse(
202 Job *job,
203 const HttpResponseInfo& response_info,
204 const SSLConfig& used_ssl_config,
205 const ProxyInfo& used_proxy_info,
206 HttpStreamBase* stream) {
207 if (!bound_job_.get())
208 OrphanJobsExcept(job);
209 else
210 DCHECK(jobs_.empty());
211 delegate_->OnHttpsProxyTunnelResponse(
212 response_info, used_ssl_config, used_proxy_info, stream);
215 int HttpStreamFactoryImpl::Request::RestartTunnelWithProxyAuth(
216 const AuthCredentials& credentials) {
217 DCHECK(bound_job_.get());
218 return bound_job_->RestartTunnelWithProxyAuth(credentials);
221 void HttpStreamFactoryImpl::Request::SetPriority(RequestPriority priority) {
222 for (std::set<HttpStreamFactoryImpl::Job*>::const_iterator it = jobs_.begin();
223 it != jobs_.end(); ++it) {
224 (*it)->SetPriority(priority);
226 if (bound_job_)
227 bound_job_->SetPriority(priority);
230 LoadState HttpStreamFactoryImpl::Request::GetLoadState() const {
231 if (bound_job_.get())
232 return bound_job_->GetLoadState();
233 DCHECK(!jobs_.empty());
235 // Just pick the first one.
236 return (*jobs_.begin())->GetLoadState();
239 bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const {
240 DCHECK(completed_);
241 return was_npn_negotiated_;
244 NextProto HttpStreamFactoryImpl::Request::protocol_negotiated()
245 const {
246 DCHECK(completed_);
247 return protocol_negotiated_;
250 bool HttpStreamFactoryImpl::Request::using_spdy() const {
251 DCHECK(completed_);
252 return using_spdy_;
255 void
256 HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() {
257 if (spdy_session_key_.get()) {
258 SpdySessionRequestMap& spdy_session_request_map =
259 factory_->spdy_session_request_map_;
260 DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key_));
261 RequestSet& request_set =
262 spdy_session_request_map[*spdy_session_key_];
263 DCHECK(ContainsKey(request_set, this));
264 request_set.erase(this);
265 if (request_set.empty())
266 spdy_session_request_map.erase(*spdy_session_key_);
267 spdy_session_key_.reset();
271 void
272 HttpStreamFactoryImpl::Request::RemoveRequestFromHttpPipeliningRequestMap() {
273 if (http_pipelining_key_.get()) {
274 HttpPipeliningRequestMap& http_pipelining_request_map =
275 factory_->http_pipelining_request_map_;
276 DCHECK(ContainsKey(http_pipelining_request_map, *http_pipelining_key_));
277 RequestVector& request_vector =
278 http_pipelining_request_map[*http_pipelining_key_];
279 for (RequestVector::iterator it = request_vector.begin();
280 it != request_vector.end(); ++it) {
281 if (*it == this) {
282 request_vector.erase(it);
283 break;
286 if (request_vector.empty())
287 http_pipelining_request_map.erase(*http_pipelining_key_);
288 http_pipelining_key_.reset();
292 void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady(
293 Job* job,
294 const base::WeakPtr<SpdySession>& spdy_session,
295 bool direct) {
296 DCHECK(job);
297 DCHECK(job->using_spdy());
299 // The first case is the usual case.
300 if (!bound_job_.get()) {
301 OrphanJobsExcept(job);
302 } else { // This is the case for HTTPS proxy tunneling.
303 DCHECK_EQ(bound_job_.get(), job);
304 DCHECK(jobs_.empty());
307 // Cache these values in case the job gets deleted.
308 const SSLConfig used_ssl_config = job->server_ssl_config();
309 const ProxyInfo used_proxy_info = job->proxy_info();
310 const bool was_npn_negotiated = job->was_npn_negotiated();
311 const NextProto protocol_negotiated =
312 job->protocol_negotiated();
313 const bool using_spdy = job->using_spdy();
314 const BoundNetLog net_log = job->net_log();
316 Complete(was_npn_negotiated, protocol_negotiated, using_spdy, net_log);
318 // Cache this so we can still use it if the request is deleted.
319 HttpStreamFactoryImpl* factory = factory_;
320 if (factory->for_websockets_) {
321 DCHECK(websocket_handshake_stream_create_helper_);
322 bool use_relative_url = direct || url().SchemeIs("wss");
323 delegate_->OnWebSocketHandshakeStreamReady(
324 job->server_ssl_config(),
325 job->proxy_info(),
326 websocket_handshake_stream_create_helper_->CreateSpdyStream(
327 spdy_session, use_relative_url));
328 } else {
329 bool use_relative_url = direct || url().SchemeIs("https");
330 delegate_->OnStreamReady(
331 job->server_ssl_config(),
332 job->proxy_info(),
333 new SpdyHttpStream(spdy_session, use_relative_url));
335 // |this| may be deleted after this point.
336 factory->OnNewSpdySessionReady(spdy_session,
337 direct,
338 used_ssl_config,
339 used_proxy_info,
340 was_npn_negotiated,
341 protocol_negotiated,
342 using_spdy,
343 net_log);
346 void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) {
347 DCHECK(job);
348 DCHECK(!bound_job_.get());
349 DCHECK(ContainsKey(jobs_, job));
350 bound_job_.reset(job);
351 jobs_.erase(job);
352 factory_->request_map_.erase(job);
354 OrphanJobs();
357 void HttpStreamFactoryImpl::Request::OrphanJobs() {
358 RemoveRequestFromSpdySessionRequestMap();
359 RemoveRequestFromHttpPipeliningRequestMap();
361 std::set<Job*> tmp;
362 tmp.swap(jobs_);
364 for (std::set<Job*>::iterator it = tmp.begin(); it != tmp.end(); ++it)
365 factory_->OrphanJob(*it, this);
368 void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) {
369 // |job| should only be NULL if we're being serviced by a late bound
370 // SpdySession or HttpPipelinedConnection (one that was not created by a job
371 // in our |jobs_| set).
372 if (!job) {
373 DCHECK(!bound_job_.get());
374 DCHECK(!jobs_.empty());
375 // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because
376 // we *WANT* to cancel the unnecessary Jobs from other requests if another
377 // Job completes first.
378 // TODO(mbelshe): Revisit this when we implement ip connection pooling of
379 // SpdySessions. Do we want to orphan the jobs for a different hostname so
380 // they complete? Or do we want to prevent connecting a new SpdySession if
381 // we've already got one available for a different hostname where the ip
382 // address matches up?
383 } else if (!bound_job_.get()) {
384 // We may have other jobs in |jobs_|. For example, if we start multiple jobs
385 // for Alternate-Protocol.
386 OrphanJobsExcept(job);
387 } else {
388 DCHECK(jobs_.empty());
392 } // namespace net