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.
5 #include "net/quic/quic_session.h"
10 #include "base/containers/hash_tables.h"
11 #include "net/quic/crypto/crypto_handshake.h"
12 #include "net/quic/quic_connection.h"
13 #include "net/quic/quic_protocol.h"
14 #include "net/quic/test_tools/quic_connection_peer.h"
15 #include "net/quic/test_tools/quic_data_stream_peer.h"
16 #include "net/quic/test_tools/quic_session_peer.h"
17 #include "net/quic/test_tools/quic_test_utils.h"
18 #include "net/quic/test_tools/reliable_quic_stream_peer.h"
19 #include "net/spdy/spdy_framer.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
27 using testing::InSequence
;
28 using testing::InvokeWithoutArgs
;
29 using testing::StrictMock
;
35 const QuicPriority kSomeMiddlePriority
= 2;
37 class TestCryptoStream
: public QuicCryptoStream
{
39 explicit TestCryptoStream(QuicSession
* session
)
40 : QuicCryptoStream(session
) {
43 virtual void OnHandshakeMessage(
44 const CryptoHandshakeMessage
& message
) OVERRIDE
{
45 encryption_established_
= true;
46 handshake_confirmed_
= true;
47 CryptoHandshakeMessage msg
;
49 session()->config()->ToHandshakeMessage(&msg
);
50 const QuicErrorCode error
= session()->config()->ProcessClientHello(
52 EXPECT_EQ(QUIC_NO_ERROR
, error
);
53 session()->OnConfigNegotiated();
54 session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED
);
57 MOCK_METHOD0(OnCanWrite
, void());
60 class TestStream
: public QuicDataStream
{
62 TestStream(QuicStreamId id
, QuicSession
* session
)
63 : QuicDataStream(id
, session
) {
66 using ReliableQuicStream::CloseWriteSide
;
68 virtual uint32
ProcessData(const char* data
, uint32 data_len
) {
72 MOCK_METHOD0(OnCanWrite
, void());
75 // Poor man's functor for use as callback in a mock.
78 StreamBlocker(QuicSession
* session
, QuicStreamId stream_id
)
80 stream_id_(stream_id
) {
83 void MarkWriteBlocked() {
84 session_
->MarkWriteBlocked(stream_id_
, kSomeMiddlePriority
);
88 QuicSession
* const session_
;
89 const QuicStreamId stream_id_
;
92 class TestSession
: public QuicSession
{
94 explicit TestSession(QuicConnection
* connection
)
95 : QuicSession(connection
, DefaultQuicConfig()),
96 crypto_stream_(this) {
99 virtual TestCryptoStream
* GetCryptoStream() OVERRIDE
{
100 return &crypto_stream_
;
103 virtual TestStream
* CreateOutgoingDataStream() OVERRIDE
{
104 TestStream
* stream
= new TestStream(GetNextStreamId(), this);
105 ActivateStream(stream
);
109 virtual TestStream
* CreateIncomingDataStream(QuicStreamId id
) OVERRIDE
{
110 return new TestStream(id
, this);
113 bool IsClosedStream(QuicStreamId id
) {
114 return QuicSession::IsClosedStream(id
);
117 QuicDataStream
* GetIncomingDataStream(QuicStreamId stream_id
) {
118 return QuicSession::GetIncomingDataStream(stream_id
);
121 TestCryptoStream crypto_stream_
;
124 class QuicSessionTest
: public ::testing::TestWithParam
<QuicVersion
> {
127 : connection_(new MockConnection(true, SupportedVersions(GetParam()))),
128 session_(connection_
) {
129 headers_
[":host"] = "www.google.com";
130 headers_
[":path"] = "/index.hml";
131 headers_
[":scheme"] = "http";
133 "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
135 "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
136 "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
137 "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
138 "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
139 "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
140 "1zFMi5vzcns38-8_Sns; "
141 "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
142 "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
143 "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
144 "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
145 "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
146 "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
147 "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
148 "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
149 "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
150 "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
151 "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
152 "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
153 "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
154 "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
155 "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
158 void CheckClosedStreams() {
159 for (int i
= kCryptoStreamId
; i
< 100; i
++) {
160 if (closed_streams_
.count(i
) == 0) {
161 EXPECT_FALSE(session_
.IsClosedStream(i
)) << " stream id: " << i
;
163 EXPECT_TRUE(session_
.IsClosedStream(i
)) << " stream id: " << i
;
168 void CloseStream(QuicStreamId id
) {
169 session_
.CloseStream(id
);
170 closed_streams_
.insert(id
);
173 MockConnection
* connection_
;
174 TestSession session_
;
175 set
<QuicStreamId
> closed_streams_
;
176 SpdyHeaderBlock headers_
;
179 INSTANTIATE_TEST_CASE_P(Tests
, QuicSessionTest
,
180 ::testing::ValuesIn(QuicSupportedVersions()));
182 TEST_P(QuicSessionTest
, PeerAddress
) {
183 EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort
), session_
.peer_address());
186 TEST_P(QuicSessionTest
, IsCryptoHandshakeConfirmed
) {
187 EXPECT_FALSE(session_
.IsCryptoHandshakeConfirmed());
188 CryptoHandshakeMessage message
;
189 session_
.crypto_stream_
.OnHandshakeMessage(message
);
190 EXPECT_TRUE(session_
.IsCryptoHandshakeConfirmed());
193 TEST_P(QuicSessionTest
, IsClosedStreamDefault
) {
194 // Ensure that no streams are initially closed.
195 for (int i
= kCryptoStreamId
; i
< 100; i
++) {
196 EXPECT_FALSE(session_
.IsClosedStream(i
)) << "stream id: " << i
;
200 TEST_P(QuicSessionTest
, ImplicitlyCreatedStreams
) {
201 ASSERT_TRUE(session_
.GetIncomingDataStream(7) != NULL
);
202 // Both 3 and 5 should be implicitly created.
203 EXPECT_FALSE(session_
.IsClosedStream(3));
204 EXPECT_FALSE(session_
.IsClosedStream(5));
205 ASSERT_TRUE(session_
.GetIncomingDataStream(5) != NULL
);
206 ASSERT_TRUE(session_
.GetIncomingDataStream(3) != NULL
);
209 TEST_P(QuicSessionTest
, IsClosedStreamLocallyCreated
) {
210 TestStream
* stream2
= session_
.CreateOutgoingDataStream();
211 EXPECT_EQ(2u, stream2
->id());
212 TestStream
* stream4
= session_
.CreateOutgoingDataStream();
213 EXPECT_EQ(4u, stream4
->id());
214 if (GetParam() <= QUIC_VERSION_12
) {
215 QuicDataStreamPeer::SetHeadersDecompressed(stream2
, true);
216 QuicDataStreamPeer::SetHeadersDecompressed(stream4
, true);
219 CheckClosedStreams();
221 CheckClosedStreams();
223 CheckClosedStreams();
226 TEST_P(QuicSessionTest
, IsClosedStreamPeerCreated
) {
227 QuicStreamId stream_id1
= GetParam() > QUIC_VERSION_12
? 5 : 3;
228 QuicStreamId stream_id2
= stream_id1
+ 2;
229 QuicDataStream
* stream1
= session_
.GetIncomingDataStream(stream_id1
);
230 QuicDataStreamPeer::SetHeadersDecompressed(stream1
, true);
231 QuicDataStream
* stream2
= session_
.GetIncomingDataStream(stream_id2
);
232 QuicDataStreamPeer::SetHeadersDecompressed(stream2
, true);
234 CheckClosedStreams();
235 CloseStream(stream_id1
);
236 CheckClosedStreams();
237 CloseStream(stream_id2
);
238 // Create a stream explicitly, and another implicitly.
239 QuicDataStream
* stream3
= session_
.GetIncomingDataStream(stream_id2
+ 4);
240 QuicDataStreamPeer::SetHeadersDecompressed(stream3
, true);
241 CheckClosedStreams();
242 // Close one, but make sure the other is still not closed
243 CloseStream(stream3
->id());
244 CheckClosedStreams();
247 TEST_P(QuicSessionTest
, StreamIdTooLarge
) {
248 QuicStreamId stream_id
= GetParam() > QUIC_VERSION_12
? 5 : 3;
249 session_
.GetIncomingDataStream(stream_id
);
250 EXPECT_CALL(*connection_
, SendConnectionClose(QUIC_INVALID_STREAM_ID
));
251 session_
.GetIncomingDataStream(stream_id
+ 102);
254 TEST_P(QuicSessionTest
, DecompressionError
) {
255 if (GetParam() > QUIC_VERSION_12
) {
256 QuicHeadersStream
* stream
= QuicSessionPeer::GetHeadersStream(&session_
);
257 const unsigned char data
[] = {
258 0x80, 0x03, 0x00, 0x01, // SPDY/3 SYN_STREAM frame
259 0x00, 0x00, 0x00, 0x25, // flags/length
260 0x00, 0x00, 0x00, 0x05, // stream id
261 0x00, 0x00, 0x00, 0x00, // associated stream id
263 'a', 'b', 'c', 'd' // invalid compressed data
265 EXPECT_CALL(*connection_
,
266 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA
,
267 "SPDY framing error."));
268 stream
->ProcessRawData(reinterpret_cast<const char*>(data
),
271 ReliableQuicStream
* stream
= session_
.GetIncomingDataStream(3);
273 "\0\0\0\0" // priority
274 "\1\0\0\0" // headers id
276 "abcd"; // invalid compressed data
277 EXPECT_CALL(*connection_
, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE
));
278 stream
->ProcessRawData(data
, arraysize(data
));
282 TEST_P(QuicSessionTest
, OnCanWrite
) {
283 TestStream
* stream2
= session_
.CreateOutgoingDataStream();
284 TestStream
* stream4
= session_
.CreateOutgoingDataStream();
285 TestStream
* stream6
= session_
.CreateOutgoingDataStream();
287 session_
.MarkWriteBlocked(stream2
->id(), kSomeMiddlePriority
);
288 session_
.MarkWriteBlocked(stream6
->id(), kSomeMiddlePriority
);
289 session_
.MarkWriteBlocked(stream4
->id(), kSomeMiddlePriority
);
292 StreamBlocker
stream2_blocker(&session_
, stream2
->id());
293 EXPECT_CALL(*stream2
, OnCanWrite()).WillOnce(
294 // Reregister, to test the loop limit.
295 InvokeWithoutArgs(&stream2_blocker
, &StreamBlocker::MarkWriteBlocked
));
296 EXPECT_CALL(*stream6
, OnCanWrite());
297 EXPECT_CALL(*stream4
, OnCanWrite());
299 EXPECT_FALSE(session_
.OnCanWrite());
302 TEST_P(QuicSessionTest
, BufferedHandshake
) {
303 EXPECT_FALSE(session_
.HasPendingHandshake()); // Default value.
305 // Test that blocking other streams does not change our status.
306 TestStream
* stream2
= session_
.CreateOutgoingDataStream();
307 StreamBlocker
stream2_blocker(&session_
, stream2
->id());
308 stream2_blocker
.MarkWriteBlocked();
309 EXPECT_FALSE(session_
.HasPendingHandshake());
311 TestStream
* stream3
= session_
.CreateOutgoingDataStream();
312 StreamBlocker
stream3_blocker(&session_
, stream3
->id());
313 stream3_blocker
.MarkWriteBlocked();
314 EXPECT_FALSE(session_
.HasPendingHandshake());
316 // Blocking (due to buffering of) the Crypto stream is detected.
317 session_
.MarkWriteBlocked(kCryptoStreamId
, kSomeMiddlePriority
);
318 EXPECT_TRUE(session_
.HasPendingHandshake());
320 TestStream
* stream4
= session_
.CreateOutgoingDataStream();
321 StreamBlocker
stream4_blocker(&session_
, stream4
->id());
322 stream4_blocker
.MarkWriteBlocked();
323 EXPECT_TRUE(session_
.HasPendingHandshake());
326 // Force most streams to re-register, which is common scenario when we block
327 // the Crypto stream, and only the crypto stream can "really" write.
329 // Due to prioritization, we *should* be asked to write the crypto stream
331 // Don't re-register the crypto stream (which signals complete writing).
332 TestCryptoStream
* crypto_stream
= session_
.GetCryptoStream();
333 EXPECT_CALL(*crypto_stream
, OnCanWrite());
335 // Re-register all other streams, to show they weren't able to proceed.
336 EXPECT_CALL(*stream2
, OnCanWrite()).WillOnce(
337 InvokeWithoutArgs(&stream2_blocker
, &StreamBlocker::MarkWriteBlocked
));
339 EXPECT_CALL(*stream3
, OnCanWrite()).WillOnce(
340 InvokeWithoutArgs(&stream3_blocker
, &StreamBlocker::MarkWriteBlocked
));
342 EXPECT_CALL(*stream4
, OnCanWrite()).WillOnce(
343 InvokeWithoutArgs(&stream4_blocker
, &StreamBlocker::MarkWriteBlocked
));
345 EXPECT_FALSE(session_
.OnCanWrite());
346 EXPECT_FALSE(session_
.HasPendingHandshake()); // Crypto stream wrote.
349 TEST_P(QuicSessionTest
, OnCanWriteWithClosedStream
) {
350 TestStream
* stream2
= session_
.CreateOutgoingDataStream();
351 TestStream
* stream4
= session_
.CreateOutgoingDataStream();
352 TestStream
* stream6
= session_
.CreateOutgoingDataStream();
354 session_
.MarkWriteBlocked(stream2
->id(), kSomeMiddlePriority
);
355 session_
.MarkWriteBlocked(stream6
->id(), kSomeMiddlePriority
);
356 session_
.MarkWriteBlocked(stream4
->id(), kSomeMiddlePriority
);
357 CloseStream(stream6
->id());
360 EXPECT_CALL(*stream2
, OnCanWrite());
361 EXPECT_CALL(*stream4
, OnCanWrite());
362 EXPECT_TRUE(session_
.OnCanWrite());
365 // Regression test for http://crbug.com/248737
366 TEST_P(QuicSessionTest
, OutOfOrderHeaders
) {
367 QuicSpdyCompressor compressor
;
368 vector
<QuicStreamFrame
> frames
;
369 QuicPacketHeader header
;
370 header
.public_header
.guid
= session_
.guid();
372 TestStream
* stream2
= session_
.CreateOutgoingDataStream();
373 TestStream
* stream4
= session_
.CreateOutgoingDataStream();
374 stream2
->CloseWriteSide();
375 stream4
->CloseWriteSide();
377 // Create frame with headers for stream2.
378 string compressed_headers1
= compressor
.CompressHeaders(headers_
);
379 QuicStreamFrame
frame1(
380 stream2
->id(), false, 0, MakeIOVector(compressed_headers1
));
382 // Create frame with headers for stream4.
383 string compressed_headers2
= compressor
.CompressHeaders(headers_
);
384 QuicStreamFrame
frame2(
385 stream4
->id(), true, 0, MakeIOVector(compressed_headers2
));
387 // Process the second frame first. This will cause the headers to
388 // be queued up and processed after the first frame is processed.
389 frames
.push_back(frame2
);
390 session_
.OnStreamFrames(frames
);
392 // Process the first frame, and un-cork the buffered headers.
394 session_
.OnStreamFrames(frames
);
396 // Ensure that the streams actually close and we don't DCHECK.
397 connection_
->CloseConnection(QUIC_CONNECTION_TIMED_OUT
, true);
400 TEST_P(QuicSessionTest
, SendGoAway
) {
401 // After sending a GoAway, ensure new incoming streams cannot be created and
402 // result in a RST being sent.
403 EXPECT_CALL(*connection_
,
404 SendGoAway(QUIC_PEER_GOING_AWAY
, 0u, "Going Away."));
405 session_
.SendGoAway(QUIC_PEER_GOING_AWAY
, "Going Away.");
406 EXPECT_TRUE(session_
.goaway_sent());
408 EXPECT_CALL(*connection_
, SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY
));
409 EXPECT_FALSE(session_
.GetIncomingDataStream(3u));
412 TEST_P(QuicSessionTest
, IncreasedTimeoutAfterCryptoHandshake
) {
413 EXPECT_EQ(kDefaultInitialTimeoutSecs
,
414 QuicConnectionPeer::GetNetworkTimeout(connection_
).ToSeconds());
415 CryptoHandshakeMessage msg
;
416 session_
.crypto_stream_
.OnHandshakeMessage(msg
);
417 EXPECT_EQ(kDefaultTimeoutSecs
,
418 QuicConnectionPeer::GetNetworkTimeout(connection_
).ToSeconds());
421 TEST_P(QuicSessionTest
, ZombieStream
) {
422 QuicStreamId stream_id1
= GetParam() > QUIC_VERSION_12
? 5 : 3;
423 QuicStreamId stream_id2
= stream_id1
+ 2;
424 StrictMock
<MockConnection
>* connection
=
425 new StrictMock
<MockConnection
>(false, SupportedVersions(GetParam()));
426 TestSession
session(connection
);
428 TestStream
* stream1
= session
.CreateOutgoingDataStream();
429 EXPECT_EQ(stream_id1
, stream1
->id());
430 TestStream
* stream2
= session
.CreateOutgoingDataStream();
431 EXPECT_EQ(stream_id2
, stream2
->id());
432 EXPECT_EQ(2u, session
.GetNumOpenStreams());
434 // Reset the stream, but since the headers have not been decompressed
435 // it will become a zombie and will continue to process data
436 // until the headers are decompressed.
437 EXPECT_CALL(*connection
, SendRstStream(stream_id1
, QUIC_STREAM_CANCELLED
));
438 session
.SendRstStream(stream_id1
, QUIC_STREAM_CANCELLED
);
440 EXPECT_EQ(1u, session
.GetNumOpenStreams());
442 vector
<QuicStreamFrame
> frames
;
443 QuicPacketHeader header
;
444 header
.public_header
.guid
= session_
.guid();
446 // Create frame with headers for stream2.
447 QuicSpdyCompressor compressor
;
448 string compressed_headers1
= compressor
.CompressHeaders(headers_
);
449 QuicStreamFrame
frame1(
450 stream1
->id(), false, 0, MakeIOVector(compressed_headers1
));
452 // Process the second frame first. This will cause the headers to
453 // be queued up and processed after the first frame is processed.
454 frames
.push_back(frame1
);
455 EXPECT_FALSE(stream1
->headers_decompressed());
457 session
.OnStreamFrames(frames
);
458 EXPECT_EQ(1u, session
.GetNumOpenStreams());
460 EXPECT_TRUE(connection
->connected());
463 TEST_P(QuicSessionTest
, ZombieStreamConnectionClose
) {
464 QuicStreamId stream_id1
= GetParam() > QUIC_VERSION_12
? 5 : 3;
465 QuicStreamId stream_id2
= stream_id1
+ 2;
466 StrictMock
<MockConnection
>* connection
=
467 new StrictMock
<MockConnection
>(false, SupportedVersions(GetParam()));
468 TestSession
session(connection
);
470 TestStream
* stream1
= session
.CreateOutgoingDataStream();
471 EXPECT_EQ(stream_id1
, stream1
->id());
472 TestStream
* stream2
= session
.CreateOutgoingDataStream();
473 EXPECT_EQ(stream_id2
, stream2
->id());
474 EXPECT_EQ(2u, session
.GetNumOpenStreams());
476 stream1
->CloseWriteSide();
477 // Reset the stream, but since the headers have not been decompressed
478 // it will become a zombie and will continue to process data
479 // until the headers are decompressed.
480 EXPECT_CALL(*connection
, SendRstStream(stream_id1
, QUIC_STREAM_CANCELLED
));
481 session
.SendRstStream(stream_id1
, QUIC_STREAM_CANCELLED
);
483 EXPECT_EQ(1u, session
.GetNumOpenStreams());
485 connection
->CloseConnection(QUIC_CONNECTION_TIMED_OUT
, false);
487 EXPECT_EQ(0u, session
.GetNumOpenStreams());
490 TEST_P(QuicSessionTest
, RstStreamBeforeHeadersDecompressed
) {
491 QuicStreamId stream_id1
= GetParam() > QUIC_VERSION_12
? 5 : 3;
492 // Send two bytes of payload.
493 QuicStreamFrame
data1(stream_id1
, false, 0, MakeIOVector("HT"));
494 vector
<QuicStreamFrame
> frames
;
495 frames
.push_back(data1
);
496 EXPECT_TRUE(session_
.OnStreamFrames(frames
));
497 EXPECT_EQ(1u, session_
.GetNumOpenStreams());
499 if (GetParam() <= QUIC_VERSION_12
) {
500 // Send a reset before the headers have been decompressed. This causes
501 // an unrecoverable compression context state.
502 EXPECT_CALL(*connection_
, SendConnectionClose(
503 QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED
));
506 QuicRstStreamFrame
rst1(stream_id1
, QUIC_STREAM_NO_ERROR
);
507 session_
.OnRstStream(rst1
);
508 EXPECT_EQ(0u, session_
.GetNumOpenStreams());