diff options
Diffstat (limited to 'chromium/net/socket/client_socket_pool_manager.cc')
-rw-r--r-- | chromium/net/socket/client_socket_pool_manager.cc | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/chromium/net/socket/client_socket_pool_manager.cc b/chromium/net/socket/client_socket_pool_manager.cc new file mode 100644 index 00000000000..71496d28646 --- /dev/null +++ b/chromium/net/socket/client_socket_pool_manager.cc @@ -0,0 +1,467 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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_manager.h" + +#include <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "net/base/load_flags.h" +#include "net/http/http_proxy_client_socket_pool.h" +#include "net/http/http_request_info.h" +#include "net/http/http_stream_factory.h" +#include "net/proxy/proxy_info.h" +#include "net/socket/client_socket_handle.h" +#include "net/socket/socks_client_socket_pool.h" +#include "net/socket/ssl_client_socket_pool.h" +#include "net/socket/transport_client_socket_pool.h" + +namespace net { + +namespace { + +// Limit of sockets of each socket pool. +int g_max_sockets_per_pool[] = { + 256, // NORMAL_SOCKET_POOL + 256 // WEBSOCKET_SOCKET_POOL +}; + +COMPILE_ASSERT(arraysize(g_max_sockets_per_pool) == + HttpNetworkSession::NUM_SOCKET_POOL_TYPES, + max_sockets_per_pool_length_mismatch); + +// Default to allow up to 6 connections per host. Experiment and tuning may +// try other values (greater than 0). Too large may cause many problems, such +// as home routers blocking the connections!?!? See http://crbug.com/12066. +// +// WebSocket connections are long-lived, and should be treated differently +// than normal other connections. 6 connections per group sounded too small +// for such use, thus we use a larger limit which was determined somewhat +// arbitrarily. +// TODO(yutak): Look at the usage and determine the right value after +// WebSocket protocol stack starts to work. +int g_max_sockets_per_group[] = { + 6, // NORMAL_SOCKET_POOL + 30 // WEBSOCKET_SOCKET_POOL +}; + +COMPILE_ASSERT(arraysize(g_max_sockets_per_group) == + HttpNetworkSession::NUM_SOCKET_POOL_TYPES, + max_sockets_per_group_length_mismatch); + +// The max number of sockets to allow per proxy server. This applies both to +// http and SOCKS proxies. See http://crbug.com/12066 and +// http://crbug.com/44501 for details about proxy server connection limits. +int g_max_sockets_per_proxy_server[] = { + kDefaultMaxSocketsPerProxyServer, // NORMAL_SOCKET_POOL + kDefaultMaxSocketsPerProxyServer // WEBSOCKET_SOCKET_POOL +}; + +COMPILE_ASSERT(arraysize(g_max_sockets_per_proxy_server) == + HttpNetworkSession::NUM_SOCKET_POOL_TYPES, + max_sockets_per_proxy_server_length_mismatch); + +// The meat of the implementation for the InitSocketHandleForHttpRequest, +// InitSocketHandleForRawConnect and PreconnectSocketsForHttpRequest methods. +int InitSocketPoolHelper(const GURL& request_url, + const HttpRequestHeaders& request_extra_headers, + int request_load_flags, + RequestPriority request_priority, + HttpNetworkSession* session, + const ProxyInfo& proxy_info, + bool force_spdy_over_ssl, + bool want_spdy_over_npn, + const SSLConfig& ssl_config_for_origin, + const SSLConfig& ssl_config_for_proxy, + bool force_tunnel, + PrivacyMode privacy_mode, + const BoundNetLog& net_log, + int num_preconnect_streams, + ClientSocketHandle* socket_handle, + HttpNetworkSession::SocketPoolType socket_pool_type, + const OnHostResolutionCallback& resolution_callback, + const CompletionCallback& callback) { + scoped_refptr<TransportSocketParams> tcp_params; + scoped_refptr<HttpProxySocketParams> http_proxy_params; + scoped_refptr<SOCKSSocketParams> socks_params; + scoped_ptr<HostPortPair> proxy_host_port; + + bool using_ssl = request_url.SchemeIs("https") || + request_url.SchemeIs("wss") || force_spdy_over_ssl; + + HostPortPair origin_host_port = + HostPortPair(request_url.HostNoBrackets(), + request_url.EffectiveIntPort()); + + if (!using_ssl && session->params().testing_fixed_http_port != 0) { + origin_host_port.set_port(session->params().testing_fixed_http_port); + } else if (using_ssl && session->params().testing_fixed_https_port != 0) { + origin_host_port.set_port(session->params().testing_fixed_https_port); + } + + bool disable_resolver_cache = + request_load_flags & LOAD_BYPASS_CACHE || + request_load_flags & LOAD_VALIDATE_CACHE || + request_load_flags & LOAD_DISABLE_CACHE; + + int load_flags = request_load_flags; + if (session->params().ignore_certificate_errors) + load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS; + + // Build the string used to uniquely identify connections of this type. + // Determine the host and port to connect to. + std::string connection_group = origin_host_port.ToString(); + DCHECK(!connection_group.empty()); + if (request_url.SchemeIs("ftp")) { + // Combining FTP with forced SPDY over SSL would be a "path to madness". + // Make sure we never do that. + DCHECK(!using_ssl); + connection_group = "ftp/" + connection_group; + } + if (using_ssl) { + // All connections in a group should use the same SSLConfig settings. + // Encode version_max in the connection group's name, unless it's the + // default version_max. (We want the common case to use the shortest + // encoding). A version_max of TLS 1.1 is encoded as "ssl(max:3.2)/" + // rather than "tlsv1.1/" because the actual protocol version, which + // is selected by the server, may not be TLS 1.1. Do not encode + // version_min in the connection group's name because version_min + // should be the same for all connections, whereas version_max may + // change for version fallbacks. + std::string prefix = "ssl/"; + if (ssl_config_for_origin.version_max != + SSLConfigService::default_version_max()) { + switch (ssl_config_for_origin.version_max) { + case SSL_PROTOCOL_VERSION_TLS1_2: + prefix = "ssl(max:3.3)/"; + break; + case SSL_PROTOCOL_VERSION_TLS1_1: + prefix = "ssl(max:3.2)/"; + break; + case SSL_PROTOCOL_VERSION_TLS1: + prefix = "ssl(max:3.1)/"; + break; + case SSL_PROTOCOL_VERSION_SSL3: + prefix = "sslv3/"; + break; + default: + CHECK(false); + break; + } + } + connection_group = prefix + connection_group; + } + + bool ignore_limits = (request_load_flags & LOAD_IGNORE_LIMITS) != 0; + if (proxy_info.is_direct()) { + tcp_params = new TransportSocketParams(origin_host_port, + request_priority, + disable_resolver_cache, + ignore_limits, + resolution_callback); + } else { + ProxyServer proxy_server = proxy_info.proxy_server(); + proxy_host_port.reset(new HostPortPair(proxy_server.host_port_pair())); + scoped_refptr<TransportSocketParams> proxy_tcp_params( + new TransportSocketParams(*proxy_host_port, + request_priority, + disable_resolver_cache, + ignore_limits, + resolution_callback)); + + if (proxy_info.is_http() || proxy_info.is_https()) { + std::string user_agent; + request_extra_headers.GetHeader(HttpRequestHeaders::kUserAgent, + &user_agent); + scoped_refptr<SSLSocketParams> ssl_params; + if (proxy_info.is_https()) { + // Set ssl_params, and unset proxy_tcp_params + ssl_params = new SSLSocketParams(proxy_tcp_params, + NULL, + NULL, + ProxyServer::SCHEME_DIRECT, + *proxy_host_port.get(), + ssl_config_for_proxy, + kPrivacyModeDisabled, + load_flags, + force_spdy_over_ssl, + want_spdy_over_npn); + proxy_tcp_params = NULL; + } + + http_proxy_params = + new HttpProxySocketParams(proxy_tcp_params, + ssl_params, + request_url, + user_agent, + origin_host_port, + session->http_auth_cache(), + session->http_auth_handler_factory(), + session->spdy_session_pool(), + force_tunnel || using_ssl); + } else { + DCHECK(proxy_info.is_socks()); + char socks_version; + if (proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5) + socks_version = '5'; + else + socks_version = '4'; + connection_group = base::StringPrintf( + "socks%c/%s", socks_version, connection_group.c_str()); + + socks_params = new SOCKSSocketParams(proxy_tcp_params, + socks_version == '5', + origin_host_port, + request_priority); + } + } + + // Change group name if privacy mode is enabled. + if (privacy_mode == kPrivacyModeEnabled) + connection_group = "pm/" + connection_group; + + // Deal with SSL - which layers on top of any given proxy. + if (using_ssl) { + scoped_refptr<SSLSocketParams> ssl_params = + new SSLSocketParams(tcp_params, + socks_params, + http_proxy_params, + proxy_info.proxy_server().scheme(), + origin_host_port, + ssl_config_for_origin, + privacy_mode, + load_flags, + force_spdy_over_ssl, + want_spdy_over_npn); + SSLClientSocketPool* ssl_pool = NULL; + if (proxy_info.is_direct()) { + ssl_pool = session->GetSSLSocketPool(socket_pool_type); + } else { + ssl_pool = session->GetSocketPoolForSSLWithProxy(socket_pool_type, + *proxy_host_port); + } + + if (num_preconnect_streams) { + RequestSocketsForPool(ssl_pool, connection_group, ssl_params, + num_preconnect_streams, net_log); + return OK; + } + + return socket_handle->Init(connection_group, ssl_params, + request_priority, callback, ssl_pool, + net_log); + } + + // Finally, get the connection started. + + if (proxy_info.is_http() || proxy_info.is_https()) { + HttpProxyClientSocketPool* pool = + session->GetSocketPoolForHTTPProxy(socket_pool_type, *proxy_host_port); + if (num_preconnect_streams) { + RequestSocketsForPool(pool, connection_group, http_proxy_params, + num_preconnect_streams, net_log); + return OK; + } + + return socket_handle->Init(connection_group, http_proxy_params, + request_priority, callback, + pool, net_log); + } + + if (proxy_info.is_socks()) { + SOCKSClientSocketPool* pool = + session->GetSocketPoolForSOCKSProxy(socket_pool_type, *proxy_host_port); + if (num_preconnect_streams) { + RequestSocketsForPool(pool, connection_group, socks_params, + num_preconnect_streams, net_log); + return OK; + } + + return socket_handle->Init(connection_group, socks_params, + request_priority, callback, pool, + net_log); + } + + DCHECK(proxy_info.is_direct()); + + TransportClientSocketPool* pool = + session->GetTransportSocketPool(socket_pool_type); + if (num_preconnect_streams) { + RequestSocketsForPool(pool, connection_group, tcp_params, + num_preconnect_streams, net_log); + return OK; + } + + return socket_handle->Init(connection_group, tcp_params, + request_priority, callback, + pool, net_log); +} + +} // namespace + +ClientSocketPoolManager::ClientSocketPoolManager() {} +ClientSocketPoolManager::~ClientSocketPoolManager() {} + +// static +int ClientSocketPoolManager::max_sockets_per_pool( + HttpNetworkSession::SocketPoolType pool_type) { + DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); + return g_max_sockets_per_pool[pool_type]; +} + +// static +void ClientSocketPoolManager::set_max_sockets_per_pool( + HttpNetworkSession::SocketPoolType pool_type, + int socket_count) { + DCHECK_LT(0, socket_count); + DCHECK_GT(1000, socket_count); // Sanity check. + DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); + g_max_sockets_per_pool[pool_type] = socket_count; + DCHECK_GE(g_max_sockets_per_pool[pool_type], + g_max_sockets_per_group[pool_type]); +} + +// static +int ClientSocketPoolManager::max_sockets_per_group( + HttpNetworkSession::SocketPoolType pool_type) { + DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); + return g_max_sockets_per_group[pool_type]; +} + +// static +void ClientSocketPoolManager::set_max_sockets_per_group( + HttpNetworkSession::SocketPoolType pool_type, + int socket_count) { + DCHECK_LT(0, socket_count); + // The following is a sanity check... but we should NEVER be near this value. + DCHECK_GT(100, socket_count); + DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); + g_max_sockets_per_group[pool_type] = socket_count; + + DCHECK_GE(g_max_sockets_per_pool[pool_type], + g_max_sockets_per_group[pool_type]); + DCHECK_GE(g_max_sockets_per_proxy_server[pool_type], + g_max_sockets_per_group[pool_type]); +} + +// static +int ClientSocketPoolManager::max_sockets_per_proxy_server( + HttpNetworkSession::SocketPoolType pool_type) { + DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); + return g_max_sockets_per_proxy_server[pool_type]; +} + +// static +void ClientSocketPoolManager::set_max_sockets_per_proxy_server( + HttpNetworkSession::SocketPoolType pool_type, + int socket_count) { + DCHECK_LT(0, socket_count); + DCHECK_GT(100, socket_count); // Sanity check. + DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); + // Assert this case early on. The max number of sockets per group cannot + // exceed the max number of sockets per proxy server. + DCHECK_LE(g_max_sockets_per_group[pool_type], socket_count); + g_max_sockets_per_proxy_server[pool_type] = socket_count; +} + +int InitSocketHandleForHttpRequest( + const GURL& request_url, + const HttpRequestHeaders& request_extra_headers, + int request_load_flags, + RequestPriority request_priority, + HttpNetworkSession* session, + const ProxyInfo& proxy_info, + bool force_spdy_over_ssl, + bool want_spdy_over_npn, + const SSLConfig& ssl_config_for_origin, + const SSLConfig& ssl_config_for_proxy, + PrivacyMode privacy_mode, + const BoundNetLog& net_log, + ClientSocketHandle* socket_handle, + const OnHostResolutionCallback& resolution_callback, + const CompletionCallback& callback) { + DCHECK(socket_handle); + return InitSocketPoolHelper( + request_url, request_extra_headers, request_load_flags, request_priority, + session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, + ssl_config_for_origin, ssl_config_for_proxy, false, privacy_mode, net_log, + 0, socket_handle, HttpNetworkSession::NORMAL_SOCKET_POOL, + resolution_callback, callback); +} + +int InitSocketHandleForWebSocketRequest( + const GURL& request_url, + const HttpRequestHeaders& request_extra_headers, + int request_load_flags, + RequestPriority request_priority, + HttpNetworkSession* session, + const ProxyInfo& proxy_info, + bool force_spdy_over_ssl, + bool want_spdy_over_npn, + const SSLConfig& ssl_config_for_origin, + const SSLConfig& ssl_config_for_proxy, + PrivacyMode privacy_mode, + const BoundNetLog& net_log, + ClientSocketHandle* socket_handle, + const OnHostResolutionCallback& resolution_callback, + const CompletionCallback& callback) { + DCHECK(socket_handle); + return InitSocketPoolHelper( + request_url, request_extra_headers, request_load_flags, request_priority, + session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, + ssl_config_for_origin, ssl_config_for_proxy, true, privacy_mode, net_log, + 0, socket_handle, HttpNetworkSession::WEBSOCKET_SOCKET_POOL, + resolution_callback, callback); +} + +int InitSocketHandleForRawConnect( + const HostPortPair& host_port_pair, + HttpNetworkSession* session, + const ProxyInfo& proxy_info, + const SSLConfig& ssl_config_for_origin, + const SSLConfig& ssl_config_for_proxy, + PrivacyMode privacy_mode, + const BoundNetLog& net_log, + ClientSocketHandle* socket_handle, + const CompletionCallback& callback) { + DCHECK(socket_handle); + // Synthesize an HttpRequestInfo. + GURL request_url = GURL("http://" + host_port_pair.ToString()); + HttpRequestHeaders request_extra_headers; + int request_load_flags = 0; + RequestPriority request_priority = MEDIUM; + + return InitSocketPoolHelper( + request_url, request_extra_headers, request_load_flags, request_priority, + session, proxy_info, false, false, ssl_config_for_origin, + ssl_config_for_proxy, true, privacy_mode, net_log, 0, socket_handle, + HttpNetworkSession::NORMAL_SOCKET_POOL, OnHostResolutionCallback(), + callback); +} + +int PreconnectSocketsForHttpRequest( + const GURL& request_url, + const HttpRequestHeaders& request_extra_headers, + int request_load_flags, + RequestPriority request_priority, + HttpNetworkSession* session, + const ProxyInfo& proxy_info, + bool force_spdy_over_ssl, + bool want_spdy_over_npn, + const SSLConfig& ssl_config_for_origin, + const SSLConfig& ssl_config_for_proxy, + PrivacyMode privacy_mode, + const BoundNetLog& net_log, + int num_preconnect_streams) { + return InitSocketPoolHelper( + request_url, request_extra_headers, request_load_flags, request_priority, + session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, + ssl_config_for_origin, ssl_config_for_proxy, false, privacy_mode, net_log, + num_preconnect_streams, NULL, HttpNetworkSession::NORMAL_SOCKET_POOL, + OnHostResolutionCallback(), CompletionCallback()); +} + +} // namespace net |