// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/tools/quic/quic_server_session.h" #include "net/quic/crypto/cached_network_parameters.h" #include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_crypto_server_stream.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_config_peer.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_data_stream_peer.h" #include "net/quic/test_tools/quic_sent_packet_manager_peer.h" #include "net/quic/test_tools/quic_session_peer.h" #include "net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/quic_spdy_server_stream.h" #include "net/tools/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using __gnu_cxx::vector; using net::test::MockConnection; using net::test::QuicConfigPeer; using net::test::QuicConnectionPeer; using net::test::QuicDataStreamPeer; using net::test::QuicSentPacketManagerPeer; using net::test::QuicSessionPeer; using net::test::QuicSustainedBandwidthRecorderPeer; using net::test::SupportedVersions; using net::test::ValueRestore; using net::test::kClientDataStreamId1; using net::test::kClientDataStreamId2; using net::test::kClientDataStreamId3; using net::test::kClientDataStreamId4; using std::string; using testing::StrictMock; using testing::_; namespace net { namespace tools { namespace test { class QuicServerSessionPeer { public: static QuicDataStream* GetIncomingDataStream( QuicServerSession* s, QuicStreamId id) { return s->GetIncomingDataStream(id); } static QuicDataStream* GetDataStream(QuicServerSession* s, QuicStreamId id) { return s->GetDataStream(id); } static void SetCryptoStream(QuicServerSession* s, QuicCryptoServerStream* crypto_stream) { s->crypto_stream_.reset(crypto_stream); } }; namespace { const size_t kMaxStreamsForTest = 10; class QuicServerSessionTest : public ::testing::TestWithParam { protected: QuicServerSessionTest() : crypto_config_(QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance()) { config_.SetMaxStreamsPerConnection(kMaxStreamsForTest, kMaxStreamsForTest); config_.SetInitialStreamFlowControlWindowToSend( kInitialStreamFlowControlWindowForTest); config_.SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); connection_ = new StrictMock(true, SupportedVersions(GetParam())); session_.reset(new QuicServerSession(config_, connection_, &owner_)); MockClock clock; handshake_message_.reset(crypto_config_.AddDefaultConfig( QuicRandom::GetInstance(), &clock, QuicCryptoServerConfig::ConfigOptions())); session_->InitializeSession(crypto_config_); visitor_ = QuicConnectionPeer::GetVisitor(connection_); } QuicVersion version() const { return connection_->version(); } StrictMock owner_; StrictMock* connection_; QuicConfig config_; QuicCryptoServerConfig crypto_config_; scoped_ptr session_; scoped_ptr handshake_message_; QuicConnectionVisitorInterface* visitor_; }; // Compares CachedNetworkParameters. MATCHER_P(EqualsProto, network_params, "") { CachedNetworkParameters reference(network_params); return (arg->bandwidth_estimate_bytes_per_second() == reference.bandwidth_estimate_bytes_per_second() && arg->bandwidth_estimate_bytes_per_second() == reference.bandwidth_estimate_bytes_per_second() && arg->max_bandwidth_estimate_bytes_per_second() == reference.max_bandwidth_estimate_bytes_per_second() && arg->max_bandwidth_timestamp_seconds() == reference.max_bandwidth_timestamp_seconds() && arg->min_rtt_ms() == reference.min_rtt_ms() && arg->previous_connection_state() == reference.previous_connection_state()); } INSTANTIATE_TEST_CASE_P(Tests, QuicServerSessionTest, ::testing::ValuesIn(QuicSupportedVersions())); TEST_P(QuicServerSessionTest, CloseStreamDueToReset) { // Open a stream, then reset it. // Send two bytes of payload to open it. QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT")); vector frames; frames.push_back(data1); session_->OnStreamFrames(frames); EXPECT_EQ(1u, session_->GetNumOpenStreams()); // Send a reset (and expect the peer to send a RST in response). QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0); EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT, 0)); visitor_->OnRstStream(rst1); EXPECT_EQ(0u, session_->GetNumOpenStreams()); // Send the same two bytes of payload in a new packet. visitor_->OnStreamFrames(frames); // The stream should not be re-opened. EXPECT_EQ(0u, session_->GetNumOpenStreams()); EXPECT_TRUE(connection_->connected()); } TEST_P(QuicServerSessionTest, NeverOpenStreamDueToReset) { // Send a reset (and expect the peer to send a RST in response). QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0); EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT, 0)); visitor_->OnRstStream(rst1); EXPECT_EQ(0u, session_->GetNumOpenStreams()); // Send two bytes of payload. QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT")); vector frames; frames.push_back(data1); visitor_->OnStreamFrames(frames); // The stream should never be opened, now that the reset is received. EXPECT_EQ(0u, session_->GetNumOpenStreams()); EXPECT_TRUE(connection_->connected()); } TEST_P(QuicServerSessionTest, AcceptClosedStream) { vector frames; // Send (empty) compressed headers followed by two bytes of data. frames.push_back(QuicStreamFrame(kClientDataStreamId1, false, 0, MakeIOVector("\1\0\0\0\0\0\0\0HT"))); frames.push_back(QuicStreamFrame(kClientDataStreamId2, false, 0, MakeIOVector("\2\0\0\0\0\0\0\0HT"))); visitor_->OnStreamFrames(frames); EXPECT_EQ(2u, session_->GetNumOpenStreams()); // Send a reset (and expect the peer to send a RST in response). QuicRstStreamFrame rst(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0); EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT, 0)); visitor_->OnRstStream(rst); // If we were tracking, we'd probably want to reject this because it's data // past the reset point of stream 3. As it's a closed stream we just drop the // data on the floor, but accept the packet because it has data for stream 5. frames.clear(); frames.push_back( QuicStreamFrame(kClientDataStreamId1, false, 2, MakeIOVector("TP"))); frames.push_back( QuicStreamFrame(kClientDataStreamId2, false, 2, MakeIOVector("TP"))); visitor_->OnStreamFrames(frames); // The stream should never be opened, now that the reset is received. EXPECT_EQ(1u, session_->GetNumOpenStreams()); EXPECT_TRUE(connection_->connected()); } TEST_P(QuicServerSessionTest, MaxOpenStreams) { // Test that the server closes the connection if a client attempts to open too // many data streams. The server accepts slightly more than the negotiated // stream limit to deal with rare cases where a client FIN/RST is lost. // The slightly increased stream limit is set during config negotiation. It // should be either an increase of 10 over negotiated limit, or a fixed // percentage scaling, whichever is larger. Test both before continuing. EXPECT_EQ(kMaxStreamsForTest, session_->get_max_open_streams()); session_->OnConfigNegotiated(); EXPECT_LT(kMaxStreamsMultiplier * kMaxStreamsForTest, kMaxStreamsForTest + kMaxStreamsMinimumIncrement); EXPECT_EQ(kMaxStreamsForTest + kMaxStreamsMinimumIncrement, session_->get_max_open_streams()); EXPECT_EQ(0u, session_->GetNumOpenStreams()); QuicStreamId stream_id = kClientDataStreamId1; // Open the max configured number of streams, should be no problem. for (size_t i = 0; i < kMaxStreamsForTest; ++i) { EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream(session_.get(), stream_id)); stream_id += 2; } // Open more streams: server should accept slightly more than the limit. for (size_t i = 0; i < kMaxStreamsMinimumIncrement; ++i) { EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream(session_.get(), stream_id)); stream_id += 2; } // Now violate the server's internal stream limit. EXPECT_CALL(*connection_, SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS)); stream_id += 2; EXPECT_FALSE( QuicServerSessionPeer::GetIncomingDataStream(session_.get(), stream_id)); } TEST_P(QuicServerSessionTest, MaxOpenStreamsImplicit) { // Test that the server closes the connection if a client attempts to open too // many data streams implicitly. The server accepts slightly more than the // negotiated stream limit to deal with rare cases where a client FIN/RST is // lost. // The slightly increased stream limit is set during config negotiation. EXPECT_EQ(kMaxStreamsForTest, session_->get_max_open_streams()); session_->OnConfigNegotiated(); EXPECT_LT(kMaxStreamsMultiplier * kMaxStreamsForTest, kMaxStreamsForTest + kMaxStreamsMinimumIncrement); EXPECT_EQ(kMaxStreamsForTest + kMaxStreamsMinimumIncrement, session_->get_max_open_streams()); EXPECT_EQ(0u, session_->GetNumOpenStreams()); EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream( session_.get(), kClientDataStreamId1)); // Implicitly open streams up to the server's limit. const int kActualMaxStreams = kMaxStreamsForTest + kMaxStreamsMinimumIncrement; const int kMaxValidStreamId = kClientDataStreamId1 + (kActualMaxStreams - 1) * 2; EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream( session_.get(), kMaxValidStreamId)); // Opening a further stream will result in connection close. EXPECT_CALL(*connection_, SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS)); EXPECT_FALSE(QuicServerSessionPeer::GetIncomingDataStream( session_.get(), kMaxValidStreamId + 2)); } TEST_P(QuicServerSessionTest, GetEvenIncomingError) { // Incoming streams on the server session must be odd. EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID)); EXPECT_EQ(nullptr, QuicServerSessionPeer::GetIncomingDataStream(session_.get(), 4)); } TEST_P(QuicServerSessionTest, SetFecProtectionFromConfig) { ValueRestore old_flag(&FLAGS_enable_quic_fec, true); // Set received config to have FEC connection option. QuicTagVector copt; copt.push_back(kFHDR); QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); session_->OnConfigNegotiated(); // Verify that headers stream is always protected and data streams are // optionally protected. EXPECT_EQ(FEC_PROTECT_ALWAYS, QuicSessionPeer::GetHeadersStream(session_.get())->fec_policy()); QuicDataStream* stream = QuicServerSessionPeer::GetIncomingDataStream( session_.get(), kClientDataStreamId1); ASSERT_TRUE(stream); EXPECT_EQ(FEC_PROTECT_OPTIONAL, stream->fec_policy()); } class MockQuicCryptoServerStream : public QuicCryptoServerStream { public: explicit MockQuicCryptoServerStream( const QuicCryptoServerConfig& crypto_config, QuicSession* session) : QuicCryptoServerStream(crypto_config, session) {} ~MockQuicCryptoServerStream() override {} MOCK_METHOD1(SendServerConfigUpdate, void(const CachedNetworkParameters* cached_network_parameters)); private: DISALLOW_COPY_AND_ASSIGN(MockQuicCryptoServerStream); }; TEST_P(QuicServerSessionTest, BandwidthEstimates) { // Test that bandwidth estimate updates are sent to the client, only after the // bandwidth estimate has changes sufficiently, and enough time has passed, // and we don't have any other data to write. int32 bandwidth_estimate_kbytes_per_second = 123; int32 max_bandwidth_estimate_kbytes_per_second = 134; int32 max_bandwidth_estimate_timestamp = 1122334455; const string serving_region = "not a real region"; session_->set_serving_region(serving_region); MockQuicCryptoServerStream* crypto_stream = new MockQuicCryptoServerStream(crypto_config_, session_.get()); QuicServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream); // Set some initial bandwidth values. QuicSentPacketManager* sent_packet_manager = QuicConnectionPeer::GetSentPacketManager(session_->connection()); QuicSustainedBandwidthRecorder& bandwidth_recorder = QuicSentPacketManagerPeer::GetBandwidthRecorder(sent_packet_manager); // Seed an rtt measurement equal to the initial default rtt. RttStats* rtt_stats = QuicSentPacketManagerPeer::GetRttStats(sent_packet_manager); rtt_stats->UpdateRtt(QuicTime::Delta::FromMicroseconds( rtt_stats->initial_rtt_us()), QuicTime::Delta::Zero(), QuicTime::Zero()); QuicSustainedBandwidthRecorderPeer::SetBandwidthEstimate( &bandwidth_recorder, bandwidth_estimate_kbytes_per_second); QuicSustainedBandwidthRecorderPeer::SetMaxBandwidthEstimate( &bandwidth_recorder, max_bandwidth_estimate_kbytes_per_second, max_bandwidth_estimate_timestamp); // Queue up some pending data. session_->MarkWriteBlocked(kCryptoStreamId, QuicWriteBlockedList::kHighestPriority); EXPECT_TRUE(session_->HasDataToWrite()); // There will be no update sent yet - not enough time has passed. QuicTime now = QuicTime::Zero(); session_->OnCongestionWindowChange(now); // Bandwidth estimate has now changed sufficiently but not enough time has // passed to send a Server Config Update. bandwidth_estimate_kbytes_per_second = bandwidth_estimate_kbytes_per_second * 1.6; session_->OnCongestionWindowChange(now); // Bandwidth estimate has now changed sufficiently and enough time has passed, // but not enough packets have been sent. int64 srtt_ms = sent_packet_manager->GetRttStats()->smoothed_rtt().ToMilliseconds(); now = now.Add(QuicTime::Delta::FromMilliseconds( kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms)); session_->OnCongestionWindowChange(now); // The connection no longer has pending data to be written. session_->OnCanWrite(); EXPECT_FALSE(session_->HasDataToWrite()); session_->OnCongestionWindowChange(now); // Bandwidth estimate has now changed sufficiently, enough time has passed, // and enough packets have been sent. QuicConnectionPeer::SetSequenceNumberOfLastSentPacket( session_->connection(), kMinPacketsBetweenServerConfigUpdates); // Verify that the proto has exactly the values we expect. CachedNetworkParameters expected_network_params; expected_network_params.set_bandwidth_estimate_bytes_per_second( bandwidth_recorder.BandwidthEstimate().ToBytesPerSecond()); expected_network_params.set_max_bandwidth_estimate_bytes_per_second( bandwidth_recorder.MaxBandwidthEstimate().ToBytesPerSecond()); expected_network_params.set_max_bandwidth_timestamp_seconds( bandwidth_recorder.MaxBandwidthTimestamp()); expected_network_params.set_min_rtt_ms(session_->connection() ->sent_packet_manager() .GetRttStats() ->min_rtt() .ToMilliseconds()); expected_network_params.set_previous_connection_state( CachedNetworkParameters::CONGESTION_AVOIDANCE); expected_network_params.set_timestamp( session_->connection()->clock()->WallNow().ToUNIXSeconds()); expected_network_params.set_serving_region(serving_region); EXPECT_CALL(*crypto_stream, SendServerConfigUpdate(EqualsProto(expected_network_params))) .Times(1); session_->OnCongestionWindowChange(now); } TEST_P(QuicServerSessionTest, BandwidthResumptionExperiment) { ValueRestore old_flag( &FLAGS_quic_enable_bandwidth_resumption_experiment, true); // Test that if a client provides a CachedNetworkParameters with the same // serving region as the current server, that this data is passed down to the // send algorithm. // Client has sent kBWRE connection option to trigger bandwidth resumption. QuicTagVector copt; copt.push_back(kBWRE); QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); const string kTestServingRegion = "a serving region"; session_->set_serving_region(kTestServingRegion); QuicCryptoServerStream* crypto_stream = static_cast( QuicSessionPeer::GetCryptoStream(session_.get())); // No effect if no CachedNetworkParameters provided. EXPECT_CALL(*connection_, ResumeConnectionState(_)).Times(0); session_->OnConfigNegotiated(); // No effect if CachedNetworkParameters provided, but different serving // regions. CachedNetworkParameters cached_network_params; cached_network_params.set_bandwidth_estimate_bytes_per_second(1); cached_network_params.set_serving_region("different serving region"); crypto_stream->set_previous_cached_network_params(cached_network_params); EXPECT_CALL(*connection_, ResumeConnectionState(_)).Times(0); session_->OnConfigNegotiated(); // Same serving region results in CachedNetworkParameters being stored. cached_network_params.set_serving_region(kTestServingRegion); crypto_stream->set_previous_cached_network_params(cached_network_params); EXPECT_CALL(*connection_, ResumeConnectionState(_)).Times(1); session_->OnConfigNegotiated(); } } // namespace } // namespace test } // namespace tools } // namespace net