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.h"
6
7 #include <string>
8
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "net/base/net_log.h"
13 #include "net/base/net_util.h"
14 #include "net/http/http_network_session.h"
15 #include "net/http/http_server_properties.h"
16 #include "net/http/http_stream_factory_impl_job.h"
17 #include "net/http/http_stream_factory_impl_request.h"
18 #include "net/spdy/spdy_http_stream.h"
19 #include "url/gurl.h"
20
21 namespace net {
22
23 namespace {
24
25 const PortAlternateProtocolPair kNoAlternateProtocol = {
26 0, UNINITIALIZED_ALTERNATE_PROTOCOL
27 };
28
UpgradeUrlToHttps(const GURL & original_url,int port)29 GURL UpgradeUrlToHttps(const GURL& original_url, int port) {
30 GURL::Replacements replacements;
31 // new_sheme and new_port need to be in scope here because GURL::Replacements
32 // references the memory contained by them directly.
33 const std::string new_scheme = "https";
34 const std::string new_port = base::IntToString(port);
35 replacements.SetSchemeStr(new_scheme);
36 replacements.SetPortStr(new_port);
37 return original_url.ReplaceComponents(replacements);
38 }
39
40 } // namespace
41
HttpStreamFactoryImpl(HttpNetworkSession * session,bool for_websockets)42 HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session,
43 bool for_websockets)
44 : session_(session),
45 for_websockets_(for_websockets) {}
46
~HttpStreamFactoryImpl()47 HttpStreamFactoryImpl::~HttpStreamFactoryImpl() {
48 DCHECK(request_map_.empty());
49 DCHECK(spdy_session_request_map_.empty());
50
51 std::set<const Job*> tmp_job_set;
52 tmp_job_set.swap(orphaned_job_set_);
53 STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end());
54 DCHECK(orphaned_job_set_.empty());
55
56 tmp_job_set.clear();
57 tmp_job_set.swap(preconnect_job_set_);
58 STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end());
59 DCHECK(preconnect_job_set_.empty());
60 }
61
RequestStream(const HttpRequestInfo & request_info,RequestPriority priority,const SSLConfig & server_ssl_config,const SSLConfig & proxy_ssl_config,HttpStreamRequest::Delegate * delegate,const BoundNetLog & net_log)62 HttpStreamRequest* HttpStreamFactoryImpl::RequestStream(
63 const HttpRequestInfo& request_info,
64 RequestPriority priority,
65 const SSLConfig& server_ssl_config,
66 const SSLConfig& proxy_ssl_config,
67 HttpStreamRequest::Delegate* delegate,
68 const BoundNetLog& net_log) {
69 DCHECK(!for_websockets_);
70 return RequestStreamInternal(request_info,
71 priority,
72 server_ssl_config,
73 proxy_ssl_config,
74 delegate,
75 NULL,
76 net_log);
77 }
78
RequestWebSocketHandshakeStream(const HttpRequestInfo & request_info,RequestPriority priority,const SSLConfig & server_ssl_config,const SSLConfig & proxy_ssl_config,HttpStreamRequest::Delegate * delegate,WebSocketHandshakeStreamBase::CreateHelper * create_helper,const BoundNetLog & net_log)79 HttpStreamRequest* HttpStreamFactoryImpl::RequestWebSocketHandshakeStream(
80 const HttpRequestInfo& request_info,
81 RequestPriority priority,
82 const SSLConfig& server_ssl_config,
83 const SSLConfig& proxy_ssl_config,
84 HttpStreamRequest::Delegate* delegate,
85 WebSocketHandshakeStreamBase::CreateHelper* create_helper,
86 const BoundNetLog& net_log) {
87 DCHECK(for_websockets_);
88 DCHECK(create_helper);
89 return RequestStreamInternal(request_info,
90 priority,
91 server_ssl_config,
92 proxy_ssl_config,
93 delegate,
94 create_helper,
95 net_log);
96 }
97
RequestStreamInternal(const HttpRequestInfo & request_info,RequestPriority priority,const SSLConfig & server_ssl_config,const SSLConfig & proxy_ssl_config,HttpStreamRequest::Delegate * delegate,WebSocketHandshakeStreamBase::CreateHelper * websocket_handshake_stream_create_helper,const BoundNetLog & net_log)98 HttpStreamRequest* HttpStreamFactoryImpl::RequestStreamInternal(
99 const HttpRequestInfo& request_info,
100 RequestPriority priority,
101 const SSLConfig& server_ssl_config,
102 const SSLConfig& proxy_ssl_config,
103 HttpStreamRequest::Delegate* delegate,
104 WebSocketHandshakeStreamBase::CreateHelper*
105 websocket_handshake_stream_create_helper,
106 const BoundNetLog& net_log) {
107 Request* request = new Request(request_info.url,
108 this,
109 delegate,
110 websocket_handshake_stream_create_helper,
111 net_log);
112
113 GURL alternate_url;
114 PortAlternateProtocolPair alternate =
115 GetAlternateProtocolRequestFor(request_info.url, &alternate_url);
116 Job* alternate_job = NULL;
117 if (alternate.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
118 // Never share connection with other jobs for FTP requests.
119 DCHECK(!request_info.url.SchemeIs("ftp"));
120
121 HttpRequestInfo alternate_request_info = request_info;
122 alternate_request_info.url = alternate_url;
123 alternate_job =
124 new Job(this, session_, alternate_request_info, priority,
125 server_ssl_config, proxy_ssl_config, net_log.net_log());
126 request->AttachJob(alternate_job);
127 alternate_job->MarkAsAlternate(request_info.url, alternate);
128 }
129
130 Job* job = new Job(this, session_, request_info, priority,
131 server_ssl_config, proxy_ssl_config, net_log.net_log());
132 request->AttachJob(job);
133 if (alternate_job) {
134 // Never share connection with other jobs for FTP requests.
135 DCHECK(!request_info.url.SchemeIs("ftp"));
136
137 job->WaitFor(alternate_job);
138 // Make sure to wait until we call WaitFor(), before starting
139 // |alternate_job|, otherwise |alternate_job| will not notify |job|
140 // appropriately.
141 alternate_job->Start(request);
142 }
143 // Even if |alternate_job| has already finished, it won't have notified the
144 // request yet, since we defer that to the next iteration of the MessageLoop,
145 // so starting |job| is always safe.
146 job->Start(request);
147 return request;
148 }
149
PreconnectStreams(int num_streams,const HttpRequestInfo & request_info,RequestPriority priority,const SSLConfig & server_ssl_config,const SSLConfig & proxy_ssl_config)150 void HttpStreamFactoryImpl::PreconnectStreams(
151 int num_streams,
152 const HttpRequestInfo& request_info,
153 RequestPriority priority,
154 const SSLConfig& server_ssl_config,
155 const SSLConfig& proxy_ssl_config) {
156 DCHECK(!for_websockets_);
157 GURL alternate_url;
158 PortAlternateProtocolPair alternate =
159 GetAlternateProtocolRequestFor(request_info.url, &alternate_url);
160 Job* job = NULL;
161 if (alternate.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
162 HttpRequestInfo alternate_request_info = request_info;
163 alternate_request_info.url = alternate_url;
164 job = new Job(this, session_, alternate_request_info, priority,
165 server_ssl_config, proxy_ssl_config, session_->net_log());
166 job->MarkAsAlternate(request_info.url, alternate);
167 } else {
168 job = new Job(this, session_, request_info, priority,
169 server_ssl_config, proxy_ssl_config, session_->net_log());
170 }
171 preconnect_job_set_.insert(job);
172 job->Preconnect(num_streams);
173 }
174
GetHostMappingRules() const175 const HostMappingRules* HttpStreamFactoryImpl::GetHostMappingRules() const {
176 return session_->params().host_mapping_rules;
177 }
178
GetAlternateProtocolRequestFor(const GURL & original_url,GURL * alternate_url)179 PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
180 const GURL& original_url,
181 GURL* alternate_url) {
182 if (!session_->params().use_alternate_protocols)
183 return kNoAlternateProtocol;
184
185 if (original_url.SchemeIs("ftp"))
186 return kNoAlternateProtocol;
187
188 HostPortPair origin = HostPortPair(original_url.HostNoBrackets(),
189 original_url.EffectiveIntPort());
190
191 HttpServerProperties& http_server_properties =
192 *session_->http_server_properties();
193 if (!http_server_properties.HasAlternateProtocol(origin))
194 return kNoAlternateProtocol;
195
196 PortAlternateProtocolPair alternate =
197 http_server_properties.GetAlternateProtocol(origin);
198 if (alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) {
199 HistogramAlternateProtocolUsage(
200 ALTERNATE_PROTOCOL_USAGE_BROKEN,
201 http_server_properties.GetAlternateProtocolExperiment());
202 return kNoAlternateProtocol;
203 }
204
205 if (!IsAlternateProtocolValid(alternate.protocol)) {
206 NOTREACHED();
207 return kNoAlternateProtocol;
208 }
209
210 // Some shared unix systems may have user home directories (like
211 // http://foo.com/~mike) which allow users to emit headers. This is a bad
212 // idea already, but with Alternate-Protocol, it provides the ability for a
213 // single user on a multi-user system to hijack the alternate protocol.
214 // These systems also enforce ports <1024 as restricted ports. So don't
215 // allow protocol upgrades to user-controllable ports.
216 const int kUnrestrictedPort = 1024;
217 if (!session_->params().enable_user_alternate_protocol_ports &&
218 (alternate.port >= kUnrestrictedPort &&
219 origin.port() < kUnrestrictedPort))
220 return kNoAlternateProtocol;
221
222 origin.set_port(alternate.port);
223 if (alternate.protocol >= NPN_SPDY_MINIMUM_VERSION &&
224 alternate.protocol <= NPN_SPDY_MAXIMUM_VERSION) {
225 if (!HttpStreamFactory::spdy_enabled())
226 return kNoAlternateProtocol;
227
228 if (session_->HasSpdyExclusion(origin))
229 return kNoAlternateProtocol;
230
231 *alternate_url = UpgradeUrlToHttps(original_url, alternate.port);
232 } else {
233 DCHECK_EQ(QUIC, alternate.protocol);
234 if (!session_->params().enable_quic ||
235 !(original_url.SchemeIs("http") ||
236 session_->params().enable_quic_https)) {
237 return kNoAlternateProtocol;
238 }
239 // TODO(rch): Figure out how to make QUIC iteract with PAC
240 // scripts. By not re-writing the URL, we will query the PAC script
241 // for the proxy to use to reach the original URL via TCP. But
242 // the alternate request will be going via UDP to a different port.
243 *alternate_url = original_url;
244 }
245 return alternate;
246 }
247
OrphanJob(Job * job,const Request * request)248 void HttpStreamFactoryImpl::OrphanJob(Job* job, const Request* request) {
249 DCHECK(ContainsKey(request_map_, job));
250 DCHECK_EQ(request_map_[job], request);
251 DCHECK(!ContainsKey(orphaned_job_set_, job));
252
253 request_map_.erase(job);
254
255 orphaned_job_set_.insert(job);
256 job->Orphan(request);
257 }
258
OnNewSpdySessionReady(const base::WeakPtr<SpdySession> & spdy_session,bool direct,const SSLConfig & used_ssl_config,const ProxyInfo & used_proxy_info,bool was_npn_negotiated,NextProto protocol_negotiated,bool using_spdy,const BoundNetLog & net_log)259 void HttpStreamFactoryImpl::OnNewSpdySessionReady(
260 const base::WeakPtr<SpdySession>& spdy_session,
261 bool direct,
262 const SSLConfig& used_ssl_config,
263 const ProxyInfo& used_proxy_info,
264 bool was_npn_negotiated,
265 NextProto protocol_negotiated,
266 bool using_spdy,
267 const BoundNetLog& net_log) {
268 while (true) {
269 if (!spdy_session)
270 break;
271 const SpdySessionKey& spdy_session_key = spdy_session->spdy_session_key();
272 // Each iteration may empty out the RequestSet for |spdy_session_key| in
273 // |spdy_session_request_map_|. So each time, check for RequestSet and use
274 // the first one.
275 //
276 // TODO(willchan): If it's important, switch RequestSet out for a FIFO
277 // queue (Order by priority first, then FIFO within same priority). Unclear
278 // that it matters here.
279 if (!ContainsKey(spdy_session_request_map_, spdy_session_key))
280 break;
281 Request* request = *spdy_session_request_map_[spdy_session_key].begin();
282 request->Complete(was_npn_negotiated,
283 protocol_negotiated,
284 using_spdy,
285 net_log);
286 if (for_websockets_) {
287 // TODO(ricea): Restore this code path when WebSocket over SPDY
288 // implementation is ready.
289 NOTREACHED();
290 } else {
291 bool use_relative_url = direct || request->url().SchemeIs("https");
292 request->OnStreamReady(
293 NULL,
294 used_ssl_config,
295 used_proxy_info,
296 new SpdyHttpStream(spdy_session, use_relative_url));
297 }
298 }
299 // TODO(mbelshe): Alert other valid requests.
300 }
301
OnOrphanedJobComplete(const Job * job)302 void HttpStreamFactoryImpl::OnOrphanedJobComplete(const Job* job) {
303 orphaned_job_set_.erase(job);
304 delete job;
305 }
306
OnPreconnectsComplete(const Job * job)307 void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) {
308 preconnect_job_set_.erase(job);
309 delete job;
310 OnPreconnectsCompleteInternal();
311 }
312
313 } // namespace net
314