Land Recent QUIC Changes.
[chromium-blink-merge.git] / net / quic / quic_session_test.cc
blobce5e206b1ff81c83403a7d78bd191b250d73afd3
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"
7 #include <set>
8 #include <vector>
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"
23 using base::hash_map;
24 using std::set;
25 using std::vector;
26 using testing::_;
27 using testing::InSequence;
28 using testing::InvokeWithoutArgs;
29 using testing::StrictMock;
31 namespace net {
32 namespace test {
33 namespace {
35 const QuicPriority kSomeMiddlePriority = 2;
37 class TestCryptoStream : public QuicCryptoStream {
38 public:
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;
48 string error_details;
49 session()->config()->ToHandshakeMessage(&msg);
50 const QuicErrorCode error = session()->config()->ProcessClientHello(
51 msg, &error_details);
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 {
61 public:
62 TestStream(QuicStreamId id, QuicSession* session)
63 : QuicDataStream(id, session) {
66 using ReliableQuicStream::CloseWriteSide;
68 virtual uint32 ProcessData(const char* data, uint32 data_len) {
69 return data_len;
72 MOCK_METHOD0(OnCanWrite, void());
75 // Poor man's functor for use as callback in a mock.
76 class StreamBlocker {
77 public:
78 StreamBlocker(QuicSession* session, QuicStreamId stream_id)
79 : session_(session),
80 stream_id_(stream_id) {
83 void MarkWriteBlocked() {
84 session_->MarkWriteBlocked(stream_id_, kSomeMiddlePriority);
87 private:
88 QuicSession* const session_;
89 const QuicStreamId stream_id_;
92 class TestSession : public QuicSession {
93 public:
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);
106 return 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> {
125 protected:
126 QuicSessionTest()
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";
132 headers_["cookie"] =
133 "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
134 "__utmc=160408618; "
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;
162 } else {
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();
220 CloseStream(4);
221 CheckClosedStreams();
222 CloseStream(2);
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
262 0x00, 0x00,
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),
269 arraysize(data));
270 } else {
271 ReliableQuicStream* stream = session_.GetIncomingDataStream(3);
272 const char data[] =
273 "\0\0\0\0" // priority
274 "\1\0\0\0" // headers id
275 "\0\0\0\4" // length
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);
291 InSequence s;
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());
325 InSequence s;
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
330 // first.
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());
359 InSequence s;
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.
393 frames[0] = frame1;
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());
511 } // namespace
512 } // namespace test
513 } // namespace net