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 disable_resolver_cache, 162 ignore_limits, 163 resolution_callback); 164 } else { 165 ProxyServer proxy_server = proxy_info.proxy_server(); 166 proxy_host_port.reset(new HostPortPair(proxy_server.host_port_pair())); 167 scoped_refptr<TransportSocketParams> proxy_tcp_params( 168 new TransportSocketParams(*proxy_host_port, 169 disable_resolver_cache, 170 ignore_limits, 171 resolution_callback)); 172 173 if (proxy_info.is_http() || proxy_info.is_https()) { 174 std::string user_agent; 175 request_extra_headers.GetHeader(HttpRequestHeaders::kUserAgent, 176 &user_agent); 177 scoped_refptr<SSLSocketParams> ssl_params; 178 if (proxy_info.is_https()) { 179 // Set ssl_params, and unset proxy_tcp_params 180 ssl_params = new SSLSocketParams(proxy_tcp_params, 181 NULL, 182 NULL, 183 *proxy_host_port.get(), 184 ssl_config_for_proxy, 185 kPrivacyModeDisabled, 186 load_flags, 187 force_spdy_over_ssl, 188 want_spdy_over_npn); 189 proxy_tcp_params = NULL; 190 } 191 192 http_proxy_params = 193 new HttpProxySocketParams(proxy_tcp_params, 194 ssl_params, 195 request_url, 196 user_agent, 197 origin_host_port, 198 session->http_auth_cache(), 199 session->http_auth_handler_factory(), 200 session->spdy_session_pool(), 201 force_tunnel || using_ssl); 202 } else { 203 DCHECK(proxy_info.is_socks()); 204 char socks_version; 205 if (proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5) 206 socks_version = '5'; 207 else 208 socks_version = '4'; 209 connection_group = base::StringPrintf( 210 "socks%c/%s", socks_version, connection_group.c_str()); 211 212 socks_params = new SOCKSSocketParams(proxy_tcp_params, 213 socks_version == '5', 214 origin_host_port); 215 } 216 } 217 218 // Change group name if privacy mode is enabled. 219 if (privacy_mode == kPrivacyModeEnabled) 220 connection_group = "pm/" + connection_group; 221 222 // Deal with SSL - which layers on top of any given proxy. 223 if (using_ssl) { 224 scoped_refptr<SSLSocketParams> ssl_params = 225 new SSLSocketParams(tcp_params, 226 socks_params, 227 http_proxy_params, 228 origin_host_port, 229 ssl_config_for_origin, 230 privacy_mode, 231 load_flags, 232 force_spdy_over_ssl, 233 want_spdy_over_npn); 234 SSLClientSocketPool* ssl_pool = NULL; 235 if (proxy_info.is_direct()) { 236 ssl_pool = session->GetSSLSocketPool(socket_pool_type); 237 } else { 238 ssl_pool = session->GetSocketPoolForSSLWithProxy(socket_pool_type, 239 *proxy_host_port); 240 } 241 242 if (num_preconnect_streams) { 243 RequestSocketsForPool(ssl_pool, connection_group, ssl_params, 244 num_preconnect_streams, net_log); 245 return OK; 246 } 247 248 return socket_handle->Init(connection_group, ssl_params, 249 request_priority, callback, ssl_pool, 250 net_log); 251 } 252 253 // Finally, get the connection started. 254 255 if (proxy_info.is_http() || proxy_info.is_https()) { 256 HttpProxyClientSocketPool* pool = 257 session->GetSocketPoolForHTTPProxy(socket_pool_type, *proxy_host_port); 258 if (num_preconnect_streams) { 259 RequestSocketsForPool(pool, connection_group, http_proxy_params, 260 num_preconnect_streams, net_log); 261 return OK; 262 } 263 264 return socket_handle->Init(connection_group, http_proxy_params, 265 request_priority, callback, 266 pool, net_log); 267 } 268 269 if (proxy_info.is_socks()) { 270 SOCKSClientSocketPool* pool = 271 session->GetSocketPoolForSOCKSProxy(socket_pool_type, *proxy_host_port); 272 if (num_preconnect_streams) { 273 RequestSocketsForPool(pool, connection_group, socks_params, 274 num_preconnect_streams, net_log); 275 return OK; 276 } 277 278 return socket_handle->Init(connection_group, socks_params, 279 request_priority, callback, pool, 280 net_log); 281 } 282 283 DCHECK(proxy_info.is_direct()); 284 285 TransportClientSocketPool* pool = 286 session->GetTransportSocketPool(socket_pool_type); 287 if (num_preconnect_streams) { 288 RequestSocketsForPool(pool, connection_group, tcp_params, 289 num_preconnect_streams, net_log); 290 return OK; 291 } 292 293 return socket_handle->Init(connection_group, tcp_params, 294 request_priority, callback, 295 pool, net_log); 296 } 297 298 } // namespace 299 300 ClientSocketPoolManager::ClientSocketPoolManager() {} 301 ClientSocketPoolManager::~ClientSocketPoolManager() {} 302 303 // static 304 int ClientSocketPoolManager::max_sockets_per_pool( 305 HttpNetworkSession::SocketPoolType pool_type) { 306 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 307 return g_max_sockets_per_pool[pool_type]; 308 } 309 310 // static 311 void ClientSocketPoolManager::set_max_sockets_per_pool( 312 HttpNetworkSession::SocketPoolType pool_type, 313 int socket_count) { 314 DCHECK_LT(0, socket_count); 315 DCHECK_GT(1000, socket_count); // Sanity check. 316 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 317 g_max_sockets_per_pool[pool_type] = socket_count; 318 DCHECK_GE(g_max_sockets_per_pool[pool_type], 319 g_max_sockets_per_group[pool_type]); 320 } 321 322 // static 323 int ClientSocketPoolManager::max_sockets_per_group( 324 HttpNetworkSession::SocketPoolType pool_type) { 325 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 326 return g_max_sockets_per_group[pool_type]; 327 } 328 329 // static 330 void ClientSocketPoolManager::set_max_sockets_per_group( 331 HttpNetworkSession::SocketPoolType pool_type, 332 int socket_count) { 333 DCHECK_LT(0, socket_count); 334 // The following is a sanity check... but we should NEVER be near this value. 335 DCHECK_GT(100, socket_count); 336 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 337 g_max_sockets_per_group[pool_type] = socket_count; 338 339 DCHECK_GE(g_max_sockets_per_pool[pool_type], 340 g_max_sockets_per_group[pool_type]); 341 DCHECK_GE(g_max_sockets_per_proxy_server[pool_type], 342 g_max_sockets_per_group[pool_type]); 343 } 344 345 // static 346 int ClientSocketPoolManager::max_sockets_per_proxy_server( 347 HttpNetworkSession::SocketPoolType pool_type) { 348 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 349 return g_max_sockets_per_proxy_server[pool_type]; 350 } 351 352 // static 353 void ClientSocketPoolManager::set_max_sockets_per_proxy_server( 354 HttpNetworkSession::SocketPoolType pool_type, 355 int socket_count) { 356 DCHECK_LT(0, socket_count); 357 DCHECK_GT(100, socket_count); // Sanity check. 358 DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); 359 // Assert this case early on. The max number of sockets per group cannot 360 // exceed the max number of sockets per proxy server. 361 DCHECK_LE(g_max_sockets_per_group[pool_type], socket_count); 362 g_max_sockets_per_proxy_server[pool_type] = socket_count; 363 } 364 365 int InitSocketHandleForHttpRequest( 366 const GURL& request_url, 367 const HttpRequestHeaders& request_extra_headers, 368 int request_load_flags, 369 RequestPriority request_priority, 370 HttpNetworkSession* session, 371 const ProxyInfo& proxy_info, 372 bool force_spdy_over_ssl, 373 bool want_spdy_over_npn, 374 const SSLConfig& ssl_config_for_origin, 375 const SSLConfig& ssl_config_for_proxy, 376 PrivacyMode privacy_mode, 377 const BoundNetLog& net_log, 378 ClientSocketHandle* socket_handle, 379 const OnHostResolutionCallback& resolution_callback, 380 const CompletionCallback& callback) { 381 DCHECK(socket_handle); 382 return InitSocketPoolHelper( 383 request_url, request_extra_headers, request_load_flags, request_priority, 384 session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, 385 ssl_config_for_origin, ssl_config_for_proxy, false, privacy_mode, net_log, 386 0, socket_handle, HttpNetworkSession::NORMAL_SOCKET_POOL, 387 resolution_callback, callback); 388 } 389 390 int InitSocketHandleForWebSocketRequest( 391 const GURL& request_url, 392 const HttpRequestHeaders& request_extra_headers, 393 int request_load_flags, 394 RequestPriority request_priority, 395 HttpNetworkSession* session, 396 const ProxyInfo& proxy_info, 397 bool force_spdy_over_ssl, 398 bool want_spdy_over_npn, 399 const SSLConfig& ssl_config_for_origin, 400 const SSLConfig& ssl_config_for_proxy, 401 PrivacyMode privacy_mode, 402 const BoundNetLog& net_log, 403 ClientSocketHandle* socket_handle, 404 const OnHostResolutionCallback& resolution_callback, 405 const CompletionCallback& callback) { 406 DCHECK(socket_handle); 407 return InitSocketPoolHelper( 408 request_url, request_extra_headers, request_load_flags, request_priority, 409 session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, 410 ssl_config_for_origin, ssl_config_for_proxy, true, privacy_mode, net_log, 411 0, socket_handle, HttpNetworkSession::WEBSOCKET_SOCKET_POOL, 412 resolution_callback, callback); 413 } 414 415 int InitSocketHandleForRawConnect( 416 const HostPortPair& host_port_pair, 417 HttpNetworkSession* session, 418 const ProxyInfo& proxy_info, 419 const SSLConfig& ssl_config_for_origin, 420 const SSLConfig& ssl_config_for_proxy, 421 PrivacyMode privacy_mode, 422 const BoundNetLog& net_log, 423 ClientSocketHandle* socket_handle, 424 const CompletionCallback& callback) { 425 DCHECK(socket_handle); 426 // Synthesize an HttpRequestInfo. 427 GURL request_url = GURL("http://" + host_port_pair.ToString()); 428 HttpRequestHeaders request_extra_headers; 429 int request_load_flags = 0; 430 RequestPriority request_priority = MEDIUM; 431 432 return InitSocketPoolHelper( 433 request_url, request_extra_headers, request_load_flags, request_priority, 434 session, proxy_info, false, false, ssl_config_for_origin, 435 ssl_config_for_proxy, true, privacy_mode, net_log, 0, socket_handle, 436 HttpNetworkSession::NORMAL_SOCKET_POOL, OnHostResolutionCallback(), 437 callback); 438 } 439 440 int InitSocketHandleForTlsConnect( 441 const HostPortPair& host_port_pair, 442 HttpNetworkSession* session, 443 const ProxyInfo& proxy_info, 444 const SSLConfig& ssl_config_for_origin, 445 const SSLConfig& ssl_config_for_proxy, 446 PrivacyMode privacy_mode, 447 const BoundNetLog& net_log, 448 ClientSocketHandle* socket_handle, 449 const CompletionCallback& callback) { 450 DCHECK(socket_handle); 451 // Synthesize an HttpRequestInfo. 452 GURL request_url = GURL("https://" + host_port_pair.ToString()); 453 HttpRequestHeaders request_extra_headers; 454 int request_load_flags = 0; 455 RequestPriority request_priority = MEDIUM; 456 457 return InitSocketPoolHelper( 458 request_url, request_extra_headers, request_load_flags, request_priority, 459 session, proxy_info, false, false, ssl_config_for_origin, 460 ssl_config_for_proxy, true, privacy_mode, net_log, 0, socket_handle, 461 HttpNetworkSession::NORMAL_SOCKET_POOL, OnHostResolutionCallback(), 462 callback); 463 } 464 465 int PreconnectSocketsForHttpRequest( 466 const GURL& request_url, 467 const HttpRequestHeaders& request_extra_headers, 468 int request_load_flags, 469 RequestPriority request_priority, 470 HttpNetworkSession* session, 471 const ProxyInfo& proxy_info, 472 bool force_spdy_over_ssl, 473 bool want_spdy_over_npn, 474 const SSLConfig& ssl_config_for_origin, 475 const SSLConfig& ssl_config_for_proxy, 476 PrivacyMode privacy_mode, 477 const BoundNetLog& net_log, 478 int num_preconnect_streams) { 479 return InitSocketPoolHelper( 480 request_url, request_extra_headers, request_load_flags, request_priority, 481 session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, 482 ssl_config_for_origin, ssl_config_for_proxy, false, privacy_mode, net_log, 483 num_preconnect_streams, NULL, HttpNetworkSession::NORMAL_SOCKET_POOL, 484 OnHostResolutionCallback(), CompletionCallback()); 485 } 486 487 } // namespace net 488