• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/logging.h"
8 #include "base/stl_util-inl.h"
9 #include "net/http/http_stream_factory_impl_job.h"
10 #include "net/spdy/spdy_http_stream.h"
11 #include "net/spdy/spdy_session.h"
12 
13 namespace net {
14 
Request(const GURL & url,HttpStreamFactoryImpl * factory,HttpStreamRequest::Delegate * delegate,const BoundNetLog & net_log)15 HttpStreamFactoryImpl::Request::Request(const GURL& url,
16                                         HttpStreamFactoryImpl* factory,
17                                         HttpStreamRequest::Delegate* delegate,
18                                         const BoundNetLog& net_log)
19     : url_(url),
20       factory_(factory),
21       delegate_(delegate),
22       net_log_(net_log),
23       completed_(false),
24       was_npn_negotiated_(false),
25       using_spdy_(false) {
26   DCHECK(factory_);
27   DCHECK(delegate_);
28 
29   net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_REQUEST, NULL);
30 }
31 
~Request()32 HttpStreamFactoryImpl::Request::~Request() {
33   if (bound_job_.get())
34     DCHECK(jobs_.empty());
35   else
36     DCHECK(!jobs_.empty());
37 
38   net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_REQUEST, NULL);
39 
40   for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
41     factory_->request_map_.erase(*it);
42 
43   STLDeleteElements(&jobs_);
44 
45   RemoveRequestFromSpdySessionRequestMap();
46 }
47 
SetSpdySessionKey(const HostPortProxyPair & spdy_session_key)48 void HttpStreamFactoryImpl::Request::SetSpdySessionKey(
49     const HostPortProxyPair& spdy_session_key) {
50   DCHECK(!spdy_session_key_.get());
51   spdy_session_key_.reset(new HostPortProxyPair(spdy_session_key));
52   RequestSet& request_set =
53       factory_->spdy_session_request_map_[spdy_session_key];
54   DCHECK(!ContainsKey(request_set, this));
55   request_set.insert(this);
56 }
57 
AttachJob(Job * job)58 void HttpStreamFactoryImpl::Request::AttachJob(Job* job) {
59   DCHECK(job);
60   jobs_.insert(job);
61   factory_->request_map_[job] = this;
62 }
63 
Complete(bool was_npn_negotiated,bool using_spdy,const NetLog::Source & job_source)64 void HttpStreamFactoryImpl::Request::Complete(
65     bool was_npn_negotiated,
66     bool using_spdy,
67     const NetLog::Source& job_source) {
68   DCHECK(!completed_);
69   completed_ = true;
70   was_npn_negotiated_ = was_npn_negotiated;
71   using_spdy_ = using_spdy;
72   net_log_.AddEvent(
73       NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB,
74       make_scoped_refptr(new NetLogSourceParameter(
75           "source_dependency", job_source)));
76 }
77 
OnStreamReady(Job * job,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,HttpStream * stream)78 void HttpStreamFactoryImpl::Request::OnStreamReady(
79     Job* job,
80     const SSLConfig& used_ssl_config,
81     const ProxyInfo& used_proxy_info,
82     HttpStream* stream) {
83   DCHECK(stream);
84   DCHECK(completed_);
85 
86   // |job| should only be NULL if we're being serviced by a late bound
87   // SpdySession (one that was not created by a job in our |jobs_| set).
88   if (!job) {
89     DCHECK(!bound_job_.get());
90     DCHECK(!jobs_.empty());
91     // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because
92     // we *WANT* to cancel the unnecessary Jobs from other requests if another
93     // Job completes first.
94     // TODO(mbelshe): Revisit this when we implement ip connection pooling of
95     // SpdySessions. Do we want to orphan the jobs for a different hostname so
96     // they complete? Or do we want to prevent connecting a new SpdySession if
97     // we've already got one available for a different hostname where the ip
98     // address matches up?
99   } else if (!bound_job_.get()) {
100     // We may have other jobs in |jobs_|. For example, if we start multiple jobs
101     // for Alternate-Protocol.
102     OrphanJobsExcept(job);
103   } else {
104     DCHECK(jobs_.empty());
105   }
106   delegate_->OnStreamReady(used_ssl_config, used_proxy_info, stream);
107 }
108 
OnStreamFailed(Job * job,int status,const SSLConfig & used_ssl_config)109 void HttpStreamFactoryImpl::Request::OnStreamFailed(
110     Job* job,
111     int status,
112     const SSLConfig& used_ssl_config) {
113   DCHECK_NE(OK, status);
114   if (!bound_job_.get()) {
115     // Hey, we've got other jobs! Maybe one of them will succeed, let's just
116     // ignore this failure.
117     if (jobs_.size() > 1) {
118       jobs_.erase(job);
119       factory_->request_map_.erase(job);
120       delete job;
121       return;
122     } else {
123       bound_job_.reset(job);
124       jobs_.erase(job);
125       DCHECK(jobs_.empty());
126       factory_->request_map_.erase(job);
127     }
128   } else {
129     DCHECK(jobs_.empty());
130   }
131   delegate_->OnStreamFailed(status, used_ssl_config);
132 }
133 
OnCertificateError(Job * job,int status,const SSLConfig & used_ssl_config,const SSLInfo & ssl_info)134 void HttpStreamFactoryImpl::Request::OnCertificateError(
135     Job* job,
136     int status,
137     const SSLConfig& used_ssl_config,
138     const SSLInfo& ssl_info) {
139   DCHECK_NE(OK, status);
140   if (!bound_job_.get())
141     OrphanJobsExcept(job);
142   else
143     DCHECK(jobs_.empty());
144   delegate_->OnCertificateError(status, used_ssl_config, ssl_info);
145 }
146 
OnNeedsProxyAuth(Job * job,const HttpResponseInfo & proxy_response,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,HttpAuthController * auth_controller)147 void HttpStreamFactoryImpl::Request::OnNeedsProxyAuth(
148     Job* job,
149     const HttpResponseInfo& proxy_response,
150     const SSLConfig& used_ssl_config,
151     const ProxyInfo& used_proxy_info,
152     HttpAuthController* auth_controller) {
153   if (!bound_job_.get())
154     OrphanJobsExcept(job);
155   else
156     DCHECK(jobs_.empty());
157   delegate_->OnNeedsProxyAuth(
158       proxy_response, used_ssl_config, used_proxy_info, auth_controller);
159 }
160 
OnNeedsClientAuth(Job * job,const SSLConfig & used_ssl_config,SSLCertRequestInfo * cert_info)161 void HttpStreamFactoryImpl::Request::OnNeedsClientAuth(
162     Job* job,
163     const SSLConfig& used_ssl_config,
164     SSLCertRequestInfo* cert_info) {
165   if (!bound_job_.get())
166     OrphanJobsExcept(job);
167   else
168     DCHECK(jobs_.empty());
169   delegate_->OnNeedsClientAuth(used_ssl_config, cert_info);
170 }
171 
OnHttpsProxyTunnelResponse(Job * job,const HttpResponseInfo & response_info,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,HttpStream * stream)172 void HttpStreamFactoryImpl::Request::OnHttpsProxyTunnelResponse(
173     Job *job,
174     const HttpResponseInfo& response_info,
175     const SSLConfig& used_ssl_config,
176     const ProxyInfo& used_proxy_info,
177     HttpStream* stream) {
178   if (!bound_job_.get())
179     OrphanJobsExcept(job);
180   else
181     DCHECK(jobs_.empty());
182   delegate_->OnHttpsProxyTunnelResponse(
183       response_info, used_ssl_config, used_proxy_info, stream);
184 }
185 
RestartTunnelWithProxyAuth(const string16 & username,const string16 & password)186 int HttpStreamFactoryImpl::Request::RestartTunnelWithProxyAuth(
187     const string16& username,
188     const string16& password) {
189   DCHECK(bound_job_.get());
190   return bound_job_->RestartTunnelWithProxyAuth(username, password);
191 }
192 
GetLoadState() const193 LoadState HttpStreamFactoryImpl::Request::GetLoadState() const {
194   if (bound_job_.get())
195     return bound_job_->GetLoadState();
196   DCHECK(!jobs_.empty());
197 
198   // Just pick the first one.
199   return (*jobs_.begin())->GetLoadState();
200 }
201 
was_npn_negotiated() const202 bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const {
203   DCHECK(completed_);
204   return was_npn_negotiated_;
205 }
206 
using_spdy() const207 bool HttpStreamFactoryImpl::Request::using_spdy() const {
208   DCHECK(completed_);
209   return using_spdy_;
210 }
211 
212 void
RemoveRequestFromSpdySessionRequestMap()213 HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() {
214   if (spdy_session_key_.get()) {
215     SpdySessionRequestMap& spdy_session_request_map =
216         factory_->spdy_session_request_map_;
217     DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key_));
218     RequestSet& request_set =
219         spdy_session_request_map[*spdy_session_key_];
220     DCHECK(ContainsKey(request_set, this));
221     request_set.erase(this);
222     if (request_set.empty())
223       spdy_session_request_map.erase(*spdy_session_key_);
224     spdy_session_key_.reset();
225   }
226 }
227 
OnSpdySessionReady(Job * job,scoped_refptr<SpdySession> spdy_session,bool direct)228 void HttpStreamFactoryImpl::Request::OnSpdySessionReady(
229     Job* job,
230     scoped_refptr<SpdySession> spdy_session,
231     bool direct) {
232   DCHECK(job);
233   DCHECK(job->using_spdy());
234 
235   // The first case is the usual case.
236   if (!bound_job_.get()) {
237     OrphanJobsExcept(job);
238   } else { // This is the case for HTTPS proxy tunneling.
239     DCHECK_EQ(bound_job_.get(), job);
240     DCHECK(jobs_.empty());
241   }
242 
243   // Cache these values in case the job gets deleted.
244   const SSLConfig used_ssl_config = job->ssl_config();
245   const ProxyInfo used_proxy_info = job->proxy_info();
246   const bool was_npn_negotiated = job->was_npn_negotiated();
247   const bool using_spdy = job->using_spdy();
248   const NetLog::Source source = job->net_log().source();
249 
250   Complete(was_npn_negotiated,
251            using_spdy,
252            source);
253 
254   // Cache this so we can still use it if the request is deleted.
255   HttpStreamFactoryImpl* factory = factory_;
256 
257   bool use_relative_url = direct || url().SchemeIs("https");
258   delegate_->OnStreamReady(
259       job->ssl_config(),
260       job->proxy_info(),
261       new SpdyHttpStream(spdy_session, use_relative_url));
262   // |this| may be deleted after this point.
263   factory->OnSpdySessionReady(
264       spdy_session, direct, used_ssl_config, used_proxy_info,
265       was_npn_negotiated, using_spdy, source);
266 }
267 
OrphanJobsExcept(Job * job)268 void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) {
269   DCHECK(job);
270   DCHECK(!bound_job_.get());
271   DCHECK(ContainsKey(jobs_, job));
272   bound_job_.reset(job);
273   jobs_.erase(job);
274   factory_->request_map_.erase(job);
275 
276   OrphanJobs();
277 }
278 
OrphanJobs()279 void HttpStreamFactoryImpl::Request::OrphanJobs() {
280   RemoveRequestFromSpdySessionRequestMap();
281 
282   std::set<Job*> tmp;
283   tmp.swap(jobs_);
284 
285   for (std::set<Job*>::iterator it = tmp.begin(); it != tmp.end(); ++it)
286     factory_->OrphanJob(*it, this);
287 }
288 
289 }  // namespace net
290