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