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