1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/socket/client_socket_pool.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/check_op.h"
11 #include "base/feature_list.h"
12 #include "base/functional/bind.h"
13 #include "net/base/features.h"
14 #include "net/base/host_port_pair.h"
15 #include "net/base/proxy_chain.h"
16 #include "net/dns/public/secure_dns_policy.h"
17 #include "net/http/http_proxy_connect_job.h"
18 #include "net/log/net_log_event_type.h"
19 #include "net/log/net_log_with_source.h"
20 #include "net/socket/connect_job.h"
21 #include "net/socket/connect_job_factory.h"
22 #include "net/socket/socks_connect_job.h"
23 #include "net/socket/ssl_connect_job.h"
24 #include "net/socket/stream_socket.h"
25 #include "net/spdy/spdy_session.h"
26 #include "net/spdy/spdy_session_pool.h"
27 #include "url/gurl.h"
28 #include "url/scheme_host_port.h"
29 #include "url/url_constants.h"
30
31 namespace net {
32
33 namespace {
34
35 // The maximum duration, in seconds, to keep used idle persistent sockets alive.
36 int64_t g_used_idle_socket_timeout_s = 300; // 5 minutes
37
38 // Invoked by the transport socket pool after host resolution is complete
39 // to allow the connection to be aborted, if a matching SPDY session can
40 // be found. Returns OnHostResolutionCallbackResult::kMayBeDeletedAsync if such
41 // a session is found, as it will post a task that may delete the calling
42 // ConnectJob. Also returns kMayBeDeletedAsync if there may already be such
43 // a task posted.
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)44 OnHostResolutionCallbackResult OnHostResolution(
45 SpdySessionPool* spdy_session_pool,
46 const SpdySessionKey& spdy_session_key,
47 bool is_for_websockets,
48 const HostPortPair& host_port_pair,
49 const std::vector<HostResolverEndpointResult>& endpoint_results,
50 const std::set<std::string>& aliases) {
51 DCHECK(host_port_pair == spdy_session_key.host_port_pair());
52
53 // It is OK to dereference spdy_session_pool, because the
54 // ClientSocketPoolManager will be destroyed in the same callback that
55 // destroys the SpdySessionPool.
56 return spdy_session_pool->OnHostResolutionComplete(
57 spdy_session_key, is_for_websockets, endpoint_results, aliases);
58 }
59
60 } // namespace
61
SocketParams(std::unique_ptr<SSLConfig> ssl_config_for_origin,std::unique_ptr<SSLConfig> base_ssl_config_for_proxies)62 ClientSocketPool::SocketParams::SocketParams(
63 std::unique_ptr<SSLConfig> ssl_config_for_origin,
64 std::unique_ptr<SSLConfig> base_ssl_config_for_proxies)
65 : ssl_config_for_origin_(std::move(ssl_config_for_origin)),
66 base_ssl_config_for_proxies_(std::move(base_ssl_config_for_proxies)) {}
67
68 ClientSocketPool::SocketParams::~SocketParams() = default;
69
70 scoped_refptr<ClientSocketPool::SocketParams>
CreateForHttpForTesting()71 ClientSocketPool::SocketParams::CreateForHttpForTesting() {
72 return base::MakeRefCounted<SocketParams>(
73 /*ssl_config_for_origin=*/nullptr,
74 /*base_ssl_config_for_proxies=*/nullptr);
75 }
76
GroupId()77 ClientSocketPool::GroupId::GroupId()
78 : privacy_mode_(PrivacyMode::PRIVACY_MODE_DISABLED) {}
79
GroupId(url::SchemeHostPort destination,PrivacyMode privacy_mode,NetworkAnonymizationKey network_anonymization_key,SecureDnsPolicy secure_dns_policy)80 ClientSocketPool::GroupId::GroupId(
81 url::SchemeHostPort destination,
82 PrivacyMode privacy_mode,
83 NetworkAnonymizationKey network_anonymization_key,
84 SecureDnsPolicy secure_dns_policy)
85 : destination_(std::move(destination)),
86 privacy_mode_(privacy_mode),
87 network_anonymization_key_(
88 NetworkAnonymizationKey::IsPartitioningEnabled()
89 ? std::move(network_anonymization_key)
90 : NetworkAnonymizationKey()),
91 secure_dns_policy_(secure_dns_policy) {
92 DCHECK(destination_.IsValid());
93
94 // ClientSocketPool only expected to be used for HTTP/HTTPS/WS/WSS cases, and
95 // "ws"/"wss" schemes should be converted to "http"/"https" equivalent first.
96 DCHECK(destination_.scheme() == url::kHttpScheme ||
97 destination_.scheme() == url::kHttpsScheme);
98 }
99
100 ClientSocketPool::GroupId::GroupId(const GroupId& group_id) = default;
101
102 ClientSocketPool::GroupId::~GroupId() = default;
103
104 ClientSocketPool::GroupId& ClientSocketPool::GroupId::operator=(
105 const GroupId& group_id) = default;
106
107 ClientSocketPool::GroupId& ClientSocketPool::GroupId::operator=(
108 GroupId&& group_id) = default;
109
ToString() const110 std::string ClientSocketPool::GroupId::ToString() const {
111 std::string result = destination_.Serialize();
112
113 if (privacy_mode_)
114 result = "pm/" + result;
115
116 if (NetworkAnonymizationKey::IsPartitioningEnabled()) {
117 result += " <";
118 result += network_anonymization_key_.ToDebugString();
119 result += ">";
120 }
121
122 switch (secure_dns_policy_) {
123 case SecureDnsPolicy::kAllow:
124 break;
125 case SecureDnsPolicy::kDisable:
126 result = "dsd/" + result;
127 break;
128 case SecureDnsPolicy::kBootstrap:
129 result = "dns_bootstrap/" + result;
130 break;
131 }
132
133 return result;
134 }
135
136 ClientSocketPool::~ClientSocketPool() = default;
137
138 // static
used_idle_socket_timeout()139 base::TimeDelta ClientSocketPool::used_idle_socket_timeout() {
140 return base::Seconds(g_used_idle_socket_timeout_s);
141 }
142
143 // static
set_used_idle_socket_timeout(base::TimeDelta timeout)144 void ClientSocketPool::set_used_idle_socket_timeout(base::TimeDelta timeout) {
145 DCHECK_GT(timeout.InSeconds(), 0);
146 g_used_idle_socket_timeout_s = timeout.InSeconds();
147 }
148
ClientSocketPool(bool is_for_websockets,const CommonConnectJobParams * common_connect_job_params,std::unique_ptr<ConnectJobFactory> connect_job_factory)149 ClientSocketPool::ClientSocketPool(
150 bool is_for_websockets,
151 const CommonConnectJobParams* common_connect_job_params,
152 std::unique_ptr<ConnectJobFactory> connect_job_factory)
153 : is_for_websockets_(is_for_websockets),
154 common_connect_job_params_(common_connect_job_params),
155 connect_job_factory_(std::move(connect_job_factory)) {}
156
NetLogTcpClientSocketPoolRequestedSocket(const NetLogWithSource & net_log,const GroupId & group_id)157 void ClientSocketPool::NetLogTcpClientSocketPoolRequestedSocket(
158 const NetLogWithSource& net_log,
159 const GroupId& group_id) {
160 // TODO(eroman): Split out the host and port parameters.
161 net_log.AddEvent(NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
162 [&] { return NetLogGroupIdParams(group_id); });
163 }
164
NetLogGroupIdParams(const GroupId & group_id)165 base::Value::Dict ClientSocketPool::NetLogGroupIdParams(
166 const GroupId& group_id) {
167 base::Value::Dict event_params;
168 event_params.Set("group_id", group_id.ToString());
169 return event_params;
170 }
171
CreateConnectJob(GroupId group_id,scoped_refptr<SocketParams> socket_params,const ProxyChain & proxy_chain,const absl::optional<NetworkTrafficAnnotationTag> & proxy_annotation_tag,RequestPriority request_priority,SocketTag socket_tag,ConnectJob::Delegate * delegate)172 std::unique_ptr<ConnectJob> ClientSocketPool::CreateConnectJob(
173 GroupId group_id,
174 scoped_refptr<SocketParams> socket_params,
175 const ProxyChain& proxy_chain,
176 const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
177 RequestPriority request_priority,
178 SocketTag socket_tag,
179 ConnectJob::Delegate* delegate) {
180 bool using_ssl = GURL::SchemeIsCryptographic(group_id.destination().scheme());
181
182 // If applicable, set up a callback to handle checking for H2 IP pooling
183 // opportunities. We don't perform H2 IP pooling to or through proxy servers,
184 // so ignore those cases.
185 OnHostResolutionCallback resolution_callback;
186 if (using_ssl && proxy_chain.is_direct()) {
187 resolution_callback = base::BindRepeating(
188 &OnHostResolution, common_connect_job_params_->spdy_session_pool,
189 // TODO(crbug.com/1206799): Pass along as SchemeHostPort.
190 SpdySessionKey(HostPortPair::FromSchemeHostPort(group_id.destination()),
191 proxy_chain, group_id.privacy_mode(),
192 SpdySessionKey::IsProxySession::kFalse, socket_tag,
193 group_id.network_anonymization_key(),
194 group_id.secure_dns_policy()),
195 is_for_websockets_);
196 }
197
198 return connect_job_factory_->CreateConnectJob(
199 group_id.destination(), proxy_chain, proxy_annotation_tag,
200 socket_params->ssl_config_for_origin(),
201 socket_params->base_ssl_config_for_proxies(), is_for_websockets_,
202 group_id.privacy_mode(), resolution_callback, request_priority,
203 socket_tag, group_id.network_anonymization_key(),
204 group_id.secure_dns_policy(), common_connect_job_params_, delegate);
205 }
206
207 } // namespace net
208