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