Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2010 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.h"
      6 
      7 #include "base/string_number_conversions.h"
      8 #include "base/stl_util-inl.h"
      9 #include "googleurl/src/gurl.h"
     10 #include "net/base/net_log.h"
     11 #include "net/base/net_util.h"
     12 #include "net/http/http_network_session.h"
     13 #include "net/http/http_stream_factory_impl_job.h"
     14 #include "net/http/http_stream_factory_impl_request.h"
     15 #include "net/spdy/spdy_http_stream.h"
     16 
     17 namespace net {
     18 
     19 namespace {
     20 
     21 GURL UpgradeUrlToHttps(const GURL& original_url) {
     22   GURL::Replacements replacements;
     23   // new_sheme and new_port need to be in scope here because GURL::Replacements
     24   // references the memory contained by them directly.
     25   const std::string new_scheme = "https";
     26   const std::string new_port = base::IntToString(443);
     27   replacements.SetSchemeStr(new_scheme);
     28   replacements.SetPortStr(new_port);
     29   return original_url.ReplaceComponents(replacements);
     30 }
     31 
     32 }  // namespace
     33 
     34 HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session)
     35     : session_(session) {}
     36 
     37 HttpStreamFactoryImpl::~HttpStreamFactoryImpl() {
     38   DCHECK(request_map_.empty());
     39   DCHECK(spdy_session_request_map_.empty());
     40 
     41   std::set<const Job*> tmp_job_set;
     42   tmp_job_set.swap(orphaned_job_set_);
     43   STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end());
     44   DCHECK(orphaned_job_set_.empty());
     45 
     46   tmp_job_set.clear();
     47   tmp_job_set.swap(preconnect_job_set_);
     48   STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end());
     49   DCHECK(preconnect_job_set_.empty());
     50 }
     51 
     52 HttpStreamRequest* HttpStreamFactoryImpl::RequestStream(
     53     const HttpRequestInfo& request_info,
     54     const SSLConfig& ssl_config,
     55     HttpStreamRequest::Delegate* delegate,
     56     const BoundNetLog& net_log) {
     57   Request* request = new Request(request_info.url, this, delegate, net_log);
     58 
     59   GURL alternate_url;
     60   bool has_alternate_protocol =
     61       GetAlternateProtocolRequestFor(request_info.url, &alternate_url);
     62   Job* alternate_job = NULL;
     63   if (has_alternate_protocol) {
     64     HttpRequestInfo alternate_request_info = request_info;
     65     alternate_request_info.url = alternate_url;
     66     alternate_job =
     67         new Job(this, session_, alternate_request_info, ssl_config, net_log);
     68     request->AttachJob(alternate_job);
     69     alternate_job->MarkAsAlternate(request_info.url);
     70   }
     71 
     72   Job* job = new Job(this, session_, request_info, ssl_config, net_log);
     73   request->AttachJob(job);
     74   if (alternate_job) {
     75     job->WaitFor(alternate_job);
     76     // Make sure to wait until we call WaitFor(), before starting
     77     // |alternate_job|, otherwise |alternate_job| will not notify |job|
     78     // appropriately.
     79     alternate_job->Start(request);
     80   }
     81   // Even if |alternate_job| has already finished, it won't have notified the
     82   // request yet, since we defer that to the next iteration of the MessageLoop,
     83   // so starting |job| is always safe.
     84   job->Start(request);
     85   return request;
     86 }
     87 
     88 void HttpStreamFactoryImpl::PreconnectStreams(
     89     int num_streams,
     90     const HttpRequestInfo& request_info,
     91     const SSLConfig& ssl_config,
     92     const BoundNetLog& net_log) {
     93   GURL alternate_url;
     94   bool has_alternate_protocol =
     95       GetAlternateProtocolRequestFor(request_info.url, &alternate_url);
     96   Job* job = NULL;
     97   if (has_alternate_protocol) {
     98     HttpRequestInfo alternate_request_info = request_info;
     99     alternate_request_info.url = alternate_url;
    100     job = new Job(this, session_, alternate_request_info, ssl_config, net_log);
    101     job->MarkAsAlternate(request_info.url);
    102   } else {
    103     job = new Job(this, session_, request_info, ssl_config, net_log);
    104   }
    105   preconnect_job_set_.insert(job);
    106   job->Preconnect(num_streams);
    107 }
    108 
    109 void HttpStreamFactoryImpl::AddTLSIntolerantServer(const HostPortPair& server) {
    110   tls_intolerant_servers_.insert(server);
    111 }
    112 
    113 bool HttpStreamFactoryImpl::IsTLSIntolerantServer(
    114     const HostPortPair& server) const {
    115   return ContainsKey(tls_intolerant_servers_, server);
    116 }
    117 
    118 bool HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
    119     const GURL& original_url,
    120     GURL* alternate_url) const {
    121   if (!spdy_enabled())
    122     return false;
    123 
    124   if (!use_alternate_protocols())
    125     return false;
    126 
    127   HostPortPair origin = HostPortPair(original_url.HostNoBrackets(),
    128                                      original_url.EffectiveIntPort());
    129 
    130   const HttpAlternateProtocols& alternate_protocols =
    131       session_->alternate_protocols();
    132   if (!alternate_protocols.HasAlternateProtocolFor(origin))
    133     return false;
    134 
    135   HttpAlternateProtocols::PortProtocolPair alternate =
    136       alternate_protocols.GetAlternateProtocolFor(origin);
    137   if (alternate.protocol == HttpAlternateProtocols::BROKEN)
    138     return false;
    139 
    140   DCHECK_LE(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol);
    141   DCHECK_GT(HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS,
    142             alternate.protocol);
    143 
    144   if (alternate.protocol != HttpAlternateProtocols::NPN_SPDY_2)
    145     return false;
    146 
    147   origin.set_port(alternate.port);
    148   if (HttpStreamFactory::HasSpdyExclusion(origin))
    149     return false;
    150 
    151   *alternate_url = UpgradeUrlToHttps(original_url);
    152   return true;
    153 }
    154 
    155 void HttpStreamFactoryImpl::OrphanJob(Job* job, const Request* request) {
    156   DCHECK(ContainsKey(request_map_, job));
    157   DCHECK_EQ(request_map_[job], request);
    158   DCHECK(!ContainsKey(orphaned_job_set_, job));
    159 
    160   request_map_.erase(job);
    161 
    162   orphaned_job_set_.insert(job);
    163   job->Orphan(request);
    164 }
    165 
    166 void HttpStreamFactoryImpl::OnSpdySessionReady(
    167     scoped_refptr<SpdySession> spdy_session,
    168     bool direct,
    169     const SSLConfig& used_ssl_config,
    170     const ProxyInfo& used_proxy_info,
    171     bool was_npn_negotiated,
    172     bool using_spdy,
    173     const NetLog::Source& source) {
    174   const HostPortProxyPair& spdy_session_key =
    175       spdy_session->host_port_proxy_pair();
    176   while (!spdy_session->IsClosed()) {
    177     // Each iteration may empty out the RequestSet for |spdy_session_key_ in
    178     // |spdy_session_request_map_|. So each time, check for RequestSet and use
    179     // the first one.
    180     //
    181     // TODO(willchan): If it's important, switch RequestSet out for a FIFO
    182     // pqueue (Order by priority first, then FIFO within same priority). Unclear
    183     // that it matters here.
    184     if (!ContainsKey(spdy_session_request_map_, spdy_session_key))
    185       break;
    186     Request* request = *spdy_session_request_map_[spdy_session_key].begin();
    187     request->Complete(was_npn_negotiated,
    188                       using_spdy,
    189                       source);
    190     bool use_relative_url = direct || request->url().SchemeIs("https");
    191     request->OnStreamReady(NULL, used_ssl_config, used_proxy_info,
    192                            new SpdyHttpStream(spdy_session, use_relative_url));
    193   }
    194   // TODO(mbelshe): Alert other valid requests.
    195 }
    196 
    197 void HttpStreamFactoryImpl::OnOrphanedJobComplete(const Job* job) {
    198   orphaned_job_set_.erase(job);
    199   delete job;
    200 }
    201 
    202 void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) {
    203   preconnect_job_set_.erase(job);
    204   delete job;
    205   OnPreconnectsCompleteInternal();
    206 }
    207 
    208 }  // namespace net
    209