OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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 <cstddef> | |
8 #include <memory> | |
9 #include <utility> | |
10 | |
11 #include "base/memory/ptr_util.h" | |
12 #include "base/memory/ref_counted.h" | |
13 #include "base/run_loop.h" | |
14 #include "base/test/histogram_tester.h" | |
15 #include "base/trace_event/memory_allocator_dump.h" | |
16 #include "base/trace_event/process_memory_dump.h" | |
17 #include "base/trace_event/trace_event_argument.h" | |
18 #include "net/dns/host_cache.h" | |
19 #include "net/http/http_network_session.h" | |
20 #include "net/log/net_log_with_source.h" | |
21 #include "net/log/test_net_log.h" | |
22 #include "net/log/test_net_log_entry.h" | |
23 #include "net/socket/client_socket_handle.h" | |
24 #include "net/socket/transport_client_socket_pool.h" | |
25 #include "net/spdy/spdy_session.h" | |
26 #include "net/spdy/spdy_stream_test_util.h" | |
27 #include "net/spdy/spdy_test_util_common.h" | |
28 #include "net/test/cert_test_util.h" | |
29 #include "net/test/gtest_util.h" | |
30 #include "net/test/test_data_directory.h" | |
31 #include "testing/gmock/include/gmock/gmock.h" | |
32 #include "testing/gtest/include/gtest/gtest.h" | |
33 | |
34 using net::test::IsError; | |
35 using net::test::IsOk; | |
36 | |
37 namespace net { | |
38 | |
39 class SpdySessionPoolTest : public ::testing::Test { | |
40 protected: | |
41 // Used by RunIPPoolingTest(). | |
42 enum SpdyPoolCloseSessionsType { | |
43 SPDY_POOL_CLOSE_SESSIONS_MANUALLY, | |
44 SPDY_POOL_CLOSE_CURRENT_SESSIONS, | |
45 SPDY_POOL_CLOSE_IDLE_SESSIONS, | |
46 }; | |
47 | |
48 SpdySessionPoolTest() : spdy_session_pool_(NULL) {} | |
49 | |
50 void CreateNetworkSession() { | |
51 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); | |
52 spdy_session_pool_ = http_session_->spdy_session_pool(); | |
53 } | |
54 | |
55 void AddSSLSocketData() { | |
56 auto ssl = base::MakeUnique<SSLSocketDataProvider>(SYNCHRONOUS, OK); | |
57 ssl->cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); | |
58 ASSERT_TRUE(ssl->cert); | |
59 session_deps_.socket_factory->AddSSLSocketDataProvider(ssl.get()); | |
60 ssl_data_vector_.push_back(std::move(ssl)); | |
61 } | |
62 | |
63 void RunIPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type); | |
64 | |
65 SpdySessionDependencies session_deps_; | |
66 std::unique_ptr<HttpNetworkSession> http_session_; | |
67 SpdySessionPool* spdy_session_pool_; | |
68 std::vector<std::unique_ptr<SSLSocketDataProvider>> ssl_data_vector_; | |
69 }; | |
70 | |
71 // A delegate that opens a new session when it is closed. | |
72 class SessionOpeningDelegate : public SpdyStream::Delegate { | |
73 public: | |
74 SessionOpeningDelegate(SpdySessionPool* spdy_session_pool, | |
75 const SpdySessionKey& key) | |
76 : spdy_session_pool_(spdy_session_pool), | |
77 key_(key) {} | |
78 | |
79 ~SessionOpeningDelegate() override {} | |
80 | |
81 void OnHeadersSent() override {} | |
82 | |
83 void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override {} | |
84 | |
85 void OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) override {} | |
86 | |
87 void OnDataSent() override {} | |
88 | |
89 void OnTrailers(const SpdyHeaderBlock& trailers) override {} | |
90 | |
91 void OnClose(int status) override { | |
92 ignore_result(CreateFakeSpdySession(spdy_session_pool_, key_)); | |
93 } | |
94 | |
95 NetLogSource source_dependency() const override { return NetLogSource(); } | |
96 | |
97 private: | |
98 SpdySessionPool* const spdy_session_pool_; | |
99 const SpdySessionKey key_; | |
100 }; | |
101 | |
102 // Set up a SpdyStream to create a new session when it is closed. | |
103 // CloseCurrentSessions should not close the newly-created session. | |
104 TEST_F(SpdySessionPoolTest, CloseCurrentSessions) { | |
105 const char kTestHost[] = "www.foo.com"; | |
106 const int kTestPort = 80; | |
107 | |
108 session_deps_.host_resolver->set_synchronous_mode(true); | |
109 | |
110 HostPortPair test_host_port_pair(kTestHost, kTestPort); | |
111 SpdySessionKey test_key = | |
112 SpdySessionKey( | |
113 test_host_port_pair, ProxyServer::Direct(), | |
114 PRIVACY_MODE_DISABLED); | |
115 | |
116 MockConnect connect_data(SYNCHRONOUS, OK); | |
117 MockRead reads[] = { | |
118 MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. | |
119 }; | |
120 | |
121 StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); | |
122 data.set_connect_data(connect_data); | |
123 session_deps_.socket_factory->AddSocketDataProvider(&data); | |
124 | |
125 SSLSocketDataProvider ssl(SYNCHRONOUS, OK); | |
126 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); | |
127 | |
128 CreateNetworkSession(); | |
129 | |
130 // Setup the first session to the first host. | |
131 base::WeakPtr<SpdySession> session = CreateSecureSpdySession( | |
132 http_session_.get(), test_key, NetLogWithSource()); | |
133 | |
134 // Flush the SpdySession::OnReadComplete() task. | |
135 base::RunLoop().RunUntilIdle(); | |
136 | |
137 // Verify that we have sessions for everything. | |
138 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key)); | |
139 | |
140 // Set the stream to create a new session when it is closed. | |
141 base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously( | |
142 SPDY_BIDIRECTIONAL_STREAM, session, GURL("http://www.foo.com"), MEDIUM, | |
143 NetLogWithSource()); | |
144 SessionOpeningDelegate delegate(spdy_session_pool_, test_key); | |
145 spdy_stream->SetDelegate(&delegate); | |
146 | |
147 // Close the current session. | |
148 spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED); | |
149 | |
150 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key)); | |
151 } | |
152 | |
153 TEST_F(SpdySessionPoolTest, CloseCurrentIdleSessions) { | |
154 MockConnect connect_data(SYNCHRONOUS, OK); | |
155 MockRead reads[] = { | |
156 MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. | |
157 }; | |
158 | |
159 session_deps_.host_resolver->set_synchronous_mode(true); | |
160 | |
161 StaticSocketDataProvider data1(reads, arraysize(reads), nullptr, 0); | |
162 data1.set_connect_data(connect_data); | |
163 session_deps_.socket_factory->AddSocketDataProvider(&data1); | |
164 | |
165 AddSSLSocketData(); | |
166 AddSSLSocketData(); | |
167 AddSSLSocketData(); | |
168 | |
169 CreateNetworkSession(); | |
170 | |
171 // Set up session 1 | |
172 const SpdyString kTestHost1("www.example.org"); | |
173 HostPortPair test_host_port_pair1(kTestHost1, 80); | |
174 SpdySessionKey key1(test_host_port_pair1, ProxyServer::Direct(), | |
175 PRIVACY_MODE_DISABLED); | |
176 base::WeakPtr<SpdySession> session1 = | |
177 CreateSecureSpdySession(http_session_.get(), key1, NetLogWithSource()); | |
178 GURL url1(kTestHost1); | |
179 base::WeakPtr<SpdyStream> spdy_stream1 = CreateStreamSynchronously( | |
180 SPDY_BIDIRECTIONAL_STREAM, session1, url1, MEDIUM, NetLogWithSource()); | |
181 ASSERT_TRUE(spdy_stream1); | |
182 | |
183 // Set up session 2 | |
184 StaticSocketDataProvider data2(reads, arraysize(reads), nullptr, 0); | |
185 data2.set_connect_data(connect_data); | |
186 session_deps_.socket_factory->AddSocketDataProvider(&data2); | |
187 const SpdyString kTestHost2("mail.example.org"); | |
188 HostPortPair test_host_port_pair2(kTestHost2, 80); | |
189 SpdySessionKey key2(test_host_port_pair2, ProxyServer::Direct(), | |
190 PRIVACY_MODE_DISABLED); | |
191 base::WeakPtr<SpdySession> session2 = | |
192 CreateSecureSpdySession(http_session_.get(), key2, NetLogWithSource()); | |
193 GURL url2(kTestHost2); | |
194 base::WeakPtr<SpdyStream> spdy_stream2 = CreateStreamSynchronously( | |
195 SPDY_BIDIRECTIONAL_STREAM, session2, url2, MEDIUM, NetLogWithSource()); | |
196 ASSERT_TRUE(spdy_stream2); | |
197 | |
198 // Set up session 3 | |
199 StaticSocketDataProvider data3(reads, arraysize(reads), nullptr, 0); | |
200 data3.set_connect_data(connect_data); | |
201 session_deps_.socket_factory->AddSocketDataProvider(&data3); | |
202 const SpdyString kTestHost3("mail.example.com"); | |
203 HostPortPair test_host_port_pair3(kTestHost3, 80); | |
204 SpdySessionKey key3(test_host_port_pair3, ProxyServer::Direct(), | |
205 PRIVACY_MODE_DISABLED); | |
206 base::WeakPtr<SpdySession> session3 = | |
207 CreateSecureSpdySession(http_session_.get(), key3, NetLogWithSource()); | |
208 GURL url3(kTestHost3); | |
209 base::WeakPtr<SpdyStream> spdy_stream3 = CreateStreamSynchronously( | |
210 SPDY_BIDIRECTIONAL_STREAM, session3, url3, MEDIUM, NetLogWithSource()); | |
211 ASSERT_TRUE(spdy_stream3); | |
212 | |
213 // All sessions are active and not closed | |
214 EXPECT_TRUE(session1->is_active()); | |
215 EXPECT_TRUE(session1->IsAvailable()); | |
216 EXPECT_TRUE(session2->is_active()); | |
217 EXPECT_TRUE(session2->IsAvailable()); | |
218 EXPECT_TRUE(session3->is_active()); | |
219 EXPECT_TRUE(session3->IsAvailable()); | |
220 | |
221 // Should not do anything, all are active | |
222 spdy_session_pool_->CloseCurrentIdleSessions(); | |
223 EXPECT_TRUE(session1->is_active()); | |
224 EXPECT_TRUE(session1->IsAvailable()); | |
225 EXPECT_TRUE(session2->is_active()); | |
226 EXPECT_TRUE(session2->IsAvailable()); | |
227 EXPECT_TRUE(session3->is_active()); | |
228 EXPECT_TRUE(session3->IsAvailable()); | |
229 | |
230 // Make sessions 1 and 3 inactive, but keep them open. | |
231 // Session 2 still open and active | |
232 session1->CloseCreatedStream(spdy_stream1, OK); | |
233 EXPECT_FALSE(spdy_stream1); | |
234 session3->CloseCreatedStream(spdy_stream3, OK); | |
235 EXPECT_FALSE(spdy_stream3); | |
236 EXPECT_FALSE(session1->is_active()); | |
237 EXPECT_TRUE(session1->IsAvailable()); | |
238 EXPECT_TRUE(session2->is_active()); | |
239 EXPECT_TRUE(session2->IsAvailable()); | |
240 EXPECT_FALSE(session3->is_active()); | |
241 EXPECT_TRUE(session3->IsAvailable()); | |
242 | |
243 // Should close session 1 and 3, 2 should be left open | |
244 spdy_session_pool_->CloseCurrentIdleSessions(); | |
245 base::RunLoop().RunUntilIdle(); | |
246 | |
247 EXPECT_FALSE(session1); | |
248 EXPECT_TRUE(session2->is_active()); | |
249 EXPECT_TRUE(session2->IsAvailable()); | |
250 EXPECT_FALSE(session3); | |
251 | |
252 // Should not do anything | |
253 spdy_session_pool_->CloseCurrentIdleSessions(); | |
254 base::RunLoop().RunUntilIdle(); | |
255 | |
256 EXPECT_TRUE(session2->is_active()); | |
257 EXPECT_TRUE(session2->IsAvailable()); | |
258 | |
259 // Make 2 not active | |
260 session2->CloseCreatedStream(spdy_stream2, OK); | |
261 base::RunLoop().RunUntilIdle(); | |
262 | |
263 EXPECT_FALSE(spdy_stream2); | |
264 EXPECT_FALSE(session2->is_active()); | |
265 EXPECT_TRUE(session2->IsAvailable()); | |
266 | |
267 // This should close session 2 | |
268 spdy_session_pool_->CloseCurrentIdleSessions(); | |
269 base::RunLoop().RunUntilIdle(); | |
270 | |
271 EXPECT_FALSE(session2); | |
272 } | |
273 | |
274 // Set up a SpdyStream to create a new session when it is closed. | |
275 // CloseAllSessions should close the newly-created session. | |
276 TEST_F(SpdySessionPoolTest, CloseAllSessions) { | |
277 const char kTestHost[] = "www.foo.com"; | |
278 const int kTestPort = 80; | |
279 | |
280 session_deps_.host_resolver->set_synchronous_mode(true); | |
281 | |
282 HostPortPair test_host_port_pair(kTestHost, kTestPort); | |
283 SpdySessionKey test_key = | |
284 SpdySessionKey( | |
285 test_host_port_pair, ProxyServer::Direct(), | |
286 PRIVACY_MODE_DISABLED); | |
287 | |
288 MockConnect connect_data(SYNCHRONOUS, OK); | |
289 MockRead reads[] = { | |
290 MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. | |
291 }; | |
292 | |
293 StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); | |
294 data.set_connect_data(connect_data); | |
295 session_deps_.socket_factory->AddSocketDataProvider(&data); | |
296 | |
297 SSLSocketDataProvider ssl(SYNCHRONOUS, OK); | |
298 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); | |
299 | |
300 CreateNetworkSession(); | |
301 | |
302 // Setup the first session to the first host. | |
303 base::WeakPtr<SpdySession> session = CreateSecureSpdySession( | |
304 http_session_.get(), test_key, NetLogWithSource()); | |
305 | |
306 // Flush the SpdySession::OnReadComplete() task. | |
307 base::RunLoop().RunUntilIdle(); | |
308 | |
309 // Verify that we have sessions for everything. | |
310 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key)); | |
311 | |
312 // Set the stream to create a new session when it is closed. | |
313 base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously( | |
314 SPDY_BIDIRECTIONAL_STREAM, session, GURL("http://www.foo.com"), MEDIUM, | |
315 NetLogWithSource()); | |
316 SessionOpeningDelegate delegate(spdy_session_pool_, test_key); | |
317 spdy_stream->SetDelegate(&delegate); | |
318 | |
319 // Close the current session. | |
320 spdy_session_pool_->CloseAllSessions(); | |
321 | |
322 EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_key)); | |
323 } | |
324 | |
325 // This test has three variants, one for each style of closing the connection. | |
326 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_SESSIONS_MANUALLY, | |
327 // the sessions are closed manually, calling SpdySessionPool::Remove() directly. | |
328 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_CURRENT_SESSIONS, | |
329 // sessions are closed with SpdySessionPool::CloseCurrentSessions(). | |
330 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_IDLE_SESSIONS, | |
331 // sessions are closed with SpdySessionPool::CloseIdleSessions(). | |
332 void SpdySessionPoolTest::RunIPPoolingTest( | |
333 SpdyPoolCloseSessionsType close_sessions_type) { | |
334 const int kTestPort = 80; | |
335 struct TestHosts { | |
336 SpdyString url; | |
337 SpdyString name; | |
338 SpdyString iplist; | |
339 SpdySessionKey key; | |
340 AddressList addresses; | |
341 } test_hosts[] = { | |
342 {"http:://www.example.org", "www.example.org", | |
343 "192.0.2.33,192.168.0.1,192.168.0.5"}, | |
344 {"http://mail.example.org", "mail.example.org", | |
345 "192.168.0.2,192.168.0.3,192.168.0.5,192.0.2.33"}, | |
346 {"http://mail.example.com", "mail.example.com", | |
347 "192.168.0.4,192.168.0.3"}, | |
348 }; | |
349 | |
350 session_deps_.host_resolver->set_synchronous_mode(true); | |
351 std::unique_ptr<HostResolver::Request> request[arraysize(test_hosts)]; | |
352 for (size_t i = 0; i < arraysize(test_hosts); i++) { | |
353 session_deps_.host_resolver->rules()->AddIPLiteralRule( | |
354 test_hosts[i].name, test_hosts[i].iplist, SpdyString()); | |
355 | |
356 // This test requires that the HostResolver cache be populated. Normal | |
357 // code would have done this already, but we do it manually. | |
358 HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); | |
359 session_deps_.host_resolver->Resolve( | |
360 info, DEFAULT_PRIORITY, &test_hosts[i].addresses, CompletionCallback(), | |
361 &request[i], NetLogWithSource()); | |
362 | |
363 // Setup a SpdySessionKey | |
364 test_hosts[i].key = SpdySessionKey( | |
365 HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct(), | |
366 PRIVACY_MODE_DISABLED); | |
367 } | |
368 | |
369 MockConnect connect_data(SYNCHRONOUS, OK); | |
370 MockRead reads[] = { | |
371 MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. | |
372 }; | |
373 | |
374 StaticSocketDataProvider data1(reads, arraysize(reads), NULL, 0); | |
375 data1.set_connect_data(connect_data); | |
376 session_deps_.socket_factory->AddSocketDataProvider(&data1); | |
377 | |
378 AddSSLSocketData(); | |
379 | |
380 CreateNetworkSession(); | |
381 | |
382 // Setup the first session to the first host. | |
383 base::WeakPtr<SpdySession> session = CreateSecureSpdySession( | |
384 http_session_.get(), test_hosts[0].key, NetLogWithSource()); | |
385 | |
386 // Flush the SpdySession::OnReadComplete() task. | |
387 base::RunLoop().RunUntilIdle(); | |
388 | |
389 // The third host has no overlap with the first, so it can't pool IPs. | |
390 EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key)); | |
391 | |
392 // The second host overlaps with the first, and should IP pool. | |
393 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key)); | |
394 | |
395 // However, if IP pooling is disabled, FindAvailableSession() should not find | |
396 // |session| for the second host. | |
397 base::WeakPtr<SpdySession> session1 = | |
398 spdy_session_pool_->FindAvailableSession( | |
399 test_hosts[1].key, GURL(test_hosts[1].url), | |
400 /* enable_ip_based_pooling = */ false, NetLogWithSource()); | |
401 EXPECT_FALSE(session1); | |
402 | |
403 // Verify that the second host, through a proxy, won't share the IP. | |
404 SpdySessionKey proxy_key(test_hosts[1].key.host_port_pair(), | |
405 ProxyServer::FromPacString("HTTP http://proxy.foo.com/"), | |
406 PRIVACY_MODE_DISABLED); | |
407 EXPECT_FALSE(HasSpdySession(spdy_session_pool_, proxy_key)); | |
408 | |
409 // Overlap between 2 and 3 does is not transitive to 1. | |
410 EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key)); | |
411 | |
412 // Create a new session to host 2. | |
413 StaticSocketDataProvider data2(reads, arraysize(reads), NULL, 0); | |
414 data2.set_connect_data(connect_data); | |
415 session_deps_.socket_factory->AddSocketDataProvider(&data2); | |
416 | |
417 AddSSLSocketData(); | |
418 | |
419 base::WeakPtr<SpdySession> session2 = CreateSecureSpdySession( | |
420 http_session_.get(), test_hosts[2].key, NetLogWithSource()); | |
421 | |
422 // Verify that we have sessions for everything. | |
423 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[0].key)); | |
424 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key)); | |
425 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[2].key)); | |
426 | |
427 // Grab the session to host 1 and verify that it is the same session | |
428 // we got with host 0, and that is a different from host 2's session. | |
429 session1 = spdy_session_pool_->FindAvailableSession( | |
430 test_hosts[1].key, GURL(test_hosts[1].url), | |
431 /* enable_ip_based_pooling = */ true, NetLogWithSource()); | |
432 EXPECT_EQ(session.get(), session1.get()); | |
433 EXPECT_NE(session2.get(), session1.get()); | |
434 | |
435 // Remove the aliases and observe that we still have a session for host1. | |
436 SpdySessionPoolPeer pool_peer(spdy_session_pool_); | |
437 pool_peer.RemoveAliases(test_hosts[0].key); | |
438 pool_peer.RemoveAliases(test_hosts[1].key); | |
439 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key)); | |
440 | |
441 // Expire the host cache | |
442 session_deps_.host_resolver->GetHostCache()->clear(); | |
443 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key)); | |
444 | |
445 // Cleanup the sessions. | |
446 switch (close_sessions_type) { | |
447 case SPDY_POOL_CLOSE_SESSIONS_MANUALLY: | |
448 session->CloseSessionOnError(ERR_ABORTED, SpdyString()); | |
449 session2->CloseSessionOnError(ERR_ABORTED, SpdyString()); | |
450 base::RunLoop().RunUntilIdle(); | |
451 EXPECT_FALSE(session); | |
452 EXPECT_FALSE(session2); | |
453 break; | |
454 case SPDY_POOL_CLOSE_CURRENT_SESSIONS: | |
455 spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED); | |
456 break; | |
457 case SPDY_POOL_CLOSE_IDLE_SESSIONS: | |
458 GURL url(test_hosts[0].url); | |
459 base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously( | |
460 SPDY_BIDIRECTIONAL_STREAM, session, url, MEDIUM, NetLogWithSource()); | |
461 GURL url1(test_hosts[1].url); | |
462 base::WeakPtr<SpdyStream> spdy_stream1 = | |
463 CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, session1, url1, | |
464 MEDIUM, NetLogWithSource()); | |
465 GURL url2(test_hosts[2].url); | |
466 base::WeakPtr<SpdyStream> spdy_stream2 = | |
467 CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, session2, url2, | |
468 MEDIUM, NetLogWithSource()); | |
469 | |
470 // Close streams to make spdy_session and spdy_session1 inactive. | |
471 session->CloseCreatedStream(spdy_stream, OK); | |
472 EXPECT_FALSE(spdy_stream); | |
473 session1->CloseCreatedStream(spdy_stream1, OK); | |
474 EXPECT_FALSE(spdy_stream1); | |
475 | |
476 // Check spdy_session and spdy_session1 are not closed. | |
477 EXPECT_FALSE(session->is_active()); | |
478 EXPECT_TRUE(session->IsAvailable()); | |
479 EXPECT_FALSE(session1->is_active()); | |
480 EXPECT_TRUE(session1->IsAvailable()); | |
481 EXPECT_TRUE(session2->is_active()); | |
482 EXPECT_TRUE(session2->IsAvailable()); | |
483 | |
484 // Test that calling CloseIdleSessions, does not cause a crash. | |
485 // http://crbug.com/181400 | |
486 spdy_session_pool_->CloseCurrentIdleSessions(); | |
487 base::RunLoop().RunUntilIdle(); | |
488 | |
489 // Verify spdy_session and spdy_session1 are closed. | |
490 EXPECT_FALSE(session); | |
491 EXPECT_FALSE(session1); | |
492 EXPECT_TRUE(session2->is_active()); | |
493 EXPECT_TRUE(session2->IsAvailable()); | |
494 | |
495 spdy_stream2->Cancel(); | |
496 EXPECT_FALSE(spdy_stream); | |
497 EXPECT_FALSE(spdy_stream1); | |
498 EXPECT_FALSE(spdy_stream2); | |
499 | |
500 session2->CloseSessionOnError(ERR_ABORTED, SpdyString()); | |
501 base::RunLoop().RunUntilIdle(); | |
502 EXPECT_FALSE(session2); | |
503 break; | |
504 } | |
505 | |
506 // Verify that the map is all cleaned up. | |
507 EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[0].key)); | |
508 EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[1].key)); | |
509 EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key)); | |
510 } | |
511 | |
512 TEST_F(SpdySessionPoolTest, IPPooling) { | |
513 RunIPPoolingTest(SPDY_POOL_CLOSE_SESSIONS_MANUALLY); | |
514 } | |
515 | |
516 TEST_F(SpdySessionPoolTest, IPPoolingCloseCurrentSessions) { | |
517 RunIPPoolingTest(SPDY_POOL_CLOSE_CURRENT_SESSIONS); | |
518 } | |
519 | |
520 TEST_F(SpdySessionPoolTest, IPPoolingCloseIdleSessions) { | |
521 RunIPPoolingTest(SPDY_POOL_CLOSE_IDLE_SESSIONS); | |
522 } | |
523 | |
524 // Regression test for https://crbug.com/643025. | |
525 TEST_F(SpdySessionPoolTest, IPPoolingNetLog) { | |
526 // Define two hosts with identical IP address. | |
527 const int kTestPort = 443; | |
528 struct TestHosts { | |
529 SpdyString name; | |
530 SpdyString iplist; | |
531 SpdySessionKey key; | |
532 AddressList addresses; | |
533 std::unique_ptr<HostResolver::Request> request; | |
534 } test_hosts[] = { | |
535 {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"}, | |
536 }; | |
537 | |
538 // Populate the HostResolver cache. | |
539 session_deps_.host_resolver->set_synchronous_mode(true); | |
540 for (size_t i = 0; i < arraysize(test_hosts); i++) { | |
541 session_deps_.host_resolver->rules()->AddIPLiteralRule( | |
542 test_hosts[i].name, test_hosts[i].iplist, SpdyString()); | |
543 | |
544 HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); | |
545 session_deps_.host_resolver->Resolve( | |
546 info, DEFAULT_PRIORITY, &test_hosts[i].addresses, CompletionCallback(), | |
547 &test_hosts[i].request, NetLogWithSource()); | |
548 | |
549 test_hosts[i].key = | |
550 SpdySessionKey(HostPortPair(test_hosts[i].name, kTestPort), | |
551 ProxyServer::Direct(), PRIVACY_MODE_DISABLED); | |
552 } | |
553 | |
554 MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; | |
555 StaticSocketDataProvider data(reads, arraysize(reads), nullptr, 0); | |
556 MockConnect connect_data(SYNCHRONOUS, OK); | |
557 data.set_connect_data(connect_data); | |
558 | |
559 session_deps_.socket_factory->AddSocketDataProvider(&data); | |
560 AddSSLSocketData(); | |
561 | |
562 CreateNetworkSession(); | |
563 | |
564 // Open SpdySession to the first host. | |
565 base::WeakPtr<SpdySession> session0 = CreateSecureSpdySession( | |
566 http_session_.get(), test_hosts[0].key, NetLogWithSource()); | |
567 | |
568 // A request to the second host should pool to the existing connection. | |
569 BoundTestNetLog net_log; | |
570 base::HistogramTester histogram_tester; | |
571 base::WeakPtr<SpdySession> session1 = | |
572 spdy_session_pool_->FindAvailableSession( | |
573 test_hosts[1].key, GURL(), | |
574 /* enable_ip_based_pooling = */ true, net_log.bound()); | |
575 EXPECT_EQ(session0.get(), session1.get()); | |
576 | |
577 ASSERT_EQ(1u, net_log.GetSize()); | |
578 histogram_tester.ExpectTotalCount("Net.SpdySessionGet", 1); | |
579 | |
580 // A request to the second host should still pool to the existing connection. | |
581 session1 = spdy_session_pool_->FindAvailableSession( | |
582 test_hosts[1].key, GURL(), | |
583 /* enable_ip_based_pooling = */ true, net_log.bound()); | |
584 EXPECT_EQ(session0.get(), session1.get()); | |
585 | |
586 ASSERT_EQ(2u, net_log.GetSize()); | |
587 histogram_tester.ExpectTotalCount("Net.SpdySessionGet", 2); | |
588 | |
589 // Both FindAvailableSession() calls should log netlog events | |
590 // indicating IP pooling. | |
591 TestNetLogEntry::List entry_list; | |
592 net_log.GetEntries(&entry_list); | |
593 EXPECT_EQ( | |
594 NetLogEventType::HTTP2_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL, | |
595 entry_list[0].type); | |
596 EXPECT_EQ( | |
597 NetLogEventType::HTTP2_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL, | |
598 entry_list[1].type); | |
599 | |
600 // Both FindAvailableSession() calls should log histogram entries | |
601 // indicating IP pooling. | |
602 histogram_tester.ExpectUniqueSample("Net.SpdySessionGet", 2, 2); | |
603 } | |
604 | |
605 TEST_F(SpdySessionPoolTest, IPPoolingDisabled) { | |
606 // Define two hosts with identical IP address. | |
607 const int kTestPort = 443; | |
608 struct TestHosts { | |
609 SpdyString name; | |
610 SpdyString iplist; | |
611 SpdySessionKey key; | |
612 AddressList addresses; | |
613 std::unique_ptr<HostResolver::Request> request; | |
614 } test_hosts[] = { | |
615 {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"}, | |
616 }; | |
617 | |
618 // Populate the HostResolver cache. | |
619 session_deps_.host_resolver->set_synchronous_mode(true); | |
620 for (size_t i = 0; i < arraysize(test_hosts); i++) { | |
621 session_deps_.host_resolver->rules()->AddIPLiteralRule( | |
622 test_hosts[i].name, test_hosts[i].iplist, SpdyString()); | |
623 | |
624 HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); | |
625 session_deps_.host_resolver->Resolve( | |
626 info, DEFAULT_PRIORITY, &test_hosts[i].addresses, CompletionCallback(), | |
627 &test_hosts[i].request, NetLogWithSource()); | |
628 | |
629 test_hosts[i].key = | |
630 SpdySessionKey(HostPortPair(test_hosts[i].name, kTestPort), | |
631 ProxyServer::Direct(), PRIVACY_MODE_DISABLED); | |
632 } | |
633 | |
634 MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; | |
635 StaticSocketDataProvider data(reads, arraysize(reads), nullptr, 0); | |
636 MockConnect connect_data(SYNCHRONOUS, OK); | |
637 data.set_connect_data(connect_data); | |
638 session_deps_.socket_factory->AddSocketDataProvider(&data); | |
639 AddSSLSocketData(); | |
640 | |
641 MockRead reads1[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; | |
642 StaticSocketDataProvider data1(reads1, arraysize(reads1), nullptr, 0); | |
643 MockConnect connect_data1(SYNCHRONOUS, OK); | |
644 data1.set_connect_data(connect_data1); | |
645 session_deps_.socket_factory->AddSocketDataProvider(&data1); | |
646 AddSSLSocketData(); | |
647 | |
648 CreateNetworkSession(); | |
649 | |
650 // Open SpdySession to the first host. | |
651 base::WeakPtr<SpdySession> session0 = CreateSecureSpdySession( | |
652 http_session_.get(), test_hosts[0].key, NetLogWithSource()); | |
653 | |
654 // A request to the second host should pool to the existing connection. | |
655 base::WeakPtr<SpdySession> session1 = | |
656 spdy_session_pool_->FindAvailableSession( | |
657 test_hosts[1].key, GURL(), | |
658 /* enable_ip_based_pooling = */ true, NetLogWithSource()); | |
659 EXPECT_EQ(session0.get(), session1.get()); | |
660 | |
661 // A request to the second host should not pool to the existing connection if | |
662 // IP based pooling is disabled. | |
663 session1 = spdy_session_pool_->FindAvailableSession( | |
664 test_hosts[1].key, GURL(), | |
665 /* enable_ip_based_pooling = */ false, NetLogWithSource()); | |
666 EXPECT_FALSE(session1); | |
667 | |
668 // It should be possible to open a new SpdySession, even if a previous call to | |
669 // FindAvailableSession() linked the second key to the first connection in the | |
670 // IP pooled bucket of SpdySessionPool::available_session_map_. | |
671 session1 = CreateSecureSpdySessionWithIpBasedPoolingDisabled( | |
672 http_session_.get(), test_hosts[1].key, NetLogWithSource()); | |
673 EXPECT_TRUE(session1); | |
674 EXPECT_NE(session0.get(), session1.get()); | |
675 } | |
676 | |
677 // Construct a Pool with SpdySessions in various availability states. Simulate | |
678 // an IP address change. Ensure sessions gracefully shut down. Regression test | |
679 // for crbug.com/379469. | |
680 TEST_F(SpdySessionPoolTest, IPAddressChanged) { | |
681 MockConnect connect_data(SYNCHRONOUS, OK); | |
682 session_deps_.host_resolver->set_synchronous_mode(true); | |
683 | |
684 // This isn't testing anything having to do with SPDY frames; we | |
685 // can ignore issues of how dependencies are set. We default to | |
686 // setting them (when doing the appropriate protocol) since that's | |
687 // where we're eventually headed for all HTTP/2 connections. | |
688 SpdyTestUtil spdy_util; | |
689 | |
690 MockRead reads[] = { | |
691 MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. | |
692 }; | |
693 SpdySerializedFrame req( | |
694 spdy_util.ConstructSpdyGet("http://www.example.org", 1, MEDIUM)); | |
695 MockWrite writes[] = {CreateMockWrite(req, 1)}; | |
696 | |
697 StaticSocketDataProvider dataA(reads, arraysize(reads), writes, | |
698 arraysize(writes)); | |
699 dataA.set_connect_data(connect_data); | |
700 session_deps_.socket_factory->AddSocketDataProvider(&dataA); | |
701 | |
702 AddSSLSocketData(); | |
703 | |
704 CreateNetworkSession(); | |
705 | |
706 // Set up session A: Going away, but with an active stream. | |
707 const SpdyString kTestHostA("www.example.org"); | |
708 HostPortPair test_host_port_pairA(kTestHostA, 80); | |
709 SpdySessionKey keyA( | |
710 test_host_port_pairA, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); | |
711 base::WeakPtr<SpdySession> sessionA = | |
712 CreateSecureSpdySession(http_session_.get(), keyA, NetLogWithSource()); | |
713 | |
714 GURL urlA("http://www.example.org"); | |
715 base::WeakPtr<SpdyStream> spdy_streamA = CreateStreamSynchronously( | |
716 SPDY_BIDIRECTIONAL_STREAM, sessionA, urlA, MEDIUM, NetLogWithSource()); | |
717 test::StreamDelegateDoNothing delegateA(spdy_streamA); | |
718 spdy_streamA->SetDelegate(&delegateA); | |
719 | |
720 SpdyHeaderBlock headers(spdy_util.ConstructGetHeaderBlock(urlA.spec())); | |
721 spdy_streamA->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND); | |
722 | |
723 base::RunLoop().RunUntilIdle(); // Allow headers to write. | |
724 EXPECT_TRUE(delegateA.send_headers_completed()); | |
725 | |
726 sessionA->MakeUnavailable(); | |
727 EXPECT_TRUE(sessionA->IsGoingAway()); | |
728 EXPECT_FALSE(delegateA.StreamIsClosed()); | |
729 | |
730 // Set up session B: Available, with a created stream. | |
731 StaticSocketDataProvider dataB(reads, arraysize(reads), writes, | |
732 arraysize(writes)); | |
733 dataB.set_connect_data(connect_data); | |
734 session_deps_.socket_factory->AddSocketDataProvider(&dataB); | |
735 | |
736 AddSSLSocketData(); | |
737 | |
738 const SpdyString kTestHostB("mail.example.org"); | |
739 HostPortPair test_host_port_pairB(kTestHostB, 80); | |
740 SpdySessionKey keyB( | |
741 test_host_port_pairB, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); | |
742 base::WeakPtr<SpdySession> sessionB = | |
743 CreateSecureSpdySession(http_session_.get(), keyB, NetLogWithSource()); | |
744 EXPECT_TRUE(sessionB->IsAvailable()); | |
745 | |
746 GURL urlB("http://mail.example.org"); | |
747 base::WeakPtr<SpdyStream> spdy_streamB = CreateStreamSynchronously( | |
748 SPDY_BIDIRECTIONAL_STREAM, sessionB, urlB, MEDIUM, NetLogWithSource()); | |
749 test::StreamDelegateDoNothing delegateB(spdy_streamB); | |
750 spdy_streamB->SetDelegate(&delegateB); | |
751 | |
752 // Set up session C: Draining. | |
753 StaticSocketDataProvider dataC(reads, arraysize(reads), writes, | |
754 arraysize(writes)); | |
755 dataC.set_connect_data(connect_data); | |
756 session_deps_.socket_factory->AddSocketDataProvider(&dataC); | |
757 | |
758 AddSSLSocketData(); | |
759 | |
760 const SpdyString kTestHostC("mail.example.com"); | |
761 HostPortPair test_host_port_pairC(kTestHostC, 80); | |
762 SpdySessionKey keyC( | |
763 test_host_port_pairC, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); | |
764 base::WeakPtr<SpdySession> sessionC = | |
765 CreateSecureSpdySession(http_session_.get(), keyC, NetLogWithSource()); | |
766 | |
767 sessionC->CloseSessionOnError(ERR_SPDY_PROTOCOL_ERROR, "Error!"); | |
768 EXPECT_TRUE(sessionC->IsDraining()); | |
769 | |
770 spdy_session_pool_->OnIPAddressChanged(); | |
771 | |
772 #if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS) | |
773 EXPECT_TRUE(sessionA->IsGoingAway()); | |
774 EXPECT_TRUE(sessionB->IsDraining()); | |
775 EXPECT_TRUE(sessionC->IsDraining()); | |
776 | |
777 EXPECT_EQ(1u, | |
778 sessionA->num_active_streams()); // Active stream is still active. | |
779 EXPECT_FALSE(delegateA.StreamIsClosed()); | |
780 | |
781 EXPECT_TRUE(delegateB.StreamIsClosed()); // Created stream was closed. | |
782 EXPECT_THAT(delegateB.WaitForClose(), IsError(ERR_NETWORK_CHANGED)); | |
783 | |
784 sessionA->CloseSessionOnError(ERR_ABORTED, "Closing"); | |
785 sessionB->CloseSessionOnError(ERR_ABORTED, "Closing"); | |
786 | |
787 EXPECT_TRUE(delegateA.StreamIsClosed()); | |
788 EXPECT_THAT(delegateA.WaitForClose(), IsError(ERR_ABORTED)); | |
789 #else | |
790 EXPECT_TRUE(sessionA->IsDraining()); | |
791 EXPECT_TRUE(sessionB->IsDraining()); | |
792 EXPECT_TRUE(sessionC->IsDraining()); | |
793 | |
794 // Both streams were closed with an error. | |
795 EXPECT_TRUE(delegateA.StreamIsClosed()); | |
796 EXPECT_THAT(delegateA.WaitForClose(), IsError(ERR_NETWORK_CHANGED)); | |
797 EXPECT_TRUE(delegateB.StreamIsClosed()); | |
798 EXPECT_THAT(delegateB.WaitForClose(), IsError(ERR_NETWORK_CHANGED)); | |
799 #endif // defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS) | |
800 } | |
801 | |
802 TEST_F(SpdySessionPoolTest, FindAvailableSession) { | |
803 SpdySessionKey key(HostPortPair("https://www.example.org", 443), | |
804 ProxyServer::Direct(), PRIVACY_MODE_DISABLED); | |
805 | |
806 MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; | |
807 StaticSocketDataProvider data(reads, arraysize(reads), nullptr, 0); | |
808 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
809 session_deps_.socket_factory->AddSocketDataProvider(&data); | |
810 | |
811 SSLSocketDataProvider ssl(SYNCHRONOUS, OK); | |
812 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); | |
813 | |
814 CreateNetworkSession(); | |
815 | |
816 base::WeakPtr<SpdySession> session = | |
817 CreateSecureSpdySession(http_session_.get(), key, NetLogWithSource()); | |
818 | |
819 // Flush the SpdySession::OnReadComplete() task. | |
820 base::RunLoop().RunUntilIdle(); | |
821 | |
822 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key)); | |
823 | |
824 // FindAvailableSession should return |session| if called with empty |url|. | |
825 base::WeakPtr<SpdySession> session1 = | |
826 spdy_session_pool_->FindAvailableSession( | |
827 key, GURL(), | |
828 /* enable_ip_based_pooling = */ true, NetLogWithSource()); | |
829 EXPECT_EQ(session.get(), session1.get()); | |
830 | |
831 // FindAvailableSession should return |session| if called with |url| for which | |
832 // there is no pushed stream on any sessions owned by |spdy_session_pool_|. | |
833 base::WeakPtr<SpdySession> session2 = | |
834 spdy_session_pool_->FindAvailableSession( | |
835 key, GURL("http://news.example.org/foo.html"), | |
836 /* enable_ip_based_pooling = */ true, NetLogWithSource()); | |
837 EXPECT_EQ(session.get(), session2.get()); | |
838 | |
839 spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED); | |
840 } | |
841 | |
842 class SpdySessionMemoryDumpTest | |
843 : public SpdySessionPoolTest, | |
844 public testing::WithParamInterface< | |
845 base::trace_event::MemoryDumpLevelOfDetail> {}; | |
846 | |
847 INSTANTIATE_TEST_CASE_P( | |
848 /* no prefix */, | |
849 SpdySessionMemoryDumpTest, | |
850 ::testing::Values(base::trace_event::MemoryDumpLevelOfDetail::DETAILED, | |
851 base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND)); | |
852 | |
853 TEST_P(SpdySessionMemoryDumpTest, DumpMemoryStats) { | |
854 SpdySessionKey key(HostPortPair("https://www.example.org", 443), | |
855 ProxyServer::Direct(), PRIVACY_MODE_DISABLED); | |
856 | |
857 MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; | |
858 StaticSocketDataProvider data(reads, arraysize(reads), nullptr, 0); | |
859 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
860 session_deps_.socket_factory->AddSocketDataProvider(&data); | |
861 | |
862 SSLSocketDataProvider ssl(SYNCHRONOUS, OK); | |
863 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); | |
864 | |
865 CreateNetworkSession(); | |
866 | |
867 base::WeakPtr<SpdySession> session = | |
868 CreateSecureSpdySession(http_session_.get(), key, NetLogWithSource()); | |
869 | |
870 // Flush the SpdySession::OnReadComplete() task. | |
871 base::RunLoop().RunUntilIdle(); | |
872 | |
873 EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key)); | |
874 base::trace_event::MemoryDumpArgs dump_args = {GetParam()}; | |
875 std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump( | |
876 new base::trace_event::ProcessMemoryDump(nullptr, dump_args)); | |
877 base::trace_event::MemoryAllocatorDump* parent_dump = | |
878 process_memory_dump->CreateAllocatorDump( | |
879 "net/http_network_session_0x123"); | |
880 spdy_session_pool_->DumpMemoryStats(process_memory_dump.get(), | |
881 parent_dump->absolute_name()); | |
882 | |
883 // Whether SpdySession::DumpMemoryStats() is invoked. | |
884 bool did_dump = false; | |
885 const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap& | |
886 allocator_dumps = process_memory_dump->allocator_dumps(); | |
887 for (const auto& pair : allocator_dumps) { | |
888 const SpdyString& dump_name = pair.first; | |
889 if (dump_name.find("spdy_session_pool") == SpdyString::npos) | |
890 continue; | |
891 std::unique_ptr<base::Value> raw_attrs = | |
892 pair.second->attributes_for_testing()->ToBaseValue(); | |
893 base::DictionaryValue* attrs; | |
894 ASSERT_TRUE(raw_attrs->GetAsDictionary(&attrs)); | |
895 base::DictionaryValue* active_session_count_attr; | |
896 ASSERT_TRUE(attrs->GetDictionary("active_session_count", | |
897 &active_session_count_attr)); | |
898 SpdyString active_session_count; | |
899 ASSERT_TRUE( | |
900 active_session_count_attr->GetString("value", &active_session_count)); | |
901 // No created stream so the session should be idle. | |
902 ASSERT_EQ("0", active_session_count); | |
903 did_dump = true; | |
904 } | |
905 EXPECT_TRUE(did_dump); | |
906 spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED); | |
907 } | |
908 | |
909 } // namespace net | |
OLD | NEW |