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