diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-08 04:29:59 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-08 04:29:59 +0000 |
commit | 04644d4599c4e3a5696cbf3feed3aaaae7ea99e9 (patch) | |
tree | 97171a1d44648a8ef2447fd360af233e6acbb1b9 | |
parent | 27ec8b6653c97bf5ba26549b484cbaf878d40ccf (diff) | |
download | chromium_src-04644d4599c4e3a5696cbf3feed3aaaae7ea99e9.zip chromium_src-04644d4599c4e3a5696cbf3feed3aaaae7ea99e9.tar.gz chromium_src-04644d4599c4e3a5696cbf3feed3aaaae7ea99e9.tar.bz2 |
[SPDY] Make SpdySessionPool keep track of available sessions
Split a SpdySessionPool's sessions into available ones and unavailable ones.
Keep all sessions in a set, and all the available sessions in a map keyed
by SpdySessionKey. This is in preparation for making SpdySessionPool
own a session even if it receives a GOAWAY frame.
Split SpdySessionPool::Remove() into two functions -- MakeSessionUnavailable()
and RemoveUnavailableSession(). For now, sessions call them at the same
time, but in the future RemoveUnavailableSession() may be called later
than MakeSessionUnavailable() (for the GOAWAY case).
Don't add a SpdySession to a pool if it encountered an error during
initialization. Also, don't do some work in SpdySession::InitializeWithSocket()
if initialization fails.
Rename functions and variables in SpdySessionPool to be concise and
consistent. Inline a bunch of one-off functions. Rewrite the loop
in SpdySessionPool::RemoveAliases().
BUG=255701
Review URL: https://chromiumcodereview.appspot.com/18600010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@210344 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/http/http_proxy_client_socket_pool.cc | 6 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.cc | 23 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 2 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 42 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 14 | ||||
-rw-r--r-- | net/spdy/spdy_session_pool.cc | 402 | ||||
-rw-r--r-- | net/spdy/spdy_session_pool.h | 145 | ||||
-rw-r--r-- | net/spdy/spdy_session_pool_unittest.cc | 12 | ||||
-rw-r--r-- | net/spdy/spdy_session_unittest.cc | 33 | ||||
-rw-r--r-- | net/spdy/spdy_test_util_common.cc | 6 | ||||
-rw-r--r-- | net/websockets/websocket_job.cc | 2 |
11 files changed, 369 insertions, 318 deletions
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc index 7b493ac..3e93008 100644 --- a/net/http/http_proxy_client_socket_pool.cc +++ b/net/http/http_proxy_client_socket_pool.cc @@ -204,7 +204,7 @@ int HttpProxyConnectJob::DoSSLConnect() { SpdySessionKey key(params_->destination().host_port_pair(), ProxyServer::Direct(), kPrivacyModeDisabled); - if (params_->spdy_session_pool()->GetIfExists(key, net_log())) { + if (params_->spdy_session_pool()->FindAvailableSession(key, net_log())) { using_spdy_ = true; next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; return OK; @@ -303,7 +303,7 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStream() { kPrivacyModeDisabled); SpdySessionPool* spdy_pool = params_->spdy_session_pool(); scoped_refptr<SpdySession> spdy_session = - spdy_pool->GetIfExists(key, net_log()); + spdy_pool->FindAvailableSession(key, net_log()); // It's possible that a session to the proxy has recently been created if (spdy_session) { if (transport_socket_handle_.get()) { @@ -313,7 +313,7 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStream() { } } else { // Create a session direct to the proxy itself - int rv = spdy_pool->GetSpdySessionFromSocket( + int rv = spdy_pool->CreateAvailableSessionFromSocket( key, transport_socket_handle_.Pass(), net_log(), OK, &spdy_session, /*using_ssl_*/ true); if (rv < 0) diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index f03ca2d..b6226ae 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -422,9 +422,9 @@ int HttpStreamFactoryImpl::Job::OnHostResolution( // It is OK to dereference spdy_session_pool, because the // ClientSocketPoolManager will be destroyed in the same callback that // destroys the SpdySessionPool. - bool has_session = - spdy_session_pool->GetIfExists(spdy_session_key, net_log).get() != NULL; - return has_session ? ERR_SPDY_SESSION_ALREADY_EXISTS : OK; + return + spdy_session_pool->FindAvailableSession(spdy_session_key, net_log) ? + ERR_SPDY_SESSION_ALREADY_EXISTS : OK; } void HttpStreamFactoryImpl::Job::OnIOComplete(int result) { @@ -762,8 +762,9 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { // straight to using that. SpdySessionKey spdy_session_key = GetSpdySessionKey(); scoped_refptr<SpdySession> spdy_session = - session_->spdy_session_pool()->GetIfExists(spdy_session_key, net_log_); - if (spdy_session.get() && CanUseExistingSpdySession()) { + session_->spdy_session_pool()->FindAvailableSession( + spdy_session_key, net_log_); + if (spdy_session && CanUseExistingSpdySession()) { // If we're preconnecting, but we already have a SpdySession, we don't // actually need to preconnect any sockets, so we're done. if (IsPreconnecting()) @@ -876,8 +877,9 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { // probably an IP pooled connection. SpdySessionKey spdy_session_key = GetSpdySessionKey(); existing_spdy_session_ = - session_->spdy_session_pool()->GetIfExists(spdy_session_key, net_log_); - if (existing_spdy_session_.get()) { + session_->spdy_session_pool()->FindAvailableSession( + spdy_session_key, net_log_); + if (existing_spdy_session_) { using_spdy_ = true; next_state_ = STATE_CREATE_STREAM; } else { @@ -1096,9 +1098,10 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { spdy_session.swap(existing_spdy_session_); } else { SpdySessionPool* spdy_pool = session_->spdy_session_pool(); - spdy_session = spdy_pool->GetIfExists(spdy_session_key, net_log_); - if (!spdy_session.get()) { - int error = spdy_pool->GetSpdySessionFromSocket(spdy_session_key, + spdy_session = spdy_pool->FindAvailableSession(spdy_session_key, net_log_); + if (!spdy_session) { + int error = + spdy_pool->CreateAvailableSessionFromSocket(spdy_session_key, connection_.Pass(), net_log_, spdy_certificate_error_, diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index b533f29..e0e9cfc 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -563,7 +563,7 @@ class SpdyNetworkTransactionTest BoundNetLog log; const scoped_refptr<HttpNetworkSession>& session = helper.session(); scoped_refptr<SpdySession> spdy_session = - session->spdy_session_pool()->GetIfExists(key, log); + session->spdy_session_pool()->FindAvailableSession(key, log); ASSERT_TRUE(spdy_session != NULL); EXPECT_EQ(0u, spdy_session->num_active_streams()); EXPECT_EQ(0u, spdy_session->num_unclaimed_pushed_streams()); diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 2ff50a028..ae995e6 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -333,7 +333,6 @@ SpdySession::PushedStreamInfo::PushedStreamInfo( SpdySession::PushedStreamInfo::~PushedStreamInfo() {} SpdySession::SpdySession(const SpdySessionKey& spdy_session_key, - SpdySessionPool* spdy_session_pool, HttpServerProperties* http_server_properties, bool verify_domain_authentication, bool enable_sending_initial_settings, @@ -349,9 +348,8 @@ SpdySession::SpdySession(const SpdySessionKey& spdy_session_key, NetLog* net_log) : weak_factory_(this), spdy_session_key_(spdy_session_key), - spdy_session_pool_(spdy_session_pool), + spdy_session_pool_(NULL), http_server_properties_(http_server_properties), - connection_(new ClientSocketHandle), read_buffer_(new IOBuffer(kReadBufferSize)), stream_hi_water_mark_(kFirstStreamId), write_pending_(false), @@ -421,10 +419,11 @@ SpdySession::~SpdySession() { CloseAllStreams(ERR_ABORTED); } - if (connection_->is_initialized()) { - // With SPDY we can't recycle sockets. - connection_->socket()->Disconnect(); - } + // TODO(akalin): Check connection->is_initialized() instead. This + // requires re-working CreateFakeSpdySession(), though. + DCHECK(connection_->socket()); + // With SPDY we can't recycle sockets. + connection_->socket()->Disconnect(); // Streams should all be gone now. DCHECK_EQ(0u, num_active_streams()); @@ -442,14 +441,17 @@ SpdySession::~SpdySession() { Error SpdySession::InitializeWithSocket( scoped_ptr<ClientSocketHandle> connection, + SpdySessionPool* spdy_session_pool, bool is_secure, int certificate_error_code) { + // TODO(akalin): Check connection->is_initialized() instead. This + // requires re-working CreateFakeSpdySession(), though. + DCHECK(connection->socket()); base::StatsCounter spdy_sessions("spdy.sessions"); spdy_sessions.Increment(); state_ = STATE_DO_READ; connection_ = connection.Pass(); - connection_->AddLayeredPool(this); is_secure_ = is_secure; certificate_error_code_ = certificate_error_code; @@ -485,18 +487,23 @@ Error SpdySession::InitializeWithSocket( buffered_spdy_framer_.reset( new BufferedSpdyFramer(NPNToSpdyVersion(protocol), enable_compression_)); buffered_spdy_framer_->set_visitor(this); - SendInitialSettings(); UMA_HISTOGRAM_ENUMERATION("Net.SpdyVersion", protocol, kProtoMaximumVersion); net_log_.AddEvent( NetLog::TYPE_SPDY_SESSION_INITIALIZED, connection_->socket()->NetLog().source().ToEventParametersCallback()); - // Write out any data that we might have to send, such as the settings frame. - WriteSocketLater(); int error = DoLoop(OK); if (error == ERR_IO_PENDING) - return OK; + error = OK; + if (error == OK) { + connection_->AddLayeredPool(this); + SendInitialSettings(); + // Write out any data that we might have to send, such as the + // settings frame. + WriteSocketLater(); + spdy_session_pool_ = spdy_session_pool; + } return static_cast<Error>(error); } @@ -685,14 +692,15 @@ int SpdySession::GetProtocolVersion() const { } bool SpdySession::CloseOneIdleConnection() { - if (!spdy_session_pool_ || num_active_streams() > 0) + DCHECK(spdy_session_pool_); + if (num_active_streams() > 0) return false; base::WeakPtr<SpdySession> weak_ptr = weak_factory_.GetWeakPtr(); // Will remove a reference to this. RemoveFromPool(); // Since the underlying socket is only returned when |this| is destroyed, // we should only return true if |this| no longer exists. - return weak_ptr.get() == NULL; + return !weak_ptr; } void SpdySession::EnqueueStreamWrite( @@ -1074,7 +1082,7 @@ int SpdySession::DoRead() { return ERR_IO_PENDING; } - CHECK(connection_.get()); + CHECK(connection_); CHECK(connection_->socket()); state_ = STATE_DO_READ_COMPLETE; return connection_->socket()->Read( @@ -1522,7 +1530,9 @@ void SpdySession::RemoveFromPool() { if (spdy_session_pool_) { SpdySessionPool* pool = spdy_session_pool_; spdy_session_pool_ = NULL; - pool->Remove(make_scoped_refptr(this)); + scoped_refptr<SpdySession> self(this); + pool->MakeSessionUnavailable(self); + pool->RemoveUnavailableSession(self); } } diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 28e53da..ed81c6c 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -200,7 +200,6 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, // |session| is the HttpNetworkSession. |net_log| is the NetLog that we log // network events to. SpdySession(const SpdySessionKey& spdy_session_key, - SpdySessionPool* spdy_session_pool, HttpServerProperties* http_server_properties, bool verify_domain_authentication, bool enable_sending_initial_settings, @@ -236,11 +235,16 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, base::WeakPtr<SpdyStream>* spdy_stream, const BoundNetLog& stream_net_log); - // Used by SpdySessionPool to initialize with a pre-existing SSL socket. For - // testing, setting is_secure to false allows initialization with a - // pre-existing TCP socket. - // Returns OK on success, or an error on failure. + // Initialize the session with the given connection. |is_secure| + // must indicate whether |connection| uses an SSL socket or not; it + // is usually true, but it can be false for testing or when SPDY is + // configured to work with non-secure sockets. + // + // Returns OK on success, or an error on failure. Never returns + // ERR_IO_PENDING. If an error is returned, the session must be + // destroyed immediately. Error InitializeWithSocket(scoped_ptr<ClientSocketHandle> connection, + SpdySessionPool* spdy_session_pool, bool is_secure, int certificate_error_code); diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc index 0d4fb19..7c7e76c 100644 --- a/net/spdy/spdy_session_pool.cc +++ b/net/spdy/spdy_session_pool.cc @@ -75,153 +75,192 @@ SpdySessionPool::~SpdySessionPool() { CertDatabase::GetInstance()->RemoveObserver(this); } -scoped_refptr<SpdySession> SpdySessionPool::GetIfExists( - const SpdySessionKey& spdy_session_key, +net::Error SpdySessionPool::CreateAvailableSessionFromSocket( + const SpdySessionKey& key, + scoped_ptr<ClientSocketHandle> connection, + const BoundNetLog& net_log, + int certificate_error_code, + scoped_refptr<SpdySession>* available_session, + bool is_secure) { + UMA_HISTOGRAM_ENUMERATION( + "Net.SpdySessionGet", IMPORTED_FROM_SOCKET, SPDY_SESSION_GET_MAX); + + scoped_refptr<SpdySession> new_session( + new SpdySession(key, + http_server_properties_, + verify_domain_authentication_, + enable_sending_initial_settings_, + enable_credential_frames_, + enable_compression_, + enable_ping_based_connection_checking_, + default_protocol_, + stream_initial_recv_window_size_, + initial_max_concurrent_streams_, + max_concurrent_streams_limit_, + time_func_, + trusted_spdy_proxy_, + net_log.net_log())); + + Error error = new_session->InitializeWithSocket( + connection.Pass(), this, is_secure, certificate_error_code); + DCHECK_NE(error, ERR_IO_PENDING); + + if (error != OK) { + new_session = NULL; + *available_session = NULL; + return error; + } + + sessions_.insert(new_session); + available_session->swap(new_session); + MapKeyToAvailableSession(key, *available_session); + + net_log.AddEvent( + NetLog::TYPE_SPDY_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET, + (*available_session)->net_log().source().ToEventParametersCallback()); + + // Look up the IP address for this session so that we can match + // future sessions (potentially to different domains) which can + // potentially be pooled with this one. Because GetPeerAddress() + // reports the proxy's address instead of the origin server, check + // to see if this is a direct connection. + if (enable_ip_pooling_ && key.proxy_server().is_direct()) { + IPEndPoint address; + if ((*available_session)->GetPeerAddress(&address) == OK) + aliases_[address] = key; + } + + return error; +} + +scoped_refptr<SpdySession> SpdySessionPool::FindAvailableSession( + const SpdySessionKey& key, const BoundNetLog& net_log) { - SpdySessionsMap::iterator it = FindSessionByKey(spdy_session_key); - if (it != sessions_.end()) { - UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", - FOUND_EXISTING, - SPDY_SESSION_GET_MAX); + AvailableSessionMap::iterator it = LookupAvailableSessionByKey(key); + if (it != available_sessions_.end()) { + UMA_HISTOGRAM_ENUMERATION( + "Net.SpdySessionGet", FOUND_EXISTING, SPDY_SESSION_GET_MAX); net_log.AddEvent( NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION, it->second->net_log().source().ToEventParametersCallback()); return it->second; } - // Check if we have a Session through a domain alias. - scoped_refptr<SpdySession> spdy_session = - GetFromAlias(spdy_session_key, net_log, true); - if (spdy_session) { + if (!enable_ip_pooling_) + return scoped_refptr<SpdySession>(); + + // Look up the key's from the resolver's cache. + net::HostResolver::RequestInfo resolve_info(key.host_port_pair()); + AddressList addresses; + int rv = resolver_->ResolveFromCache(resolve_info, &addresses, net_log); + DCHECK_NE(rv, ERR_IO_PENDING); + if (rv != OK) + return scoped_refptr<SpdySession>(); + + // Check if we have a session through a domain alias. + for (AddressList::const_iterator address_it = addresses.begin(); + address_it != addresses.end(); + ++address_it) { + AliasMap::const_iterator alias_it = aliases_.find(*address_it); + if (alias_it == aliases_.end()) + continue; + + // We found an alias. + const SpdySessionKey& alias_key = alias_it->second; + + // We can reuse this session only if the proxy and privacy + // settings match. + if (!(alias_key.proxy_server() == key.proxy_server()) || + !(alias_key.privacy_mode() == key.privacy_mode())) + continue; + + AvailableSessionMap::iterator available_session_it = + LookupAvailableSessionByKey(alias_key); + if (available_session_it == available_sessions_.end()) { + NOTREACHED(); // It shouldn't be in the aliases table if we can't get it! + continue; + } + + const scoped_refptr<SpdySession>& available_session = + available_session_it->second; + DCHECK(ContainsKey(sessions_, available_session)); + // If the session is a secure one, we need to verify that the + // server is authenticated to serve traffic for |host_port_proxy_pair| too. + if (!available_session->VerifyDomainAuthentication( + key.host_port_pair().host())) { + UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2); + continue; + } + + UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 1, 2); UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", FOUND_EXISTING_FROM_IP_POOL, SPDY_SESSION_GET_MAX); net_log.AddEvent( NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL, - spdy_session->net_log().source().ToEventParametersCallback()); + available_session->net_log().source().ToEventParametersCallback()); // Add this session to the map so that we can find it next time. - AddSession(spdy_session_key, spdy_session); - spdy_session->AddPooledAlias(spdy_session_key); - return spdy_session; + MapKeyToAvailableSession(key, available_session); + available_session->AddPooledAlias(key); + return available_session; } return scoped_refptr<SpdySession>(); } -net::Error SpdySessionPool::GetSpdySessionFromSocket( - const SpdySessionKey& spdy_session_key, - scoped_ptr<ClientSocketHandle> connection, - const BoundNetLog& net_log, - int certificate_error_code, - scoped_refptr<SpdySession>* spdy_session, - bool is_secure) { - UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", - IMPORTED_FROM_SOCKET, - SPDY_SESSION_GET_MAX); - // Create the SPDY session and add it to the pool. - *spdy_session = new SpdySession(spdy_session_key, this, - http_server_properties_, - verify_domain_authentication_, - enable_sending_initial_settings_, - enable_credential_frames_, - enable_compression_, - enable_ping_based_connection_checking_, - default_protocol_, - stream_initial_recv_window_size_, - initial_max_concurrent_streams_, - max_concurrent_streams_limit_, - time_func_, - trusted_spdy_proxy_, - net_log.net_log()); - AddSession(spdy_session_key, *spdy_session); - - net_log.AddEvent( - NetLog::TYPE_SPDY_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET, - (*spdy_session)->net_log().source().ToEventParametersCallback()); - - // We have a new session. Lookup the IP address for this session so that we - // can match future Sessions (potentially to different domains) which can - // potentially be pooled with this one. Because GetPeerAddress() reports the - // proxy's address instead of the origin server, check to see if this is a - // direct connection. - if (enable_ip_pooling_ && - spdy_session_key.proxy_server().is_direct()) { - IPEndPoint address; - if (connection->socket()->GetPeerAddress(&address) == OK) - aliases_[address] = spdy_session_key; +void SpdySessionPool::MakeSessionUnavailable( + const scoped_refptr<SpdySession>& available_session) { + UnmapKey(available_session->spdy_session_key()); + RemoveAliases(available_session->spdy_session_key()); + const std::set<SpdySessionKey>& aliases = available_session->pooled_aliases(); + for (std::set<SpdySessionKey>::const_iterator it = aliases.begin(); + it != aliases.end(); ++it) { + UnmapKey(*it); + RemoveAliases(*it); } + DCHECK(!IsSessionAvailable(available_session)); +} + +void SpdySessionPool::RemoveUnavailableSession( + const scoped_refptr<SpdySession>& unavailable_session) { + DCHECK(!IsSessionAvailable(unavailable_session)); + + unavailable_session->net_log().AddEvent( + NetLog::TYPE_SPDY_SESSION_POOL_REMOVE_SESSION, + unavailable_session->net_log().source().ToEventParametersCallback()); - // Now we can initialize the session with the SSL socket. - return (*spdy_session)->InitializeWithSocket(connection.Pass(), is_secure, - certificate_error_code); + SessionSet::iterator it = sessions_.find(unavailable_session); + CHECK(it != sessions_.end()); + sessions_.erase(it); } // Make a copy of |sessions_| in the Close* functions below to avoid -// reentrancy problems. Due to aliases, it doesn't suffice to simply -// increment the iterator before closing. +// reentrancy problems. Since arbitrary functions get called by close +// handlers, it doesn't suffice to simply increment the iterator +// before closing. void SpdySessionPool::CloseCurrentSessions(net::Error error) { - SpdySessionsMap sessions_copy = sessions_; - for (SpdySessionsMap::const_iterator it = sessions_copy.begin(); - it != sessions_copy.end(); ++it) { - TryCloseSession(it->first, error, "Closing current sessions."); - } + CloseCurrentSessionsHelper(error, "Closing current sessions.", + false /* idle_only */); } void SpdySessionPool::CloseCurrentIdleSessions() { - SpdySessionsMap sessions_copy = sessions_; - for (SpdySessionsMap::const_iterator it = sessions_copy.begin(); - it != sessions_copy.end(); ++it) { - if (!it->second->is_active()) - TryCloseSession(it->first, ERR_ABORTED, "Closing idle sessions."); - } + CloseCurrentSessionsHelper(ERR_ABORTED, "Closing idle sessions.", + true /* idle_only */); } void SpdySessionPool::CloseAllSessions() { while (!sessions_.empty()) { - SpdySessionsMap sessions_copy = sessions_; - for (SpdySessionsMap::const_iterator it = sessions_copy.begin(); - it != sessions_copy.end(); ++it) { - TryCloseSession(it->first, ERR_ABORTED, "Closing all sessions."); - } - } -} - -void SpdySessionPool::TryCloseSession(const SpdySessionKey& key, - net::Error error, - const std::string& description) { - SpdySessionsMap::const_iterator it = sessions_.find(key); - if (it == sessions_.end()) - return; - scoped_refptr<SpdySession> session = it->second; - session->CloseSessionOnError(error, description); - if (DCHECK_IS_ON()) { - it = sessions_.find(key); - // A new session with the same key may have been added, but it - // must not be the one we just closed. - if (it != sessions_.end()) - DCHECK_NE(it->second, session); - } -} - -void SpdySessionPool::Remove(const scoped_refptr<SpdySession>& session) { - RemoveSession(session->spdy_session_key()); - session->net_log().AddEvent( - NetLog::TYPE_SPDY_SESSION_POOL_REMOVE_SESSION, - session->net_log().source().ToEventParametersCallback()); - - const std::set<SpdySessionKey>& aliases = session->pooled_aliases(); - for (std::set<SpdySessionKey>::const_iterator it = aliases.begin(); - it != aliases.end(); ++it) { - RemoveSession(*it); + CloseCurrentSessionsHelper(ERR_ABORTED, "Closing all sessions.", + false /* idle_only */); } } base::Value* SpdySessionPool::SpdySessionPoolInfoToValue() const { base::ListValue* list = new base::ListValue(); - for (SpdySessionsMap::const_iterator it = sessions_.begin(); - it != sessions_.end(); ++it) { + for (AvailableSessionMap::const_iterator it = available_sessions_.begin(); + it != available_sessions_.end(); ++it) { // Only add the session if the key in the map matches the main // host_port_proxy_pair (not an alias). const SpdySessionKey& key = it->first; @@ -241,57 +280,6 @@ void SpdySessionPool::OnSSLConfigChanged() { CloseCurrentSessions(ERR_NETWORK_CHANGED); } -scoped_refptr<SpdySession> SpdySessionPool::GetFromAlias( - const SpdySessionKey& spdy_session_key, - const BoundNetLog& net_log, - bool record_histograms) { - // We should only be checking aliases when there is no direct session. - DCHECK(FindSessionByKey(spdy_session_key) == sessions_.end()); - - if (!enable_ip_pooling_) - return NULL; - - AddressList addresses; - if (!LookupAddresses(spdy_session_key, net_log, &addresses)) - return NULL; - for (AddressList::const_iterator iter = addresses.begin(); - iter != addresses.end(); - ++iter) { - SpdyAliasMap::const_iterator alias_iter = aliases_.find(*iter); - if (alias_iter == aliases_.end()) - continue; - - // We found an alias. - const SpdySessionKey& alias_key = alias_iter->second; - - // If the proxy and privacy settings match, we can reuse this session. - if (!(alias_key.proxy_server() == spdy_session_key.proxy_server()) || - !(alias_key.privacy_mode() == - spdy_session_key.privacy_mode())) - continue; - - SpdySessionsMap::iterator it = FindSessionByKey(alias_key); - if (it == sessions_.end()) { - NOTREACHED(); // It shouldn't be in the aliases table if we can't get it! - continue; - } - - scoped_refptr<SpdySession> spdy_session = it->second; - // If the SPDY session is a secure one, we need to verify that the server - // is authenticated to serve traffic for |host_port_proxy_pair| too. - if (!spdy_session->VerifyDomainAuthentication( - spdy_session_key.host_port_pair().host())) { - if (record_histograms) - UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2); - continue; - } - if (record_histograms) - UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 1, 2); - return spdy_session; - } - return NULL; -} - void SpdySessionPool::OnCertAdded(const X509Certificate* cert) { CloseCurrentSessions(ERR_NETWORK_CHANGED); } @@ -304,10 +292,20 @@ void SpdySessionPool::OnCertTrustChanged(const X509Certificate* cert) { CloseCurrentSessions(ERR_NETWORK_CHANGED); } +bool SpdySessionPool::IsSessionAvailable( + const scoped_refptr<SpdySession>& session) const { + for (AvailableSessionMap::const_iterator it = available_sessions_.begin(); + it != available_sessions_.end(); ++it) { + if (it->second == session) + return true; + } + return false; +} + const SpdySessionKey& SpdySessionPool::NormalizeListKey( - const SpdySessionKey& spdy_session_key) const { + const SpdySessionKey& key) const { if (!force_single_domain_) - return spdy_session_key; + return key; static SpdySessionKey* single_domain_key = NULL; if (!single_domain_key) { @@ -319,49 +317,63 @@ const SpdySessionKey& SpdySessionPool::NormalizeListKey( return *single_domain_key; } -void SpdySessionPool::AddSession( - const SpdySessionKey& spdy_session_key, - const scoped_refptr<SpdySession>& spdy_session) { - const SpdySessionKey& key = NormalizeListKey(spdy_session_key); - std::pair<SpdySessionsMap::iterator, bool> result = - sessions_.insert(std::make_pair(key, spdy_session)); +void SpdySessionPool::MapKeyToAvailableSession( + const SpdySessionKey& key, + const scoped_refptr<SpdySession>& session) { + DCHECK(ContainsKey(sessions_, session)); + const SpdySessionKey& normalized_key = NormalizeListKey(key); + std::pair<AvailableSessionMap::iterator, bool> result = + available_sessions_.insert(std::make_pair(normalized_key, session)); CHECK(result.second); } -SpdySessionPool::SpdySessionsMap::iterator -SpdySessionPool::FindSessionByKey(const SpdySessionKey& spdy_session_key) { - const SpdySessionKey& key = NormalizeListKey(spdy_session_key); - return sessions_.find(key); -} - -void SpdySessionPool::RemoveSession(const SpdySessionKey& spdy_session_key) { - SpdySessionsMap::iterator it = FindSessionByKey(spdy_session_key); - CHECK(it != sessions_.end()); - sessions_.erase(it); - RemoveAliases(spdy_session_key); +SpdySessionPool::AvailableSessionMap::iterator +SpdySessionPool::LookupAvailableSessionByKey( + const SpdySessionKey& key) { + const SpdySessionKey& normalized_key = NormalizeListKey(key); + return available_sessions_.find(normalized_key); } -bool SpdySessionPool::LookupAddresses(const SpdySessionKey& spdy_session_key, - const BoundNetLog& net_log, - AddressList* addresses) const { - net::HostResolver::RequestInfo resolve_info( - spdy_session_key.host_port_pair()); - int rv = resolver_->ResolveFromCache(resolve_info, addresses, net_log); - DCHECK_NE(ERR_IO_PENDING, rv); - return rv == OK; +void SpdySessionPool::UnmapKey(const SpdySessionKey& key) { + AvailableSessionMap::iterator it = LookupAvailableSessionByKey(key); + CHECK(it != available_sessions_.end()); + available_sessions_.erase(it); } -void SpdySessionPool::RemoveAliases(const SpdySessionKey& spdy_session_key) { +void SpdySessionPool::RemoveAliases(const SpdySessionKey& key) { // Walk the aliases map, find references to this pair. // TODO(mbelshe): Figure out if this is too expensive. - SpdyAliasMap::iterator alias_it = aliases_.begin(); - while (alias_it != aliases_.end()) { - if (alias_it->second.Equals(spdy_session_key)) { - aliases_.erase(alias_it); - alias_it = aliases_.begin(); // Iterator was invalidated. - continue; + for (AliasMap::iterator it = aliases_.begin(); it != aliases_.end(); ) { + if (it->second.Equals(key)) { + AliasMap::iterator old_it = it; + ++it; + aliases_.erase(old_it); + } else { + ++it; } - ++alias_it; + } +} + +void SpdySessionPool::CloseCurrentSessionsHelper( + Error error, + const std::string& description, + bool idle_only) { + SessionSet current_sessions = sessions_; + for (SessionSet::const_iterator it = current_sessions.begin(); + it != current_sessions.end(); ++it) { + if (!ContainsKey(sessions_, *it)) + continue; + + // TODO(akalin): Handle unavailable sessions once those aren't + // removed immediately. + DCHECK(IsSessionAvailable(*it)); + + if (idle_only && (*it)->is_active()) + continue; + + (*it)->CloseSessionOnError(error, description); + DCHECK(!IsSessionAvailable(*it)); + DCHECK(!ContainsKey(sessions_, *it)); } } diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h index 2e6b993..7152527 100644 --- a/net/spdy/spdy_session_pool.h +++ b/net/spdy/spdy_session_pool.h @@ -6,6 +6,7 @@ #define NET_SPDY_SPDY_SESSION_POOL_H_ #include <map> +#include <set> #include <string> #include "base/basictypes.h" @@ -56,32 +57,49 @@ class NET_EXPORT SpdySessionPool const std::string& trusted_spdy_proxy); virtual ~SpdySessionPool(); - // Returns the SPDY session for the given key, or NULL if there is - // none. - scoped_refptr<SpdySession> GetIfExists( - const SpdySessionKey& spdy_session_key, - const BoundNetLog& net_log); + // In the functions below, a session is "available" if this pool has + // a reference to it and there is some SpdySessionKey for which + // FindAvailableSession() will return it. A session is "unavailable" + // if this pool has a reference to it but it won't be returned by + // FindAvailableSession() for any SpdySessionKey; for example, this + // can happen when a session receives a GOAWAY frame and is still + // processing existing streams. - // Builds a SpdySession from an existing SSL socket. There must not - // already be a session for the given key. Note that ownership of - // |connection| is transferred from the caller to the SpdySession. + // Create a new SPDY session from an existing socket. There must + // not already be a session for the given key. // - // |certificate_error_code| is used to indicate the certificate - // error encountered when connecting the SSL socket. OK means there - // was no error. For testing and when SPDY is configured to work - // with non-secure sockets, setting is_secure to false allows Spdy - // to connect with a pre-existing TCP socket. + // |is_secure| can be false for testing or when SPDY is configured + // to work with non-secure sockets. If |is_secure| is true, + // |certificate_error_code| indicates that the certificate error + // encountered when connecting the SSL socket, with OK meaning there + // was no error. // - // Returns OK on success, and the |spdy_session| will be provided. - // Returns an error on failure, and |spdy_session| will be NULL. - net::Error GetSpdySessionFromSocket( - const SpdySessionKey& spdy_session_key, + // If successful, OK is returned and |available_session| will be + // non-NULL and available. Otherwise, an error is returned and + // |available_session| will be NULL. + net::Error CreateAvailableSessionFromSocket( + const SpdySessionKey& key, scoped_ptr<ClientSocketHandle> connection, const BoundNetLog& net_log, int certificate_error_code, - scoped_refptr<SpdySession>* spdy_session, + scoped_refptr<SpdySession>* available_session, bool is_secure); + // Find an available session for the given key, or NULL if there isn't one. + scoped_refptr<SpdySession> FindAvailableSession(const SpdySessionKey& key, + const BoundNetLog& net_log); + + // Remove all mappings and aliases for the given session, which must + // still be available. Except for in tests, this must be called by + // the given session itself. + void MakeSessionUnavailable( + const scoped_refptr<SpdySession>& available_session); + + // Removes an unavailable session from the pool. Except for in + // tests, this must be called by the given session itself. + void RemoveUnavailableSession( + const scoped_refptr<SpdySession>& unavailable_session); + // Close only the currently existing SpdySessions with |error|. // Let any new ones created while this method is running continue to // live. @@ -96,15 +114,6 @@ class NET_EXPORT SpdySessionPool // closing the current ones. void CloseAllSessions(); - // Look up the session for the given key and close it if found. - void TryCloseSession(const SpdySessionKey& key, - net::Error error, - const std::string& description); - - // Removes a SpdySession from the SpdySessionPool. This should only be called - // by SpdySession, because otherwise session->state_ is not set to CLOSED. - void Remove(const scoped_refptr<SpdySession>& session); - // Creates a Value summary of the state of the spdy session pool. The caller // responsible for deleting the returned value. base::Value* SpdySessionPoolInfoToValue() const; @@ -132,58 +141,54 @@ class NET_EXPORT SpdySessionPool private: friend class SpdySessionPoolPeer; // For testing. - typedef std::map<SpdySessionKey, scoped_refptr<SpdySession> > SpdySessionsMap; - typedef std::map<IPEndPoint, SpdySessionKey> SpdyAliasMap; + typedef std::set<scoped_refptr<SpdySession> > SessionSet; + typedef std::map<SpdySessionKey, scoped_refptr<SpdySession> > + AvailableSessionMap; + typedef std::map<IPEndPoint, SpdySessionKey> AliasMap; - // Looks up any aliases for the given key, which must not already - // have a session, and returns the session for first matching one, - // or NULL if there is none. If a matching session is found, it is - // then inserted into |sessions_|. - scoped_refptr<SpdySession> GetFromAlias( - const SpdySessionKey& spdy_session_key, - const BoundNetLog& net_log, - bool record_histograms); + // Returns true iff |session| is in |available_sessions_|. + bool IsSessionAvailable(const scoped_refptr<SpdySession>& session) const; // Returns a normalized version of the given key suitable for lookup - // into |sessions_|. - const SpdySessionKey& NormalizeListKey( - const SpdySessionKey& spdy_session_key) const; - - // Add the given key/session mapping. There must not already be a - // session for the given key. - void AddSession(const SpdySessionKey& spdy_session_key, - const scoped_refptr<SpdySession>& spdy_session); - - // Returns an iterator into |sessions_| for the given key, which may - // be equal to |sessions_.end()|. - SpdySessionsMap::iterator FindSessionByKey( - const SpdySessionKey& spdy_session_key); - - // Remove the session associated with |spdy_session_key|, which must - // exist. - void RemoveSession(const SpdySessionKey& spdy_session_key); - - // Does a DNS cache lookup for |spdy_session_key|, and returns - // the |addresses| found. - // Returns true if addresses found, false otherwise. - bool LookupAddresses(const SpdySessionKey& spdy_session_key, - const BoundNetLog& net_log, - AddressList* addresses) const; - - // Remove all aliases for |spdy_session_key| from the aliases table. - void RemoveAliases(const SpdySessionKey& spdy_session_key); + // into |available_sessions_|. + const SpdySessionKey& NormalizeListKey(const SpdySessionKey& key) const; + + // Map the given key to the given session. There must not already be + // a mapping for |key|. + void MapKeyToAvailableSession(const SpdySessionKey& key, + const scoped_refptr<SpdySession>& session); + + // Returns an iterator into |available_sessions_| for the given key, + // which may be equal to |available_sessions_.end()|. + AvailableSessionMap::iterator LookupAvailableSessionByKey( + const SpdySessionKey& key); + + // Remove the mapping of the given key, which must exist. + void UnmapKey(const SpdySessionKey& key); + + // Remove all aliases for |key| from the aliases table. + void RemoveAliases(const SpdySessionKey& key); + + // Close only the currently existing SpdySessions with |error|. Let + // any new ones created while this method is running continue to + // live. If |idle_only| is true only idle sessions are closed. + void CloseCurrentSessionsHelper( + Error error, + const std::string& description, + bool idle_only); HttpServerProperties* const http_server_properties_; - // This is a map of session keys to sessions. A session may appear + // The set of all sessions. This is a superset of the sessions in + // |available_sessions_|. + SessionSet sessions_; + + // This is a map of available sessions by key. A session may appear // more than once in this map if it has aliases. - // - // TODO(akalin): Have a map which owns the sessions and another one - // for the aliased session cache. - SpdySessionsMap sessions_; + AvailableSessionMap available_sessions_; // A map of IPEndPoint aliases for sessions. - SpdyAliasMap aliases_; + AliasMap aliases_; static bool g_force_single_domain; diff --git a/net/spdy/spdy_session_pool_unittest.cc b/net/spdy/spdy_session_pool_unittest.cc index 7d99ea3..576d30a 100644 --- a/net/spdy/spdy_session_pool_unittest.cc +++ b/net/spdy/spdy_session_pool_unittest.cc @@ -400,7 +400,8 @@ void SpdySessionPoolTest::RunIPPoolingTest( // Grab the session to host 1 and verify that it is the same session // we got with host 0, and that is a different from host 2's session. scoped_refptr<SpdySession> session1 = - spdy_session_pool_->GetIfExists(test_hosts[1].key, BoundNetLog()); + spdy_session_pool_->FindAvailableSession( + test_hosts[1].key, BoundNetLog()); EXPECT_EQ(session.get(), session1.get()); EXPECT_NE(session2.get(), session1.get()); @@ -417,9 +418,11 @@ void SpdySessionPoolTest::RunIPPoolingTest( // Cleanup the sessions. switch (close_sessions_type) { case SPDY_POOL_CLOSE_SESSIONS_MANUALLY: - spdy_session_pool_->Remove(session); + spdy_session_pool_->MakeSessionUnavailable(session); + spdy_session_pool_->RemoveUnavailableSession(session); session = NULL; - spdy_session_pool_->Remove(session2); + spdy_session_pool_->MakeSessionUnavailable(session2); + spdy_session_pool_->RemoveUnavailableSession(session2); session2 = NULL; break; case SPDY_POOL_CLOSE_CURRENT_SESSIONS: @@ -469,7 +472,8 @@ void SpdySessionPoolTest::RunIPPoolingTest( EXPECT_EQ(NULL, spdy_stream.get()); EXPECT_EQ(NULL, spdy_stream1.get()); EXPECT_EQ(NULL, spdy_stream2.get()); - spdy_session_pool_->Remove(session2); + spdy_session_pool_->MakeSessionUnavailable(session2); + spdy_session_pool_->RemoveUnavailableSession(session2); session2 = NULL; break; } diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc index dae5837..be14048 100644 --- a/net/spdy/spdy_session_unittest.cc +++ b/net/spdy/spdy_session_unittest.cc @@ -262,7 +262,8 @@ TEST_P(SpdySessionTest, GoAway) { session = NULL; // Delete the second session. - spdy_session_pool_->Remove(session2); + spdy_session_pool_->MakeSessionUnavailable(session2); + spdy_session_pool_->RemoveUnavailableSession(session2); session2 = NULL; EXPECT_EQ(NULL, spdy_stream2.get()); } @@ -1438,7 +1439,9 @@ TEST_P(SpdySessionTest, VerifyDomainAuthentication) { HttpNetworkSession::NORMAL_SOCKET_POOL), BoundNetLog())); - EXPECT_EQ(OK, session->InitializeWithSocket(connection.Pass(), false, OK)); + EXPECT_EQ(OK, + session->InitializeWithSocket( + connection.Pass(), spdy_session_pool_, false, OK)); EXPECT_TRUE(session->VerifyDomainAuthentication("www.example.org")); EXPECT_TRUE(session->VerifyDomainAuthentication("mail.example.org")); EXPECT_TRUE(session->VerifyDomainAuthentication("mail.example.com")); @@ -1508,7 +1511,9 @@ TEST_P(SpdySessionTest, ConnectionPooledWithTlsChannelId) { HttpNetworkSession::NORMAL_SOCKET_POOL), BoundNetLog())); - EXPECT_EQ(OK, session->InitializeWithSocket(connection.Pass(), false, OK)); + EXPECT_EQ(OK, + session->InitializeWithSocket( + connection.Pass(), spdy_session_pool_, false, OK)); EXPECT_TRUE(session->VerifyDomainAuthentication("www.example.org")); EXPECT_TRUE(session->VerifyDomainAuthentication("mail.example.org")); EXPECT_FALSE(session->VerifyDomainAuthentication("mail.example.com")); @@ -1782,14 +1787,17 @@ TEST_P(SpdySessionTest, NeedsCredentials) { HttpNetworkSession::NORMAL_SOCKET_POOL), BoundNetLog())); - EXPECT_EQ(OK, session->InitializeWithSocket(connection.Pass(), true, OK)); + EXPECT_EQ(OK, + session->InitializeWithSocket( + connection.Pass(), spdy_session_pool_, true, OK)); EXPECT_EQ(spdy_util_.spdy_version() >= SPDY3, session->NeedsCredentials()); // Flush the SpdySession::OnReadComplete() task. base::MessageLoop::current()->RunUntilIdle(); - spdy_session_pool_->Remove(session); + spdy_session_pool_->MakeSessionUnavailable(session); + spdy_session_pool_->RemoveUnavailableSession(session); } // Test that SpdySession::DoRead reads data from the socket without yielding. @@ -2298,7 +2306,7 @@ TEST_P(SpdySessionTest, CloseOneIdleConnectionWithAlias) { info, &addresses, CompletionCallback(), NULL, BoundNetLog()); // Get a session for |key2|, which should return the session created earlier. scoped_refptr<SpdySession> session2 = - spdy_session_pool_->GetIfExists(key2, BoundNetLog()); + spdy_session_pool_->FindAvailableSession(key2, BoundNetLog()); ASSERT_EQ(session1.get(), session2.get()); EXPECT_FALSE(pool->IsStalled()); @@ -2501,11 +2509,13 @@ TEST_P(SpdySessionTest, SpdySessionKeyPrivacyMode) { EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_privacy_enabled)); EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_privacy_disabled)); - spdy_session_pool_->Remove(session_privacy_enabled); + spdy_session_pool_->MakeSessionUnavailable(session_privacy_enabled); + spdy_session_pool_->RemoveUnavailableSession(session_privacy_enabled); EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_enabled)); EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_privacy_disabled)); - spdy_session_pool_->Remove(session_privacy_disabled); + spdy_session_pool_->MakeSessionUnavailable(session_privacy_disabled); + spdy_session_pool_->RemoveUnavailableSession(session_privacy_disabled); EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_enabled)); EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_disabled)); } @@ -2572,13 +2582,16 @@ TEST_P(SpdySessionTest, SendCredentials) { HttpNetworkSession::NORMAL_SOCKET_POOL), BoundNetLog())); - EXPECT_EQ(OK, session->InitializeWithSocket(connection.Pass(), true, OK)); + EXPECT_EQ(OK, + session->InitializeWithSocket( + connection.Pass(), spdy_session_pool_, true, OK)); EXPECT_TRUE(session->NeedsCredentials()); // Flush the SpdySession::OnReadComplete() task. base::MessageLoop::current()->RunUntilIdle(); - spdy_session_pool_->Remove(session); + spdy_session_pool_->MakeSessionUnavailable(session); + spdy_session_pool_->RemoveUnavailableSession(session); EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key)); } diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc index 34da314..54982b9 100644 --- a/net/spdy/spdy_test_util_common.cc +++ b/net/spdy/spdy_test_util_common.cc @@ -482,7 +482,7 @@ SpdyURLRequestContext::~SpdyURLRequestContext() { } bool HasSpdySession(SpdySessionPool* pool, const SpdySessionKey& key) { - return pool->GetIfExists(key, BoundNetLog()) != NULL; + return pool->FindAvailableSession(key, BoundNetLog()) != NULL; } namespace { @@ -542,7 +542,7 @@ scoped_refptr<SpdySession> CreateSpdySessionHelper( scoped_refptr<SpdySession> spdy_session; EXPECT_EQ( OK, - http_session->spdy_session_pool()->GetSpdySessionFromSocket( + http_session->spdy_session_pool()->CreateAvailableSessionFromSocket( key, connection.Pass(), net_log, OK, &spdy_session, is_secure)); EXPECT_TRUE(HasSpdySession(http_session->spdy_session_pool(), key)); @@ -628,7 +628,7 @@ scoped_refptr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool, handle->set_socket(new FakeSpdySessionClientSocket()); EXPECT_EQ( OK, - pool->GetSpdySessionFromSocket( + pool->CreateAvailableSessionFromSocket( key, handle.Pass(), BoundNetLog(), OK, &spdy_session, true /* is_secure */)); EXPECT_TRUE(HasSpdySession(pool, key)); diff --git a/net/websockets/websocket_job.cc b/net/websockets/websocket_job.cc index 301f0ae..34d4a25 100644 --- a/net/websockets/websocket_job.cc +++ b/net/websockets/websocket_job.cc @@ -597,7 +597,7 @@ int WebSocketJob::TrySpdyStream() { // Forbid wss downgrade to SPDY without SSL. // TODO(toyoshim): Does it realize the same policy with HTTP? scoped_refptr<SpdySession> spdy_session = - spdy_pool->GetIfExists(key, *socket_->net_log()); + spdy_pool->FindAvailableSession(key, *socket_->net_log()); if (!spdy_session) return OK; |