1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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_manager.h" 6 7 #include <string> 8 9 #include "base/basictypes.h" 10 #include "base/logging.h" 11 #include "base/strings/stringprintf.h" 12 #include "net/base/load_flags.h" 13 #include "net/http/http_proxy_client_socket_pool.h" 14 #include "net/http/http_request_info.h" 15 #include "net/http/http_stream_factory.h" 16 #include "net/proxy/proxy_info.h" 17 #include "net/socket/client_socket_handle.h" 18 #include "net/socket/socks_client_socket_pool.h" 19 #include "net/socket/ssl_client_socket_pool.h" 20 #include "net/socket/transport_client_socket_pool.h" 21 22 namespace net { 23 24 namespace { 25 26 // Limit of sockets of each socket pool. 27 int g_max_sockets_per_pool[] = { 28 256, // NORMAL_SOCKET_POOL 29 256 // WEBSOCKET_SOCKET_POOL 30 }; 31 32 COMPILE_ASSERT(arraysize(g_max_sockets_per_pool) == 33 HttpNetworkSession::NUM_SOCKET_POOL_TYPES, 34 max_sockets_per_pool_length_mismatch); 35 36 // Default to allow up to 6 connections per host. Experiment and tuning may 37 // try other values (greater than 0). Too large may cause many problems, such 38 // as home routers blocking the connections!?!? See http://crbug.com/12066. 39 // 40 // WebSocket connections are long-lived, and should be treated differently 41 // than normal other connections. 6 connections per group sounded too small 42 // for such use, thus we use a larger limit which was determined somewhat 43 // arbitrarily. 44 // TODO(yutak): Look at the usage and determine the right value after 45 // WebSocket protocol stack starts to work. 46 int g_max_sockets_per_group[] = { 47 6, // NORMAL_SOCKET_POOL 48 30 // WEBSOCKET_SOCKET_POOL 49 }; 50 51 COMPILE_ASSERT(arraysize(g_max_sockets_per_group) == 52 HttpNetworkSession::NUM_SOCKET_POOL_TYPES, 53 max_sockets_per_group_length_mismatch); 54 55 // The max number of sockets to allow per proxy server. This applies both to 56 // http and SOCKS proxies. See http://crbug.com/12066 and 57 // http://crbug.com/44501 for details about proxy server connection limits. 58 int g_max_sockets_per_proxy_server[] = { 59 kDefaultMaxSocketsPerProxyServer, // NORMAL_SOCKET_POOL 60 kDefaultMaxSocketsPerProxyServer // WEBSOCKET_SOCKET_POOL 61 }; 62 63 COMPILE_ASSERT(arraysize(g_max_sockets_per_proxy_server) == 64 HttpNetworkSession::NUM_SOCKET_POOL_TYPES, 65 max_sockets_per_proxy_server_length_mismatch); 66 67 // The meat of the implementation for the InitSocketHandleForHttpRequest, 68 // InitSocketHandleForRawConnect and PreconnectSocketsForHttpRequest methods. 69 int InitSocketPoolHelper(const GURL& request_url, 70 const HttpRequestHeaders& request_extra_headers, 71 int request_load_flags, 72 RequestPriority request_priority, 73 HttpNetworkSession* session, 74 const ProxyInfo& proxy_info, 75 bool force_spdy_over_ssl, 76 bool want_spdy_over_npn, 77 const SSLConfig& ssl_config_for_origin, 78 const SSLConfig& ssl_config_for_proxy, 79 bool force_tunnel, 80 PrivacyMode privacy_mode, 81 const BoundNetLog& net_log, 82 int num_preconnect_streams, 83 ClientSocketHandle* socket_handle, 84 HttpNetworkSession::SocketPoolType socket_pool_type, 85 const OnHostResolutionCallback& resolution_callback, 86 const CompletionCallback& callback) { 87 scoped_refptr<TransportSocketParams> tcp_params; 88 scoped_refptr<HttpProxySocketParams> http_proxy_params; 89 scoped_refptr<SOCKSSocketParams> socks_params; 90 scoped_ptr<HostPortPair> proxy_host_port; 91 92 bool using_ssl = request_url.SchemeIs("https") || 93 request_url.SchemeIs("wss") || force_spdy_over_ssl; 94 95 HostPortPair origin_host_port = 96 HostPortPair(request_url.HostNoBrackets(), 97 request_url.EffectiveIntPort()); 98 99 if (!using_ssl && session->params().testing_fixed_http_port != 0) { 100 origin_host_port.set_port(session->params().testing_fixed_http_port); 101 } else if (using_ssl && session->params().testing_fixed_https_port != 0) { 102 origin_host_port.set_port(session->params().testing_fixed_https_port); 103 } 104 105 bool disable_resolver_cache = 106 request_load_flags & LOAD_BYPASS_CACHE || 107 request_load_flags & LOAD_VALIDATE_CACHE || 108 request_load_flags & LOAD_DISABLE_CACHE; 109 110 int load_flags = request_load_flags; 111 if (session->params().ignore_certificate_errors) 112 load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS; 113 114 // Build the string used to uniquely identify connections of this type. 115 // Determine the host and port to connect to. 116 std::string connection_group = origin_host_port.ToString(); 117 DCHECK(!connection_group.empty()); 118 if (request_url.SchemeIs("ftp")) { 119 // Combining FTP with forced SPDY over SSL would be a "path to madness". 120 // Make sure we never do that. 121 DCHECK(!using_ssl); 122 connection_group = "ftp/" + connection_group; 123 } 124 if (using_ssl) { 125 // All connections in a group should use the same SSLConfig settings. 126 // Encode version_max in the connection group's name, unless it's the 127 // default version_max. (We want the common case to use the shortest 128 // encoding). A version_max of TLS 1.1 is encoded as "ssl(max:3.2)/" 129 // rather than "tlsv1.1/" because the actual protocol version, which 130 // is selected by the server, may not be TLS 1.1. Do not encode 131 // version_min in the connection group's name because version_min 132 // should be the same for all connections, whereas version_max may 133 // change for version fallbacks. 134 std::string prefix = "ssl/"; 135 if (ssl_config_for_origin.version_max != 136 SSLConfigService::default_version_max()) { 137 switch (ssl_config_for_origin.version_max) { 138 case SSL_PROTOCOL_VERSION_TLS1_2: 139 prefix = "ssl(max:3.3)/"; 140 break; 141 case SSL_PROTOCOL_VERSION_TLS1_1: 142 prefix = "ssl(max:3.2)/"; 143 break; 144 case SSL_PROTOCOL_VERSION_TLS1: 145 prefix = "ssl(max:3.1)/"; 146 break; 147 case SSL_PROTOCOL_VERSION_SSL3: 148 prefix = "sslv3/"; 149 break; 150 default: 151 CHECK(false); 152 break; 153 } 154 } 155 connection_group = prefix + connection_group; 156 } 157 158 bool ignore_limits = (request_load_flags & LOAD_IGNORE_LIMITS) != 0; 159 if (proxy_info.is_direct()) { 160 tcp_params = new TransportSocketParams(origin_host_port, 161 request_priority, 162 disable_resolver_cache, 163 ignore_limits, 164 resolution_callback); 165 } else { 166 ProxyServer proxy_server = proxy_info.proxy_server(); 167 proxy_host_port.reset(new HostPortPair(proxy_server.host_port_pair())); 168 scoped_refptr<TransportSocketParams> proxy_tcp_params( 169 new TransportSocketParams(*proxy_host_port, 170 request_priority, 171 disable_resolver_cache, 172 ignore_limits, 173 resolution_callback)); 174 175 if (proxy_info.is_http() || proxy_info.is_https()) { 176 std::string user_agent; 177 request_extra_headers.GetHeader(HttpRequestHeaders::kUserAgent, 178 &user_agent); 179 scoped_refptr<SSLSocketParams> ssl_params; 180 if (proxy_info.is_https()) { 181 // Set ssl_params, and unset proxy_tcp_params 182 ssl_params = new SSLSocketParams(proxy_tcp_params, 183 NULL, 184 NULL, 185 ProxyServer::SCHEME_DIRECT, 186 *proxy_host_port.get(), 187 ssl_config_for_proxy, 188 kPrivacyModeDisabled, 189 load_flags, 190 force_spdy_over_ssl, 191 want_spdy_over_npn); 192 proxy_tcp_params = NULL; 193 } 194 195 http_proxy_params = 196 new HttpProxySocketParams(proxy_tcp_params, 197 ssl_params, 198 request_url, 199 user_agent, 200 origin_host_port, 201 session->http_auth_cache(), 202 session->http_auth_handler_factory(), 203 session->spdy_session_pool(), 204 force_tunnel || using_ssl); 205 } else { 206 DCHECK(proxy_info.is_socks()); 207 char socks_version; 208 if (proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5) 209 socks_version = '5'; 210 else 211 socks_version = '4'; 212 connection_group = base::StringPrintf( 213 "socks%c/%s", socks_version, connection_group.c_str()); 214 215 socks_params = new SOCKSSocketParams(proxy_tcp_params, 216 socks_version == '5', 217 origin_host_port, 218 request_priority); 219 } 220 } 221 222 // Change group name if privacy mode is enabled. 223 if (privacy_mode == kPrivacyModeEnabled) 224 connection_group = "pm/" + connection_group; 225 226 // Deal with SSL - which layers on top of any given proxy. 227 if (using_ssl) { 228 scoped_refptr<SSLSocketParams> ssl_params = 229 new SSLSocketParams(tcp_params, 230 socks_params, 231 http_proxy_params, 232 proxy_info.proxy_server().scheme(), 233 origin_host_port, 234 ssl_config_for_origin, 235 privacy_mode, 236 load_flags, 237 force_spdy_over_ssl, 238 want_spdy_over_npn); 239 SSLClientSocketPool* ssl_pool = NULL; 240 if (proxy_info.is_direct()) { 241 ssl_pool = session->GetSSLSocketPool(socket_pool_type); 242 } else { 243 ssl_pool = session->GetSocketPoolForSSLWithProxy(socket_pool_type, 244 *proxy_host_port); 245 } 246 247 if (num_preconnect_streams) { 248 RequestSocketsForPool(ssl_pool, connection_group, ssl_params, 249 num_preconnect_streams, net_log); 250 return OK; 251 } 252 253 return socket_handle->Init(connection_group, ssl_params, 254 request_priority, callback, ssl_pool, 255 net_log); 256 } 257 258 // Finally, get the connection started. 259 260 if (proxy_info.is_http() || proxy_info.is_https()) { 261 HttpProxyClientSocketPool* pool = 262 session->GetSocketPoolForHTTPProxy(socket_pool_type, *proxy_host_port); 263 if (num_preconnect_streams) { 264 RequestSocketsForPool(pool, connection_group, http_proxy_params, 265 num_preconnect_streams, net_log); 266 return OK; 267 } 268 269 return socket_handle->Init(connection_group, http_proxy_params, 270 request_priority, callback, 271 pool, net_log); 272 } 273 274 if (proxy_info.is_socks()) { 275 SOCKSClientSocketPool* pool = 276 session->GetSocketPoolForSOCKSProxy(socket_pool_type, *proxy_host_port); 277 if (num_preconnect_streams) { 278 RequestSocketsForPool(pool, connection_group, socks_params, 279 num_preconnect_streams, net_log); 280 return OK; 281 } 282 283 return socket_handle->Init(connection_group, socks_params, 284 request_priority, callback, pool, 285 net_log); 286 } 287 288 DCHECK(proxy_info.is_direct()); 289 290 TransportClientSocketPool* pool = 291 session->GetTransportSocketPool(socket_pool_type); 292 if (num_preconnect_streams) { 293 RequestSocketsForPool(pool, connection_group, tcp_params, 294 num_preconnect_streams, net_log); 295 return OK; 296 } 297 298 return socket_handle->Init(connection_group, tcp_params, 299 request_priority, callback, 300 pool, net_log); 301 } 302 303 } // namespace 304 305 ClientSocketPoolManager::ClientSocketPoolManager() {} 306 ClientSocketPoolManager::~ClientSocketPoolManager() {} 307 308 // static 309 int ClientSocketPoolManager::max_sockets_per_pool( 310 HttpNetworkSession::SocketPoolType pool_type) { 311 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 312 return g_max_sockets_per_pool[pool_type]; 313 } 314 315 // static 316 void ClientSocketPoolManager::set_max_sockets_per_pool( 317 HttpNetworkSession::SocketPoolType pool_type, 318 int socket_count) { 319 DCHECK_LT(0, socket_count); 320 DCHECK_GT(1000, socket_count); // Sanity check. 321 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 322 g_max_sockets_per_pool[pool_type] = socket_count; 323 DCHECK_GE(g_max_sockets_per_pool[pool_type], 324 g_max_sockets_per_group[pool_type]); 325 } 326 327 // static 328 int ClientSocketPoolManager::max_sockets_per_group( 329 HttpNetworkSession::SocketPoolType pool_type) { 330 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 331 return g_max_sockets_per_group[pool_type]; 332 } 333 334 // static 335 void ClientSocketPoolManager::set_max_sockets_per_group( 336 HttpNetworkSession::SocketPoolType pool_type, 337 int socket_count) { 338 DCHECK_LT(0, socket_count); 339 // The following is a sanity check... but we should NEVER be near this value. 340 DCHECK_GT(100, socket_count); 341 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 342 g_max_sockets_per_group[pool_type] = socket_count; 343 344 DCHECK_GE(g_max_sockets_per_pool[pool_type], 345 g_max_sockets_per_group[pool_type]); 346 DCHECK_GE(g_max_sockets_per_proxy_server[pool_type], 347 g_max_sockets_per_group[pool_type]); 348 } 349 350 // static 351 int ClientSocketPoolManager::max_sockets_per_proxy_server( 352 HttpNetworkSession::SocketPoolType pool_type) { 353 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 354 return g_max_sockets_per_proxy_server[pool_type]; 355 } 356 357 // static 358 void ClientSocketPoolManager::set_max_sockets_per_proxy_server( 359 HttpNetworkSession::SocketPoolType pool_type, 360 int socket_count) { 361 DCHECK_LT(0, socket_count); 362 DCHECK_GT(100, socket_count); // Sanity check. 363 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 364 // Assert this case early on. The max number of sockets per group cannot 365 // exceed the max number of sockets per proxy server. 366 DCHECK_LE(g_max_sockets_per_group[pool_type], socket_count); 367 g_max_sockets_per_proxy_server[pool_type] = socket_count; 368 } 369 370 int InitSocketHandleForHttpRequest( 371 const GURL& request_url, 372 const HttpRequestHeaders& request_extra_headers, 373 int request_load_flags, 374 RequestPriority request_priority, 375 HttpNetworkSession* session, 376 const ProxyInfo& proxy_info, 377 bool force_spdy_over_ssl, 378 bool want_spdy_over_npn, 379 const SSLConfig& ssl_config_for_origin, 380 const SSLConfig& ssl_config_for_proxy, 381 PrivacyMode privacy_mode, 382 const BoundNetLog& net_log, 383 ClientSocketHandle* socket_handle, 384 const OnHostResolutionCallback& resolution_callback, 385 const CompletionCallback& callback) { 386 DCHECK(socket_handle); 387 return InitSocketPoolHelper( 388 request_url, request_extra_headers, request_load_flags, request_priority, 389 session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, 390 ssl_config_for_origin, ssl_config_for_proxy, false, privacy_mode, net_log, 391 0, socket_handle, HttpNetworkSession::NORMAL_SOCKET_POOL, 392 resolution_callback, callback); 393 } 394 395 int InitSocketHandleForWebSocketRequest( 396 const GURL& request_url, 397 const HttpRequestHeaders& request_extra_headers, 398 int request_load_flags, 399 RequestPriority request_priority, 400 HttpNetworkSession* session, 401 const ProxyInfo& proxy_info, 402 bool force_spdy_over_ssl, 403 bool want_spdy_over_npn, 404 const SSLConfig& ssl_config_for_origin, 405 const SSLConfig& ssl_config_for_proxy, 406 PrivacyMode privacy_mode, 407 const BoundNetLog& net_log, 408 ClientSocketHandle* socket_handle, 409 const OnHostResolutionCallback& resolution_callback, 410 const CompletionCallback& callback) { 411 DCHECK(socket_handle); 412 return InitSocketPoolHelper( 413 request_url, request_extra_headers, request_load_flags, request_priority, 414 session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, 415 ssl_config_for_origin, ssl_config_for_proxy, true, privacy_mode, net_log, 416 0, socket_handle, HttpNetworkSession::WEBSOCKET_SOCKET_POOL, 417 resolution_callback, callback); 418 } 419 420 int InitSocketHandleForRawConnect( 421 const HostPortPair& host_port_pair, 422 HttpNetworkSession* session, 423 const ProxyInfo& proxy_info, 424 const SSLConfig& ssl_config_for_origin, 425 const SSLConfig& ssl_config_for_proxy, 426 PrivacyMode privacy_mode, 427 const BoundNetLog& net_log, 428 ClientSocketHandle* socket_handle, 429 const CompletionCallback& callback) { 430 DCHECK(socket_handle); 431 // Synthesize an HttpRequestInfo. 432 GURL request_url = GURL("http://" + host_port_pair.ToString()); 433 HttpRequestHeaders request_extra_headers; 434 int request_load_flags = 0; 435 RequestPriority request_priority = MEDIUM; 436 437 return InitSocketPoolHelper( 438 request_url, request_extra_headers, request_load_flags, request_priority, 439 session, proxy_info, false, false, ssl_config_for_origin, 440 ssl_config_for_proxy, true, privacy_mode, net_log, 0, socket_handle, 441 HttpNetworkSession::NORMAL_SOCKET_POOL, OnHostResolutionCallback(), 442 callback); 443 } 444 445 int PreconnectSocketsForHttpRequest( 446 const GURL& request_url, 447 const HttpRequestHeaders& request_extra_headers, 448 int request_load_flags, 449 RequestPriority request_priority, 450 HttpNetworkSession* session, 451 const ProxyInfo& proxy_info, 452 bool force_spdy_over_ssl, 453 bool want_spdy_over_npn, 454 const SSLConfig& ssl_config_for_origin, 455 const SSLConfig& ssl_config_for_proxy, 456 PrivacyMode privacy_mode, 457 const BoundNetLog& net_log, 458 int num_preconnect_streams) { 459 return InitSocketPoolHelper( 460 request_url, request_extra_headers, request_load_flags, request_priority, 461 session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, 462 ssl_config_for_origin, ssl_config_for_proxy, false, privacy_mode, net_log, 463 num_preconnect_streams, NULL, HttpNetworkSession::NORMAL_SOCKET_POOL, 464 OnHostResolutionCallback(), CompletionCallback()); 465 } 466 467 } // namespace net 468