client_socket_pool_manager.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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
22namespace net {
23
24namespace {
25
26// Limit of sockets of each socket pool.
27int g_max_sockets_per_pool[] = {
28  256,  // NORMAL_SOCKET_POOL
29  256   // WEBSOCKET_SOCKET_POOL
30};
31
32COMPILE_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.
46int g_max_sockets_per_group[] = {
47  6,  // NORMAL_SOCKET_POOL
48  30  // WEBSOCKET_SOCKET_POOL
49};
50
51COMPILE_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.
58int g_max_sockets_per_proxy_server[] = {
59  kDefaultMaxSocketsPerProxyServer,  // NORMAL_SOCKET_POOL
60  kDefaultMaxSocketsPerProxyServer   // WEBSOCKET_SOCKET_POOL
61};
62
63COMPILE_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.
69int 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
305ClientSocketPoolManager::ClientSocketPoolManager() {}
306ClientSocketPoolManager::~ClientSocketPoolManager() {}
307
308// static
309int 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
316void 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
328int 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
335void 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
351int 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
358void 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
370int 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
395int 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
420int 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
445int 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