Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Side by Side Diff: net/spdy/spdy_session_pool.cc

Issue 2832973003: Split net/spdy into core and chromium subdirectories. (Closed)
Patch Set: Fix some more build rules. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/spdy/spdy_session_pool.h ('k') | net/spdy/spdy_session_pool_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 <utility>
8
9 #include "base/logging.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/stl_util.h"
14 #include "base/trace_event/memory_allocator_dump.h"
15 #include "base/trace_event/process_memory_dump.h"
16 #include "base/trace_event/trace_event.h"
17 #include "base/values.h"
18 #include "net/base/address_list.h"
19 #include "net/base/trace_constants.h"
20 #include "net/http/http_network_session.h"
21 #include "net/http/http_server_properties.h"
22 #include "net/log/net_log_event_type.h"
23 #include "net/log/net_log_source.h"
24 #include "net/log/net_log_with_source.h"
25 #include "net/spdy/hpack/hpack_constants.h"
26 #include "net/spdy/hpack/hpack_huffman_table.h"
27 #include "net/spdy/hpack/hpack_static_table.h"
28 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h"
29 #include "net/spdy/platform/api/spdy_string_utils.h"
30 #include "net/spdy/spdy_session.h"
31
32 namespace net {
33
34 namespace {
35
36 enum SpdySessionGetTypes {
37 CREATED_NEW = 0,
38 FOUND_EXISTING = 1,
39 FOUND_EXISTING_FROM_IP_POOL = 2,
40 IMPORTED_FROM_SOCKET = 3,
41 SPDY_SESSION_GET_MAX = 4
42 };
43
44 } // namespace
45
46 SpdySessionPool::SpdySessionPool(
47 HostResolver* resolver,
48 SSLConfigService* ssl_config_service,
49 HttpServerProperties* http_server_properties,
50 TransportSecurityState* transport_security_state,
51 bool enable_ping_based_connection_checking,
52 size_t session_max_recv_window_size,
53 const SettingsMap& initial_settings,
54 SpdySessionPool::TimeFunc time_func,
55 ProxyDelegate* proxy_delegate)
56 : http_server_properties_(http_server_properties),
57 transport_security_state_(transport_security_state),
58 ssl_config_service_(ssl_config_service),
59 resolver_(resolver),
60 enable_sending_initial_data_(true),
61 enable_ping_based_connection_checking_(
62 enable_ping_based_connection_checking),
63 session_max_recv_window_size_(session_max_recv_window_size),
64 initial_settings_(initial_settings),
65 time_func_(time_func),
66 push_delegate_(nullptr),
67 proxy_delegate_(proxy_delegate) {
68 NetworkChangeNotifier::AddIPAddressObserver(this);
69 if (ssl_config_service_.get())
70 ssl_config_service_->AddObserver(this);
71 CertDatabase::GetInstance()->AddObserver(this);
72 }
73
74 SpdySessionPool::~SpdySessionPool() {
75 CloseAllSessions();
76
77 while (!sessions_.empty()) {
78 // Destroy sessions to enforce that lifetime is scoped to SpdySessionPool.
79 // Write callbacks queued upon session drain are not invoked.
80 RemoveUnavailableSession((*sessions_.begin())->GetWeakPtr());
81 }
82
83 if (ssl_config_service_.get())
84 ssl_config_service_->RemoveObserver(this);
85 NetworkChangeNotifier::RemoveIPAddressObserver(this);
86 CertDatabase::GetInstance()->RemoveObserver(this);
87 }
88
89 base::WeakPtr<SpdySession> SpdySessionPool::CreateAvailableSessionFromSocket(
90 const SpdySessionKey& key,
91 std::unique_ptr<ClientSocketHandle> connection,
92 const NetLogWithSource& net_log,
93 bool is_secure) {
94 TRACE_EVENT0(kNetTracingCategory,
95 "SpdySessionPool::CreateAvailableSessionFromSocket");
96
97 UMA_HISTOGRAM_ENUMERATION(
98 "Net.SpdySessionGet", IMPORTED_FROM_SOCKET, SPDY_SESSION_GET_MAX);
99
100 auto new_session = base::MakeUnique<SpdySession>(
101 key, http_server_properties_, transport_security_state_,
102 enable_sending_initial_data_, enable_ping_based_connection_checking_,
103 session_max_recv_window_size_, initial_settings_, time_func_,
104 push_delegate_, proxy_delegate_, net_log.net_log());
105
106 new_session->InitializeWithSocket(std::move(connection), this, is_secure);
107
108 base::WeakPtr<SpdySession> available_session = new_session->GetWeakPtr();
109 sessions_.insert(new_session.release());
110 MapKeyToAvailableSession(key, available_session);
111
112 net_log.AddEvent(
113 NetLogEventType::HTTP2_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET,
114 available_session->net_log().source().ToEventParametersCallback());
115
116 // Look up the IP address for this session so that we can match
117 // future sessions (potentially to different domains) which can
118 // potentially be pooled with this one. Because GetPeerAddress()
119 // reports the proxy's address instead of the origin server, check
120 // to see if this is a direct connection.
121 if (key.proxy_server().is_direct()) {
122 IPEndPoint address;
123 if (available_session->GetPeerAddress(&address) == OK)
124 aliases_[address] = key;
125 }
126
127 return available_session;
128 }
129
130 base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession(
131 const SpdySessionKey& key,
132 const GURL& url,
133 bool enable_ip_based_pooling,
134 const NetLogWithSource& net_log) {
135 UnclaimedPushedStreamMap::iterator url_it =
136 unclaimed_pushed_streams_.find(url);
137 if (!url.is_empty() && url_it != unclaimed_pushed_streams_.end()) {
138 DCHECK(url.SchemeIsCryptographic());
139 for (WeakSessionList::iterator it = url_it->second.begin();
140 it != url_it->second.end();) {
141 base::WeakPtr<SpdySession> spdy_session = *it;
142 // Lazy deletion of destroyed SpdySessions.
143 if (!spdy_session) {
144 it = url_it->second.erase(it);
145 continue;
146 }
147 ++it;
148 const SpdySessionKey& spdy_session_key = spdy_session->spdy_session_key();
149 if (!(spdy_session_key.proxy_server() == key.proxy_server()) ||
150 !(spdy_session_key.privacy_mode() == key.privacy_mode())) {
151 continue;
152 }
153 if (!spdy_session->VerifyDomainAuthentication(
154 key.host_port_pair().host())) {
155 continue;
156 }
157 return spdy_session;
158 }
159 if (url_it->second.empty()) {
160 unclaimed_pushed_streams_.erase(url_it);
161 }
162 }
163
164 AvailableSessionMap::iterator it = LookupAvailableSessionByKey(key);
165 if (it != available_sessions_.end()) {
166 if (key.Equals(it->second->spdy_session_key())) {
167 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", FOUND_EXISTING,
168 SPDY_SESSION_GET_MAX);
169 net_log.AddEvent(
170 NetLogEventType::HTTP2_SESSION_POOL_FOUND_EXISTING_SESSION,
171 it->second->net_log().source().ToEventParametersCallback());
172 } else {
173 if (!enable_ip_based_pooling) {
174 // Remove session from available sessions and from aliases, and remove
175 // key from the session's pooled alias set, so that a new session can be
176 // created with this |key|.
177 it->second->RemovePooledAlias(key);
178 UnmapKey(key);
179 RemoveAliases(key);
180 return base::WeakPtr<SpdySession>();
181 }
182
183 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
184 FOUND_EXISTING_FROM_IP_POOL,
185 SPDY_SESSION_GET_MAX);
186 net_log.AddEvent(
187 NetLogEventType::
188 HTTP2_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL,
189 it->second->net_log().source().ToEventParametersCallback());
190 }
191 return it->second;
192 }
193
194 if (!enable_ip_based_pooling)
195 return base::WeakPtr<SpdySession>();
196
197 // Look up IP addresses from resolver cache.
198 HostResolver::RequestInfo resolve_info(key.host_port_pair());
199 AddressList addresses;
200 int rv = resolver_->ResolveFromCache(resolve_info, &addresses, net_log);
201 DCHECK_NE(rv, ERR_IO_PENDING);
202 if (rv != OK)
203 return base::WeakPtr<SpdySession>();
204
205 // Check if we have a session through a domain alias.
206 for (AddressList::const_iterator address_it = addresses.begin();
207 address_it != addresses.end();
208 ++address_it) {
209 AliasMap::const_iterator alias_it = aliases_.find(*address_it);
210 if (alias_it == aliases_.end())
211 continue;
212
213 // We found an alias.
214 const SpdySessionKey& alias_key = alias_it->second;
215
216 // We can reuse this session only if the proxy and privacy
217 // settings match.
218 if (!(alias_key.proxy_server() == key.proxy_server()) ||
219 !(alias_key.privacy_mode() == key.privacy_mode()))
220 continue;
221
222 AvailableSessionMap::iterator available_session_it =
223 LookupAvailableSessionByKey(alias_key);
224 if (available_session_it == available_sessions_.end()) {
225 NOTREACHED(); // It shouldn't be in the aliases table if we can't get it!
226 continue;
227 }
228
229 const base::WeakPtr<SpdySession>& available_session =
230 available_session_it->second;
231 DCHECK(base::ContainsKey(sessions_, available_session.get()));
232 // If the session is a secure one, we need to verify that the
233 // server is authenticated to serve traffic for |host_port_proxy_pair| too.
234 if (!available_session->VerifyDomainAuthentication(
235 key.host_port_pair().host())) {
236 UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2);
237 continue;
238 }
239
240 UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 1, 2);
241 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
242 FOUND_EXISTING_FROM_IP_POOL,
243 SPDY_SESSION_GET_MAX);
244 net_log.AddEvent(
245 NetLogEventType::HTTP2_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL,
246 available_session->net_log().source().ToEventParametersCallback());
247 // Add this session to the map so that we can find it next time.
248 MapKeyToAvailableSession(key, available_session);
249 available_session->AddPooledAlias(key);
250 return available_session;
251 }
252
253 return base::WeakPtr<SpdySession>();
254 }
255
256 void SpdySessionPool::MakeSessionUnavailable(
257 const base::WeakPtr<SpdySession>& available_session) {
258 UnmapKey(available_session->spdy_session_key());
259 RemoveAliases(available_session->spdy_session_key());
260 const std::set<SpdySessionKey>& aliases = available_session->pooled_aliases();
261 for (std::set<SpdySessionKey>::const_iterator it = aliases.begin();
262 it != aliases.end(); ++it) {
263 UnmapKey(*it);
264 RemoveAliases(*it);
265 }
266 DCHECK(!IsSessionAvailable(available_session));
267 }
268
269 void SpdySessionPool::RemoveUnavailableSession(
270 const base::WeakPtr<SpdySession>& unavailable_session) {
271 DCHECK(!IsSessionAvailable(unavailable_session));
272
273 unavailable_session->net_log().AddEvent(
274 NetLogEventType::HTTP2_SESSION_POOL_REMOVE_SESSION);
275
276 SessionSet::iterator it = sessions_.find(unavailable_session.get());
277 CHECK(it != sessions_.end());
278 std::unique_ptr<SpdySession> owned_session(*it);
279 sessions_.erase(it);
280 }
281
282 // Make a copy of |sessions_| in the Close* functions below to avoid
283 // reentrancy problems. Since arbitrary functions get called by close
284 // handlers, it doesn't suffice to simply increment the iterator
285 // before closing.
286
287 void SpdySessionPool::CloseCurrentSessions(Error error) {
288 CloseCurrentSessionsHelper(error, "Closing current sessions.",
289 false /* idle_only */);
290 }
291
292 void SpdySessionPool::CloseCurrentIdleSessions() {
293 CloseCurrentSessionsHelper(ERR_ABORTED, "Closing idle sessions.",
294 true /* idle_only */);
295 }
296
297 void SpdySessionPool::CloseAllSessions() {
298 while (!available_sessions_.empty()) {
299 CloseCurrentSessionsHelper(ERR_ABORTED, "Closing all sessions.",
300 false /* idle_only */);
301 }
302 }
303
304 void SpdySessionPool::RegisterUnclaimedPushedStream(
305 GURL url,
306 base::WeakPtr<SpdySession> spdy_session) {
307 DCHECK(!url.is_empty());
308 // This SpdySessionPool must own |spdy_session|.
309 DCHECK(base::ContainsKey(sessions_, spdy_session.get()));
310 UnclaimedPushedStreamMap::iterator url_it =
311 unclaimed_pushed_streams_.lower_bound(url);
312 if (url_it == unclaimed_pushed_streams_.end() || url_it->first != url) {
313 WeakSessionList list;
314 list.push_back(std::move(spdy_session));
315 UnclaimedPushedStreamMap::value_type value(std::move(url), std::move(list));
316 unclaimed_pushed_streams_.insert(url_it, std::move(value));
317 return;
318 }
319 url_it->second.push_back(spdy_session);
320 }
321
322 void SpdySessionPool::UnregisterUnclaimedPushedStream(
323 const GURL& url,
324 SpdySession* spdy_session) {
325 DCHECK(!url.is_empty());
326 UnclaimedPushedStreamMap::iterator url_it =
327 unclaimed_pushed_streams_.find(url);
328 DCHECK(url_it != unclaimed_pushed_streams_.end());
329 size_t removed = 0;
330 for (WeakSessionList::iterator it = url_it->second.begin();
331 it != url_it->second.end();) {
332 // Lazy deletion of destroyed SpdySessions.
333 if (!*it) {
334 it = url_it->second.erase(it);
335 continue;
336 }
337 if (it->get() == spdy_session) {
338 it = url_it->second.erase(it);
339 ++removed;
340 break;
341 }
342 ++it;
343 }
344 if (url_it->second.empty()) {
345 unclaimed_pushed_streams_.erase(url_it);
346 }
347 DCHECK_EQ(1u, removed);
348 }
349
350 std::unique_ptr<base::Value> SpdySessionPool::SpdySessionPoolInfoToValue()
351 const {
352 std::unique_ptr<base::ListValue> list(new base::ListValue());
353
354 for (AvailableSessionMap::const_iterator it = available_sessions_.begin();
355 it != available_sessions_.end(); ++it) {
356 // Only add the session if the key in the map matches the main
357 // host_port_proxy_pair (not an alias).
358 const SpdySessionKey& key = it->first;
359 const SpdySessionKey& session_key = it->second->spdy_session_key();
360 if (key.Equals(session_key))
361 list->Append(it->second->GetInfoAsValue());
362 }
363 return std::move(list);
364 }
365
366 void SpdySessionPool::OnIPAddressChanged() {
367 WeakSessionList current_sessions = GetCurrentSessions();
368 for (WeakSessionList::const_iterator it = current_sessions.begin();
369 it != current_sessions.end(); ++it) {
370 if (!*it)
371 continue;
372
373 // For OSs that terminate TCP connections upon relevant network changes,
374 // attempt to preserve active streams by marking all sessions as going
375 // away, rather than explicitly closing them. Streams may still fail due
376 // to a generated TCP reset.
377 #if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS)
378 (*it)->MakeUnavailable();
379 (*it)->StartGoingAway(kLastStreamId, ERR_NETWORK_CHANGED);
380 (*it)->MaybeFinishGoingAway();
381 #else
382 (*it)->CloseSessionOnError(ERR_NETWORK_CHANGED,
383 "Closing current sessions.");
384 DCHECK((*it)->IsDraining());
385 #endif // defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS)
386 DCHECK(!IsSessionAvailable(*it));
387 }
388 }
389
390 void SpdySessionPool::OnSSLConfigChanged() {
391 CloseCurrentSessions(ERR_NETWORK_CHANGED);
392 }
393
394 void SpdySessionPool::OnCertDBChanged() {
395 CloseCurrentSessions(ERR_CERT_DATABASE_CHANGED);
396 }
397
398 void SpdySessionPool::DumpMemoryStats(
399 base::trace_event::ProcessMemoryDump* pmd,
400 const SpdyString& parent_dump_absolute_name) const {
401 if (sessions_.empty())
402 return;
403 size_t total_size = 0;
404 size_t buffer_size = 0;
405 size_t cert_count = 0;
406 size_t cert_size = 0;
407 size_t num_active_sessions = 0;
408 for (auto* session : sessions_) {
409 StreamSocket::SocketMemoryStats stats;
410 bool is_session_active = false;
411 total_size += session->DumpMemoryStats(&stats, &is_session_active);
412 buffer_size += stats.buffer_size;
413 cert_count += stats.cert_count;
414 cert_size += stats.cert_size;
415 if (is_session_active)
416 num_active_sessions++;
417 }
418 total_size += SpdyEstimateMemoryUsage(ObtainHpackHuffmanTable()) +
419 SpdyEstimateMemoryUsage(ObtainHpackStaticTable());
420 base::trace_event::MemoryAllocatorDump* dump =
421 pmd->CreateAllocatorDump(SpdyStringPrintf(
422 "%s/spdy_session_pool", parent_dump_absolute_name.c_str()));
423 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
424 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
425 total_size);
426 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
427 base::trace_event::MemoryAllocatorDump::kUnitsObjects,
428 sessions_.size());
429 dump->AddScalar("active_session_count",
430 base::trace_event::MemoryAllocatorDump::kUnitsObjects,
431 num_active_sessions);
432 dump->AddScalar("buffer_size",
433 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
434 buffer_size);
435 dump->AddScalar("cert_count",
436 base::trace_event::MemoryAllocatorDump::kUnitsObjects,
437 cert_count);
438 dump->AddScalar("cert_size",
439 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
440 cert_size);
441 }
442
443 bool SpdySessionPool::IsSessionAvailable(
444 const base::WeakPtr<SpdySession>& session) const {
445 for (AvailableSessionMap::const_iterator it = available_sessions_.begin();
446 it != available_sessions_.end(); ++it) {
447 if (it->second.get() == session.get())
448 return true;
449 }
450 return false;
451 }
452
453 void SpdySessionPool::MapKeyToAvailableSession(
454 const SpdySessionKey& key,
455 const base::WeakPtr<SpdySession>& session) {
456 DCHECK(base::ContainsKey(sessions_, session.get()));
457 std::pair<AvailableSessionMap::iterator, bool> result =
458 available_sessions_.insert(std::make_pair(key, session));
459 CHECK(result.second);
460 }
461
462 SpdySessionPool::AvailableSessionMap::iterator
463 SpdySessionPool::LookupAvailableSessionByKey(
464 const SpdySessionKey& key) {
465 return available_sessions_.find(key);
466 }
467
468 void SpdySessionPool::UnmapKey(const SpdySessionKey& key) {
469 AvailableSessionMap::iterator it = LookupAvailableSessionByKey(key);
470 CHECK(it != available_sessions_.end());
471 available_sessions_.erase(it);
472 }
473
474 void SpdySessionPool::RemoveAliases(const SpdySessionKey& key) {
475 // Walk the aliases map, find references to this pair.
476 // TODO(mbelshe): Figure out if this is too expensive.
477 for (AliasMap::iterator it = aliases_.begin(); it != aliases_.end(); ) {
478 if (it->second.Equals(key)) {
479 AliasMap::iterator old_it = it;
480 ++it;
481 aliases_.erase(old_it);
482 } else {
483 ++it;
484 }
485 }
486 }
487
488 SpdySessionPool::WeakSessionList SpdySessionPool::GetCurrentSessions() const {
489 WeakSessionList current_sessions;
490 for (SessionSet::const_iterator it = sessions_.begin();
491 it != sessions_.end(); ++it) {
492 current_sessions.push_back((*it)->GetWeakPtr());
493 }
494 return current_sessions;
495 }
496
497 void SpdySessionPool::CloseCurrentSessionsHelper(Error error,
498 const SpdyString& description,
499 bool idle_only) {
500 WeakSessionList current_sessions = GetCurrentSessions();
501 for (WeakSessionList::const_iterator it = current_sessions.begin();
502 it != current_sessions.end(); ++it) {
503 if (!*it)
504 continue;
505
506 if (idle_only && (*it)->is_active())
507 continue;
508
509 (*it)->CloseSessionOnError(error, description);
510 DCHECK(!IsSessionAvailable(*it));
511 }
512 }
513
514 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/spdy_session_pool.h ('k') | net/spdy/spdy_session_pool_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698