Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/http/http_stream_factory_impl.h" | 5 #include "net/http/http_stream_factory_impl.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/memory/ptr_util.h" | |
| 10 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
| 11 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
| 12 #include "net/http/http_network_session.h" | 13 #include "net/http/http_network_session.h" |
| 13 #include "net/http/http_server_properties.h" | 14 #include "net/http/http_server_properties.h" |
| 14 #include "net/http/http_stream_factory_impl_job.h" | 15 #include "net/http/http_stream_factory_impl_job.h" |
| 16 #include "net/http/http_stream_factory_impl_job_controller.h" | |
| 15 #include "net/http/http_stream_factory_impl_request.h" | 17 #include "net/http/http_stream_factory_impl_request.h" |
| 16 #include "net/http/transport_security_state.h" | 18 #include "net/http/transport_security_state.h" |
| 17 #include "net/log/net_log.h" | 19 #include "net/log/net_log.h" |
| 18 #include "net/quic/quic_server_id.h" | 20 #include "net/quic/quic_server_id.h" |
| 19 #include "net/spdy/bidirectional_stream_spdy_impl.h" | 21 #include "net/spdy/bidirectional_stream_spdy_impl.h" |
| 20 #include "net/spdy/spdy_http_stream.h" | 22 #include "net/spdy/spdy_http_stream.h" |
| 21 #include "url/gurl.h" | 23 #include "url/gurl.h" |
| 22 | 24 |
| 23 namespace net { | 25 namespace net { |
| 24 | 26 |
| 25 HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session, | 27 HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session, |
| 26 bool for_websockets) | 28 bool for_websockets) |
| 27 : session_(session), | 29 : session_(session), |
| 28 for_websockets_(for_websockets) {} | 30 for_websockets_(for_websockets) {} |
| 29 | 31 |
| 30 HttpStreamFactoryImpl::~HttpStreamFactoryImpl() { | 32 HttpStreamFactoryImpl::~HttpStreamFactoryImpl() { |
| 31 DCHECK(request_map_.empty()); | 33 DCHECK(request_map_.empty()); |
| 32 DCHECK(spdy_session_request_map_.empty()); | 34 DCHECK(spdy_session_request_map_.empty()); |
| 33 | 35 job_controller_set_.clear(); |
| 34 std::set<const Job*> tmp_job_set; | |
| 35 tmp_job_set.swap(orphaned_job_set_); | |
| 36 STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end()); | |
| 37 DCHECK(orphaned_job_set_.empty()); | |
| 38 | |
| 39 tmp_job_set.clear(); | |
| 40 tmp_job_set.swap(preconnect_job_set_); | |
| 41 STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end()); | |
| 42 DCHECK(preconnect_job_set_.empty()); | |
| 43 } | 36 } |
| 44 | 37 |
| 45 HttpStreamRequest* HttpStreamFactoryImpl::RequestStream( | 38 HttpStreamRequest* HttpStreamFactoryImpl::RequestStream( |
| 46 const HttpRequestInfo& request_info, | 39 const HttpRequestInfo& request_info, |
| 47 RequestPriority priority, | 40 RequestPriority priority, |
| 48 const SSLConfig& server_ssl_config, | 41 const SSLConfig& server_ssl_config, |
| 49 const SSLConfig& proxy_ssl_config, | 42 const SSLConfig& proxy_ssl_config, |
| 50 HttpStreamRequest::Delegate* delegate, | 43 HttpStreamRequest::Delegate* delegate, |
| 51 const BoundNetLog& net_log) { | 44 const BoundNetLog& net_log) { |
| 52 DCHECK(!for_websockets_); | 45 DCHECK(!for_websockets_); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 88 HttpStreamRequest* HttpStreamFactoryImpl::RequestStreamInternal( | 81 HttpStreamRequest* HttpStreamFactoryImpl::RequestStreamInternal( |
| 89 const HttpRequestInfo& request_info, | 82 const HttpRequestInfo& request_info, |
| 90 RequestPriority priority, | 83 RequestPriority priority, |
| 91 const SSLConfig& server_ssl_config, | 84 const SSLConfig& server_ssl_config, |
| 92 const SSLConfig& proxy_ssl_config, | 85 const SSLConfig& proxy_ssl_config, |
| 93 HttpStreamRequest::Delegate* delegate, | 86 HttpStreamRequest::Delegate* delegate, |
| 94 WebSocketHandshakeStreamBase::CreateHelper* | 87 WebSocketHandshakeStreamBase::CreateHelper* |
| 95 websocket_handshake_stream_create_helper, | 88 websocket_handshake_stream_create_helper, |
| 96 HttpStreamRequest::StreamType stream_type, | 89 HttpStreamRequest::StreamType stream_type, |
| 97 const BoundNetLog& net_log) { | 90 const BoundNetLog& net_log) { |
| 98 Request* request = new Request(request_info.url, this, delegate, | 91 JobController* job_controller = new JobController(this, delegate, session_); |
| 99 websocket_handshake_stream_create_helper, | 92 job_controller_set_.insert(base::WrapUnique(job_controller)); |
| 100 net_log, stream_type); | |
| 101 HostPortPair destination(HostPortPair::FromURL(request_info.url)); | |
| 102 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination); | |
| 103 | 93 |
| 104 Job* job = | 94 Request* request = job_controller->Start( |
| 105 new Job(this, session_, request_info, priority, server_ssl_config, | 95 request_info, delegate, websocket_handshake_stream_create_helper, net_log, |
| 106 proxy_ssl_config, destination, origin_url, net_log.net_log()); | 96 stream_type, priority, server_ssl_config, proxy_ssl_config); |
| 107 request->AttachJob(job); | |
| 108 | 97 |
| 109 const AlternativeService alternative_service = | |
| 110 GetAlternativeServiceFor(request_info, delegate, stream_type); | |
| 111 | |
| 112 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) { | |
| 113 // Never share connection with other jobs for FTP requests. | |
| 114 DVLOG(1) << "Selected alternative service (host: " | |
| 115 << alternative_service.host_port_pair().host() | |
| 116 << " port: " << alternative_service.host_port_pair().port() << ")"; | |
| 117 | |
| 118 DCHECK(!request_info.url.SchemeIs("ftp")); | |
| 119 HostPortPair alternative_destination(alternative_service.host_port_pair()); | |
| 120 ignore_result( | |
| 121 ApplyHostMappingRules(request_info.url, &alternative_destination)); | |
| 122 | |
| 123 Job* alternative_job = | |
| 124 new Job(this, session_, request_info, priority, server_ssl_config, | |
| 125 proxy_ssl_config, alternative_destination, origin_url, | |
| 126 alternative_service, net_log.net_log()); | |
| 127 request->AttachJob(alternative_job); | |
| 128 | |
| 129 job->WaitFor(alternative_job); | |
| 130 // Make sure to wait until we call WaitFor(), before starting | |
| 131 // |alternative_job|, otherwise |alternative_job| will not notify |job| | |
| 132 // appropriately. | |
| 133 alternative_job->Start(request); | |
| 134 } | |
| 135 | |
| 136 // Even if |alternative_job| has already finished, it will not have notified | |
| 137 // the request yet, since we defer that to the next iteration of the | |
| 138 // MessageLoop, so starting |job| is always safe. | |
| 139 job->Start(request); | |
| 140 return request; | 98 return request; |
| 141 } | 99 } |
| 142 | 100 |
| 143 void HttpStreamFactoryImpl::PreconnectStreams( | 101 void HttpStreamFactoryImpl::PreconnectStreams( |
| 144 int num_streams, | 102 int num_streams, |
| 145 const HttpRequestInfo& request_info) { | 103 const HttpRequestInfo& request_info) { |
| 146 SSLConfig server_ssl_config; | 104 SSLConfig server_ssl_config; |
| 147 SSLConfig proxy_ssl_config; | 105 SSLConfig proxy_ssl_config; |
| 148 session_->GetSSLConfig(request_info, &server_ssl_config, &proxy_ssl_config); | 106 session_->GetSSLConfig(request_info, &server_ssl_config, &proxy_ssl_config); |
| 149 // All preconnects should perform EV certificate verification. | 107 // All preconnects should perform EV certificate verification. |
| 150 server_ssl_config.verify_ev_cert = true; | 108 server_ssl_config.verify_ev_cert = true; |
| 151 proxy_ssl_config.verify_ev_cert = true; | 109 proxy_ssl_config.verify_ev_cert = true; |
| 152 | 110 |
| 153 DCHECK(!for_websockets_); | 111 DCHECK(!for_websockets_); |
| 154 AlternativeService alternative_service = GetAlternativeServiceFor( | 112 |
| 155 request_info, nullptr, HttpStreamRequest::HTTP_STREAM); | 113 JobController* job_controller = new JobController(this, nullptr, session_); |
| 156 HostPortPair destination(HostPortPair::FromURL(request_info.url)); | 114 job_controller_set_.insert(base::WrapUnique(job_controller)); |
| 157 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination); | 115 job_controller->Preconnect(num_streams, request_info, server_ssl_config, |
| 158 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) { | 116 proxy_ssl_config); |
| 159 if (session_->params().quic_disable_preconnect_if_0rtt && | |
| 160 alternative_service.protocol == QUIC && | |
| 161 session_->quic_stream_factory()->ZeroRTTEnabledFor(QuicServerId( | |
| 162 alternative_service.host_port_pair(), request_info.privacy_mode))) { | |
| 163 return; | |
| 164 } | |
| 165 destination = alternative_service.host_port_pair(); | |
| 166 ignore_result(ApplyHostMappingRules(request_info.url, &destination)); | |
| 167 } | |
| 168 // Due to how the socket pools handle priorities and idle sockets, only IDLE | |
| 169 // priority currently makes sense for preconnects. The priority for | |
| 170 // preconnects is currently ignored (see RequestSocketsForPool()), but could | |
| 171 // be used at some point for proxy resolution or something. | |
| 172 Job* job = new Job(this, session_, request_info, IDLE, server_ssl_config, | |
| 173 proxy_ssl_config, destination, origin_url, | |
| 174 alternative_service, session_->net_log()); | |
| 175 preconnect_job_set_.insert(job); | |
| 176 job->Preconnect(num_streams); | |
| 177 } | 117 } |
| 178 | 118 |
| 179 const HostMappingRules* HttpStreamFactoryImpl::GetHostMappingRules() const { | 119 const HostMappingRules* HttpStreamFactoryImpl::GetHostMappingRules() const { |
| 180 return session_->params().host_mapping_rules; | 120 return session_->params().host_mapping_rules; |
| 181 } | 121 } |
| 182 | 122 |
| 183 AlternativeService HttpStreamFactoryImpl::GetAlternativeServiceFor( | |
| 184 const HttpRequestInfo& request_info, | |
| 185 HttpStreamRequest::Delegate* delegate, | |
| 186 HttpStreamRequest::StreamType stream_type) { | |
| 187 GURL original_url = request_info.url; | |
| 188 | |
| 189 if (original_url.SchemeIs("ftp")) | |
| 190 return AlternativeService(); | |
| 191 | |
| 192 url::SchemeHostPort origin(original_url); | |
| 193 HttpServerProperties& http_server_properties = | |
| 194 *session_->http_server_properties(); | |
| 195 const AlternativeServiceVector alternative_service_vector = | |
| 196 http_server_properties.GetAlternativeServices(origin); | |
| 197 if (alternative_service_vector.empty()) | |
| 198 return AlternativeService(); | |
| 199 | |
| 200 bool quic_advertised = false; | |
| 201 bool quic_all_broken = true; | |
| 202 | |
| 203 const bool enable_different_host = | |
| 204 session_->params().enable_alternative_service_with_different_host; | |
| 205 | |
| 206 // First Alt-Svc that is not marked as broken. | |
| 207 AlternativeService first_alternative_service; | |
| 208 | |
| 209 for (const AlternativeService& alternative_service : | |
| 210 alternative_service_vector) { | |
| 211 DCHECK(IsAlternateProtocolValid(alternative_service.protocol)); | |
| 212 if (!quic_advertised && alternative_service.protocol == QUIC) | |
| 213 quic_advertised = true; | |
| 214 if (http_server_properties.IsAlternativeServiceBroken( | |
| 215 alternative_service)) { | |
| 216 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN); | |
| 217 continue; | |
| 218 } | |
| 219 | |
| 220 if (origin.host() != alternative_service.host && !enable_different_host) | |
| 221 continue; | |
| 222 | |
| 223 // Some shared unix systems may have user home directories (like | |
| 224 // http://foo.com/~mike) which allow users to emit headers. This is a bad | |
| 225 // idea already, but with Alternate-Protocol, it provides the ability for a | |
| 226 // single user on a multi-user system to hijack the alternate protocol. | |
| 227 // These systems also enforce ports <1024 as restricted ports. So don't | |
| 228 // allow protocol upgrades to user-controllable ports. | |
| 229 const int kUnrestrictedPort = 1024; | |
| 230 if (!session_->params().enable_user_alternate_protocol_ports && | |
| 231 (alternative_service.port >= kUnrestrictedPort && | |
| 232 origin.port() < kUnrestrictedPort)) | |
| 233 continue; | |
| 234 | |
| 235 if (alternative_service.protocol >= NPN_SPDY_MINIMUM_VERSION && | |
| 236 alternative_service.protocol <= NPN_SPDY_MAXIMUM_VERSION) { | |
| 237 if (!HttpStreamFactory::spdy_enabled()) | |
| 238 continue; | |
| 239 | |
| 240 // Cache this entry if we don't have a non-broken Alt-Svc yet. | |
| 241 if (first_alternative_service.protocol == | |
| 242 UNINITIALIZED_ALTERNATE_PROTOCOL) | |
| 243 first_alternative_service = alternative_service; | |
| 244 continue; | |
| 245 } | |
| 246 | |
| 247 DCHECK_EQ(QUIC, alternative_service.protocol); | |
| 248 quic_all_broken = false; | |
| 249 if (!session_->params().enable_quic) | |
| 250 continue; | |
| 251 | |
| 252 if (!IsQuicWhitelistedForHost(origin.host())) | |
| 253 continue; | |
| 254 | |
| 255 if (stream_type == HttpStreamRequest::BIDIRECTIONAL_STREAM && | |
| 256 session_->params().quic_disable_bidirectional_streams) { | |
| 257 continue; | |
| 258 } | |
| 259 | |
| 260 if (session_->quic_stream_factory()->IsQuicDisabled( | |
| 261 alternative_service.port)) | |
| 262 continue; | |
| 263 | |
| 264 if (!original_url.SchemeIs("https")) | |
| 265 continue; | |
| 266 | |
| 267 // Check whether there is an existing QUIC session to use for this origin. | |
| 268 HostPortPair destination(alternative_service.host_port_pair()); | |
| 269 ignore_result(ApplyHostMappingRules(original_url, &destination)); | |
| 270 QuicServerId server_id(destination, request_info.privacy_mode); | |
| 271 | |
| 272 HostPortPair origin_copy(origin.host(), origin.port()); | |
| 273 ignore_result(ApplyHostMappingRules(original_url, &origin_copy)); | |
| 274 | |
| 275 if (session_->quic_stream_factory()->CanUseExistingSession( | |
| 276 server_id, origin_copy.host())) { | |
| 277 return alternative_service; | |
| 278 } | |
| 279 | |
| 280 // Cache this entry if we don't have a non-broken Alt-Svc yet. | |
| 281 if (first_alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) | |
| 282 first_alternative_service = alternative_service; | |
| 283 } | |
| 284 | |
| 285 // Ask delegate to mark QUIC as broken for the origin. | |
| 286 if (quic_advertised && quic_all_broken && delegate != nullptr) | |
| 287 delegate->OnQuicBroken(); | |
| 288 | |
| 289 return first_alternative_service; | |
| 290 } | |
| 291 | |
| 292 void HttpStreamFactoryImpl::OrphanJob(Job* job, const Request* request) { | |
| 293 DCHECK(ContainsKey(request_map_, job)); | |
| 294 DCHECK_EQ(request_map_[job], request); | |
| 295 DCHECK(!ContainsKey(orphaned_job_set_, job)); | |
| 296 | |
| 297 request_map_.erase(job); | |
| 298 | |
| 299 orphaned_job_set_.insert(job); | |
| 300 job->Orphan(request); | |
| 301 } | |
| 302 | |
| 303 void HttpStreamFactoryImpl::OnNewSpdySessionReady( | 123 void HttpStreamFactoryImpl::OnNewSpdySessionReady( |
| 304 const base::WeakPtr<SpdySession>& spdy_session, | 124 const base::WeakPtr<SpdySession>& spdy_session, |
| 305 bool direct, | 125 bool direct, |
| 306 const SSLConfig& used_ssl_config, | 126 const SSLConfig& used_ssl_config, |
| 307 const ProxyInfo& used_proxy_info, | 127 const ProxyInfo& used_proxy_info, |
| 308 bool was_npn_negotiated, | 128 bool was_npn_negotiated, |
| 309 NextProto protocol_negotiated, | 129 NextProto protocol_negotiated, |
| 310 bool using_spdy, | 130 bool using_spdy, |
| 311 const BoundNetLog& net_log) { | 131 const BoundNetLog& net_log) { |
| 312 while (true) { | 132 while (true) { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 324 break; | 144 break; |
| 325 Request* request = *spdy_session_request_map_[spdy_session_key].begin(); | 145 Request* request = *spdy_session_request_map_[spdy_session_key].begin(); |
| 326 request->Complete(was_npn_negotiated, protocol_negotiated, using_spdy); | 146 request->Complete(was_npn_negotiated, protocol_negotiated, using_spdy); |
| 327 if (for_websockets_) { | 147 if (for_websockets_) { |
| 328 // TODO(ricea): Restore this code path when WebSocket over SPDY | 148 // TODO(ricea): Restore this code path when WebSocket over SPDY |
| 329 // implementation is ready. | 149 // implementation is ready. |
| 330 NOTREACHED(); | 150 NOTREACHED(); |
| 331 } else if (request->stream_type() == | 151 } else if (request->stream_type() == |
| 332 HttpStreamRequest::BIDIRECTIONAL_STREAM) { | 152 HttpStreamRequest::BIDIRECTIONAL_STREAM) { |
| 333 request->OnBidirectionalStreamImplReady( | 153 request->OnBidirectionalStreamImplReady( |
| 334 nullptr, used_ssl_config, used_proxy_info, | 154 used_ssl_config, used_proxy_info, |
| 335 new BidirectionalStreamSpdyImpl(spdy_session)); | 155 new BidirectionalStreamSpdyImpl(spdy_session)); |
| 336 } else { | 156 } else { |
| 337 bool use_relative_url = direct || request->url().SchemeIs("https"); | 157 bool use_relative_url = direct || request->url().SchemeIs("https"); |
| 338 request->OnStreamReady( | 158 request->OnStreamReady( |
| 339 nullptr, used_ssl_config, used_proxy_info, | 159 used_ssl_config, used_proxy_info, |
| 340 new SpdyHttpStream(spdy_session, use_relative_url)); | 160 new SpdyHttpStream(spdy_session, use_relative_url)); |
| 341 } | 161 } |
| 342 } | 162 } |
| 343 // TODO(mbelshe): Alert other valid requests. | 163 // TODO(mbelshe): Alert other valid requests. |
| 344 } | 164 } |
| 345 | 165 |
| 346 void HttpStreamFactoryImpl::OnOrphanedJobComplete(const Job* job) { | 166 void HttpStreamFactoryImpl::OnJobControllerComplete(JobController* controller) { |
| 347 orphaned_job_set_.erase(job); | 167 auto it = std::find_if(job_controller_set_.begin(), job_controller_set_.end(), |
| 348 delete job; | 168 [controller](const std::unique_ptr<JobController>& c) { |
| 349 } | 169 return c.get() == controller; |
| 170 }); | |
|
Ryan Hamilton
2016/05/13 20:50:40
holy cow, that is hideous :(
That's almost enough
Zhongyi Shi
2016/05/13 22:22:19
Done.
| |
| 171 CHECK(it != job_controller_set_.end()); | |
| 350 | 172 |
| 351 void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) { | 173 job_controller_set_.erase(it); |
| 352 preconnect_job_set_.erase(job); | |
| 353 delete job; | |
| 354 OnPreconnectsCompleteInternal(); | |
| 355 } | |
| 356 | |
| 357 bool HttpStreamFactoryImpl::IsQuicWhitelistedForHost(const std::string& host) { | |
| 358 bool whitelist_needed = false; | |
| 359 for (QuicVersion version : session_->params().quic_supported_versions) { | |
| 360 if (version <= QUIC_VERSION_30) { | |
| 361 whitelist_needed = true; | |
| 362 break; | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 // The QUIC whitelist is not needed in QUIC versions after 30. | |
| 367 if (!whitelist_needed) | |
| 368 return true; | |
| 369 | |
| 370 if (session_->params().transport_security_state->IsGooglePinnedHost(host)) | |
| 371 return true; | |
| 372 | |
| 373 return ContainsKey(session_->params().quic_host_whitelist, | |
| 374 base::ToLowerASCII(host)); | |
| 375 } | 174 } |
| 376 | 175 |
| 377 } // namespace net | 176 } // namespace net |
| OLD | NEW |