| // 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/socket/client_socket_pool.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/check_op.h" |
| #include "base/feature_list.h" |
| #include "net/base/features.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/proxy_server.h" |
| #include "net/dns/public/secure_dns_policy.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/connect_job_factory.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" |
| #include "url/gurl.h" |
| #include "url/scheme_host_port.h" |
| #include "url/url_constants.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 std::vector<HostResolverEndpointResult>& endpoint_results, |
| const std::set<std::string>& aliases) { |
| 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, endpoint_results, aliases); |
| } |
| |
| } // namespace |
| |
| ClientSocketPool::SocketParams::SocketParams( |
| std::unique_ptr<SSLConfig> ssl_config_for_origin, |
| std::unique_ptr<SSLConfig> 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> |
| ClientSocketPool::SocketParams::CreateForHttpForTesting() { |
| return base::MakeRefCounted<SocketParams>(nullptr /* ssl_config_for_origin */, |
| nullptr /* ssl_config_for_proxy */); |
| } |
| |
| ClientSocketPool::GroupId::GroupId() |
| : privacy_mode_(PrivacyMode::PRIVACY_MODE_DISABLED) {} |
| |
| ClientSocketPool::GroupId::GroupId(url::SchemeHostPort destination, |
| PrivacyMode privacy_mode, |
| NetworkIsolationKey network_isolation_key, |
| SecureDnsPolicy secure_dns_policy) |
| : destination_(std::move(destination)), |
| privacy_mode_(privacy_mode), |
| network_isolation_key_( |
| base::FeatureList::IsEnabled( |
| features::kPartitionConnectionsByNetworkIsolationKey) |
| ? std::move(network_isolation_key) |
| : NetworkIsolationKey()), |
| secure_dns_policy_(secure_dns_policy) { |
| DCHECK(destination_.IsValid()); |
| |
| // ClientSocketPool only expected to be used for HTTP/HTTPS/WS/WSS cases, and |
| // "ws"/"wss" schemes should be converted to "http"/"https" equivalent first. |
| DCHECK(destination_.scheme() == url::kHttpScheme || |
| destination_.scheme() == url::kHttpsScheme); |
| } |
| |
| 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_.Serialize(); |
| |
| if (privacy_mode_) |
| result = "pm/" + result; |
| |
| if (base::FeatureList::IsEnabled( |
| features::kPartitionConnectionsByNetworkIsolationKey)) { |
| result += " <"; |
| result += network_isolation_key_.ToDebugString(); |
| result += ">"; |
| } |
| |
| switch (secure_dns_policy_) { |
| case SecureDnsPolicy::kAllow: |
| break; |
| case SecureDnsPolicy::kDisable: |
| result = "dsd/" + result; |
| break; |
| case SecureDnsPolicy::kBootstrap: |
| result = "dns_bootstrap/" + result; |
| break; |
| } |
| |
| return result; |
| } |
| |
| ClientSocketPool::~ClientSocketPool() = default; |
| |
| // static |
| base::TimeDelta ClientSocketPool::used_idle_socket_timeout() { |
| return base::Seconds(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( |
| bool is_for_websockets, |
| const CommonConnectJobParams* common_connect_job_params, |
| std::unique_ptr<ConnectJobFactory> connect_job_factory) |
| : is_for_websockets_(is_for_websockets), |
| common_connect_job_params_(common_connect_job_params), |
| connect_job_factory_(std::move(connect_job_factory)) {} |
| |
| void ClientSocketPool::NetLogTcpClientSocketPoolRequestedSocket( |
| const NetLogWithSource& net_log, |
| const GroupId& group_id) { |
| // 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::Dict event_params; |
| event_params.Set("group_id", group_id.ToString()); |
| return base::Value(std::move(event_params)); |
| } |
| |
| std::unique_ptr<ConnectJob> ClientSocketPool::CreateConnectJob( |
| GroupId group_id, |
| scoped_refptr<SocketParams> socket_params, |
| const ProxyServer& proxy_server, |
| const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag, |
| RequestPriority request_priority, |
| SocketTag socket_tag, |
| ConnectJob::Delegate* delegate) { |
| bool using_ssl = GURL::SchemeIsCryptographic(group_id.destination().scheme()); |
| |
| // 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, |
| // TODO(crbug.com/1206799): Pass along as SchemeHostPort. |
| SpdySessionKey(HostPortPair::FromSchemeHostPort(group_id.destination()), |
| proxy_server, group_id.privacy_mode(), |
| SpdySessionKey::IsProxySession::kFalse, socket_tag, |
| group_id.network_isolation_key(), |
| group_id.secure_dns_policy()), |
| 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.secure_dns_policy()), |
| is_for_websockets_); |
| } |
| |
| return connect_job_factory_->CreateConnectJob( |
| 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.secure_dns_policy(), common_connect_job_params_, delegate); |
| } |
| |
| } // namespace net |