Home | History | Annotate | Download | only in http
      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     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 
     37 HttpStreamFactoryImpl::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 
     54 void 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 
     64 bool 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 
     76 void HttpStreamFactoryImpl::Request::AttachJob(Job* job) {
     77   DCHECK(job);
     78   jobs_.insert(job);
     79   factory_->request_map_[job] = this;
     80 }
     81 
     82 void 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 
    100 void 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 
    113 void 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 
    126 void 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 
    160 void 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 
    173 void 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 
    187 void 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 
    198 void 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 
    212 int HttpStreamFactoryImpl::Request::RestartTunnelWithProxyAuth(
    213     const AuthCredentials& credentials) {
    214   DCHECK(bound_job_.get());
    215   return bound_job_->RestartTunnelWithProxyAuth(credentials);
    216 }
    217 
    218 LoadState HttpStreamFactoryImpl::Request::GetLoadState() const {
    219   if (bound_job_.get())
    220     return bound_job_->GetLoadState();
    221   DCHECK(!jobs_.empty());
    222 
    223   // Just pick the first one.
    224   return (*jobs_.begin())->GetLoadState();
    225 }
    226 
    227 bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const {
    228   DCHECK(completed_);
    229   return was_npn_negotiated_;
    230 }
    231 
    232 NextProto HttpStreamFactoryImpl::Request::protocol_negotiated()
    233     const {
    234   DCHECK(completed_);
    235   return protocol_negotiated_;
    236 }
    237 
    238 bool HttpStreamFactoryImpl::Request::using_spdy() const {
    239   DCHECK(completed_);
    240   return using_spdy_;
    241 }
    242 
    243 void
    244 HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() {
    245   if (spdy_session_key_.get()) {
    246     SpdySessionRequestMap& spdy_session_request_map =
    247         factory_->spdy_session_request_map_;
    248     DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key_));
    249     RequestSet& request_set =
    250         spdy_session_request_map[*spdy_session_key_];
    251     DCHECK(ContainsKey(request_set, this));
    252     request_set.erase(this);
    253     if (request_set.empty())
    254       spdy_session_request_map.erase(*spdy_session_key_);
    255     spdy_session_key_.reset();
    256   }
    257 }
    258 
    259 void
    260 HttpStreamFactoryImpl::Request::RemoveRequestFromHttpPipeliningRequestMap() {
    261   if (http_pipelining_key_.get()) {
    262     HttpPipeliningRequestMap& http_pipelining_request_map =
    263         factory_->http_pipelining_request_map_;
    264     DCHECK(ContainsKey(http_pipelining_request_map, *http_pipelining_key_));
    265     RequestVector& request_vector =
    266         http_pipelining_request_map[*http_pipelining_key_];
    267     for (RequestVector::iterator it = request_vector.begin();
    268          it != request_vector.end(); ++it) {
    269       if (*it == this) {
    270         request_vector.erase(it);
    271         break;
    272       }
    273     }
    274     if (request_vector.empty())
    275       http_pipelining_request_map.erase(*http_pipelining_key_);
    276     http_pipelining_key_.reset();
    277   }
    278 }
    279 
    280 void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady(
    281     Job* job,
    282     const base::WeakPtr<SpdySession>& spdy_session,
    283     bool direct) {
    284   DCHECK(job);
    285   DCHECK(job->using_spdy());
    286 
    287   // The first case is the usual case.
    288   if (!bound_job_.get()) {
    289     OrphanJobsExcept(job);
    290   } else {  // This is the case for HTTPS proxy tunneling.
    291     DCHECK_EQ(bound_job_.get(), job);
    292     DCHECK(jobs_.empty());
    293   }
    294 
    295   // Cache these values in case the job gets deleted.
    296   const SSLConfig used_ssl_config = job->server_ssl_config();
    297   const ProxyInfo used_proxy_info = job->proxy_info();
    298   const bool was_npn_negotiated = job->was_npn_negotiated();
    299   const NextProto protocol_negotiated =
    300       job->protocol_negotiated();
    301   const bool using_spdy = job->using_spdy();
    302   const BoundNetLog net_log = job->net_log();
    303 
    304   Complete(was_npn_negotiated, protocol_negotiated, using_spdy, net_log);
    305 
    306   // Cache this so we can still use it if the request is deleted.
    307   HttpStreamFactoryImpl* factory = factory_;
    308   if (factory->for_websockets_) {
    309     DCHECK(websocket_stream_factory_);
    310     bool use_relative_url = direct || url().SchemeIs("wss");
    311     delegate_->OnWebSocketStreamReady(
    312         job->server_ssl_config(),
    313         job->proxy_info(),
    314         websocket_stream_factory_->CreateSpdyStream(spdy_session,
    315                                                     use_relative_url));
    316   } else {
    317     bool use_relative_url = direct || url().SchemeIs("https");
    318     delegate_->OnStreamReady(
    319         job->server_ssl_config(),
    320         job->proxy_info(),
    321         new SpdyHttpStream(spdy_session, use_relative_url));
    322   }
    323   // |this| may be deleted after this point.
    324   factory->OnNewSpdySessionReady(spdy_session,
    325                                  direct,
    326                                  used_ssl_config,
    327                                  used_proxy_info,
    328                                  was_npn_negotiated,
    329                                  protocol_negotiated,
    330                                  using_spdy,
    331                                  net_log);
    332 }
    333 
    334 void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) {
    335   DCHECK(job);
    336   DCHECK(!bound_job_.get());
    337   DCHECK(ContainsKey(jobs_, job));
    338   bound_job_.reset(job);
    339   jobs_.erase(job);
    340   factory_->request_map_.erase(job);
    341 
    342   OrphanJobs();
    343 }
    344 
    345 void HttpStreamFactoryImpl::Request::OrphanJobs() {
    346   RemoveRequestFromSpdySessionRequestMap();
    347   RemoveRequestFromHttpPipeliningRequestMap();
    348 
    349   std::set<Job*> tmp;
    350   tmp.swap(jobs_);
    351 
    352   for (std::set<Job*>::iterator it = tmp.begin(); it != tmp.end(); ++it)
    353     factory_->OrphanJob(*it, this);
    354 }
    355 
    356 void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) {
    357   // |job| should only be NULL if we're being serviced by a late bound
    358   // SpdySession or HttpPipelinedConnection (one that was not created by a job
    359   // in our |jobs_| set).
    360   if (!job) {
    361     DCHECK(!bound_job_.get());
    362     DCHECK(!jobs_.empty());
    363     // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because
    364     // we *WANT* to cancel the unnecessary Jobs from other requests if another
    365     // Job completes first.
    366     // TODO(mbelshe): Revisit this when we implement ip connection pooling of
    367     // SpdySessions. Do we want to orphan the jobs for a different hostname so
    368     // they complete? Or do we want to prevent connecting a new SpdySession if
    369     // we've already got one available for a different hostname where the ip
    370     // address matches up?
    371   } else if (!bound_job_.get()) {
    372     // We may have other jobs in |jobs_|. For example, if we start multiple jobs
    373     // for Alternate-Protocol.
    374     OrphanJobsExcept(job);
    375   } else {
    376     DCHECK(jobs_.empty());
    377   }
    378 }
    379 
    380 }  // namespace net
    381