• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Request(const GURL & url,HttpStreamFactoryImpl * factory,HttpStreamRequest::Delegate * delegate,WebSocketHandshakeStreamBase::CreateHelper * websocket_handshake_stream_create_helper,const BoundNetLog & net_log)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 
~Request()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 
SetSpdySessionKey(const SpdySessionKey & spdy_session_key)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 
AttachJob(Job * job)65 void HttpStreamFactoryImpl::Request::AttachJob(Job* job) {
66   DCHECK(job);
67   jobs_.insert(job);
68   factory_->request_map_[job] = this;
69 }
70 
Complete(bool was_npn_negotiated,NextProto protocol_negotiated,bool using_spdy,const BoundNetLog & job_net_log)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 
OnStreamReady(Job * job,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,HttpStreamBase * stream)89 void HttpStreamFactoryImpl::Request::OnStreamReady(
90     Job* job,
91     const SSLConfig& used_ssl_config,
92     const ProxyInfo& used_proxy_info,
93     HttpStreamBase* 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 
OnWebSocketHandshakeStreamReady(Job * job,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,WebSocketHandshakeStreamBase * stream)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 
OnStreamFailed(Job * job,int status,const SSLConfig & used_ssl_config)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 
OnCertificateError(Job * job,int status,const SSLConfig & used_ssl_config,const SSLInfo & ssl_info)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 
OnNeedsProxyAuth(Job * job,const HttpResponseInfo & proxy_response,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,HttpAuthController * auth_controller)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 
OnNeedsClientAuth(Job * job,const SSLConfig & used_ssl_config,SSLCertRequestInfo * cert_info)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 
OnHttpsProxyTunnelResponse(Job * job,const HttpResponseInfo & response_info,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,HttpStreamBase * stream)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     HttpStreamBase* 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 
RestartTunnelWithProxyAuth(const AuthCredentials & credentials)197 int HttpStreamFactoryImpl::Request::RestartTunnelWithProxyAuth(
198     const AuthCredentials& credentials) {
199   DCHECK(bound_job_.get());
200   return bound_job_->RestartTunnelWithProxyAuth(credentials);
201 }
202 
SetPriority(RequestPriority priority)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 
GetLoadState() const212 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 
was_npn_negotiated() const221 bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const {
222   DCHECK(completed_);
223   return was_npn_negotiated_;
224 }
225 
protocol_negotiated() const226 NextProto HttpStreamFactoryImpl::Request::protocol_negotiated()
227     const {
228   DCHECK(completed_);
229   return protocol_negotiated_;
230 }
231 
using_spdy() const232 bool HttpStreamFactoryImpl::Request::using_spdy() const {
233   DCHECK(completed_);
234   return using_spdy_;
235 }
236 
237 void
RemoveRequestFromSpdySessionRequestMap()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 
HasSpdySessionKey() const253 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).
OnNewSpdySessionReady(Job * job,scoped_ptr<HttpStream> stream,const base::WeakPtr<SpdySession> & spdy_session,bool direct)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 
OrphanJobsExcept(Job * job)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 
OrphanJobs()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 
OnJobSucceeded(Job * job)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
373