| 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 |