• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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.h"
6 
7 #include "net/spdy/spdy_io_buffer.h"
8 #include "net/spdy/spdy_session_pool.h"
9 #include "net/spdy/spdy_stream.h"
10 #include "net/spdy/spdy_test_util.h"
11 #include "testing/platform_test.h"
12 
13 namespace net {
14 
15 // TODO(cbentzel): Expose compression setter/getter in public SpdySession
16 //                 interface rather than going through all these contortions.
17 class SpdySessionTest : public PlatformTest {
18  public:
TurnOffCompression()19   static void TurnOffCompression() {
20     spdy::SpdyFramer::set_enable_compression_default(false);
21   }
22 };
23 
24 namespace {
25 
26 // Test the SpdyIOBuffer class.
TEST_F(SpdySessionTest,SpdyIOBuffer)27 TEST_F(SpdySessionTest, SpdyIOBuffer) {
28   std::priority_queue<SpdyIOBuffer> queue_;
29   const size_t kQueueSize = 100;
30 
31   // Insert 100 items; pri 100 to 1.
32   for (size_t index = 0; index < kQueueSize; ++index) {
33     SpdyIOBuffer buffer(new IOBuffer(), 0, kQueueSize - index, NULL);
34     queue_.push(buffer);
35   }
36 
37   // Insert several priority 0 items last.
38   const size_t kNumDuplicates = 12;
39   IOBufferWithSize* buffers[kNumDuplicates];
40   for (size_t index = 0; index < kNumDuplicates; ++index) {
41     buffers[index] = new IOBufferWithSize(index+1);
42     queue_.push(SpdyIOBuffer(buffers[index], buffers[index]->size(), 0, NULL));
43   }
44 
45   EXPECT_EQ(kQueueSize + kNumDuplicates, queue_.size());
46 
47   // Verify the P0 items come out in FIFO order.
48   for (size_t index = 0; index < kNumDuplicates; ++index) {
49     SpdyIOBuffer buffer = queue_.top();
50     EXPECT_EQ(0, buffer.priority());
51     EXPECT_EQ(index + 1, buffer.size());
52     queue_.pop();
53   }
54 
55   int priority = 1;
56   while (queue_.size()) {
57     SpdyIOBuffer buffer = queue_.top();
58     EXPECT_EQ(priority++, buffer.priority());
59     queue_.pop();
60   }
61 }
62 
TEST_F(SpdySessionTest,GoAway)63 TEST_F(SpdySessionTest, GoAway) {
64   SpdySessionDependencies session_deps;
65   session_deps.host_resolver->set_synchronous_mode(true);
66 
67   MockConnect connect_data(false, OK);
68   scoped_ptr<spdy::SpdyFrame> goaway(ConstructSpdyGoAway());
69   MockRead reads[] = {
70     CreateMockRead(*goaway),
71     MockRead(false, 0, 0)  // EOF
72   };
73   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
74   data.set_connect_data(connect_data);
75   session_deps.socket_factory->AddSocketDataProvider(&data);
76 
77   SSLSocketDataProvider ssl(false, OK);
78   session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
79 
80   scoped_refptr<HttpNetworkSession> http_session(
81       SpdySessionDependencies::SpdyCreateSession(&session_deps));
82 
83   const std::string kTestHost("www.foo.com");
84   const int kTestPort = 80;
85   HostPortPair test_host_port_pair(kTestHost, kTestPort);
86   HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct());
87 
88   SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
89   EXPECT_FALSE(spdy_session_pool->HasSession(pair));
90   scoped_refptr<SpdySession> session =
91       spdy_session_pool->Get(pair, BoundNetLog());
92   EXPECT_TRUE(spdy_session_pool->HasSession(pair));
93 
94   scoped_refptr<TransportSocketParams> transport_params(
95       new TransportSocketParams(test_host_port_pair,
96                                 MEDIUM,
97                                 GURL(),
98                                 false,
99                                 false));
100   scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
101   EXPECT_EQ(OK,
102             connection->Init(test_host_port_pair.ToString(),
103                              transport_params, MEDIUM,
104                              NULL, http_session->transport_socket_pool(),
105                              BoundNetLog()));
106   EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK));
107 
108   // Flush the SpdySession::OnReadComplete() task.
109   MessageLoop::current()->RunAllPending();
110 
111   EXPECT_FALSE(spdy_session_pool->HasSession(pair));
112 
113   scoped_refptr<SpdySession> session2 =
114       spdy_session_pool->Get(pair, BoundNetLog());
115 
116   // Delete the first session.
117   session = NULL;
118 
119   // Delete the second session.
120   spdy_session_pool->Remove(session2);
121   session2 = NULL;
122 }
123 
124 class StreamReleaserCallback : public CallbackRunner<Tuple1<int> > {
125  public:
StreamReleaserCallback(SpdySession * session,SpdyStream * first_stream)126   StreamReleaserCallback(SpdySession* session,
127                          SpdyStream* first_stream)
128       : session_(session), first_stream_(first_stream) {}
~StreamReleaserCallback()129   ~StreamReleaserCallback() {}
130 
WaitForResult()131   int WaitForResult() { return callback_.WaitForResult(); }
132 
RunWithParams(const Tuple1<int> & params)133   virtual void RunWithParams(const Tuple1<int>& params) {
134     session_->CloseSessionOnError(ERR_FAILED, false);
135     session_ = NULL;
136     first_stream_->Cancel();
137     first_stream_ = NULL;
138     stream_->Cancel();
139     stream_ = NULL;
140     callback_.RunWithParams(params);
141   }
142 
stream()143   scoped_refptr<SpdyStream>* stream() { return &stream_; }
144 
145  private:
146   scoped_refptr<SpdySession> session_;
147   scoped_refptr<SpdyStream> first_stream_;
148   scoped_refptr<SpdyStream> stream_;
149   TestCompletionCallback callback_;
150 };
151 
152 // Start with max concurrent streams set to 1.  Request two streams.  Receive a
153 // settings frame setting max concurrent streams to 2.  Have the callback
154 // release the stream, which releases its reference (the last) to the session.
155 // Make sure nothing blows up.
156 // http://crbug.com/57331
TEST_F(SpdySessionTest,OnSettings)157 TEST_F(SpdySessionTest, OnSettings) {
158   SpdySessionDependencies session_deps;
159   session_deps.host_resolver->set_synchronous_mode(true);
160 
161   spdy::SpdySettings new_settings;
162   spdy::SettingsFlagsAndId id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
163   id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
164   const size_t max_concurrent_streams = 2;
165   new_settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
166 
167   // Set up the socket so we read a SETTINGS frame that raises max concurrent
168   // streams to 2.
169   MockConnect connect_data(false, OK);
170   scoped_ptr<spdy::SpdyFrame> settings_frame(
171       ConstructSpdySettings(new_settings));
172   MockRead reads[] = {
173     CreateMockRead(*settings_frame),
174     MockRead(false, 0, 0)  // EOF
175   };
176 
177   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
178   data.set_connect_data(connect_data);
179   session_deps.socket_factory->AddSocketDataProvider(&data);
180 
181   SSLSocketDataProvider ssl(false, OK);
182   session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
183 
184   scoped_refptr<HttpNetworkSession> http_session(
185       SpdySessionDependencies::SpdyCreateSession(&session_deps));
186 
187   const std::string kTestHost("www.foo.com");
188   const int kTestPort = 80;
189   HostPortPair test_host_port_pair(kTestHost, kTestPort);
190   HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct());
191 
192   // Initialize the SpdySettingsStorage with 1 max concurrent streams.
193   SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
194   spdy::SpdySettings old_settings;
195   id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
196   old_settings.push_back(spdy::SpdySetting(id, 1));
197   spdy_session_pool->mutable_spdy_settings()->Set(
198       test_host_port_pair, old_settings);
199 
200   // Create a session.
201   EXPECT_FALSE(spdy_session_pool->HasSession(pair));
202   scoped_refptr<SpdySession> session =
203       spdy_session_pool->Get(pair, BoundNetLog());
204   ASSERT_TRUE(spdy_session_pool->HasSession(pair));
205 
206   scoped_refptr<TransportSocketParams> transport_params(
207       new TransportSocketParams(test_host_port_pair,
208                                 MEDIUM,
209                                 GURL(),
210                                 false,
211                                 false));
212   scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
213   EXPECT_EQ(OK,
214             connection->Init(test_host_port_pair.ToString(),
215                              transport_params, MEDIUM,
216                              NULL, http_session->transport_socket_pool(),
217                              BoundNetLog()));
218   EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK));
219 
220   // Create 2 streams.  First will succeed.  Second will be pending.
221   scoped_refptr<SpdyStream> spdy_stream1;
222   TestCompletionCallback callback1;
223   GURL url("http://www.google.com");
224   EXPECT_EQ(OK,
225             session->CreateStream(url,
226                                   MEDIUM, /* priority, not important */
227                                   &spdy_stream1,
228                                   BoundNetLog(),
229                                   &callback1));
230 
231   StreamReleaserCallback stream_releaser(session, spdy_stream1);
232 
233   ASSERT_EQ(ERR_IO_PENDING,
234             session->CreateStream(url,
235                                   MEDIUM, /* priority, not important */
236                                   stream_releaser.stream(),
237                                   BoundNetLog(),
238                                   &stream_releaser));
239 
240   // Make sure |stream_releaser| holds the last refs.
241   session = NULL;
242   spdy_stream1 = NULL;
243 
244   EXPECT_EQ(OK, stream_releaser.WaitForResult());
245 }
246 
247 // Start with max concurrent streams set to 1.  Request two streams.  When the
248 // first completes, have the callback close itself, which should trigger the
249 // second stream creation.  Then cancel that one immediately.  Don't crash.
250 // http://crbug.com/63532
TEST_F(SpdySessionTest,CancelPendingCreateStream)251 TEST_F(SpdySessionTest, CancelPendingCreateStream) {
252   SpdySessionDependencies session_deps;
253   session_deps.host_resolver->set_synchronous_mode(true);
254 
255   MockRead reads[] = {
256     MockRead(false, ERR_IO_PENDING)  // Stall forever.
257   };
258 
259   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
260   MockConnect connect_data(false, OK);
261 
262   data.set_connect_data(connect_data);
263   session_deps.socket_factory->AddSocketDataProvider(&data);
264 
265   SSLSocketDataProvider ssl(false, OK);
266   session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
267 
268   scoped_refptr<HttpNetworkSession> http_session(
269       SpdySessionDependencies::SpdyCreateSession(&session_deps));
270 
271   const std::string kTestHost("www.foo.com");
272   const int kTestPort = 80;
273   HostPortPair test_host_port_pair(kTestHost, kTestPort);
274   HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct());
275 
276   // Initialize the SpdySettingsStorage with 1 max concurrent streams.
277   SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
278   spdy::SpdySettings settings;
279   spdy::SettingsFlagsAndId id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
280   id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
281   id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
282   settings.push_back(spdy::SpdySetting(id, 1));
283   spdy_session_pool->mutable_spdy_settings()->Set(
284       test_host_port_pair, settings);
285 
286   // Create a session.
287   EXPECT_FALSE(spdy_session_pool->HasSession(pair));
288   scoped_refptr<SpdySession> session =
289       spdy_session_pool->Get(pair, BoundNetLog());
290   ASSERT_TRUE(spdy_session_pool->HasSession(pair));
291 
292   scoped_refptr<TransportSocketParams> transport_params(
293       new TransportSocketParams(test_host_port_pair,
294                                 MEDIUM,
295                                 GURL(),
296                                 false,
297                                 false));
298   scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
299   EXPECT_EQ(OK,
300             connection->Init(test_host_port_pair.ToString(),
301                              transport_params, MEDIUM,
302                              NULL, http_session->transport_socket_pool(),
303                              BoundNetLog()));
304   EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK));
305 
306   // Use scoped_ptr to let us invalidate the memory when we want to, to trigger
307   // a valgrind error if the callback is invoked when it's not supposed to be.
308   scoped_ptr<TestCompletionCallback> callback(new TestCompletionCallback);
309 
310   // Create 2 streams.  First will succeed.  Second will be pending.
311   scoped_refptr<SpdyStream> spdy_stream1;
312   GURL url("http://www.google.com");
313   ASSERT_EQ(OK,
314             session->CreateStream(url,
315                                   MEDIUM, /* priority, not important */
316                                   &spdy_stream1,
317                                   BoundNetLog(),
318                                   callback.get()));
319 
320   scoped_refptr<SpdyStream> spdy_stream2;
321   ASSERT_EQ(ERR_IO_PENDING,
322             session->CreateStream(url,
323                                   MEDIUM, /* priority, not important */
324                                   &spdy_stream2,
325                                   BoundNetLog(),
326                                   callback.get()));
327 
328   // Release the first one, this will allow the second to be created.
329   spdy_stream1->Cancel();
330   spdy_stream1 = NULL;
331 
332   session->CancelPendingCreateStreams(&spdy_stream2);
333   callback.reset();
334 
335   // Should not crash when running the pending callback.
336   MessageLoop::current()->RunAllPending();
337 }
338 
TEST_F(SpdySessionTest,SendSettingsOnNewSession)339 TEST_F(SpdySessionTest, SendSettingsOnNewSession) {
340   SpdySessionDependencies session_deps;
341   session_deps.host_resolver->set_synchronous_mode(true);
342 
343   MockRead reads[] = {
344     MockRead(false, ERR_IO_PENDING)  // Stall forever.
345   };
346 
347   // Create the bogus setting that we want to verify is sent out.
348   // Note that it will be marked as SETTINGS_FLAG_PERSISTED when sent out.  But
349   // to set it into the SpdySettingsStorage, we need to mark as
350   // SETTINGS_FLAG_PLEASE_PERSIST.
351   spdy::SpdySettings settings;
352   const uint32 kBogusSettingId = 0xABAB;
353   const uint32 kBogusSettingValue = 0xCDCD;
354   spdy::SettingsFlagsAndId id(kBogusSettingId);
355   id.set_id(kBogusSettingId);
356   id.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
357   settings.push_back(spdy::SpdySetting(id, kBogusSettingValue));
358   MockConnect connect_data(false, OK);
359   scoped_ptr<spdy::SpdyFrame> settings_frame(
360       ConstructSpdySettings(settings));
361   MockWrite writes[] = {
362     CreateMockWrite(*settings_frame),
363   };
364 
365   StaticSocketDataProvider data(
366       reads, arraysize(reads), writes, arraysize(writes));
367   data.set_connect_data(connect_data);
368   session_deps.socket_factory->AddSocketDataProvider(&data);
369 
370   SSLSocketDataProvider ssl(false, OK);
371   session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
372 
373   scoped_refptr<HttpNetworkSession> http_session(
374       SpdySessionDependencies::SpdyCreateSession(&session_deps));
375 
376   const std::string kTestHost("www.foo.com");
377   const int kTestPort = 80;
378   HostPortPair test_host_port_pair(kTestHost, kTestPort);
379   HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct());
380 
381   id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
382   settings.clear();
383   settings.push_back(spdy::SpdySetting(id, kBogusSettingValue));
384   SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
385   spdy_session_pool->mutable_spdy_settings()->Set(
386       test_host_port_pair, settings);
387   EXPECT_FALSE(spdy_session_pool->HasSession(pair));
388   scoped_refptr<SpdySession> session =
389       spdy_session_pool->Get(pair, BoundNetLog());
390   EXPECT_TRUE(spdy_session_pool->HasSession(pair));
391 
392   scoped_refptr<TransportSocketParams> transport_params(
393       new TransportSocketParams(test_host_port_pair,
394                                 MEDIUM,
395                                 GURL(),
396                                 false,
397                                 false));
398   scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
399   EXPECT_EQ(OK,
400             connection->Init(test_host_port_pair.ToString(),
401                              transport_params, MEDIUM,
402                              NULL, http_session->transport_socket_pool(),
403                              BoundNetLog()));
404   EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK));
405   MessageLoop::current()->RunAllPending();
406   EXPECT_TRUE(data.at_write_eof());
407 }
408 
409 // This test has two variants, one for each style of closing the connection.
410 // If |clean_via_close_current_sessions| is false, the sessions are closed
411 // manually, calling SpdySessionPool::Remove() directly.  If it is true,
412 // sessions are closed with SpdySessionPool::CloseCurrentSessions().
IPPoolingTest(bool clean_via_close_current_sessions)413 void IPPoolingTest(bool clean_via_close_current_sessions) {
414   const int kTestPort = 80;
415   struct TestHosts {
416     std::string name;
417     std::string iplist;
418     HostPortProxyPair pair;
419   } test_hosts[] = {
420     { "www.foo.com",    "192.168.0.1,192.168.0.5" },
421     { "images.foo.com", "192.168.0.2,192.168.0.3,192.168.0.5" },
422     { "js.foo.com",     "192.168.0.4,192.168.0.3" },
423   };
424 
425   SpdySessionDependencies session_deps;
426   session_deps.host_resolver->set_synchronous_mode(true);
427   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_hosts); i++) {
428     session_deps.host_resolver->rules()->AddIPLiteralRule(test_hosts[i].name,
429         test_hosts[i].iplist, "");
430 
431     // This test requires that the HostResolver cache be populated.  Normal
432     // code would have done this already, but we do it manually.
433     HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
434     AddressList result;
435     session_deps.host_resolver->Resolve(
436         info, &result, NULL, NULL, BoundNetLog());
437 
438     // Setup a HostPortProxyPair
439     test_hosts[i].pair = HostPortProxyPair(
440         HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct());
441   }
442 
443   MockConnect connect_data(false, OK);
444   MockRead reads[] = {
445     MockRead(false, ERR_IO_PENDING)  // Stall forever.
446   };
447 
448   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
449   data.set_connect_data(connect_data);
450   session_deps.socket_factory->AddSocketDataProvider(&data);
451 
452   SSLSocketDataProvider ssl(false, OK);
453   session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
454 
455   scoped_refptr<HttpNetworkSession> http_session(
456       SpdySessionDependencies::SpdyCreateSession(&session_deps));
457 
458   // Setup the first session to the first host.
459   SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
460   EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[0].pair));
461   scoped_refptr<SpdySession> session =
462       spdy_session_pool->Get(test_hosts[0].pair, BoundNetLog());
463   EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[0].pair));
464 
465   HostPortPair test_host_port_pair(test_hosts[0].name, kTestPort);
466   scoped_refptr<TransportSocketParams> transport_params(
467       new TransportSocketParams(test_host_port_pair,
468                           MEDIUM,
469                           GURL(),
470                           false,
471                           false));
472   scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
473   EXPECT_EQ(OK,
474             connection->Init(test_host_port_pair.ToString(),
475                              transport_params, MEDIUM,
476                              NULL, http_session->transport_socket_pool(),
477                              BoundNetLog()));
478   EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK));
479 
480   // Flush the SpdySession::OnReadComplete() task.
481   MessageLoop::current()->RunAllPending();
482 
483   // The third host has no overlap with the first, so it can't pool IPs.
484   EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair));
485 
486   // The second host overlaps with the first, and should IP pool.
487   EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair));
488 
489   // Verify that the second host, through a proxy, won't share the IP.
490   HostPortProxyPair proxy_pair(test_hosts[1].pair.first,
491       ProxyServer::FromPacString("HTTP http://proxy.foo.com/"));
492   EXPECT_FALSE(spdy_session_pool->HasSession(proxy_pair));
493 
494   // Overlap between 2 and 3 does is not transitive to 1.
495   EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair));
496 
497   // Create a new session to host 2.
498   scoped_refptr<SpdySession> session2 =
499       spdy_session_pool->Get(test_hosts[2].pair, BoundNetLog());
500 
501   // Verify that we have sessions for everything.
502   EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[0].pair));
503   EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair));
504   EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[2].pair));
505 
506   // Cleanup the sessions.
507   if (!clean_via_close_current_sessions) {
508     spdy_session_pool->Remove(session);
509     session = NULL;
510     spdy_session_pool->Remove(session2);
511     session2 = NULL;
512   } else {
513     spdy_session_pool->CloseCurrentSessions();
514   }
515 
516   // Verify that the map is all cleaned up.
517   EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[0].pair));
518   EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[1].pair));
519   EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair));
520 }
521 
TEST_F(SpdySessionTest,IPPooling)522 TEST_F(SpdySessionTest, IPPooling) {
523   IPPoolingTest(false);
524 }
525 
TEST_F(SpdySessionTest,IPPoolingCloseCurrentSessions)526 TEST_F(SpdySessionTest, IPPoolingCloseCurrentSessions) {
527   IPPoolingTest(true);
528 }
529 
530 }  // namespace
531 
532 }  // namespace net
533