blob: 0d4fb1949a07db40a11d829590bba3a03040bbf9 [file] [log] [blame]
[email protected]d245c342012-02-23 20:49:151// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]aea80602009-09-18 00:55:082// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]dab9c7d2010-02-06 21:44:325#include "net/spdy/spdy_session_pool.h"
[email protected]aea80602009-09-18 00:55:086
[email protected]aea80602009-09-18 00:55:087#include "base/logging.h"
[email protected]8b114dd72011-03-25 05:33:028#include "base/metrics/histogram.h"
[email protected]1ce7b66b2010-10-12 20:32:449#include "base/values.h"
[email protected]ab739042011-04-07 15:22:2810#include "net/base/address_list.h"
[email protected]f4580332010-09-25 21:20:2711#include "net/http/http_network_session.h"
[email protected]53bfa31c2011-11-15 19:20:3112#include "net/http/http_server_properties.h"
[email protected]dab9c7d2010-02-06 21:44:3213#include "net/spdy/spdy_session.h"
[email protected]aea80602009-09-18 00:55:0814
[email protected]8b114dd72011-03-25 05:33:0215
[email protected]aea80602009-09-18 00:55:0816namespace net {
17
[email protected]8b114dd72011-03-25 05:33:0218namespace {
19
20enum SpdySessionGetTypes {
21 CREATED_NEW = 0,
22 FOUND_EXISTING = 1,
23 FOUND_EXISTING_FROM_IP_POOL = 2,
24 IMPORTED_FROM_SOCKET = 3,
25 SPDY_SESSION_GET_MAX = 4
26};
27
[email protected]41d64e82013-07-03 22:44:2628} // namespace
[email protected]aea80602009-09-18 00:55:0829
[email protected]61b4efc2012-04-27 18:12:5030SpdySessionPool::SpdySessionPool(
31 HostResolver* resolver,
32 SSLConfigService* ssl_config_service,
33 HttpServerProperties* http_server_properties,
[email protected]f9cf5572012-12-04 15:52:0934 bool force_single_domain,
35 bool enable_ip_pooling,
36 bool enable_credential_frames,
37 bool enable_compression,
38 bool enable_ping_based_connection_checking,
39 NextProto default_protocol,
[email protected]a3827a02013-02-22 00:04:3240 size_t stream_initial_recv_window_size,
[email protected]f9cf5572012-12-04 15:52:0941 size_t initial_max_concurrent_streams,
42 size_t max_concurrent_streams_limit,
43 SpdySessionPool::TimeFunc time_func,
[email protected]61b4efc2012-04-27 18:12:5044 const std::string& trusted_spdy_proxy)
[email protected]53bfa31c2011-11-15 19:20:3145 : http_server_properties_(http_server_properties),
46 ssl_config_service_(ssl_config_service),
[email protected]b9ec6882011-07-01 07:40:2647 resolver_(resolver),
[email protected]61b4efc2012-04-27 18:12:5048 verify_domain_authentication_(true),
[email protected]e8bde622012-06-14 23:20:2449 enable_sending_initial_settings_(true),
[email protected]f9cf5572012-12-04 15:52:0950 force_single_domain_(force_single_domain),
51 enable_ip_pooling_(enable_ip_pooling),
52 enable_credential_frames_(enable_credential_frames),
53 enable_compression_(enable_compression),
54 enable_ping_based_connection_checking_(
55 enable_ping_based_connection_checking),
56 default_protocol_(default_protocol),
[email protected]a3827a02013-02-22 00:04:3257 stream_initial_recv_window_size_(stream_initial_recv_window_size),
[email protected]f9cf5572012-12-04 15:52:0958 initial_max_concurrent_streams_(initial_max_concurrent_streams),
59 max_concurrent_streams_limit_(max_concurrent_streams_limit),
60 time_func_(time_func),
[email protected]61b4efc2012-04-27 18:12:5061 trusted_spdy_proxy_(
62 HostPortPair::FromString(trusted_spdy_proxy)) {
[email protected]232a5812011-03-04 22:42:0863 NetworkChangeNotifier::AddIPAddressObserver(this);
[email protected]90499482013-06-01 00:39:5064 if (ssl_config_service_.get())
[email protected]7abf7d22010-09-04 01:41:5965 ssl_config_service_->AddObserver(this);
[email protected]7fda9a402012-09-10 14:11:0766 CertDatabase::GetInstance()->AddObserver(this);
[email protected]b846acd2010-06-07 18:13:1067}
68
[email protected]955fc2e72010-02-08 20:37:3069SpdySessionPool::~SpdySessionPool() {
[email protected]d1eda932009-11-04 01:03:1070 CloseAllSessions();
[email protected]b846acd2010-06-07 18:13:1071
[email protected]90499482013-06-01 00:39:5072 if (ssl_config_service_.get())
[email protected]7abf7d22010-09-04 01:41:5973 ssl_config_service_->RemoveObserver(this);
[email protected]232a5812011-03-04 22:42:0874 NetworkChangeNotifier::RemoveIPAddressObserver(this);
[email protected]7fda9a402012-09-10 14:11:0775 CertDatabase::GetInstance()->RemoveObserver(this);
[email protected]d1eda932009-11-04 01:03:1076}
[email protected]aea80602009-09-18 00:55:0877
[email protected]e3ceb682011-06-28 23:55:4678scoped_refptr<SpdySession> SpdySessionPool::GetIfExists(
[email protected]e6d017652013-05-17 18:01:4079 const SpdySessionKey& spdy_session_key,
[email protected]e3ceb682011-06-28 23:55:4680 const BoundNetLog& net_log) {
[email protected]41d64e82013-07-03 22:44:2681 SpdySessionsMap::iterator it = FindSessionByKey(spdy_session_key);
82 if (it != sessions_.end()) {
[email protected]8b114dd72011-03-25 05:33:0283 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
84 FOUND_EXISTING,
85 SPDY_SESSION_GET_MAX);
[email protected]00cd9c42010-11-02 20:15:5786 net_log.AddEvent(
[email protected]41d64e82013-07-03 22:44:2687 NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION,
88 it->second->net_log().source().ToEventParametersCallback());
89 return it->second;
90 }
91
92 // Check if we have a Session through a domain alias.
93 scoped_refptr<SpdySession> spdy_session =
94 GetFromAlias(spdy_session_key, net_log, true);
95 if (spdy_session) {
96 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
97 FOUND_EXISTING_FROM_IP_POOL,
98 SPDY_SESSION_GET_MAX);
99 net_log.AddEvent(
100 NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL,
101 spdy_session->net_log().source().ToEventParametersCallback());
102 // Add this session to the map so that we can find it next time.
103 AddSession(spdy_session_key, spdy_session);
104 spdy_session->AddPooledAlias(spdy_session_key);
[email protected]8b114dd72011-03-25 05:33:02105 return spdy_session;
[email protected]6cd3bd202010-08-30 05:23:06106 }
[email protected]aea80602009-09-18 00:55:08107
[email protected]41d64e82013-07-03 22:44:26108 return scoped_refptr<SpdySession>();
[email protected]aea80602009-09-18 00:55:08109}
110
[email protected]9e9e842e2010-07-23 23:09:15111net::Error SpdySessionPool::GetSpdySessionFromSocket(
[email protected]e6d017652013-05-17 18:01:40112 const SpdySessionKey& spdy_session_key,
[email protected]41d64e82013-07-03 22:44:26113 scoped_ptr<ClientSocketHandle> connection,
[email protected]26ef6582010-06-24 02:30:47114 const BoundNetLog& net_log,
[email protected]bdbda462010-06-28 17:30:37115 int certificate_error_code,
[email protected]9e9e842e2010-07-23 23:09:15116 scoped_refptr<SpdySession>* spdy_session,
117 bool is_secure) {
[email protected]8b114dd72011-03-25 05:33:02118 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
119 IMPORTED_FROM_SOCKET,
120 SPDY_SESSION_GET_MAX);
[email protected]26ef6582010-06-24 02:30:47121 // Create the SPDY session and add it to the pool.
[email protected]e6d017652013-05-17 18:01:40122 *spdy_session = new SpdySession(spdy_session_key, this,
[email protected]53bfa31c2011-11-15 19:20:31123 http_server_properties_,
[email protected]b9ec6882011-07-01 07:40:26124 verify_domain_authentication_,
[email protected]e8bde622012-06-14 23:20:24125 enable_sending_initial_settings_,
[email protected]f9cf5572012-12-04 15:52:09126 enable_credential_frames_,
127 enable_compression_,
128 enable_ping_based_connection_checking_,
129 default_protocol_,
[email protected]a3827a02013-02-22 00:04:32130 stream_initial_recv_window_size_,
[email protected]f9cf5572012-12-04 15:52:09131 initial_max_concurrent_streams_,
132 max_concurrent_streams_limit_,
133 time_func_,
[email protected]61b4efc2012-04-27 18:12:50134 trusted_spdy_proxy_,
[email protected]f4580332010-09-25 21:20:27135 net_log.net_log());
[email protected]41d64e82013-07-03 22:44:26136 AddSession(spdy_session_key, *spdy_session);
[email protected]26ef6582010-06-24 02:30:47137
[email protected]00cd9c42010-11-02 20:15:57138 net_log.AddEvent(
139 NetLog::TYPE_SPDY_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET,
[email protected]b618d1e2012-06-13 20:45:43140 (*spdy_session)->net_log().source().ToEventParametersCallback());
[email protected]6cd3bd202010-08-30 05:23:06141
[email protected]46da33be2011-07-19 21:58:04142 // We have a new session. Lookup the IP address for this session so that we
143 // can match future Sessions (potentially to different domains) which can
144 // potentially be pooled with this one. Because GetPeerAddress() reports the
145 // proxy's address instead of the origin server, check to see if this is a
146 // direct connection.
[email protected]e6d017652013-05-17 18:01:40147 if (enable_ip_pooling_ &&
148 spdy_session_key.proxy_server().is_direct()) {
[email protected]a3528692012-06-08 00:11:42149 IPEndPoint address;
150 if (connection->socket()->GetPeerAddress(&address) == OK)
[email protected]41d64e82013-07-03 22:44:26151 aliases_[address] = spdy_session_key;
[email protected]46da33be2011-07-19 21:58:04152 }
153
[email protected]26ef6582010-06-24 02:30:47154 // Now we can initialize the session with the SSL socket.
[email protected]41d64e82013-07-03 22:44:26155 return (*spdy_session)->InitializeWithSocket(connection.Pass(), is_secure,
[email protected]6cd3bd202010-08-30 05:23:06156 certificate_error_code);
[email protected]9f7c4fd2009-11-24 18:50:15157}
158
[email protected]41d64e82013-07-03 22:44:26159// Make a copy of |sessions_| in the Close* functions below to avoid
160// reentrancy problems. Due to aliases, it doesn't suffice to simply
161// increment the iterator before closing.
[email protected]8b114dd72011-03-25 05:33:02162
[email protected]41d64e82013-07-03 22:44:26163void SpdySessionPool::CloseCurrentSessions(net::Error error) {
164 SpdySessionsMap sessions_copy = sessions_;
165 for (SpdySessionsMap::const_iterator it = sessions_copy.begin();
166 it != sessions_copy.end(); ++it) {
167 TryCloseSession(it->first, error, "Closing current sessions.");
168 }
169}
170
171void SpdySessionPool::CloseCurrentIdleSessions() {
172 SpdySessionsMap sessions_copy = sessions_;
173 for (SpdySessionsMap::const_iterator it = sessions_copy.begin();
174 it != sessions_copy.end(); ++it) {
175 if (!it->second->is_active())
176 TryCloseSession(it->first, ERR_ABORTED, "Closing idle sessions.");
177 }
178}
179
180void SpdySessionPool::CloseAllSessions() {
181 while (!sessions_.empty()) {
182 SpdySessionsMap sessions_copy = sessions_;
183 for (SpdySessionsMap::const_iterator it = sessions_copy.begin();
184 it != sessions_copy.end(); ++it) {
185 TryCloseSession(it->first, ERR_ABORTED, "Closing all sessions.");
186 }
187 }
188}
189
190void SpdySessionPool::TryCloseSession(const SpdySessionKey& key,
191 net::Error error,
192 const std::string& description) {
193 SpdySessionsMap::const_iterator it = sessions_.find(key);
194 if (it == sessions_.end())
195 return;
196 scoped_refptr<SpdySession> session = it->second;
197 session->CloseSessionOnError(error, description);
198 if (DCHECK_IS_ON()) {
199 it = sessions_.find(key);
200 // A new session with the same key may have been added, but it
201 // must not be the one we just closed.
202 if (it != sessions_.end())
203 DCHECK_NE(it->second, session);
204 }
[email protected]d1eda932009-11-04 01:03:10205}
206
[email protected]955fc2e72010-02-08 20:37:30207void SpdySessionPool::Remove(const scoped_refptr<SpdySession>& session) {
[email protected]41d64e82013-07-03 22:44:26208 RemoveSession(session->spdy_session_key());
[email protected]00cd9c42010-11-02 20:15:57209 session->net_log().AddEvent(
210 NetLog::TYPE_SPDY_SESSION_POOL_REMOVE_SESSION,
[email protected]b618d1e2012-06-13 20:45:43211 session->net_log().source().ToEventParametersCallback());
[email protected]6cbfa852012-03-14 06:35:54212
[email protected]e6d017652013-05-17 18:01:40213 const std::set<SpdySessionKey>& aliases = session->pooled_aliases();
214 for (std::set<SpdySessionKey>::const_iterator it = aliases.begin();
[email protected]6cbfa852012-03-14 06:35:54215 it != aliases.end(); ++it) {
[email protected]41d64e82013-07-03 22:44:26216 RemoveSession(*it);
[email protected]6cbfa852012-03-14 06:35:54217 }
218}
219
[email protected]ea5ef4c2013-06-13 22:50:27220base::Value* SpdySessionPool::SpdySessionPoolInfoToValue() const {
221 base::ListValue* list = new base::ListValue();
[email protected]1ce7b66b2010-10-12 20:32:44222
[email protected]1ce7b66b2010-10-12 20:32:44223 for (SpdySessionsMap::const_iterator it = sessions_.begin();
[email protected]9e1bdd32011-02-03 21:48:34224 it != sessions_.end(); ++it) {
[email protected]41d64e82013-07-03 22:44:26225 // Only add the session if the key in the map matches the main
226 // host_port_proxy_pair (not an alias).
227 const SpdySessionKey& key = it->first;
228 const SpdySessionKey& session_key = it->second->spdy_session_key();
229 if (key.Equals(session_key))
230 list->Append(it->second->GetInfoAsValue());
[email protected]1ce7b66b2010-10-12 20:32:44231 }
232 return list;
233}
234
[email protected]66761b952010-06-25 21:30:38235void SpdySessionPool::OnIPAddressChanged() {
[email protected]7af985a2012-12-14 22:40:42236 CloseCurrentSessions(ERR_NETWORK_CHANGED);
[email protected]6dd1134e2013-04-30 21:13:09237 http_server_properties_->ClearAllSpdySettings();
[email protected]66761b952010-06-25 21:30:38238}
239
[email protected]7abf7d22010-09-04 01:41:59240void SpdySessionPool::OnSSLConfigChanged() {
[email protected]7af985a2012-12-14 22:40:42241 CloseCurrentSessions(ERR_NETWORK_CHANGED);
[email protected]7abf7d22010-09-04 01:41:59242}
243
[email protected]8b114dd72011-03-25 05:33:02244scoped_refptr<SpdySession> SpdySessionPool::GetFromAlias(
[email protected]e6d017652013-05-17 18:01:40245 const SpdySessionKey& spdy_session_key,
[email protected]8b114dd72011-03-25 05:33:02246 const BoundNetLog& net_log,
[email protected]41d64e82013-07-03 22:44:26247 bool record_histograms) {
[email protected]8b114dd72011-03-25 05:33:02248 // We should only be checking aliases when there is no direct session.
[email protected]41d64e82013-07-03 22:44:26249 DCHECK(FindSessionByKey(spdy_session_key) == sessions_.end());
[email protected]8b114dd72011-03-25 05:33:02250
[email protected]f9cf5572012-12-04 15:52:09251 if (!enable_ip_pooling_)
[email protected]8b114dd72011-03-25 05:33:02252 return NULL;
253
254 AddressList addresses;
[email protected]e6d017652013-05-17 18:01:40255 if (!LookupAddresses(spdy_session_key, net_log, &addresses))
[email protected]8b114dd72011-03-25 05:33:02256 return NULL;
[email protected]7054e78f2012-05-07 21:44:56257 for (AddressList::const_iterator iter = addresses.begin();
258 iter != addresses.end();
259 ++iter) {
260 SpdyAliasMap::const_iterator alias_iter = aliases_.find(*iter);
261 if (alias_iter == aliases_.end())
[email protected]8b114dd72011-03-25 05:33:02262 continue;
263
264 // We found an alias.
[email protected]e6d017652013-05-17 18:01:40265 const SpdySessionKey& alias_key = alias_iter->second;
[email protected]8b114dd72011-03-25 05:33:02266
[email protected]e6d017652013-05-17 18:01:40267 // If the proxy and privacy settings match, we can reuse this session.
268 if (!(alias_key.proxy_server() == spdy_session_key.proxy_server()) ||
269 !(alias_key.privacy_mode() ==
270 spdy_session_key.privacy_mode()))
[email protected]8b114dd72011-03-25 05:33:02271 continue;
272
[email protected]41d64e82013-07-03 22:44:26273 SpdySessionsMap::iterator it = FindSessionByKey(alias_key);
274 if (it == sessions_.end()) {
[email protected]8b114dd72011-03-25 05:33:02275 NOTREACHED(); // It shouldn't be in the aliases table if we can't get it!
276 continue;
277 }
278
[email protected]41d64e82013-07-03 22:44:26279 scoped_refptr<SpdySession> spdy_session = it->second;
[email protected]8b114dd72011-03-25 05:33:02280 // If the SPDY session is a secure one, we need to verify that the server
281 // is authenticated to serve traffic for |host_port_proxy_pair| too.
282 if (!spdy_session->VerifyDomainAuthentication(
[email protected]e6d017652013-05-17 18:01:40283 spdy_session_key.host_port_pair().host())) {
[email protected]8b114dd72011-03-25 05:33:02284 if (record_histograms)
285 UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2);
286 continue;
287 }
288 if (record_histograms)
289 UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 1, 2);
290 return spdy_session;
291 }
292 return NULL;
293}
294
[email protected]7fda9a402012-09-10 14:11:07295void SpdySessionPool::OnCertAdded(const X509Certificate* cert) {
[email protected]7af985a2012-12-14 22:40:42296 CloseCurrentSessions(ERR_NETWORK_CHANGED);
[email protected]c940d372011-04-13 17:20:18297}
298
299void SpdySessionPool::OnCertTrustChanged(const X509Certificate* cert) {
300 // Per wtc, we actually only need to CloseCurrentSessions when trust is
301 // reduced. CloseCurrentSessions now because OnCertTrustChanged does not
302 // tell us this.
303 // See comments in ClientSocketPoolManager::OnCertTrustChanged.
[email protected]7af985a2012-12-14 22:40:42304 CloseCurrentSessions(ERR_NETWORK_CHANGED);
[email protected]62635c72011-03-10 04:16:25305}
306
[email protected]e6d017652013-05-17 18:01:40307const SpdySessionKey& SpdySessionPool::NormalizeListKey(
308 const SpdySessionKey& spdy_session_key) const {
[email protected]f9cf5572012-12-04 15:52:09309 if (!force_single_domain_)
[email protected]e6d017652013-05-17 18:01:40310 return spdy_session_key;
[email protected]ea1cc2cd2011-02-22 16:47:38311
[email protected]e6d017652013-05-17 18:01:40312 static SpdySessionKey* single_domain_key = NULL;
313 if (!single_domain_key) {
[email protected]ea1cc2cd2011-02-22 16:47:38314 HostPortPair single_domain = HostPortPair("singledomain.com", 80);
[email protected]e6d017652013-05-17 18:01:40315 single_domain_key = new SpdySessionKey(single_domain,
316 ProxyServer::Direct(),
317 kPrivacyModeDisabled);
[email protected]ea1cc2cd2011-02-22 16:47:38318 }
[email protected]e6d017652013-05-17 18:01:40319 return *single_domain_key;
[email protected]ea1cc2cd2011-02-22 16:47:38320}
321
[email protected]41d64e82013-07-03 22:44:26322void SpdySessionPool::AddSession(
323 const SpdySessionKey& spdy_session_key,
324 const scoped_refptr<SpdySession>& spdy_session) {
[email protected]e6d017652013-05-17 18:01:40325 const SpdySessionKey& key = NormalizeListKey(spdy_session_key);
[email protected]41d64e82013-07-03 22:44:26326 std::pair<SpdySessionsMap::iterator, bool> result =
327 sessions_.insert(std::make_pair(key, spdy_session));
328 CHECK(result.second);
[email protected]aea80602009-09-18 00:55:08329}
330
[email protected]41d64e82013-07-03 22:44:26331SpdySessionPool::SpdySessionsMap::iterator
332SpdySessionPool::FindSessionByKey(const SpdySessionKey& spdy_session_key) {
[email protected]e6d017652013-05-17 18:01:40333 const SpdySessionKey& key = NormalizeListKey(spdy_session_key);
[email protected]41d64e82013-07-03 22:44:26334 return sessions_.find(key);
[email protected]d1eda932009-11-04 01:03:10335}
336
[email protected]41d64e82013-07-03 22:44:26337void SpdySessionPool::RemoveSession(const SpdySessionKey& spdy_session_key) {
338 SpdySessionsMap::iterator it = FindSessionByKey(spdy_session_key);
339 CHECK(it != sessions_.end());
340 sessions_.erase(it);
[email protected]e6d017652013-05-17 18:01:40341 RemoveAliases(spdy_session_key);
[email protected]8b114dd72011-03-25 05:33:02342}
343
[email protected]e6d017652013-05-17 18:01:40344bool SpdySessionPool::LookupAddresses(const SpdySessionKey& spdy_session_key,
[email protected]ae940212012-03-06 15:44:13345 const BoundNetLog& net_log,
[email protected]8b114dd72011-03-25 05:33:02346 AddressList* addresses) const {
[email protected]e6d017652013-05-17 18:01:40347 net::HostResolver::RequestInfo resolve_info(
348 spdy_session_key.host_port_pair());
[email protected]ae940212012-03-06 15:44:13349 int rv = resolver_->ResolveFromCache(resolve_info, addresses, net_log);
[email protected]8b114dd72011-03-25 05:33:02350 DCHECK_NE(ERR_IO_PENDING, rv);
351 return rv == OK;
352}
353
[email protected]e6d017652013-05-17 18:01:40354void SpdySessionPool::RemoveAliases(const SpdySessionKey& spdy_session_key) {
[email protected]8b114dd72011-03-25 05:33:02355 // Walk the aliases map, find references to this pair.
356 // TODO(mbelshe): Figure out if this is too expensive.
357 SpdyAliasMap::iterator alias_it = aliases_.begin();
358 while (alias_it != aliases_.end()) {
[email protected]e6d017652013-05-17 18:01:40359 if (alias_it->second.Equals(spdy_session_key)) {
[email protected]8b114dd72011-03-25 05:33:02360 aliases_.erase(alias_it);
361 alias_it = aliases_.begin(); // Iterator was invalidated.
362 continue;
363 }
364 ++alias_it;
365 }
[email protected]aea80602009-09-18 00:55:08366}
367
[email protected]aea80602009-09-18 00:55:08368} // namespace net