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