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