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.
4
5#include "net/quic/quic_session.h"
6
7#include <set>
8#include <vector>
9
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_test_utils.h"
17#include "net/quic/test_tools/reliable_quic_stream_peer.h"
18#include "net/spdy/spdy_framer.h"
19#include "testing/gmock/include/gmock/gmock.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22using base::hash_map;
23using std::set;
24using std::vector;
25using testing::_;
26using testing::InSequence;
27using testing::InvokeWithoutArgs;
28using testing::StrictMock;
29
30namespace net {
31namespace test {
32namespace {
33
34const QuicPriority kSomeMiddlePriority = 2;
35
36class TestCryptoStream : public QuicCryptoStream {
37 public:
38  explicit TestCryptoStream(QuicSession* session)
39      : QuicCryptoStream(session) {
40  }
41
42  virtual void OnHandshakeMessage(
43      const CryptoHandshakeMessage& message) OVERRIDE {
44    encryption_established_ = true;
45    handshake_confirmed_ = true;
46    CryptoHandshakeMessage msg;
47    string error_details;
48    session()->config()->ToHandshakeMessage(&msg);
49    const QuicErrorCode error = session()->config()->ProcessClientHello(
50        msg, &error_details);
51    EXPECT_EQ(QUIC_NO_ERROR, error);
52    session()->OnConfigNegotiated();
53    session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
54  }
55
56  MOCK_METHOD0(OnCanWrite, void());
57};
58
59class TestStream : public QuicDataStream {
60 public:
61  TestStream(QuicStreamId id, QuicSession* session)
62      : QuicDataStream(id, session) {
63  }
64
65  using ReliableQuicStream::CloseWriteSide;
66
67  virtual uint32 ProcessData(const char* data, uint32 data_len) {
68    return data_len;
69  }
70
71  MOCK_METHOD0(OnCanWrite, void());
72};
73
74// Poor man's functor for use as callback in a mock.
75class StreamBlocker {
76 public:
77  StreamBlocker(QuicSession* session, QuicStreamId stream_id)
78      : session_(session),
79        stream_id_(stream_id) {
80  }
81
82  void MarkWriteBlocked() {
83    session_->MarkWriteBlocked(stream_id_, kSomeMiddlePriority);
84  }
85
86 private:
87  QuicSession* const session_;
88  const QuicStreamId stream_id_;
89};
90
91class TestSession : public QuicSession {
92 public:
93  explicit TestSession(QuicConnection* connection)
94      : QuicSession(connection, DefaultQuicConfig()),
95        crypto_stream_(this) {
96  }
97
98  virtual TestCryptoStream* GetCryptoStream() OVERRIDE {
99    return &crypto_stream_;
100  }
101
102  virtual TestStream* CreateOutgoingDataStream() OVERRIDE {
103    TestStream* stream = new TestStream(GetNextStreamId(), this);
104    ActivateStream(stream);
105    return stream;
106  }
107
108  virtual TestStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE {
109    return new TestStream(id, this);
110  }
111
112  bool IsClosedStream(QuicStreamId id) {
113    return QuicSession::IsClosedStream(id);
114  }
115
116  QuicDataStream* GetIncomingReliableStream(QuicStreamId stream_id) {
117    return QuicSession::GetIncomingReliableStream(stream_id);
118  }
119
120  TestCryptoStream crypto_stream_;
121};
122
123class QuicSessionTest : public ::testing::Test {
124 protected:
125  QuicSessionTest()
126      : connection_(new MockConnection(true)),
127        session_(connection_) {
128    headers_[":host"] = "www.google.com";
129    headers_[":path"] = "/index.hml";
130    headers_[":scheme"] = "http";
131    headers_["cookie"] =
132        "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
133        "__utmc=160408618; "
134        "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
135        "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
136        "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
137        "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
138        "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
139        "1zFMi5vzcns38-8_Sns; "
140        "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
141        "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
142        "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
143        "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
144        "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
145        "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
146        "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
147        "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
148        "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
149        "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
150        "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
151        "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
152        "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
153        "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
154        "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
155  }
156
157  void CheckClosedStreams() {
158    for (int i = kCryptoStreamId; i < 100; i++) {
159      if (closed_streams_.count(i) == 0) {
160        EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i;
161      } else {
162        EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i;
163      }
164    }
165  }
166
167  void CloseStream(QuicStreamId id) {
168    session_.CloseStream(id);
169    closed_streams_.insert(id);
170  }
171
172  MockConnection* connection_;
173  TestSession session_;
174  set<QuicStreamId> closed_streams_;
175  SpdyHeaderBlock headers_;
176};
177
178TEST_F(QuicSessionTest, PeerAddress) {
179  EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort), session_.peer_address());
180}
181
182TEST_F(QuicSessionTest, IsCryptoHandshakeConfirmed) {
183  EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
184  CryptoHandshakeMessage message;
185  session_.crypto_stream_.OnHandshakeMessage(message);
186  EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
187}
188
189TEST_F(QuicSessionTest, IsClosedStreamDefault) {
190  // Ensure that no streams are initially closed.
191  for (int i = kCryptoStreamId; i < 100; i++) {
192    EXPECT_FALSE(session_.IsClosedStream(i));
193  }
194}
195
196TEST_F(QuicSessionTest, ImplicitlyCreatedStreams) {
197  ASSERT_TRUE(session_.GetIncomingReliableStream(7) != NULL);
198  // Both 3 and 5 should be implicitly created.
199  EXPECT_FALSE(session_.IsClosedStream(3));
200  EXPECT_FALSE(session_.IsClosedStream(5));
201  ASSERT_TRUE(session_.GetIncomingReliableStream(5) != NULL);
202  ASSERT_TRUE(session_.GetIncomingReliableStream(3) != NULL);
203}
204
205TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) {
206  TestStream* stream2 = session_.CreateOutgoingDataStream();
207  EXPECT_EQ(2u, stream2->id());
208  QuicDataStreamPeer::SetHeadersDecompressed(stream2, true);
209  TestStream* stream4 = session_.CreateOutgoingDataStream();
210  EXPECT_EQ(4u, stream4->id());
211  QuicDataStreamPeer::SetHeadersDecompressed(stream4, true);
212
213  CheckClosedStreams();
214  CloseStream(4);
215  CheckClosedStreams();
216  CloseStream(2);
217  CheckClosedStreams();
218}
219
220TEST_F(QuicSessionTest, IsClosedStreamPeerCreated) {
221  QuicDataStream* stream3 = session_.GetIncomingReliableStream(3);
222  QuicDataStreamPeer::SetHeadersDecompressed(stream3, true);
223  QuicDataStream* stream5 = session_.GetIncomingReliableStream(5);
224  QuicDataStreamPeer::SetHeadersDecompressed(stream5, true);
225
226  CheckClosedStreams();
227  CloseStream(3);
228  CheckClosedStreams();
229  CloseStream(5);
230  // Create stream id 9, and implicitly 7
231  QuicDataStream* stream9 = session_.GetIncomingReliableStream(9);
232  QuicDataStreamPeer::SetHeadersDecompressed(stream9, true);
233  CheckClosedStreams();
234  // Close 9, but make sure 7 is still not closed
235  CloseStream(9);
236  CheckClosedStreams();
237}
238
239TEST_F(QuicSessionTest, StreamIdTooLarge) {
240  session_.GetIncomingReliableStream(3);
241  EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID));
242  session_.GetIncomingReliableStream(105);
243}
244
245TEST_F(QuicSessionTest, DecompressionError) {
246  ReliableQuicStream* stream = session_.GetIncomingReliableStream(3);
247  EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE));
248  const char data[] =
249      "\0\0\0\0"   // priority
250      "\1\0\0\0"   // headers id
251      "\0\0\0\4"   // length
252      "abcd";      // invalid compressed data
253  stream->ProcessRawData(data, arraysize(data));
254}
255
256TEST_F(QuicSessionTest, OnCanWrite) {
257  TestStream* stream2 = session_.CreateOutgoingDataStream();
258  TestStream* stream4 = session_.CreateOutgoingDataStream();
259  TestStream* stream6 = session_.CreateOutgoingDataStream();
260
261  session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
262  session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
263  session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
264
265  InSequence s;
266  StreamBlocker stream2_blocker(&session_, stream2->id());
267  EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
268      // Reregister, to test the loop limit.
269      InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
270  EXPECT_CALL(*stream6, OnCanWrite());
271  EXPECT_CALL(*stream4, OnCanWrite());
272
273  EXPECT_FALSE(session_.OnCanWrite());
274}
275
276TEST_F(QuicSessionTest, BufferedHandshake) {
277  EXPECT_FALSE(session_.HasPendingHandshake());  // Default value.
278
279  // Test that blocking other streams does not change our status.
280  TestStream* stream2 = session_.CreateOutgoingDataStream();
281  StreamBlocker stream2_blocker(&session_, stream2->id());
282  stream2_blocker.MarkWriteBlocked();
283  EXPECT_FALSE(session_.HasPendingHandshake());
284
285  TestStream* stream3 = session_.CreateOutgoingDataStream();
286  StreamBlocker stream3_blocker(&session_, stream3->id());
287  stream3_blocker.MarkWriteBlocked();
288  EXPECT_FALSE(session_.HasPendingHandshake());
289
290  // Blocking (due to buffering of) the Crypto stream is detected.
291  session_.MarkWriteBlocked(kCryptoStreamId, kSomeMiddlePriority);
292  EXPECT_TRUE(session_.HasPendingHandshake());
293
294  TestStream* stream4 = session_.CreateOutgoingDataStream();
295  StreamBlocker stream4_blocker(&session_, stream4->id());
296  stream4_blocker.MarkWriteBlocked();
297  EXPECT_TRUE(session_.HasPendingHandshake());
298
299  InSequence s;
300  // Force most streams to re-register, which is common scenario when we block
301  // the Crypto stream, and only the crypto stream can "really" write.
302
303  // Due to prioritization, we *should* be asked to write the crypto stream
304  // first.
305  // Don't re-register the crypto stream (which signals complete writing).
306  TestCryptoStream* crypto_stream = session_.GetCryptoStream();
307  EXPECT_CALL(*crypto_stream, OnCanWrite());
308
309  // Re-register all other streams, to show they weren't able to proceed.
310  EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
311      InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
312
313  EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(
314      InvokeWithoutArgs(&stream3_blocker, &StreamBlocker::MarkWriteBlocked));
315
316  EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(
317      InvokeWithoutArgs(&stream4_blocker, &StreamBlocker::MarkWriteBlocked));
318
319  EXPECT_FALSE(session_.OnCanWrite());
320  EXPECT_FALSE(session_.HasPendingHandshake());  // Crypto stream wrote.
321}
322
323TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
324  TestStream* stream2 = session_.CreateOutgoingDataStream();
325  TestStream* stream4 = session_.CreateOutgoingDataStream();
326  TestStream* stream6 = session_.CreateOutgoingDataStream();
327
328  session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
329  session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
330  session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
331  CloseStream(stream6->id());
332
333  InSequence s;
334  EXPECT_CALL(*stream2, OnCanWrite());
335  EXPECT_CALL(*stream4, OnCanWrite());
336  EXPECT_TRUE(session_.OnCanWrite());
337}
338
339// Regression test for http://crbug.com/248737
340TEST_F(QuicSessionTest, OutOfOrderHeaders) {
341  QuicSpdyCompressor compressor;
342  vector<QuicStreamFrame> frames;
343  QuicPacketHeader header;
344  header.public_header.guid = session_.guid();
345
346  TestStream* stream2 = session_.CreateOutgoingDataStream();
347  TestStream* stream4 = session_.CreateOutgoingDataStream();
348  stream2->CloseWriteSide();
349  stream4->CloseWriteSide();
350
351  // Create frame with headers for stream2.
352  string compressed_headers1 = compressor.CompressHeaders(headers_);
353  QuicStreamFrame frame1(
354      stream2->id(), false, 0, MakeIOVector(compressed_headers1));
355
356  // Create frame with headers for stream4.
357  string compressed_headers2 = compressor.CompressHeaders(headers_);
358  QuicStreamFrame frame2(
359      stream4->id(), true, 0, MakeIOVector(compressed_headers2));
360
361  // Process the second frame first.  This will cause the headers to
362  // be queued up and processed after the first frame is processed.
363  frames.push_back(frame2);
364  session_.OnStreamFrames(frames);
365
366  // Process the first frame, and un-cork the buffered headers.
367  frames[0] = frame1;
368  session_.OnStreamFrames(frames);
369
370  // Ensure that the streams actually close and we don't DCHECK.
371  connection_->CloseConnection(QUIC_CONNECTION_TIMED_OUT, true);
372}
373
374TEST_F(QuicSessionTest, SendGoAway) {
375  // After sending a GoAway, ensure new incoming streams cannot be created and
376  // result in a RST being sent.
377  EXPECT_CALL(*connection_,
378              SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away."));
379  session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
380  EXPECT_TRUE(session_.goaway_sent());
381
382  EXPECT_CALL(*connection_, SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY));
383  EXPECT_FALSE(session_.GetIncomingReliableStream(3u));
384}
385
386TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) {
387  EXPECT_EQ(kDefaultInitialTimeoutSecs,
388            QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
389  CryptoHandshakeMessage msg;
390  session_.crypto_stream_.OnHandshakeMessage(msg);
391  EXPECT_EQ(kDefaultTimeoutSecs,
392            QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
393}
394
395TEST_F(QuicSessionTest, ZombieStream) {
396  StrictMock<MockConnection>* connection =
397      new StrictMock<MockConnection>(false);
398  TestSession session(connection);
399
400  TestStream* stream3 = session.CreateOutgoingDataStream();
401  EXPECT_EQ(3u, stream3->id());
402  TestStream* stream5 = session.CreateOutgoingDataStream();
403  EXPECT_EQ(5u, stream5->id());
404  EXPECT_EQ(2u, session.GetNumOpenStreams());
405
406  // Reset the stream, but since the headers have not been decompressed
407  // it will become a zombie and will continue to process data
408  // until the headers are decompressed.
409  EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED));
410  session.SendRstStream(3, QUIC_STREAM_CANCELLED);
411
412  EXPECT_EQ(1u, session.GetNumOpenStreams());
413
414  vector<QuicStreamFrame> frames;
415  QuicPacketHeader header;
416  header.public_header.guid = session_.guid();
417
418  // Create frame with headers for stream2.
419  QuicSpdyCompressor compressor;
420  string compressed_headers1 = compressor.CompressHeaders(headers_);
421  QuicStreamFrame frame1(
422      stream3->id(), false, 0, MakeIOVector(compressed_headers1));
423
424  // Process the second frame first.  This will cause the headers to
425  // be queued up and processed after the first frame is processed.
426  frames.push_back(frame1);
427  EXPECT_FALSE(stream3->headers_decompressed());
428
429  session.OnStreamFrames(frames);
430  EXPECT_EQ(1u, session.GetNumOpenStreams());
431
432  EXPECT_TRUE(connection->connected());
433}
434
435TEST_F(QuicSessionTest, ZombieStreamConnectionClose) {
436  StrictMock<MockConnection>* connection =
437      new StrictMock<MockConnection>(false);
438  TestSession session(connection);
439
440  TestStream* stream3 = session.CreateOutgoingDataStream();
441  EXPECT_EQ(3u, stream3->id());
442  TestStream* stream5 = session.CreateOutgoingDataStream();
443  EXPECT_EQ(5u, stream5->id());
444  EXPECT_EQ(2u, session.GetNumOpenStreams());
445
446  stream3->CloseWriteSide();
447  // Reset the stream, but since the headers have not been decompressed
448  // it will become a zombie and will continue to process data
449  // until the headers are decompressed.
450  EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED));
451  session.SendRstStream(3, QUIC_STREAM_CANCELLED);
452
453  EXPECT_EQ(1u, session.GetNumOpenStreams());
454
455  connection->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false);
456
457  EXPECT_EQ(0u, session.GetNumOpenStreams());
458}
459
460TEST_F(QuicSessionTest, RstStreamBeforeHeadersDecompressed) {
461  // Send two bytes of payload.
462  QuicStreamFrame data1(3, false, 0, MakeIOVector("HT"));
463  vector<QuicStreamFrame> frames;
464  frames.push_back(data1);
465  EXPECT_TRUE(session_.OnStreamFrames(frames));
466  EXPECT_EQ(1u, session_.GetNumOpenStreams());
467
468  // Send a reset before the headers have been decompressed.  This causes
469  // an unrecoverable compression context state.
470  EXPECT_CALL(*connection_, SendConnectionClose(
471      QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED));
472
473  QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR);
474  session_.OnRstStream(rst1);
475  EXPECT_EQ(0u, session_.GetNumOpenStreams());
476}
477
478}  // namespace
479}  // namespace test
480}  // namespace net
481