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