OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/http/http_stream_factory_impl_request.h" | |
6 | |
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" | |
13 | |
14 namespace net { | |
15 | |
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_); | |
35 | |
36 net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_REQUEST); | |
37 } | |
38 | |
39 HttpStreamFactoryImpl::Request::~Request() { | |
40 if (bound_job_.get()) | |
41 DCHECK(jobs_.empty()); | |
42 else | |
43 DCHECK(!jobs_.empty()); | |
44 | |
45 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_REQUEST); | |
46 | |
47 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) | |
48 factory_->request_map_.erase(*it); | |
49 | |
50 RemoveRequestFromSpdySessionRequestMap(); | |
51 | |
52 STLDeleteElements(&jobs_); | |
53 } | |
54 | |
55 void HttpStreamFactoryImpl::Request::SetSpdySessionKey( | |
56 const SpdySessionKey& spdy_session_key) { | |
57 CHECK(!spdy_session_key_.get()); | |
58 spdy_session_key_.reset(new SpdySessionKey(spdy_session_key)); | |
59 RequestSet& request_set = | |
60 factory_->spdy_session_request_map_[spdy_session_key]; | |
61 DCHECK(!ContainsKey(request_set, this)); | |
62 request_set.insert(this); | |
63 } | |
64 | |
65 void HttpStreamFactoryImpl::Request::AttachJob(Job* job) { | |
66 DCHECK(job); | |
67 jobs_.insert(job); | |
68 factory_->request_map_[job] = this; | |
69 } | |
70 | |
71 void HttpStreamFactoryImpl::Request::Complete( | |
72 bool was_npn_negotiated, | |
73 NextProto protocol_negotiated, | |
74 bool using_spdy, | |
75 const BoundNetLog& job_net_log) { | |
76 DCHECK(!completed_); | |
77 completed_ = true; | |
78 was_npn_negotiated_ = was_npn_negotiated; | |
79 protocol_negotiated_ = protocol_negotiated; | |
80 using_spdy_ = using_spdy; | |
81 net_log_.AddEvent( | |
82 NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB, | |
83 job_net_log.source().ToEventParametersCallback()); | |
84 job_net_log.AddEvent( | |
85 NetLog::TYPE_HTTP_STREAM_JOB_BOUND_TO_REQUEST, | |
86 net_log_.source().ToEventParametersCallback()); | |
87 } | |
88 | |
89 void HttpStreamFactoryImpl::Request::OnStreamReady( | |
90 Job* job, | |
91 const SSLConfig& used_ssl_config, | |
92 const ProxyInfo& used_proxy_info, | |
93 HttpStream* stream) { | |
94 DCHECK(!factory_->for_websockets_); | |
95 DCHECK(stream); | |
96 DCHECK(completed_); | |
97 | |
98 OnJobSucceeded(job); | |
99 delegate_->OnStreamReady(used_ssl_config, used_proxy_info, stream); | |
100 } | |
101 | |
102 void HttpStreamFactoryImpl::Request::OnWebSocketHandshakeStreamReady( | |
103 Job* job, | |
104 const SSLConfig& used_ssl_config, | |
105 const ProxyInfo& used_proxy_info, | |
106 WebSocketHandshakeStreamBase* stream) { | |
107 DCHECK(factory_->for_websockets_); | |
108 DCHECK(stream); | |
109 DCHECK(completed_); | |
110 | |
111 OnJobSucceeded(job); | |
112 delegate_->OnWebSocketHandshakeStreamReady( | |
113 used_ssl_config, used_proxy_info, stream); | |
114 } | |
115 | |
116 void HttpStreamFactoryImpl::Request::OnStreamFailed( | |
117 Job* job, | |
118 int status, | |
119 const SSLConfig& used_ssl_config) { | |
120 DCHECK_NE(OK, status); | |
121 DCHECK(job); | |
122 if (!bound_job_.get()) { | |
123 // Hey, we've got other jobs! Maybe one of them will succeed, let's just | |
124 // ignore this failure. | |
125 if (jobs_.size() > 1) { | |
126 jobs_.erase(job); | |
127 factory_->request_map_.erase(job); | |
128 // Notify all the other jobs that this one failed. | |
129 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) | |
130 (*it)->MarkOtherJobComplete(*job); | |
131 delete job; | |
132 return; | |
133 } else { | |
134 bound_job_.reset(job); | |
135 jobs_.erase(job); | |
136 DCHECK(jobs_.empty()); | |
137 factory_->request_map_.erase(job); | |
138 } | |
139 } else { | |
140 DCHECK(jobs_.empty()); | |
141 } | |
142 delegate_->OnStreamFailed(status, used_ssl_config); | |
143 } | |
144 | |
145 void HttpStreamFactoryImpl::Request::OnCertificateError( | |
146 Job* job, | |
147 int status, | |
148 const SSLConfig& used_ssl_config, | |
149 const SSLInfo& ssl_info) { | |
150 DCHECK_NE(OK, status); | |
151 if (!bound_job_.get()) | |
152 OrphanJobsExcept(job); | |
153 else | |
154 DCHECK(jobs_.empty()); | |
155 delegate_->OnCertificateError(status, used_ssl_config, ssl_info); | |
156 } | |
157 | |
158 void HttpStreamFactoryImpl::Request::OnNeedsProxyAuth( | |
159 Job* job, | |
160 const HttpResponseInfo& proxy_response, | |
161 const SSLConfig& used_ssl_config, | |
162 const ProxyInfo& used_proxy_info, | |
163 HttpAuthController* auth_controller) { | |
164 if (!bound_job_.get()) | |
165 OrphanJobsExcept(job); | |
166 else | |
167 DCHECK(jobs_.empty()); | |
168 delegate_->OnNeedsProxyAuth( | |
169 proxy_response, used_ssl_config, used_proxy_info, auth_controller); | |
170 } | |
171 | |
172 void HttpStreamFactoryImpl::Request::OnNeedsClientAuth( | |
173 Job* job, | |
174 const SSLConfig& used_ssl_config, | |
175 SSLCertRequestInfo* cert_info) { | |
176 if (!bound_job_.get()) | |
177 OrphanJobsExcept(job); | |
178 else | |
179 DCHECK(jobs_.empty()); | |
180 delegate_->OnNeedsClientAuth(used_ssl_config, cert_info); | |
181 } | |
182 | |
183 void HttpStreamFactoryImpl::Request::OnHttpsProxyTunnelResponse( | |
184 Job *job, | |
185 const HttpResponseInfo& response_info, | |
186 const SSLConfig& used_ssl_config, | |
187 const ProxyInfo& used_proxy_info, | |
188 HttpStream* stream) { | |
189 if (!bound_job_.get()) | |
190 OrphanJobsExcept(job); | |
191 else | |
192 DCHECK(jobs_.empty()); | |
193 delegate_->OnHttpsProxyTunnelResponse( | |
194 response_info, used_ssl_config, used_proxy_info, stream); | |
195 } | |
196 | |
197 int HttpStreamFactoryImpl::Request::RestartTunnelWithProxyAuth( | |
198 const AuthCredentials& credentials) { | |
199 DCHECK(bound_job_.get()); | |
200 return bound_job_->RestartTunnelWithProxyAuth(credentials); | |
201 } | |
202 | |
203 void HttpStreamFactoryImpl::Request::SetPriority(RequestPriority priority) { | |
204 for (std::set<HttpStreamFactoryImpl::Job*>::const_iterator it = jobs_.begin(); | |
205 it != jobs_.end(); ++it) { | |
206 (*it)->SetPriority(priority); | |
207 } | |
208 if (bound_job_) | |
209 bound_job_->SetPriority(priority); | |
210 } | |
211 | |
212 LoadState HttpStreamFactoryImpl::Request::GetLoadState() const { | |
213 if (bound_job_.get()) | |
214 return bound_job_->GetLoadState(); | |
215 DCHECK(!jobs_.empty()); | |
216 | |
217 // Just pick the first one. | |
218 return (*jobs_.begin())->GetLoadState(); | |
219 } | |
220 | |
221 bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const { | |
222 DCHECK(completed_); | |
223 return was_npn_negotiated_; | |
224 } | |
225 | |
226 NextProto HttpStreamFactoryImpl::Request::protocol_negotiated() | |
227 const { | |
228 DCHECK(completed_); | |
229 return protocol_negotiated_; | |
230 } | |
231 | |
232 bool HttpStreamFactoryImpl::Request::using_spdy() const { | |
233 DCHECK(completed_); | |
234 return using_spdy_; | |
235 } | |
236 | |
237 void | |
238 HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() { | |
239 if (spdy_session_key_.get()) { | |
240 SpdySessionRequestMap& spdy_session_request_map = | |
241 factory_->spdy_session_request_map_; | |
242 DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key_)); | |
243 RequestSet& request_set = | |
244 spdy_session_request_map[*spdy_session_key_]; | |
245 DCHECK(ContainsKey(request_set, this)); | |
246 request_set.erase(this); | |
247 if (request_set.empty()) | |
248 spdy_session_request_map.erase(*spdy_session_key_); | |
249 spdy_session_key_.reset(); | |
250 } | |
251 } | |
252 | |
253 bool HttpStreamFactoryImpl::Request::HasSpdySessionKey() const { | |
254 return spdy_session_key_.get() != NULL; | |
255 } | |
256 | |
257 // TODO(jgraettinger): Currently, HttpStreamFactoryImpl::Job notifies a | |
258 // Request that the session is ready, which in turn notifies it's delegate, | |
259 // and then it notifies HttpStreamFactoryImpl so that /other/ requests may | |
260 // be woken, but only if the spdy_session is still okay. This is tough to grok. | |
261 // Instead, see if Job can notify HttpStreamFactoryImpl only, and have one | |
262 // path for notifying any requests waiting for the session (including the | |
263 // request which spawned it). | |
264 void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady( | |
265 Job* job, | |
266 scoped_ptr<HttpStream> stream, | |
267 const base::WeakPtr<SpdySession>& spdy_session, | |
268 bool direct) { | |
269 DCHECK(job); | |
270 DCHECK(job->using_spdy()); | |
271 | |
272 // Note: |spdy_session| may be NULL. In that case, |delegate_| should still | |
273 // receive |stream| so the error propogates up correctly, however there is no | |
274 // point in broadcasting |spdy_session| to other requests. | |
275 | |
276 // The first case is the usual case. | |
277 if (!bound_job_.get()) { | |
278 OrphanJobsExcept(job); | |
279 } else { // This is the case for HTTPS proxy tunneling. | |
280 DCHECK_EQ(bound_job_.get(), job); | |
281 DCHECK(jobs_.empty()); | |
282 } | |
283 | |
284 // Cache these values in case the job gets deleted. | |
285 const SSLConfig used_ssl_config = job->server_ssl_config(); | |
286 const ProxyInfo used_proxy_info = job->proxy_info(); | |
287 const bool was_npn_negotiated = job->was_npn_negotiated(); | |
288 const NextProto protocol_negotiated = | |
289 job->protocol_negotiated(); | |
290 const bool using_spdy = job->using_spdy(); | |
291 const BoundNetLog net_log = job->net_log(); | |
292 | |
293 Complete(was_npn_negotiated, protocol_negotiated, using_spdy, net_log); | |
294 | |
295 // Cache this so we can still use it if the request is deleted. | |
296 HttpStreamFactoryImpl* factory = factory_; | |
297 if (factory->for_websockets_) { | |
298 // TODO(ricea): Re-instate this code when WebSockets over SPDY is | |
299 // implemented. | |
300 NOTREACHED(); | |
301 } else { | |
302 delegate_->OnStreamReady(job->server_ssl_config(), job->proxy_info(), | |
303 stream.release()); | |
304 } | |
305 // |this| may be deleted after this point. | |
306 if (spdy_session && spdy_session->IsAvailable()) { | |
307 factory->OnNewSpdySessionReady(spdy_session, | |
308 direct, | |
309 used_ssl_config, | |
310 used_proxy_info, | |
311 was_npn_negotiated, | |
312 protocol_negotiated, | |
313 using_spdy, | |
314 net_log); | |
315 } | |
316 } | |
317 | |
318 void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) { | |
319 DCHECK(job); | |
320 DCHECK(!bound_job_.get()); | |
321 DCHECK(ContainsKey(jobs_, job)); | |
322 bound_job_.reset(job); | |
323 jobs_.erase(job); | |
324 factory_->request_map_.erase(job); | |
325 | |
326 OrphanJobs(); | |
327 } | |
328 | |
329 void HttpStreamFactoryImpl::Request::OrphanJobs() { | |
330 RemoveRequestFromSpdySessionRequestMap(); | |
331 | |
332 std::set<Job*> tmp; | |
333 tmp.swap(jobs_); | |
334 | |
335 for (std::set<Job*>::iterator it = tmp.begin(); it != tmp.end(); ++it) | |
336 factory_->OrphanJob(*it, this); | |
337 } | |
338 | |
339 void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) { | |
340 // |job| should only be NULL if we're being serviced by a late bound | |
341 // SpdySession (one that was not created by a job in our |jobs_| set). | |
342 if (!job) { | |
343 DCHECK(!bound_job_.get()); | |
344 DCHECK(!jobs_.empty()); | |
345 // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because | |
346 // we *WANT* to cancel the unnecessary Jobs from other requests if another | |
347 // Job completes first. | |
348 // TODO(mbelshe): Revisit this when we implement ip connection pooling of | |
349 // SpdySessions. Do we want to orphan the jobs for a different hostname so | |
350 // they complete? Or do we want to prevent connecting a new SpdySession if | |
351 // we've already got one available for a different hostname where the ip | |
352 // address matches up? | |
353 return; | |
354 } | |
355 if (!bound_job_.get()) { | |
356 if (jobs_.size() > 1) | |
357 job->ReportJobSuccededForRequest(); | |
358 // Notify all the other jobs that this one succeeded. | |
359 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) { | |
360 if (*it != job) { | |
361 (*it)->MarkOtherJobComplete(*job); | |
362 } | |
363 } | |
364 // We may have other jobs in |jobs_|. For example, if we start multiple jobs | |
365 // for Alternate-Protocol. | |
366 OrphanJobsExcept(job); | |
367 return; | |
368 } | |
369 DCHECK(jobs_.empty()); | |
370 } | |
371 | |
372 } // namespace net | |
OLD | NEW |