spdy_session_pool.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/spdy/spdy_session_pool.h" 6 7#include "base/logging.h" 8#include "base/metrics/histogram.h" 9#include "base/values.h" 10#include "net/base/address_list.h" 11#include "net/http/http_network_session.h" 12#include "net/http/http_server_properties.h" 13#include "net/spdy/spdy_session.h" 14 15 16namespace net { 17 18namespace { 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 28} // namespace 29 30SpdySessionPool::SpdySessionPool( 31 HostResolver* resolver, 32 SSLConfigService* ssl_config_service, 33 const base::WeakPtr<HttpServerProperties>& http_server_properties, 34 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, 40 size_t stream_initial_recv_window_size, 41 size_t initial_max_concurrent_streams, 42 size_t max_concurrent_streams_limit, 43 SpdySessionPool::TimeFunc time_func, 44 const std::string& trusted_spdy_proxy) 45 : http_server_properties_(http_server_properties), 46 ssl_config_service_(ssl_config_service), 47 resolver_(resolver), 48 verify_domain_authentication_(true), 49 enable_sending_initial_data_(true), 50 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 // TODO(akalin): Force callers to have a valid value of 57 // |default_protocol_|. 58 default_protocol_( 59 (default_protocol == kProtoUnknown) ? 60 kProtoSPDY3 : default_protocol), 61 stream_initial_recv_window_size_(stream_initial_recv_window_size), 62 initial_max_concurrent_streams_(initial_max_concurrent_streams), 63 max_concurrent_streams_limit_(max_concurrent_streams_limit), 64 time_func_(time_func), 65 trusted_spdy_proxy_( 66 HostPortPair::FromString(trusted_spdy_proxy)) { 67 DCHECK(default_protocol_ >= kProtoSPDYMinimumVersion && 68 default_protocol_ <= kProtoSPDYMaximumVersion); 69 NetworkChangeNotifier::AddIPAddressObserver(this); 70 if (ssl_config_service_.get()) 71 ssl_config_service_->AddObserver(this); 72 CertDatabase::GetInstance()->AddObserver(this); 73} 74 75SpdySessionPool::~SpdySessionPool() { 76 CloseAllSessions(); 77 78 if (ssl_config_service_.get()) 79 ssl_config_service_->RemoveObserver(this); 80 NetworkChangeNotifier::RemoveIPAddressObserver(this); 81 CertDatabase::GetInstance()->RemoveObserver(this); 82} 83 84net::Error SpdySessionPool::CreateAvailableSessionFromSocket( 85 const SpdySessionKey& key, 86 scoped_ptr<ClientSocketHandle> connection, 87 const BoundNetLog& net_log, 88 int certificate_error_code, 89 base::WeakPtr<SpdySession>* available_session, 90 bool is_secure) { 91 DCHECK_GE(default_protocol_, kProtoSPDYMinimumVersion); 92 DCHECK_LE(default_protocol_, kProtoSPDYMaximumVersion); 93 94 UMA_HISTOGRAM_ENUMERATION( 95 "Net.SpdySessionGet", IMPORTED_FROM_SOCKET, SPDY_SESSION_GET_MAX); 96 97 scoped_ptr<SpdySession> new_session( 98 new SpdySession(key, 99 http_server_properties_, 100 verify_domain_authentication_, 101 enable_sending_initial_data_, 102 enable_credential_frames_, 103 enable_compression_, 104 enable_ping_based_connection_checking_, 105 default_protocol_, 106 stream_initial_recv_window_size_, 107 initial_max_concurrent_streams_, 108 max_concurrent_streams_limit_, 109 time_func_, 110 trusted_spdy_proxy_, 111 net_log.net_log())); 112 113 Error error = new_session->InitializeWithSocket( 114 connection.Pass(), this, is_secure, certificate_error_code); 115 DCHECK_NE(error, ERR_IO_PENDING); 116 117 if (error != OK) { 118 available_session->reset(); 119 return error; 120 } 121 122 *available_session = new_session->GetWeakPtr(); 123 sessions_.insert(new_session.release()); 124 MapKeyToAvailableSession(key, *available_session); 125 126 net_log.AddEvent( 127 NetLog::TYPE_SPDY_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET, 128 (*available_session)->net_log().source().ToEventParametersCallback()); 129 130 // Look up the IP address for this session so that we can match 131 // future sessions (potentially to different domains) which can 132 // potentially be pooled with this one. Because GetPeerAddress() 133 // reports the proxy's address instead of the origin server, check 134 // to see if this is a direct connection. 135 if (enable_ip_pooling_ && key.proxy_server().is_direct()) { 136 IPEndPoint address; 137 if ((*available_session)->GetPeerAddress(&address) == OK) 138 aliases_[address] = key; 139 } 140 141 return error; 142} 143 144base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession( 145 const SpdySessionKey& key, 146 const BoundNetLog& net_log) { 147 AvailableSessionMap::iterator it = LookupAvailableSessionByKey(key); 148 if (it != available_sessions_.end()) { 149 UMA_HISTOGRAM_ENUMERATION( 150 "Net.SpdySessionGet", FOUND_EXISTING, SPDY_SESSION_GET_MAX); 151 net_log.AddEvent( 152 NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION, 153 it->second->net_log().source().ToEventParametersCallback()); 154 return it->second; 155 } 156 157 if (!enable_ip_pooling_) 158 return base::WeakPtr<SpdySession>(); 159 160 // Look up the key's from the resolver's cache. 161 net::HostResolver::RequestInfo resolve_info(key.host_port_pair()); 162 AddressList addresses; 163 int rv = resolver_->ResolveFromCache(resolve_info, &addresses, net_log); 164 DCHECK_NE(rv, ERR_IO_PENDING); 165 if (rv != OK) 166 return base::WeakPtr<SpdySession>(); 167 168 // Check if we have a session through a domain alias. 169 for (AddressList::const_iterator address_it = addresses.begin(); 170 address_it != addresses.end(); 171 ++address_it) { 172 AliasMap::const_iterator alias_it = aliases_.find(*address_it); 173 if (alias_it == aliases_.end()) 174 continue; 175 176 // We found an alias. 177 const SpdySessionKey& alias_key = alias_it->second; 178 179 // We can reuse this session only if the proxy and privacy 180 // settings match. 181 if (!(alias_key.proxy_server() == key.proxy_server()) || 182 !(alias_key.privacy_mode() == key.privacy_mode())) 183 continue; 184 185 AvailableSessionMap::iterator available_session_it = 186 LookupAvailableSessionByKey(alias_key); 187 if (available_session_it == available_sessions_.end()) { 188 NOTREACHED(); // It shouldn't be in the aliases table if we can't get it! 189 continue; 190 } 191 192 const base::WeakPtr<SpdySession>& available_session = 193 available_session_it->second; 194 DCHECK(ContainsKey(sessions_, available_session.get())); 195 // If the session is a secure one, we need to verify that the 196 // server is authenticated to serve traffic for |host_port_proxy_pair| too. 197 if (!available_session->VerifyDomainAuthentication( 198 key.host_port_pair().host())) { 199 UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2); 200 continue; 201 } 202 203 UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 1, 2); 204 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", 205 FOUND_EXISTING_FROM_IP_POOL, 206 SPDY_SESSION_GET_MAX); 207 net_log.AddEvent( 208 NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL, 209 available_session->net_log().source().ToEventParametersCallback()); 210 // Add this session to the map so that we can find it next time. 211 MapKeyToAvailableSession(key, available_session); 212 available_session->AddPooledAlias(key); 213 return available_session; 214 } 215 216 return base::WeakPtr<SpdySession>(); 217} 218 219void SpdySessionPool::MakeSessionUnavailable( 220 const base::WeakPtr<SpdySession>& available_session) { 221 UnmapKey(available_session->spdy_session_key()); 222 RemoveAliases(available_session->spdy_session_key()); 223 const std::set<SpdySessionKey>& aliases = available_session->pooled_aliases(); 224 for (std::set<SpdySessionKey>::const_iterator it = aliases.begin(); 225 it != aliases.end(); ++it) { 226 UnmapKey(*it); 227 RemoveAliases(*it); 228 } 229 DCHECK(!IsSessionAvailable(available_session)); 230} 231 232void SpdySessionPool::RemoveUnavailableSession( 233 const base::WeakPtr<SpdySession>& unavailable_session) { 234 DCHECK(!IsSessionAvailable(unavailable_session)); 235 236 unavailable_session->net_log().AddEvent( 237 NetLog::TYPE_SPDY_SESSION_POOL_REMOVE_SESSION, 238 unavailable_session->net_log().source().ToEventParametersCallback()); 239 240 SessionSet::iterator it = sessions_.find(unavailable_session.get()); 241 CHECK(it != sessions_.end()); 242 scoped_ptr<SpdySession> owned_session(*it); 243 sessions_.erase(it); 244} 245 246// Make a copy of |sessions_| in the Close* functions below to avoid 247// reentrancy problems. Since arbitrary functions get called by close 248// handlers, it doesn't suffice to simply increment the iterator 249// before closing. 250 251void SpdySessionPool::CloseCurrentSessions(net::Error error) { 252 CloseCurrentSessionsHelper(error, "Closing current sessions.", 253 false /* idle_only */); 254} 255 256void SpdySessionPool::CloseCurrentIdleSessions() { 257 CloseCurrentSessionsHelper(ERR_ABORTED, "Closing idle sessions.", 258 true /* idle_only */); 259} 260 261void SpdySessionPool::CloseAllSessions() { 262 while (!sessions_.empty()) { 263 CloseCurrentSessionsHelper(ERR_ABORTED, "Closing all sessions.", 264 false /* idle_only */); 265 } 266} 267 268base::Value* SpdySessionPool::SpdySessionPoolInfoToValue() const { 269 base::ListValue* list = new base::ListValue(); 270 271 for (AvailableSessionMap::const_iterator it = available_sessions_.begin(); 272 it != available_sessions_.end(); ++it) { 273 // Only add the session if the key in the map matches the main 274 // host_port_proxy_pair (not an alias). 275 const SpdySessionKey& key = it->first; 276 const SpdySessionKey& session_key = it->second->spdy_session_key(); 277 if (key.Equals(session_key)) 278 list->Append(it->second->GetInfoAsValue()); 279 } 280 return list; 281} 282 283void SpdySessionPool::OnIPAddressChanged() { 284 CloseCurrentSessions(ERR_NETWORK_CHANGED); 285 http_server_properties_->ClearAllSpdySettings(); 286} 287 288void SpdySessionPool::OnSSLConfigChanged() { 289 CloseCurrentSessions(ERR_NETWORK_CHANGED); 290} 291 292void SpdySessionPool::OnCertAdded(const X509Certificate* cert) { 293 CloseCurrentSessions(ERR_NETWORK_CHANGED); 294} 295 296void SpdySessionPool::OnCertTrustChanged(const X509Certificate* cert) { 297 // Per wtc, we actually only need to CloseCurrentSessions when trust is 298 // reduced. CloseCurrentSessions now because OnCertTrustChanged does not 299 // tell us this. 300 // See comments in ClientSocketPoolManager::OnCertTrustChanged. 301 CloseCurrentSessions(ERR_NETWORK_CHANGED); 302} 303 304bool SpdySessionPool::IsSessionAvailable( 305 const base::WeakPtr<SpdySession>& session) const { 306 for (AvailableSessionMap::const_iterator it = available_sessions_.begin(); 307 it != available_sessions_.end(); ++it) { 308 if (it->second.get() == session.get()) 309 return true; 310 } 311 return false; 312} 313 314const SpdySessionKey& SpdySessionPool::NormalizeListKey( 315 const SpdySessionKey& key) const { 316 if (!force_single_domain_) 317 return key; 318 319 static SpdySessionKey* single_domain_key = NULL; 320 if (!single_domain_key) { 321 HostPortPair single_domain = HostPortPair("singledomain.com", 80); 322 single_domain_key = new SpdySessionKey(single_domain, 323 ProxyServer::Direct(), 324 kPrivacyModeDisabled); 325 } 326 return *single_domain_key; 327} 328 329void SpdySessionPool::MapKeyToAvailableSession( 330 const SpdySessionKey& key, 331 const base::WeakPtr<SpdySession>& session) { 332 DCHECK(ContainsKey(sessions_, session.get())); 333 const SpdySessionKey& normalized_key = NormalizeListKey(key); 334 std::pair<AvailableSessionMap::iterator, bool> result = 335 available_sessions_.insert(std::make_pair(normalized_key, session)); 336 CHECK(result.second); 337} 338 339SpdySessionPool::AvailableSessionMap::iterator 340SpdySessionPool::LookupAvailableSessionByKey( 341 const SpdySessionKey& key) { 342 const SpdySessionKey& normalized_key = NormalizeListKey(key); 343 return available_sessions_.find(normalized_key); 344} 345 346void SpdySessionPool::UnmapKey(const SpdySessionKey& key) { 347 AvailableSessionMap::iterator it = LookupAvailableSessionByKey(key); 348 CHECK(it != available_sessions_.end()); 349 available_sessions_.erase(it); 350} 351 352void SpdySessionPool::RemoveAliases(const SpdySessionKey& key) { 353 // Walk the aliases map, find references to this pair. 354 // TODO(mbelshe): Figure out if this is too expensive. 355 for (AliasMap::iterator it = aliases_.begin(); it != aliases_.end(); ) { 356 if (it->second.Equals(key)) { 357 AliasMap::iterator old_it = it; 358 ++it; 359 aliases_.erase(old_it); 360 } else { 361 ++it; 362 } 363 } 364} 365 366SpdySessionPool::WeakSessionList SpdySessionPool::GetCurrentSessions() const { 367 WeakSessionList current_sessions; 368 for (SessionSet::const_iterator it = sessions_.begin(); 369 it != sessions_.end(); ++it) { 370 current_sessions.push_back((*it)->GetWeakPtr()); 371 } 372 return current_sessions; 373} 374 375void SpdySessionPool::CloseCurrentSessionsHelper( 376 Error error, 377 const std::string& description, 378 bool idle_only) { 379 WeakSessionList current_sessions = GetCurrentSessions(); 380 for (WeakSessionList::const_iterator it = current_sessions.begin(); 381 it != current_sessions.end(); ++it) { 382 if (!*it) 383 continue; 384 385 if (idle_only && (*it)->is_active()) 386 continue; 387 388 (*it)->CloseSessionOnError(error, description); 389 DCHECK(!IsSessionAvailable(*it)); 390 DCHECK(!*it); 391 } 392} 393 394} // namespace net 395