Home | History | Annotate | Download | only in quic
      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 
     22 using base::hash_map;
     23 using std::set;
     24 using std::vector;
     25 using testing::_;
     26 using testing::InSequence;
     27 using testing::InvokeWithoutArgs;
     28 using testing::StrictMock;
     29 
     30 namespace net {
     31 namespace test {
     32 namespace {
     33 
     34 const QuicPriority kSomeMiddlePriority = 2;
     35 
     36 class 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 
     59 class 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.
     75 class 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 
     91 class 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 
    123 class 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 
    178 TEST_F(QuicSessionTest, PeerAddress) {
    179   EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort), session_.peer_address());
    180 }
    181 
    182 TEST_F(QuicSessionTest, IsCryptoHandshakeConfirmed) {
    183   EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
    184   CryptoHandshakeMessage message;
    185   session_.crypto_stream_.OnHandshakeMessage(message);
    186   EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
    187 }
    188 
    189 TEST_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 
    196 TEST_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 
    205 TEST_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 
    220 TEST_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 
    239 TEST_F(QuicSessionTest, StreamIdTooLarge) {
    240   session_.GetIncomingReliableStream(3);
    241   EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID));
    242   session_.GetIncomingReliableStream(105);
    243 }
    244 
    245 TEST_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 
    256 TEST_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 
    276 TEST_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 
    323 TEST_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
    340 TEST_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 
    374 TEST_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 
    386 TEST_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 
    395 TEST_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 
    435 TEST_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 
    460 TEST_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