#include "net/socket/client_socket_pool.h"
#include <memory>
#include <string_view>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/strings/strcat.h"
#include "net/base/features.h"
#include "net/base/host_port_pair.h"
#include "net/base/proxy_chain.h"
#include "net/base/session_usage.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 "net/ssl/ssl_config.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#include "url/url_constants.h"
#if BUILDFLAG(ENABLE_ARKWEB_EXT)
#include "arkweb/ohos_nweb_ex/build/features/features.h"
#endif
namespace net {
namespace {
int64_t g_used_idle_socket_timeout_s = 300;
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());
return spdy_session_pool->OnHostResolutionComplete(
spdy_session_key, is_for_websockets, endpoint_results, aliases);
}
}
ClientSocketPool::SocketParams::SocketParams(
const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs)
: allowed_bad_certs_(allowed_bad_certs) {}
ClientSocketPool::SocketParams::~SocketParams() = default;
scoped_refptr<ClientSocketPool::SocketParams>
ClientSocketPool::SocketParams::CreateForHttpForTesting() {
return base::MakeRefCounted<SocketParams>(
std::vector<SSLConfig::CertAndStatus>());
}
std::string_view ClientSocketPool::GroupId::GetPrivacyModeGroupIdPrefix(
PrivacyMode privacy_mode) {
switch (privacy_mode) {
case PrivacyMode::PRIVACY_MODE_DISABLED:
return "";
case PrivacyMode::PRIVACY_MODE_ENABLED:
return "pm/";
case PrivacyMode::PRIVACY_MODE_ENABLED_WITHOUT_CLIENT_CERTS:
return "pmwocc/";
case PrivacyMode::PRIVACY_MODE_ENABLED_PARTITIONED_STATE_ALLOWED:
return "pmpsa/";
}
}
std::string_view ClientSocketPool::GroupId::GetSecureDnsPolicyGroupIdPrefix(
SecureDnsPolicy secure_dns_policy) {
switch (secure_dns_policy) {
case SecureDnsPolicy::kAllow:
return "";
case SecureDnsPolicy::kDisable:
return "dsd/";
case SecureDnsPolicy::kBootstrap:
return "dns_bootstrap/";
}
}
ClientSocketPool::GroupId::GroupId()
: privacy_mode_(PrivacyMode::PRIVACY_MODE_DISABLED) {}
ClientSocketPool::GroupId::GroupId(
url::SchemeHostPort destination,
PrivacyMode privacy_mode,
NetworkAnonymizationKey network_anonymization_key,
SecureDnsPolicy secure_dns_policy,
bool disable_cert_network_fetches
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
bool secure_dns_only
#endif
)
: destination_(std::move(destination)),
privacy_mode_(privacy_mode),
network_anonymization_key_(
NetworkAnonymizationKey::IsPartitioningEnabled()
? std::move(network_anonymization_key)
: NetworkAnonymizationKey()),
secure_dns_policy_(secure_dns_policy),
disable_cert_network_fetches_(disable_cert_network_fetches)
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
secure_dns_only_(secure_dns_only)
#endif
{
DCHECK(destination_.IsValid());
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 {
return base::StrCat(
{disable_cert_network_fetches_ ? "disable_cert_network_fetches/" : "",
GetSecureDnsPolicyGroupIdPrefix(secure_dns_policy_),
GetPrivacyModeGroupIdPrefix(privacy_mode_), destination_.Serialize(),
NetworkAnonymizationKey::IsPartitioningEnabled()
? base::StrCat(
{" <", network_anonymization_key_.ToDebugString(), ">"})
: ""
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
secure_dns_only_ ? "sdo/" : ""
#endif
});
}
ClientSocketPool::~ClientSocketPool() = default;
base::TimeDelta ClientSocketPool::used_idle_socket_timeout() {
return base::Seconds(g_used_idle_socket_timeout_s);
}
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(
size_t socket_soft_cap,
SocketPoolAdditionalCapacity additional_capacity,
const ProxyChain& proxy_chain,
bool is_for_websockets,
const CommonConnectJobParams* common_connect_job_params,
std::unique_ptr<ConnectJobFactory> connect_job_factory)
: socket_soft_cap_(socket_soft_cap),
additional_capacity_(additional_capacity),
proxy_chain_(proxy_chain),
is_for_websockets_(is_for_websockets),
common_connect_job_params_(common_connect_job_params),
connect_job_factory_(std::move(connect_job_factory)) {
#if BUILDFLAG(ARKWEB_EXT_NETWORK_CONNECTION) || \
BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
utils = std::make_unique<ArkWebClientSocketPoolExt>(this);
#endif
}
void ClientSocketPool::NetLogTcpClientSocketPoolRequestedSocket(
const NetLogWithSource& net_log,
const GroupId& group_id) {
net_log.AddEvent(NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
[&] { return NetLogGroupIdParams(group_id); });
}
base::Value::Dict ClientSocketPool::NetLogGroupIdParams(
const GroupId& group_id) {
return base::Value::Dict().Set("group_id", group_id.ToString());
}
std::unique_ptr<ConnectJob> ClientSocketPool::CreateConnectJob(
GroupId group_id,
scoped_refptr<SocketParams> socket_params,
const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
RequestPriority request_priority,
SocketTag socket_tag,
ConnectJob::Delegate* delegate) {
bool using_ssl = GURL::SchemeIsCryptographic(group_id.destination().scheme());
OnHostResolutionCallback resolution_callback;
if (using_ssl && GetProxyChain().is_direct()) {
resolution_callback = base::BindRepeating(
&OnHostResolution, common_connect_job_params_->spdy_session_pool,
SpdySessionKey(HostPortPair::FromSchemeHostPort(group_id.destination()),
group_id.privacy_mode(), GetProxyChain(),
SessionUsage::kDestination, socket_tag,
group_id.network_anonymization_key(),
group_id.secure_dns_policy(),
group_id.disable_cert_network_fetches()),
is_for_websockets_);
}
bool force_tunnel = is_for_websockets_;
ConnectJobFactory::AlpnMode alpn_mode =
is_for_websockets_ ? ConnectJobFactory::AlpnMode::kHttp11Only
: ConnectJobFactory::AlpnMode::kHttpAll;
return connect_job_factory_->CreateConnectJob(
group_id.destination(), GetProxyChain(), proxy_annotation_tag,
socket_params->allowed_bad_certs(), alpn_mode, force_tunnel,
group_id.privacy_mode(), resolution_callback, request_priority,
socket_tag, group_id.network_anonymization_key(),
group_id.secure_dns_policy(), group_id.disable_cert_network_fetches(),
common_connect_job_params_, delegate
#if BUILDFLAG(ARKWEB_EXT_HTTP_DNS_FALLBACK)
,
group_id.secure_dns_only()
#endif
);
}
void ClientSocketPool::UpdateStateBeforeAllocation() {
state_ = additional_capacity_.NextStateBeforeAllocation(
State(), SocketsInUse(), SocketSoftCap());
}
void ClientSocketPool::UpdateStateAfterRelease() {
state_ = additional_capacity_.NextStateAfterRelease(State(), SocketsInUse(),
SocketSoftCap());
}
}