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