// 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/socket/client_socket_pool.h" #include #include "base/bind.h" #include "base/check_op.h" #include "base/feature_list.h" #include "net/base/features.h" #include "net/http/http_proxy_connect_job.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_with_source.h" #include "net/socket/connect_job.h" #include "net/socket/socks_connect_job.h" #include "net/socket/ssl_connect_job.h" #include "net/socket/stream_socket.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" namespace net { namespace { // The maximum duration, in seconds, to keep used idle persistent sockets alive. int64_t g_used_idle_socket_timeout_s = 300; // 5 minutes // Invoked by the transport socket pool after host resolution is complete // to allow the connection to be aborted, if a matching SPDY session can // be found. Returns OnHostResolutionCallbackResult::kMayBeDeletedAsync if such // a session is found, as it will post a task that may delete the calling // ConnectJob. Also returns kMayBeDeletedAsync if there may already be such // a task posted. OnHostResolutionCallbackResult OnHostResolution( SpdySessionPool* spdy_session_pool, const SpdySessionKey& spdy_session_key, bool is_for_websockets, const HostPortPair& host_port_pair, const AddressList& addresses) { DCHECK(host_port_pair == spdy_session_key.host_port_pair()); // It is OK to dereference spdy_session_pool, because the // ClientSocketPoolManager will be destroyed in the same callback that // destroys the SpdySessionPool. return spdy_session_pool->OnHostResolutionComplete( spdy_session_key, is_for_websockets, addresses); } } // namespace ClientSocketPool::SocketParams::SocketParams( std::unique_ptr ssl_config_for_origin, std::unique_ptr ssl_config_for_proxy) : ssl_config_for_origin_(std::move(ssl_config_for_origin)), ssl_config_for_proxy_(std::move(ssl_config_for_proxy)) {} ClientSocketPool::SocketParams::~SocketParams() = default; scoped_refptr ClientSocketPool::SocketParams::CreateForHttpForTesting() { return base::MakeRefCounted(nullptr /* ssl_config_for_origin */, nullptr /* ssl_config_for_proxy */); } ClientSocketPool::GroupId::GroupId() : socket_type_(SocketType::kHttp), privacy_mode_(PrivacyMode::PRIVACY_MODE_DISABLED) {} ClientSocketPool::GroupId::GroupId(const HostPortPair& destination, SocketType socket_type, PrivacyMode privacy_mode, NetworkIsolationKey network_isolation_key, bool disable_secure_dns) : destination_(destination), socket_type_(socket_type), privacy_mode_(privacy_mode), network_isolation_key_( base::FeatureList::IsEnabled( features::kPartitionConnectionsByNetworkIsolationKey) ? network_isolation_key : NetworkIsolationKey()), disable_secure_dns_(disable_secure_dns) {} ClientSocketPool::GroupId::GroupId(const GroupId& group_id) = default; ClientSocketPool::GroupId::~GroupId() = default; ClientSocketPool::GroupId& ClientSocketPool::GroupId::operator=( const GroupId& group_id) = default; ClientSocketPool::GroupId& ClientSocketPool::GroupId::operator=( GroupId&& group_id) = default; std::string ClientSocketPool::GroupId::ToString() const { std::string result = destination_.ToString(); switch (socket_type_) { case ClientSocketPool::SocketType::kHttp: break; case ClientSocketPool::SocketType::kSsl: result = "ssl/" + result; break; } if (privacy_mode_) result = "pm/" + result; if (base::FeatureList::IsEnabled( features::kPartitionConnectionsByNetworkIsolationKey)) { result += " <"; result += network_isolation_key_.ToDebugString(); result += ">"; } if (disable_secure_dns_) result = "dsd/" + result; return result; } ClientSocketPool::~ClientSocketPool() = default; // static base::TimeDelta ClientSocketPool::used_idle_socket_timeout() { return base::TimeDelta::FromSeconds(g_used_idle_socket_timeout_s); } // static void ClientSocketPool::set_used_idle_socket_timeout(base::TimeDelta timeout) { DCHECK_GT(timeout.InSeconds(), 0); g_used_idle_socket_timeout_s = timeout.InSeconds(); } ClientSocketPool::ClientSocketPool() = default; void ClientSocketPool::NetLogTcpClientSocketPoolRequestedSocket( const NetLogWithSource& net_log, const GroupId& group_id) { if (net_log.IsCapturing()) { // TODO(eroman): Split out the host and port parameters. net_log.AddEvent(NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET, [&] { return NetLogGroupIdParams(group_id); }); } } base::Value ClientSocketPool::NetLogGroupIdParams(const GroupId& group_id) { base::Value event_params(base::Value::Type::DICTIONARY); event_params.SetStringKey("group_id", group_id.ToString()); return event_params; } std::unique_ptr ClientSocketPool::CreateConnectJob( GroupId group_id, scoped_refptr socket_params, const ProxyServer& proxy_server, const base::Optional& proxy_annotation_tag, bool is_for_websockets, const CommonConnectJobParams* common_connect_job_params, RequestPriority request_priority, SocketTag socket_tag, ConnectJob::Delegate* delegate) { bool using_ssl = group_id.socket_type() == ClientSocketPool::SocketType::kSsl; // If applicable, set up a callback to handle checking for H2 IP pooling // opportunities. OnHostResolutionCallback resolution_callback; if (using_ssl && proxy_server.is_direct()) { resolution_callback = base::BindRepeating( &OnHostResolution, common_connect_job_params->spdy_session_pool, SpdySessionKey( group_id.destination(), proxy_server, group_id.privacy_mode(), SpdySessionKey::IsProxySession::kFalse, socket_tag, group_id.network_isolation_key(), group_id.disable_secure_dns()), is_for_websockets); } else if (proxy_server.is_https()) { resolution_callback = base::BindRepeating( &OnHostResolution, common_connect_job_params->spdy_session_pool, SpdySessionKey(proxy_server.host_port_pair(), ProxyServer::Direct(), group_id.privacy_mode(), SpdySessionKey::IsProxySession::kTrue, socket_tag, group_id.network_isolation_key(), group_id.disable_secure_dns()), is_for_websockets); } return ConnectJob::CreateConnectJob( using_ssl, group_id.destination(), proxy_server, proxy_annotation_tag, socket_params->ssl_config_for_origin(), socket_params->ssl_config_for_proxy(), is_for_websockets, group_id.privacy_mode(), resolution_callback, request_priority, socket_tag, group_id.network_isolation_key(), group_id.disable_secure_dns(), common_connect_job_params, delegate); } } // namespace net