// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/http/http_stream_factory.h" #include #include #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_usage_estimator.h" #include "base/trace_event/process_memory_dump.h" #include "net/base/host_mapping_rules.h" #include "net/base/host_port_pair.h" #include "net/base/parse_number.h" #include "net/base/port_util.h" #include "net/http/http_network_session.h" #include "net/http/http_response_headers.h" #include "net/http/http_server_properties.h" #include "net/http/http_stream_factory_job.h" #include "net/http/http_stream_factory_job_controller.h" #include "net/http/transport_security_state.h" #include "net/quic/chromium/quic_http_utils.h" #include "net/spdy/bidirectional_stream_spdy_impl.h" #include "net/spdy/spdy_http_stream.h" #include "net/third_party/quic/core/quic_packets.h" #include "net/third_party/quic/core/quic_server_id.h" #include "net/third_party/spdy/core/spdy_alt_svc_wire_format.h" #include "url/gurl.h" #include "url/scheme_host_port.h" #include "url/url_constants.h" namespace net { HttpStreamFactory::HttpStreamFactory(HttpNetworkSession* session) : session_(session), job_factory_(std::make_unique()) {} HttpStreamFactory::~HttpStreamFactory() {} void HttpStreamFactory::ProcessAlternativeServices( HttpNetworkSession* session, const HttpResponseHeaders* headers, const url::SchemeHostPort& http_server) { if (!headers->HasHeader(kAlternativeServiceHeader)) return; std::string alternative_service_str; headers->GetNormalizedHeader(kAlternativeServiceHeader, &alternative_service_str); spdy::SpdyAltSvcWireFormat::AlternativeServiceVector alternative_service_vector; if (!spdy::SpdyAltSvcWireFormat::ParseHeaderFieldValue( alternative_service_str, &alternative_service_vector)) { return; } // Convert spdy::SpdyAltSvcWireFormat::AlternativeService entries // to net::AlternativeServiceInfo. AlternativeServiceInfoVector alternative_service_info_vector; for (const spdy::SpdyAltSvcWireFormat::AlternativeService& alternative_service_entry : alternative_service_vector) { NextProto protocol = NextProtoFromString(alternative_service_entry.protocol_id); if (!IsAlternateProtocolValid(protocol) || !session->IsProtocolEnabled(protocol) || !IsPortValid(alternative_service_entry.port)) { continue; } // Check if QUIC version is supported. Filter supported QUIC versions. QuicTransportVersionVector advertised_versions; if (protocol == kProtoQUIC && !alternative_service_entry.version.empty()) { advertised_versions = FilterSupportedAltSvcVersions( alternative_service_entry, session->params().quic_supported_versions, session->params().support_ietf_format_quic_altsvc); if (advertised_versions.empty()) continue; } AlternativeService alternative_service(protocol, alternative_service_entry.host, alternative_service_entry.port); base::Time expiration = base::Time::Now() + base::TimeDelta::FromSeconds(alternative_service_entry.max_age); AlternativeServiceInfo alternative_service_info; if (protocol == kProtoQUIC) { alternative_service_info = AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( alternative_service, expiration, advertised_versions); } else { alternative_service_info = AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo( alternative_service, expiration); } alternative_service_info_vector.push_back(alternative_service_info); } session->http_server_properties()->SetAlternativeServices( RewriteHost(http_server), alternative_service_info_vector); } url::SchemeHostPort HttpStreamFactory::RewriteHost( const url::SchemeHostPort& server) { HostPortPair host_port_pair(server.host(), server.port()); const HostMappingRules* mapping_rules = GetHostMappingRules(); if (mapping_rules) mapping_rules->RewriteHost(&host_port_pair); return url::SchemeHostPort(server.scheme(), host_port_pair.host(), host_port_pair.port()); } std::unique_ptr HttpStreamFactory::RequestStream( const HttpRequestInfo& request_info, RequestPriority priority, const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config, HttpStreamRequest::Delegate* delegate, bool enable_ip_based_pooling, bool enable_alternative_services, const NetLogWithSource& net_log) { return RequestStreamInternal( request_info, priority, server_ssl_config, proxy_ssl_config, delegate, nullptr, HttpStreamRequest::HTTP_STREAM, false /* is_websocket */, enable_ip_based_pooling, enable_alternative_services, net_log); } std::unique_ptr HttpStreamFactory::RequestWebSocketHandshakeStream( const HttpRequestInfo& request_info, RequestPriority priority, const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config, HttpStreamRequest::Delegate* delegate, WebSocketHandshakeStreamBase::CreateHelper* create_helper, bool enable_ip_based_pooling, bool enable_alternative_services, const NetLogWithSource& net_log) { DCHECK(create_helper); return RequestStreamInternal( request_info, priority, server_ssl_config, proxy_ssl_config, delegate, create_helper, HttpStreamRequest::HTTP_STREAM, true /* is_websocket */, enable_ip_based_pooling, enable_alternative_services, net_log); } std::unique_ptr HttpStreamFactory::RequestBidirectionalStreamImpl( const HttpRequestInfo& request_info, RequestPriority priority, const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config, HttpStreamRequest::Delegate* delegate, bool enable_ip_based_pooling, bool enable_alternative_services, const NetLogWithSource& net_log) { DCHECK(request_info.url.SchemeIs(url::kHttpsScheme)); return RequestStreamInternal( request_info, priority, server_ssl_config, proxy_ssl_config, delegate, nullptr, HttpStreamRequest::BIDIRECTIONAL_STREAM, false /* is_websocket */, enable_ip_based_pooling, enable_alternative_services, net_log); } std::unique_ptr HttpStreamFactory::RequestStreamInternal( const HttpRequestInfo& request_info, RequestPriority priority, const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config, HttpStreamRequest::Delegate* delegate, WebSocketHandshakeStreamBase::CreateHelper* websocket_handshake_stream_create_helper, HttpStreamRequest::StreamType stream_type, bool is_websocket, bool enable_ip_based_pooling, bool enable_alternative_services, const NetLogWithSource& net_log) { auto job_controller = std::make_unique( this, delegate, session_, job_factory_.get(), request_info, /* is_preconnect = */ false, is_websocket, enable_ip_based_pooling, enable_alternative_services, server_ssl_config, proxy_ssl_config); JobController* job_controller_raw_ptr = job_controller.get(); job_controller_set_.insert(std::move(job_controller)); return job_controller_raw_ptr->Start(delegate, websocket_handshake_stream_create_helper, net_log, stream_type, priority); } void HttpStreamFactory::PreconnectStreams(int num_streams, const HttpRequestInfo& request_info) { DCHECK(request_info.url.is_valid()); SSLConfig server_ssl_config; SSLConfig proxy_ssl_config; session_->GetSSLConfig(request_info, &server_ssl_config, &proxy_ssl_config); auto job_controller = std::make_unique( this, nullptr, session_, job_factory_.get(), request_info, /* is_preconnect = */ true, /* is_websocket = */ false, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, server_ssl_config, proxy_ssl_config); JobController* job_controller_raw_ptr = job_controller.get(); job_controller_set_.insert(std::move(job_controller)); job_controller_raw_ptr->Preconnect(num_streams); } const HostMappingRules* HttpStreamFactory::GetHostMappingRules() const { return &session_->params().host_mapping_rules; } void HttpStreamFactory::OnJobControllerComplete(JobController* controller) { for (auto it = job_controller_set_.begin(); it != job_controller_set_.end(); ++it) { if (it->get() == controller) { job_controller_set_.erase(it); return; } } NOTREACHED(); } HttpStreamFactory::PreconnectingProxyServer::PreconnectingProxyServer( ProxyServer proxy_server, PrivacyMode privacy_mode) : proxy_server(proxy_server), privacy_mode(privacy_mode) {} bool HttpStreamFactory::PreconnectingProxyServer::operator<( const PreconnectingProxyServer& other) const { return std::tie(proxy_server, privacy_mode) < std::tie(other.proxy_server, other.privacy_mode); } bool HttpStreamFactory::PreconnectingProxyServer::operator==( const PreconnectingProxyServer& other) const { return proxy_server == other.proxy_server && privacy_mode == other.privacy_mode; } bool HttpStreamFactory::OnInitConnection(const JobController& controller, const ProxyInfo& proxy_info, PrivacyMode privacy_mode) { if (!controller.is_preconnect()) { // Connection initialization can be skipped only for the preconnect jobs. return false; } if (!ProxyServerSupportsPriorities(proxy_info)) return false; PreconnectingProxyServer preconnecting_proxy_server(proxy_info.proxy_server(), privacy_mode); if (base::ContainsKey(preconnecting_proxy_servers_, preconnecting_proxy_server)) { UMA_HISTOGRAM_EXACT_LINEAR("Net.PreconnectSkippedToProxyServers", 1, 2); // Skip preconnect to the proxy server since we are already preconnecting // (probably via some other job). See https://crbug.com/682041 for details. return true; } // Add the proxy server to the set of preconnecting proxy servers. // The maximum size of |preconnecting_proxy_servers_|. static const size_t kMaxPreconnectingServerSize = 3; if (preconnecting_proxy_servers_.size() >= kMaxPreconnectingServerSize) { // Erase the first entry. A better approach (at the cost of higher memory // overhead) may be to erase the least recently used entry. preconnecting_proxy_servers_.erase(preconnecting_proxy_servers_.begin()); } preconnecting_proxy_servers_.insert(preconnecting_proxy_server); DCHECK_GE(kMaxPreconnectingServerSize, preconnecting_proxy_servers_.size()); // The first preconnect should be allowed. return false; } void HttpStreamFactory::OnStreamReady(const ProxyInfo& proxy_info, PrivacyMode privacy_mode) { if (proxy_info.is_empty()) return; preconnecting_proxy_servers_.erase( PreconnectingProxyServer(proxy_info.proxy_server(), privacy_mode)); } bool HttpStreamFactory::ProxyServerSupportsPriorities( const ProxyInfo& proxy_info) const { if (proxy_info.is_empty() || !proxy_info.proxy_server().is_valid()) return false; if (!proxy_info.proxy_server().is_https()) return false; HostPortPair host_port_pair = proxy_info.proxy_server().host_port_pair(); DCHECK(!host_port_pair.IsEmpty()); url::SchemeHostPort scheme_host_port("https", host_port_pair.host(), host_port_pair.port()); return session_->http_server_properties()->SupportsRequestPriority( scheme_host_port); } void HttpStreamFactory::DumpMemoryStats( base::trace_event::ProcessMemoryDump* pmd, const std::string& parent_absolute_name) const { if (job_controller_set_.empty()) return; std::string name = base::StringPrintf("%s/stream_factory", parent_absolute_name.c_str()); base::trace_event::MemoryAllocatorDump* factory_dump = pmd->CreateAllocatorDump(name); size_t alt_job_count = 0; size_t main_job_count = 0; size_t num_controllers_for_preconnect = 0; for (const auto& it : job_controller_set_) { // For a preconnect controller, it should have exactly the main job. if (it->is_preconnect()) { num_controllers_for_preconnect++; continue; } // For non-preconnects. if (it->HasPendingAltJob()) alt_job_count++; if (it->HasPendingMainJob()) main_job_count++; } factory_dump->AddScalar( base::trace_event::MemoryAllocatorDump::kNameSize, base::trace_event::MemoryAllocatorDump::kUnitsBytes, base::trace_event::EstimateMemoryUsage(job_controller_set_)); factory_dump->AddScalar( base::trace_event::MemoryAllocatorDump::kNameObjectCount, base::trace_event::MemoryAllocatorDump::kUnitsObjects, job_controller_set_.size()); // The number of non-preconnect controllers with a pending alt job. factory_dump->AddScalar("alt_job_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects, alt_job_count); // The number of non-preconnect controllers with a pending main job. factory_dump->AddScalar("main_job_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects, main_job_count); // The number of preconnect controllers. factory_dump->AddScalar("preconnect_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects, num_controllers_for_preconnect); } } // namespace net