422 lines
17 KiB
C++
422 lines
17 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/http/http_network_session.h"
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <utility>
|
|
|
|
#include "base/atomic_sequence_num.h"
|
|
#include "base/check_op.h"
|
|
#include "base/compiler_specific.h"
|
|
#include "base/containers/contains.h"
|
|
#include "base/notreached.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "base/values.h"
|
|
#include "build/build_config.h"
|
|
#include "net/base/features.h"
|
|
#include "net/dns/host_resolver.h"
|
|
#include "net/http/http_auth_handler_factory.h"
|
|
#include "net/http/http_response_body_drainer.h"
|
|
#include "net/http/http_stream_factory.h"
|
|
#include "net/http/url_security_manager.h"
|
|
#include "net/proxy_resolution/proxy_resolution_service.h"
|
|
#include "net/quic/platform/impl/quic_chromium_clock.h"
|
|
#include "net/quic/quic_crypto_client_stream_factory.h"
|
|
#include "net/quic/quic_stream_factory.h"
|
|
#include "net/socket/client_socket_factory.h"
|
|
#include "net/socket/client_socket_pool_manager_impl.h"
|
|
#include "net/socket/next_proto.h"
|
|
#include "net/socket/ssl_client_socket.h"
|
|
#include "net/spdy/spdy_session.h"
|
|
#include "net/spdy/spdy_session_pool.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_packets.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_tag.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
|
|
|
|
namespace net {
|
|
|
|
// The maximum receive window sizes for HTTP/2 sessions and streams.
|
|
const int32_t kSpdySessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB
|
|
const int32_t kSpdyStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB
|
|
|
|
// Value of SETTINGS_ENABLE_PUSH reflecting that server push is not supported.
|
|
const uint32_t kSpdyDisablePush = 0;
|
|
|
|
namespace {
|
|
|
|
// Keep all HTTP2 parameters in |http2_settings|, even the ones that are not
|
|
// implemented, to be sent to the server.
|
|
// Set default values for settings that |http2_settings| does not specify.
|
|
spdy::SettingsMap AddDefaultHttp2Settings(spdy::SettingsMap http2_settings) {
|
|
// Server push is not supported.
|
|
http2_settings[spdy::SETTINGS_ENABLE_PUSH] = kSpdyDisablePush;
|
|
|
|
// For other setting parameters, set default values only if |http2_settings|
|
|
// does not have a value set for given setting.
|
|
auto it = http2_settings.find(spdy::SETTINGS_HEADER_TABLE_SIZE);
|
|
if (it == http2_settings.end())
|
|
http2_settings[spdy::SETTINGS_HEADER_TABLE_SIZE] = kSpdyMaxHeaderTableSize;
|
|
|
|
it = http2_settings.find(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
|
|
if (it == http2_settings.end())
|
|
http2_settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] =
|
|
kSpdyMaxConcurrentPushedStreams;
|
|
|
|
it = http2_settings.find(spdy::SETTINGS_INITIAL_WINDOW_SIZE);
|
|
if (it == http2_settings.end())
|
|
http2_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] =
|
|
kSpdyStreamMaxRecvWindowSize;
|
|
|
|
it = http2_settings.find(spdy::SETTINGS_MAX_HEADER_LIST_SIZE);
|
|
if (it == http2_settings.end())
|
|
http2_settings[spdy::SETTINGS_MAX_HEADER_LIST_SIZE] =
|
|
kSpdyMaxHeaderListSize;
|
|
|
|
return http2_settings;
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
HttpNetworkSessionParams::HttpNetworkSessionParams()
|
|
: spdy_session_max_recv_window_size(kSpdySessionMaxRecvWindowSize),
|
|
spdy_session_max_queued_capped_frames(kSpdySessionMaxQueuedCappedFrames),
|
|
time_func(&base::TimeTicks::Now) {
|
|
enable_early_data =
|
|
base::FeatureList::IsEnabled(features::kEnableTLS13EarlyData);
|
|
use_dns_https_svcb_alpn =
|
|
base::FeatureList::IsEnabled(features::kUseDnsHttpsSvcbAlpn);
|
|
}
|
|
|
|
HttpNetworkSessionParams::HttpNetworkSessionParams(
|
|
const HttpNetworkSessionParams& other) = default;
|
|
|
|
HttpNetworkSessionParams::~HttpNetworkSessionParams() = default;
|
|
|
|
HttpNetworkSessionContext::HttpNetworkSessionContext()
|
|
: client_socket_factory(nullptr),
|
|
host_resolver(nullptr),
|
|
cert_verifier(nullptr),
|
|
transport_security_state(nullptr),
|
|
ct_policy_enforcer(nullptr),
|
|
sct_auditing_delegate(nullptr),
|
|
proxy_resolution_service(nullptr),
|
|
proxy_delegate(nullptr),
|
|
http_user_agent_settings(nullptr),
|
|
ssl_config_service(nullptr),
|
|
http_auth_handler_factory(nullptr),
|
|
net_log(nullptr),
|
|
socket_performance_watcher_factory(nullptr),
|
|
network_quality_estimator(nullptr),
|
|
quic_context(nullptr),
|
|
#if BUILDFLAG(ENABLE_REPORTING)
|
|
reporting_service(nullptr),
|
|
network_error_logging_service(nullptr),
|
|
#endif
|
|
quic_crypto_client_stream_factory(
|
|
QuicCryptoClientStreamFactory::GetDefaultFactory()) {
|
|
}
|
|
|
|
HttpNetworkSessionContext::HttpNetworkSessionContext(
|
|
const HttpNetworkSessionContext& other) = default;
|
|
|
|
HttpNetworkSessionContext::~HttpNetworkSessionContext() = default;
|
|
|
|
// TODO(mbelshe): Move the socket factories into HttpStreamFactory.
|
|
HttpNetworkSession::HttpNetworkSession(const HttpNetworkSessionParams& params,
|
|
const HttpNetworkSessionContext& context)
|
|
: net_log_(context.net_log),
|
|
http_server_properties_(context.http_server_properties),
|
|
cert_verifier_(context.cert_verifier),
|
|
http_auth_handler_factory_(context.http_auth_handler_factory),
|
|
host_resolver_(context.host_resolver),
|
|
#if BUILDFLAG(ENABLE_REPORTING)
|
|
reporting_service_(context.reporting_service),
|
|
network_error_logging_service_(context.network_error_logging_service),
|
|
#endif
|
|
proxy_resolution_service_(context.proxy_resolution_service),
|
|
ssl_config_service_(context.ssl_config_service),
|
|
http_auth_cache_(
|
|
params.key_auth_cache_server_entries_by_network_anonymization_key),
|
|
ssl_client_session_cache_(SSLClientSessionCache::Config()),
|
|
ssl_client_context_(context.ssl_config_service,
|
|
context.cert_verifier,
|
|
context.transport_security_state,
|
|
context.ct_policy_enforcer,
|
|
&ssl_client_session_cache_,
|
|
context.sct_auditing_delegate),
|
|
quic_stream_factory_(context.net_log,
|
|
context.host_resolver,
|
|
context.ssl_config_service,
|
|
context.client_socket_factory,
|
|
context.http_server_properties,
|
|
context.cert_verifier,
|
|
context.ct_policy_enforcer,
|
|
context.transport_security_state,
|
|
context.sct_auditing_delegate,
|
|
context.socket_performance_watcher_factory,
|
|
context.quic_crypto_client_stream_factory,
|
|
context.quic_context),
|
|
spdy_session_pool_(context.host_resolver,
|
|
&ssl_client_context_,
|
|
context.http_server_properties,
|
|
context.transport_security_state,
|
|
context.quic_context->params()->supported_versions,
|
|
params.enable_spdy_ping_based_connection_checking,
|
|
params.enable_http2,
|
|
params.enable_quic,
|
|
params.spdy_session_max_recv_window_size,
|
|
params.spdy_session_max_queued_capped_frames,
|
|
AddDefaultHttp2Settings(params.http2_settings),
|
|
params.enable_http2_settings_grease,
|
|
params.greased_http2_frame,
|
|
params.http2_end_stream_with_data_frame,
|
|
params.enable_priority_update,
|
|
params.spdy_go_away_on_ip_change,
|
|
params.time_func,
|
|
context.network_quality_estimator,
|
|
// cleanup_sessions_on_ip_address_changed
|
|
!params.ignore_ip_address_changes),
|
|
http_stream_factory_(std::make_unique<HttpStreamFactory>(this)),
|
|
params_(params),
|
|
context_(context) {
|
|
DCHECK(proxy_resolution_service_);
|
|
DCHECK(ssl_config_service_);
|
|
CHECK(http_server_properties_);
|
|
DCHECK(context_.client_socket_factory);
|
|
|
|
normal_socket_pool_manager_ = std::make_unique<ClientSocketPoolManagerImpl>(
|
|
CreateCommonConnectJobParams(false /* for_websockets */),
|
|
CreateCommonConnectJobParams(true /* for_websockets */),
|
|
NORMAL_SOCKET_POOL,
|
|
// cleanup_on_ip_address_change
|
|
!params.ignore_ip_address_changes);
|
|
websocket_socket_pool_manager_ =
|
|
std::make_unique<ClientSocketPoolManagerImpl>(
|
|
CreateCommonConnectJobParams(false /* for_websockets */),
|
|
CreateCommonConnectJobParams(true /* for_websockets */),
|
|
WEBSOCKET_SOCKET_POOL,
|
|
// cleanup_on_ip_address_change
|
|
!params.ignore_ip_address_changes);
|
|
|
|
if (params_.enable_http2) {
|
|
next_protos_.push_back(kProtoHTTP2);
|
|
if (base::FeatureList::IsEnabled(features::kAlpsForHttp2)) {
|
|
// Enable ALPS for HTTP/2 with empty data.
|
|
application_settings_[kProtoHTTP2] = {};
|
|
}
|
|
}
|
|
|
|
next_protos_.push_back(kProtoHTTP11);
|
|
|
|
http_server_properties_->SetMaxServerConfigsStoredInProperties(
|
|
context.quic_context->params()->max_server_configs_stored_in_properties);
|
|
http_server_properties_->SetBrokenAlternativeServicesDelayParams(
|
|
context.quic_context->params()
|
|
->initial_delay_for_broken_alternative_service,
|
|
context.quic_context->params()->exponential_backoff_on_initial_delay);
|
|
|
|
if (!params_.disable_idle_sockets_close_on_memory_pressure) {
|
|
memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
|
|
FROM_HERE, base::BindRepeating(&HttpNetworkSession::OnMemoryPressure,
|
|
base::Unretained(this)));
|
|
}
|
|
}
|
|
|
|
HttpNetworkSession::~HttpNetworkSession() {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
response_drainers_.clear();
|
|
// TODO(bnc): CloseAllSessions() is also called in SpdySessionPool destructor,
|
|
// one of the two calls should be removed.
|
|
spdy_session_pool_.CloseAllSessions();
|
|
}
|
|
|
|
void HttpNetworkSession::StartResponseDrainer(
|
|
std::unique_ptr<HttpResponseBodyDrainer> drainer) {
|
|
DCHECK(!base::Contains(response_drainers_, drainer.get()));
|
|
HttpResponseBodyDrainer* drainer_ptr = drainer.get();
|
|
response_drainers_.insert(std::move(drainer));
|
|
drainer_ptr->Start(this);
|
|
}
|
|
|
|
void HttpNetworkSession::RemoveResponseDrainer(
|
|
HttpResponseBodyDrainer* drainer) {
|
|
DCHECK(base::Contains(response_drainers_, drainer));
|
|
|
|
response_drainers_.erase(response_drainers_.find(drainer));
|
|
}
|
|
|
|
ClientSocketPool* HttpNetworkSession::GetSocketPool(
|
|
SocketPoolType pool_type,
|
|
const ProxyServer& proxy_server) {
|
|
return GetSocketPoolManager(pool_type)->GetSocketPool(proxy_server);
|
|
}
|
|
|
|
base::Value HttpNetworkSession::SocketPoolInfoToValue() const {
|
|
// TODO(yutak): Should merge values from normal pools and WebSocket pools.
|
|
return normal_socket_pool_manager_->SocketPoolInfoToValue();
|
|
}
|
|
|
|
std::unique_ptr<base::Value> HttpNetworkSession::SpdySessionPoolInfoToValue()
|
|
const {
|
|
return spdy_session_pool_.SpdySessionPoolInfoToValue();
|
|
}
|
|
|
|
base::Value HttpNetworkSession::QuicInfoToValue() const {
|
|
base::Value::Dict dict;
|
|
dict.Set("sessions", quic_stream_factory_.QuicStreamFactoryInfoToValue());
|
|
dict.Set("quic_enabled", IsQuicEnabled());
|
|
|
|
const QuicParams* quic_params = context_.quic_context->params();
|
|
|
|
base::Value::List connection_options;
|
|
for (const auto& option : quic_params->connection_options)
|
|
connection_options.Append(quic::QuicTagToString(option));
|
|
dict.Set("connection_options", std::move(connection_options));
|
|
|
|
base::Value::List supported_versions;
|
|
for (const auto& version : quic_params->supported_versions)
|
|
supported_versions.Append(ParsedQuicVersionToString(version));
|
|
dict.Set("supported_versions", std::move(supported_versions));
|
|
|
|
base::Value::List origins_to_force_quic_on;
|
|
for (const auto& origin : quic_params->origins_to_force_quic_on)
|
|
origins_to_force_quic_on.Append(origin.ToString());
|
|
dict.Set("origins_to_force_quic_on", std::move(origins_to_force_quic_on));
|
|
|
|
dict.Set("max_packet_length",
|
|
static_cast<int>(quic_params->max_packet_length));
|
|
dict.Set(
|
|
"max_server_configs_stored_in_properties",
|
|
static_cast<int>(quic_params->max_server_configs_stored_in_properties));
|
|
dict.Set("idle_connection_timeout_seconds",
|
|
static_cast<int>(quic_params->idle_connection_timeout.InSeconds()));
|
|
dict.Set("reduced_ping_timeout_seconds",
|
|
static_cast<int>(quic_params->reduced_ping_timeout.InSeconds()));
|
|
dict.Set("retry_without_alt_svc_on_quic_errors",
|
|
quic_params->retry_without_alt_svc_on_quic_errors);
|
|
dict.Set("close_sessions_on_ip_change",
|
|
quic_params->close_sessions_on_ip_change);
|
|
dict.Set("goaway_sessions_on_ip_change",
|
|
quic_params->goaway_sessions_on_ip_change);
|
|
dict.Set("migrate_sessions_on_network_change_v2",
|
|
quic_params->migrate_sessions_on_network_change_v2);
|
|
dict.Set("migrate_sessions_early_v2", quic_params->migrate_sessions_early_v2);
|
|
dict.Set("retransmittable_on_wire_timeout_milliseconds",
|
|
static_cast<int>(
|
|
quic_params->retransmittable_on_wire_timeout.InMilliseconds()));
|
|
dict.Set("retry_on_alternate_network_before_handshake",
|
|
quic_params->retry_on_alternate_network_before_handshake);
|
|
dict.Set("migrate_idle_sessions", quic_params->migrate_idle_sessions);
|
|
dict.Set(
|
|
"idle_session_migration_period_seconds",
|
|
static_cast<int>(quic_params->idle_session_migration_period.InSeconds()));
|
|
dict.Set("max_time_on_non_default_network_seconds",
|
|
static_cast<int>(
|
|
quic_params->max_time_on_non_default_network.InSeconds()));
|
|
dict.Set("max_num_migrations_to_non_default_network_on_write_error",
|
|
quic_params->max_migrations_to_non_default_network_on_write_error);
|
|
dict.Set(
|
|
"max_num_migrations_to_non_default_network_on_path_degrading",
|
|
quic_params->max_migrations_to_non_default_network_on_path_degrading);
|
|
dict.Set("allow_server_migration", quic_params->allow_server_migration);
|
|
dict.Set("race_stale_dns_on_connection",
|
|
quic_params->race_stale_dns_on_connection);
|
|
dict.Set("estimate_initial_rtt", quic_params->estimate_initial_rtt);
|
|
dict.Set("server_push_cancellation", params_.enable_server_push_cancellation);
|
|
dict.Set("initial_rtt_for_handshake_milliseconds",
|
|
static_cast<int>(
|
|
quic_params->initial_rtt_for_handshake.InMilliseconds()));
|
|
|
|
return base::Value(std::move(dict));
|
|
}
|
|
|
|
void HttpNetworkSession::CloseAllConnections(int net_error,
|
|
const char* net_log_reason_utf8) {
|
|
normal_socket_pool_manager_->FlushSocketPoolsWithError(net_error,
|
|
net_log_reason_utf8);
|
|
websocket_socket_pool_manager_->FlushSocketPoolsWithError(
|
|
net_error, net_log_reason_utf8);
|
|
spdy_session_pool_.CloseCurrentSessions(static_cast<net::Error>(net_error));
|
|
quic_stream_factory_.CloseAllSessions(net_error, quic::QUIC_PEER_GOING_AWAY);
|
|
}
|
|
|
|
void HttpNetworkSession::CloseIdleConnections(const char* net_log_reason_utf8) {
|
|
normal_socket_pool_manager_->CloseIdleSockets(net_log_reason_utf8);
|
|
websocket_socket_pool_manager_->CloseIdleSockets(net_log_reason_utf8);
|
|
spdy_session_pool_.CloseCurrentIdleSessions(net_log_reason_utf8);
|
|
}
|
|
|
|
void HttpNetworkSession::SetServerPushDelegate(
|
|
std::unique_ptr<ServerPushDelegate> push_delegate) {
|
|
DCHECK(push_delegate);
|
|
if (!params_.enable_server_push_cancellation || push_delegate_)
|
|
return;
|
|
|
|
push_delegate_ = std::move(push_delegate);
|
|
spdy_session_pool_.set_server_push_delegate(push_delegate_.get());
|
|
quic_stream_factory_.set_server_push_delegate(push_delegate_.get());
|
|
}
|
|
|
|
bool HttpNetworkSession::IsQuicEnabled() const {
|
|
return params_.enable_quic;
|
|
}
|
|
|
|
void HttpNetworkSession::DisableQuic() {
|
|
params_.enable_quic = false;
|
|
}
|
|
|
|
void HttpNetworkSession::ClearSSLSessionCache() {
|
|
ssl_client_session_cache_.Flush();
|
|
}
|
|
|
|
CommonConnectJobParams HttpNetworkSession::CreateCommonConnectJobParams(
|
|
bool for_websockets) {
|
|
// Use null websocket_endpoint_lock_manager, which is only set for WebSockets,
|
|
// and only when not using a proxy.
|
|
return CommonConnectJobParams(
|
|
context_.client_socket_factory, context_.host_resolver, &http_auth_cache_,
|
|
context_.http_auth_handler_factory, &spdy_session_pool_,
|
|
&context_.quic_context->params()->supported_versions,
|
|
&quic_stream_factory_, context_.proxy_delegate,
|
|
context_.http_user_agent_settings, &ssl_client_context_,
|
|
context_.socket_performance_watcher_factory,
|
|
context_.network_quality_estimator, context_.net_log,
|
|
for_websockets ? &websocket_endpoint_lock_manager_ : nullptr);
|
|
}
|
|
|
|
ClientSocketPoolManager* HttpNetworkSession::GetSocketPoolManager(
|
|
SocketPoolType pool_type) {
|
|
switch (pool_type) {
|
|
case NORMAL_SOCKET_POOL:
|
|
return normal_socket_pool_manager_.get();
|
|
case WEBSOCKET_SOCKET_POOL:
|
|
return websocket_socket_pool_manager_.get();
|
|
default:
|
|
NOTREACHED();
|
|
break;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void HttpNetworkSession::OnMemoryPressure(
|
|
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
|
|
DCHECK(!params_.disable_idle_sockets_close_on_memory_pressure);
|
|
|
|
switch (memory_pressure_level) {
|
|
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
|
|
break;
|
|
|
|
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
|
|
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
|
|
CloseIdleConnections("Low memory");
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace net
|