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