- add sources.
[platform/framework/web/crosswalk.git] / src / net / spdy / spdy_session_pool_unittest.cc
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 <string>
9
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "net/dns/host_cache.h"
13 #include "net/http/http_network_session.h"
14 #include "net/socket/client_socket_handle.h"
15 #include "net/socket/transport_client_socket_pool.h"
16 #include "net/spdy/spdy_session.h"
17 #include "net/spdy/spdy_test_util_common.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace net {
21
22 namespace {
23
24 class SpdySessionPoolTest : public ::testing::Test,
25                             public ::testing::WithParamInterface<NextProto> {
26  protected:
27   // Used by RunIPPoolingTest().
28   enum SpdyPoolCloseSessionsType {
29     SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
30     SPDY_POOL_CLOSE_CURRENT_SESSIONS,
31     SPDY_POOL_CLOSE_IDLE_SESSIONS,
32   };
33
34   SpdySessionPoolTest()
35       : session_deps_(GetParam()),
36         spdy_session_pool_(NULL) {}
37
38   void CreateNetworkSession() {
39     http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
40     spdy_session_pool_ = http_session_->spdy_session_pool();
41   }
42
43   void RunIPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type);
44
45   SpdySessionDependencies session_deps_;
46   scoped_refptr<HttpNetworkSession> http_session_;
47   SpdySessionPool* spdy_session_pool_;
48 };
49
50 INSTANTIATE_TEST_CASE_P(
51     NextProto,
52     SpdySessionPoolTest,
53     testing::Values(kProtoDeprecatedSPDY2,
54                     kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
55                     kProtoHTTP2Draft04));
56
57 // A delegate that opens a new session when it is closed.
58 class SessionOpeningDelegate : public SpdyStream::Delegate {
59  public:
60   SessionOpeningDelegate(SpdySessionPool* spdy_session_pool,
61                          const SpdySessionKey& key)
62       : spdy_session_pool_(spdy_session_pool),
63         key_(key) {}
64
65   virtual ~SessionOpeningDelegate() {}
66
67   virtual void OnRequestHeadersSent() OVERRIDE {}
68
69   virtual SpdyResponseHeadersStatus OnResponseHeadersUpdated(
70       const SpdyHeaderBlock& response_headers) OVERRIDE {
71     return RESPONSE_HEADERS_ARE_COMPLETE;
72   }
73
74   virtual void OnDataReceived(scoped_ptr<SpdyBuffer> buffer) OVERRIDE {}
75
76   virtual void OnDataSent() OVERRIDE {}
77
78   virtual void OnClose(int status) OVERRIDE {
79     ignore_result(CreateFakeSpdySession(spdy_session_pool_, key_));
80   }
81
82  private:
83   SpdySessionPool* const spdy_session_pool_;
84   const SpdySessionKey key_;
85 };
86
87 // Set up a SpdyStream to create a new session when it is closed.
88 // CloseCurrentSessions should not close the newly-created session.
89 TEST_P(SpdySessionPoolTest, CloseCurrentSessions) {
90   const char kTestHost[] = "www.foo.com";
91   const int kTestPort = 80;
92
93   session_deps_.host_resolver->set_synchronous_mode(true);
94
95   HostPortPair test_host_port_pair(kTestHost, kTestPort);
96   SpdySessionKey test_key =
97       SpdySessionKey(
98           test_host_port_pair, ProxyServer::Direct(),
99           kPrivacyModeDisabled);
100
101   MockConnect connect_data(SYNCHRONOUS, OK);
102   MockRead reads[] = {
103     MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
104   };
105
106   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
107   data.set_connect_data(connect_data);
108   session_deps_.socket_factory->AddSocketDataProvider(&data);
109
110   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
111   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
112
113   CreateNetworkSession();
114
115   // Setup the first session to the first host.
116   base::WeakPtr<SpdySession> session =
117       CreateInsecureSpdySession(http_session_, test_key, BoundNetLog());
118
119   // Flush the SpdySession::OnReadComplete() task.
120   base::MessageLoop::current()->RunUntilIdle();
121
122   // Verify that we have sessions for everything.
123   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
124
125   // Set the stream to create a new session when it is closed.
126   base::WeakPtr<SpdyStream> spdy_stream =
127       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
128                                 session, GURL("http://www.foo.com"),
129                                 MEDIUM, BoundNetLog());
130   SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
131   spdy_stream->SetDelegate(&delegate);
132
133   // Close the current session.
134   spdy_session_pool_->CloseCurrentSessions(net::ERR_ABORTED);
135
136   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
137 }
138
139 TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
140   MockConnect connect_data(SYNCHRONOUS, OK);
141   MockRead reads[] = {
142     MockRead(ASYNC, 0, 0)  // EOF
143   };
144
145   session_deps_.host_resolver->set_synchronous_mode(true);
146
147   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
148   data.set_connect_data(connect_data);
149   session_deps_.socket_factory->AddSocketDataProvider(&data);
150
151   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
152   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
153
154   CreateNetworkSession();
155
156   // Set up session 1
157   const std::string kTestHost1("http://www.a.com");
158   HostPortPair test_host_port_pair1(kTestHost1, 80);
159   SpdySessionKey key1(test_host_port_pair1, ProxyServer::Direct(),
160                       kPrivacyModeDisabled);
161   base::WeakPtr<SpdySession> session1 =
162       CreateInsecureSpdySession(http_session_, key1, BoundNetLog());
163   GURL url1(kTestHost1);
164   base::WeakPtr<SpdyStream> spdy_stream1 =
165       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
166                                 session1, url1, MEDIUM, BoundNetLog());
167   ASSERT_TRUE(spdy_stream1.get() != NULL);
168
169   // Set up session 2
170   session_deps_.socket_factory->AddSocketDataProvider(&data);
171   const std::string kTestHost2("http://www.b.com");
172   HostPortPair test_host_port_pair2(kTestHost2, 80);
173   SpdySessionKey key2(test_host_port_pair2, ProxyServer::Direct(),
174                       kPrivacyModeDisabled);
175   base::WeakPtr<SpdySession> session2 =
176       CreateInsecureSpdySession(http_session_, key2, BoundNetLog());
177   GURL url2(kTestHost2);
178   base::WeakPtr<SpdyStream> spdy_stream2 =
179       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
180                                 session2, url2, MEDIUM, BoundNetLog());
181   ASSERT_TRUE(spdy_stream2.get() != NULL);
182
183   // Set up session 3
184   session_deps_.socket_factory->AddSocketDataProvider(&data);
185   const std::string kTestHost3("http://www.c.com");
186   HostPortPair test_host_port_pair3(kTestHost3, 80);
187   SpdySessionKey key3(test_host_port_pair3, ProxyServer::Direct(),
188                       kPrivacyModeDisabled);
189   base::WeakPtr<SpdySession> session3 =
190       CreateInsecureSpdySession(http_session_, key3, BoundNetLog());
191   GURL url3(kTestHost3);
192   base::WeakPtr<SpdyStream> spdy_stream3 =
193       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
194                                 session3, url3, MEDIUM, BoundNetLog());
195   ASSERT_TRUE(spdy_stream3.get() != NULL);
196
197   // All sessions are active and not closed
198   EXPECT_TRUE(session1->is_active());
199   EXPECT_FALSE(session1->IsClosed());
200   EXPECT_TRUE(session2->is_active());
201   EXPECT_FALSE(session2->IsClosed());
202   EXPECT_TRUE(session3->is_active());
203   EXPECT_FALSE(session3->IsClosed());
204
205   // Should not do anything, all are active
206   spdy_session_pool_->CloseCurrentIdleSessions();
207   EXPECT_TRUE(session1->is_active());
208   EXPECT_FALSE(session1->IsClosed());
209   EXPECT_TRUE(session2->is_active());
210   EXPECT_FALSE(session2->IsClosed());
211   EXPECT_TRUE(session3->is_active());
212   EXPECT_FALSE(session3->IsClosed());
213
214   // Make sessions 1 and 3 inactive, but keep them open.
215   // Session 2 still open and active
216   session1->CloseCreatedStream(spdy_stream1, OK);
217   EXPECT_EQ(NULL, spdy_stream1.get());
218   session3->CloseCreatedStream(spdy_stream3, OK);
219   EXPECT_EQ(NULL, spdy_stream3.get());
220   EXPECT_FALSE(session1->is_active());
221   EXPECT_FALSE(session1->IsClosed());
222   EXPECT_TRUE(session2->is_active());
223   EXPECT_FALSE(session2->IsClosed());
224   EXPECT_FALSE(session3->is_active());
225   EXPECT_FALSE(session3->IsClosed());
226
227   // Should close session 1 and 3, 2 should be left open
228   spdy_session_pool_->CloseCurrentIdleSessions();
229   EXPECT_TRUE(session1 == NULL);
230   EXPECT_TRUE(session2->is_active());
231   EXPECT_FALSE(session2->IsClosed());
232   EXPECT_TRUE(session3 == NULL);
233
234   // Should not do anything
235   spdy_session_pool_->CloseCurrentIdleSessions();
236   EXPECT_TRUE(session2->is_active());
237   EXPECT_FALSE(session2->IsClosed());
238
239   // Make 2 not active
240   session2->CloseCreatedStream(spdy_stream2, OK);
241   EXPECT_EQ(NULL, spdy_stream2.get());
242   EXPECT_FALSE(session2->is_active());
243   EXPECT_FALSE(session2->IsClosed());
244
245   // This should close session 2
246   spdy_session_pool_->CloseCurrentIdleSessions();
247   EXPECT_TRUE(session2 == NULL);
248 }
249
250 // Set up a SpdyStream to create a new session when it is closed.
251 // CloseAllSessions should close the newly-created session.
252 TEST_P(SpdySessionPoolTest, CloseAllSessions) {
253   const char kTestHost[] = "www.foo.com";
254   const int kTestPort = 80;
255
256   session_deps_.host_resolver->set_synchronous_mode(true);
257
258   HostPortPair test_host_port_pair(kTestHost, kTestPort);
259   SpdySessionKey test_key =
260       SpdySessionKey(
261           test_host_port_pair, ProxyServer::Direct(),
262           kPrivacyModeDisabled);
263
264   MockConnect connect_data(SYNCHRONOUS, OK);
265   MockRead reads[] = {
266     MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
267   };
268
269   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
270   data.set_connect_data(connect_data);
271   session_deps_.socket_factory->AddSocketDataProvider(&data);
272
273   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
274   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
275
276   CreateNetworkSession();
277
278   // Setup the first session to the first host.
279   base::WeakPtr<SpdySession> session =
280       CreateInsecureSpdySession(http_session_, test_key, BoundNetLog());
281
282   // Flush the SpdySession::OnReadComplete() task.
283   base::MessageLoop::current()->RunUntilIdle();
284
285   // Verify that we have sessions for everything.
286   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
287
288   // Set the stream to create a new session when it is closed.
289   base::WeakPtr<SpdyStream> spdy_stream =
290       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
291                                 session, GURL("http://www.foo.com"),
292                                 MEDIUM, BoundNetLog());
293   SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
294   spdy_stream->SetDelegate(&delegate);
295
296   // Close the current session.
297   spdy_session_pool_->CloseAllSessions();
298
299   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_key));
300 }
301
302 // This test has three variants, one for each style of closing the connection.
303 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
304 // the sessions are closed manually, calling SpdySessionPool::Remove() directly.
305 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_CURRENT_SESSIONS,
306 // sessions are closed with SpdySessionPool::CloseCurrentSessions().
307 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_IDLE_SESSIONS,
308 // sessions are closed with SpdySessionPool::CloseIdleSessions().
309 void SpdySessionPoolTest::RunIPPoolingTest(
310     SpdyPoolCloseSessionsType close_sessions_type) {
311   const int kTestPort = 80;
312   struct TestHosts {
313     std::string url;
314     std::string name;
315     std::string iplist;
316     SpdySessionKey key;
317     AddressList addresses;
318   } test_hosts[] = {
319     { "http:://www.foo.com",
320       "www.foo.com",
321       "192.0.2.33,192.168.0.1,192.168.0.5"
322     },
323     { "http://js.foo.com",
324       "js.foo.com",
325       "192.168.0.2,192.168.0.3,192.168.0.5,192.0.2.33"
326     },
327     { "http://images.foo.com",
328       "images.foo.com",
329       "192.168.0.4,192.168.0.3"
330     },
331   };
332
333   session_deps_.host_resolver->set_synchronous_mode(true);
334   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_hosts); i++) {
335     session_deps_.host_resolver->rules()->AddIPLiteralRule(
336         test_hosts[i].name, test_hosts[i].iplist, std::string());
337
338     // This test requires that the HostResolver cache be populated.  Normal
339     // code would have done this already, but we do it manually.
340     HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
341     session_deps_.host_resolver->Resolve(info,
342                                          DEFAULT_PRIORITY,
343                                          &test_hosts[i].addresses,
344                                          CompletionCallback(),
345                                          NULL,
346                                          BoundNetLog());
347
348     // Setup a SpdySessionKey
349     test_hosts[i].key = SpdySessionKey(
350         HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct(),
351         kPrivacyModeDisabled);
352   }
353
354   MockConnect connect_data(SYNCHRONOUS, OK);
355   MockRead reads[] = {
356     MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
357   };
358
359   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
360   data.set_connect_data(connect_data);
361   session_deps_.socket_factory->AddSocketDataProvider(&data);
362
363   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
364   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
365
366   CreateNetworkSession();
367
368   // Setup the first session to the first host.
369   base::WeakPtr<SpdySession> session =
370       CreateInsecureSpdySession(
371           http_session_, test_hosts[0].key, BoundNetLog());
372
373   // Flush the SpdySession::OnReadComplete() task.
374   base::MessageLoop::current()->RunUntilIdle();
375
376   // The third host has no overlap with the first, so it can't pool IPs.
377   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
378
379   // The second host overlaps with the first, and should IP pool.
380   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
381
382   // Verify that the second host, through a proxy, won't share the IP.
383   SpdySessionKey proxy_key(test_hosts[1].key.host_port_pair(),
384       ProxyServer::FromPacString("HTTP http://proxy.foo.com/"),
385       kPrivacyModeDisabled);
386   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, proxy_key));
387
388   // Overlap between 2 and 3 does is not transitive to 1.
389   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
390
391   // Create a new session to host 2.
392   session_deps_.socket_factory->AddSocketDataProvider(&data);
393   base::WeakPtr<SpdySession> session2 =
394       CreateInsecureSpdySession(
395           http_session_, test_hosts[2].key, BoundNetLog());
396
397   // Verify that we have sessions for everything.
398   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
399   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
400   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
401
402   // Grab the session to host 1 and verify that it is the same session
403   // we got with host 0, and that is a different from host 2's session.
404   base::WeakPtr<SpdySession> session1 =
405       spdy_session_pool_->FindAvailableSession(
406           test_hosts[1].key, BoundNetLog());
407   EXPECT_EQ(session.get(), session1.get());
408   EXPECT_NE(session2.get(), session1.get());
409
410   // Remove the aliases and observe that we still have a session for host1.
411   SpdySessionPoolPeer pool_peer(spdy_session_pool_);
412   pool_peer.RemoveAliases(test_hosts[0].key);
413   pool_peer.RemoveAliases(test_hosts[1].key);
414   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
415
416   // Expire the host cache
417   session_deps_.host_resolver->GetHostCache()->clear();
418   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
419
420   // Cleanup the sessions.
421   switch (close_sessions_type) {
422     case SPDY_POOL_CLOSE_SESSIONS_MANUALLY:
423       session->CloseSessionOnError(ERR_ABORTED, std::string());
424       EXPECT_TRUE(session == NULL);
425       session2->CloseSessionOnError(ERR_ABORTED, std::string());
426       EXPECT_TRUE(session2 == NULL);
427       break;
428     case SPDY_POOL_CLOSE_CURRENT_SESSIONS:
429       spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED);
430       break;
431     case SPDY_POOL_CLOSE_IDLE_SESSIONS:
432       GURL url(test_hosts[0].url);
433       base::WeakPtr<SpdyStream> spdy_stream =
434           CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
435                                     session, url, MEDIUM, BoundNetLog());
436       GURL url1(test_hosts[1].url);
437       base::WeakPtr<SpdyStream> spdy_stream1 =
438           CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
439                                     session1, url1, MEDIUM, BoundNetLog());
440       GURL url2(test_hosts[2].url);
441       base::WeakPtr<SpdyStream> spdy_stream2 =
442           CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
443                                     session2, url2, MEDIUM, BoundNetLog());
444
445       // Close streams to make spdy_session and spdy_session1 inactive.
446       session->CloseCreatedStream(spdy_stream, OK);
447       EXPECT_EQ(NULL, spdy_stream.get());
448       session1->CloseCreatedStream(spdy_stream1, OK);
449       EXPECT_EQ(NULL, spdy_stream1.get());
450
451       // Check spdy_session and spdy_session1 are not closed.
452       EXPECT_FALSE(session->is_active());
453       EXPECT_FALSE(session->IsClosed());
454       EXPECT_FALSE(session1->is_active());
455       EXPECT_FALSE(session1->IsClosed());
456       EXPECT_TRUE(session2->is_active());
457       EXPECT_FALSE(session2->IsClosed());
458
459       // Test that calling CloseIdleSessions, does not cause a crash.
460       // http://crbug.com/181400
461       spdy_session_pool_->CloseCurrentIdleSessions();
462
463       // Verify spdy_session and spdy_session1 are closed.
464       EXPECT_TRUE(session == NULL);
465       EXPECT_TRUE(session1 == NULL);
466       EXPECT_TRUE(session2->is_active());
467       EXPECT_FALSE(session2->IsClosed());
468
469       spdy_stream2->Cancel();
470       EXPECT_EQ(NULL, spdy_stream.get());
471       EXPECT_EQ(NULL, spdy_stream1.get());
472       EXPECT_EQ(NULL, spdy_stream2.get());
473       session2->CloseSessionOnError(ERR_ABORTED, std::string());
474       EXPECT_TRUE(session2 == NULL);
475       break;
476   }
477
478   // Verify that the map is all cleaned up.
479   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
480   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
481   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
482 }
483
484 TEST_P(SpdySessionPoolTest, IPPooling) {
485   RunIPPoolingTest(SPDY_POOL_CLOSE_SESSIONS_MANUALLY);
486 }
487
488 TEST_P(SpdySessionPoolTest, IPPoolingCloseCurrentSessions) {
489   RunIPPoolingTest(SPDY_POOL_CLOSE_CURRENT_SESSIONS);
490 }
491
492 TEST_P(SpdySessionPoolTest, IPPoolingCloseIdleSessions) {
493   RunIPPoolingTest(SPDY_POOL_CLOSE_IDLE_SESSIONS);
494 }
495
496 }  // namespace
497
498 }  // namespace net