• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UpgradeUrlToHttps(const GURL & original_url)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 
HttpStreamFactoryImpl(HttpNetworkSession * session)34 HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session)
35     : session_(session) {}
36 
~HttpStreamFactoryImpl()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 
RequestStream(const HttpRequestInfo & request_info,const SSLConfig & ssl_config,HttpStreamRequest::Delegate * delegate,const BoundNetLog & net_log)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 
PreconnectStreams(int num_streams,const HttpRequestInfo & request_info,const SSLConfig & ssl_config,const BoundNetLog & net_log)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 
AddTLSIntolerantServer(const HostPortPair & server)109 void HttpStreamFactoryImpl::AddTLSIntolerantServer(const HostPortPair& server) {
110   tls_intolerant_servers_.insert(server);
111 }
112 
IsTLSIntolerantServer(const HostPortPair & server) const113 bool HttpStreamFactoryImpl::IsTLSIntolerantServer(
114     const HostPortPair& server) const {
115   return ContainsKey(tls_intolerant_servers_, server);
116 }
117 
GetAlternateProtocolRequestFor(const GURL & original_url,GURL * alternate_url) const118 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 
OrphanJob(Job * job,const Request * request)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 
OnSpdySessionReady(scoped_refptr<SpdySession> spdy_session,bool direct,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,bool was_npn_negotiated,bool using_spdy,const NetLog::Source & source)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 
OnOrphanedJobComplete(const Job * job)197 void HttpStreamFactoryImpl::OnOrphanedJobComplete(const Job* job) {
198   orphaned_job_set_.erase(job);
199   delete job;
200 }
201 
OnPreconnectsComplete(const Job * job)202 void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) {
203   preconnect_job_set_.erase(job);
204   delete job;
205   OnPreconnectsCompleteInternal();
206 }
207 
208 }  // namespace net
209