|  | // 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 "quic/tools/quic_simple_server_session.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "absl/strings/str_cat.h" | 
|  | #include "quic/core/crypto/null_encrypter.h" | 
|  | #include "quic/core/crypto/quic_crypto_server_config.h" | 
|  | #include "quic/core/crypto/quic_random.h" | 
|  | #include "quic/core/http/http_encoder.h" | 
|  | #include "quic/core/proto/cached_network_parameters_proto.h" | 
|  | #include "quic/core/quic_connection.h" | 
|  | #include "quic/core/quic_crypto_server_stream.h" | 
|  | #include "quic/core/quic_utils.h" | 
|  | #include "quic/core/quic_versions.h" | 
|  | #include "quic/core/tls_server_handshaker.h" | 
|  | #include "quic/platform/api/quic_containers.h" | 
|  | #include "quic/platform/api/quic_expect_bug.h" | 
|  | #include "quic/platform/api/quic_flags.h" | 
|  | #include "quic/platform/api/quic_socket_address.h" | 
|  | #include "quic/platform/api/quic_test.h" | 
|  | #include "quic/test_tools/crypto_test_utils.h" | 
|  | #include "quic/test_tools/mock_quic_session_visitor.h" | 
|  | #include "quic/test_tools/quic_config_peer.h" | 
|  | #include "quic/test_tools/quic_connection_peer.h" | 
|  | #include "quic/test_tools/quic_sent_packet_manager_peer.h" | 
|  | #include "quic/test_tools/quic_session_peer.h" | 
|  | #include "quic/test_tools/quic_spdy_session_peer.h" | 
|  | #include "quic/test_tools/quic_stream_peer.h" | 
|  | #include "quic/test_tools/quic_sustained_bandwidth_recorder_peer.h" | 
|  | #include "quic/test_tools/quic_test_utils.h" | 
|  | #include "quic/tools/quic_backend_response.h" | 
|  | #include "quic/tools/quic_memory_cache_backend.h" | 
|  | #include "quic/tools/quic_simple_server_stream.h" | 
|  |  | 
|  | using testing::_; | 
|  | using testing::AnyNumber; | 
|  | using testing::AtLeast; | 
|  | using testing::InSequence; | 
|  | using testing::Invoke; | 
|  | using testing::Return; | 
|  | using testing::StrictMock; | 
|  |  | 
|  | namespace quic { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | // Data to be sent on a request stream.  In Google QUIC, this is interpreted as | 
|  | // DATA payload (there is no framing on request streams).  In IETF QUIC, this is | 
|  | // interpreted as HEADERS frame (type 0x1) with payload length 122 ('z').  Since | 
|  | // no payload is included, QPACK decoder will not be invoked. | 
|  | const char* const kStreamData = "\1z"; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class QuicSimpleServerSessionPeer { | 
|  | public: | 
|  | static void SetCryptoStream(QuicSimpleServerSession* s, | 
|  | QuicCryptoServerStreamBase* crypto_stream) { | 
|  | s->crypto_stream_.reset(crypto_stream); | 
|  | } | 
|  |  | 
|  | static QuicSpdyStream* CreateIncomingStream(QuicSimpleServerSession* s, | 
|  | QuicStreamId id) { | 
|  | return s->CreateIncomingStream(id); | 
|  | } | 
|  |  | 
|  | static QuicSimpleServerStream* CreateOutgoingUnidirectionalStream( | 
|  | QuicSimpleServerSession* s) { | 
|  | return s->CreateOutgoingUnidirectionalStream(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const size_t kMaxStreamsForTest = 10; | 
|  |  | 
|  | class MockQuicCryptoServerStream : public QuicCryptoServerStream { | 
|  | public: | 
|  | explicit MockQuicCryptoServerStream( | 
|  | const QuicCryptoServerConfig* crypto_config, | 
|  | QuicCompressedCertsCache* compressed_certs_cache, | 
|  | QuicSession* session, | 
|  | QuicCryptoServerStreamBase::Helper* helper) | 
|  | : QuicCryptoServerStream(crypto_config, | 
|  | compressed_certs_cache, | 
|  | session, | 
|  | helper) {} | 
|  | MockQuicCryptoServerStream(const MockQuicCryptoServerStream&) = delete; | 
|  | MockQuicCryptoServerStream& operator=(const MockQuicCryptoServerStream&) = | 
|  | delete; | 
|  | ~MockQuicCryptoServerStream() override {} | 
|  |  | 
|  | MOCK_METHOD(void, | 
|  | SendServerConfigUpdate, | 
|  | (const CachedNetworkParameters*), | 
|  | (override)); | 
|  |  | 
|  | bool encryption_established() const override { return true; } | 
|  | }; | 
|  |  | 
|  | class MockTlsServerHandshaker : public TlsServerHandshaker { | 
|  | public: | 
|  | explicit MockTlsServerHandshaker(QuicSession* session, | 
|  | const QuicCryptoServerConfig* crypto_config) | 
|  | : TlsServerHandshaker(session, crypto_config) {} | 
|  | MockTlsServerHandshaker(const MockTlsServerHandshaker&) = delete; | 
|  | MockTlsServerHandshaker& operator=(const MockTlsServerHandshaker&) = delete; | 
|  | ~MockTlsServerHandshaker() override {} | 
|  |  | 
|  | MOCK_METHOD(void, | 
|  | SendServerConfigUpdate, | 
|  | (const CachedNetworkParameters*), | 
|  | (override)); | 
|  |  | 
|  | bool encryption_established() const override { return true; } | 
|  | }; | 
|  |  | 
|  | QuicCryptoServerStreamBase* CreateMockCryptoServerStream( | 
|  | const QuicCryptoServerConfig* crypto_config, | 
|  | QuicCompressedCertsCache* compressed_certs_cache, | 
|  | QuicSession* session, | 
|  | QuicCryptoServerStreamBase::Helper* helper) { | 
|  | switch (session->connection()->version().handshake_protocol) { | 
|  | case PROTOCOL_QUIC_CRYPTO: | 
|  | return new MockQuicCryptoServerStream( | 
|  | crypto_config, compressed_certs_cache, session, helper); | 
|  | case PROTOCOL_TLS1_3: | 
|  | return new MockTlsServerHandshaker(session, crypto_config); | 
|  | case PROTOCOL_UNSUPPORTED: | 
|  | break; | 
|  | } | 
|  | QUIC_BUG(quic_bug_10933_1) | 
|  | << "Unknown handshake protocol: " | 
|  | << static_cast<int>(session->connection()->version().handshake_protocol); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | class MockQuicConnectionWithSendStreamData : public MockQuicConnection { | 
|  | public: | 
|  | MockQuicConnectionWithSendStreamData( | 
|  | MockQuicConnectionHelper* helper, | 
|  | MockAlarmFactory* alarm_factory, | 
|  | Perspective perspective, | 
|  | const ParsedQuicVersionVector& supported_versions) | 
|  | : MockQuicConnection(helper, | 
|  | alarm_factory, | 
|  | perspective, | 
|  | supported_versions) { | 
|  | auto consume_all_data = [](QuicStreamId /*id*/, size_t write_length, | 
|  | QuicStreamOffset /*offset*/, | 
|  | StreamSendingState state) { | 
|  | return QuicConsumedData(write_length, state != NO_FIN); | 
|  | }; | 
|  | ON_CALL(*this, SendStreamData(_, _, _, _)) | 
|  | .WillByDefault(Invoke(consume_all_data)); | 
|  | } | 
|  |  | 
|  | MOCK_METHOD(QuicConsumedData, | 
|  | SendStreamData, | 
|  | (QuicStreamId id, | 
|  | size_t write_length, | 
|  | QuicStreamOffset offset, | 
|  | StreamSendingState state), | 
|  | (override)); | 
|  | }; | 
|  |  | 
|  | class MockQuicSimpleServerSession : public QuicSimpleServerSession { | 
|  | public: | 
|  | MockQuicSimpleServerSession( | 
|  | const QuicConfig& config, | 
|  | QuicConnection* connection, | 
|  | QuicSession::Visitor* visitor, | 
|  | QuicCryptoServerStreamBase::Helper* helper, | 
|  | const QuicCryptoServerConfig* crypto_config, | 
|  | QuicCompressedCertsCache* compressed_certs_cache, | 
|  | QuicSimpleServerBackend* quic_simple_server_backend) | 
|  | : QuicSimpleServerSession(config, | 
|  | CurrentSupportedVersions(), | 
|  | connection, | 
|  | visitor, | 
|  | helper, | 
|  | crypto_config, | 
|  | compressed_certs_cache, | 
|  | quic_simple_server_backend) {} | 
|  | // Methods taking non-copyable types like Http2HeaderBlock by value cannot be | 
|  | // mocked directly. | 
|  | void WritePushPromise(QuicStreamId original_stream_id, | 
|  | QuicStreamId promised_stream_id, | 
|  | spdy::Http2HeaderBlock headers) override { | 
|  | return WritePushPromiseMock(original_stream_id, promised_stream_id, | 
|  | headers); | 
|  | } | 
|  | MOCK_METHOD(void, | 
|  | WritePushPromiseMock, | 
|  | (QuicStreamId original_stream_id, | 
|  | QuicStreamId promised_stream_id, | 
|  | const spdy::Http2HeaderBlock& headers), | 
|  | ()); | 
|  |  | 
|  | MOCK_METHOD(void, SendBlocked, (QuicStreamId), (override)); | 
|  | MOCK_METHOD(bool, | 
|  | WriteControlFrame, | 
|  | (const QuicFrame& frame, TransmissionType type), | 
|  | (override)); | 
|  | }; | 
|  |  | 
|  | class QuicSimpleServerSessionTest | 
|  | : public QuicTestWithParam<ParsedQuicVersion> { | 
|  | public: | 
|  | // The function ensures that A) the MAX_STREAMS frames get properly deleted | 
|  | // (since the test uses a 'did we leak memory' check ... if we just lose the | 
|  | // frame, the test fails) and B) returns true (instead of the default, false) | 
|  | // which ensures that the rest of the system thinks that the frame actually | 
|  | // was transmitted. | 
|  | bool ClearMaxStreamsControlFrame(const QuicFrame& frame) { | 
|  | if (frame.type == MAX_STREAMS_FRAME) { | 
|  | DeleteFrame(&const_cast<QuicFrame&>(frame)); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | QuicSimpleServerSessionTest() | 
|  | : crypto_config_(QuicCryptoServerConfig::TESTING, | 
|  | QuicRandom::GetInstance(), | 
|  | crypto_test_utils::ProofSourceForTesting(), | 
|  | KeyExchangeSource::Default()), | 
|  | compressed_certs_cache_( | 
|  | QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) { | 
|  | config_.SetMaxBidirectionalStreamsToSend(kMaxStreamsForTest); | 
|  | QuicConfigPeer::SetReceivedMaxBidirectionalStreams(&config_, | 
|  | kMaxStreamsForTest); | 
|  | config_.SetMaxUnidirectionalStreamsToSend(kMaxStreamsForTest); | 
|  |  | 
|  | config_.SetInitialStreamFlowControlWindowToSend( | 
|  | kInitialStreamFlowControlWindowForTest); | 
|  | config_.SetInitialMaxStreamDataBytesIncomingBidirectionalToSend( | 
|  | kInitialStreamFlowControlWindowForTest); | 
|  | config_.SetInitialMaxStreamDataBytesOutgoingBidirectionalToSend( | 
|  | kInitialStreamFlowControlWindowForTest); | 
|  | config_.SetInitialMaxStreamDataBytesUnidirectionalToSend( | 
|  | kInitialStreamFlowControlWindowForTest); | 
|  | config_.SetInitialSessionFlowControlWindowToSend( | 
|  | kInitialSessionFlowControlWindowForTest); | 
|  | if (VersionUsesHttp3(transport_version())) { | 
|  | QuicConfigPeer::SetReceivedMaxUnidirectionalStreams( | 
|  | &config_, kMaxStreamsForTest + 3); | 
|  | } else { | 
|  | QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(&config_, | 
|  | kMaxStreamsForTest); | 
|  | } | 
|  |  | 
|  | ParsedQuicVersionVector supported_versions = SupportedVersions(version()); | 
|  | connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>( | 
|  | &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions); | 
|  | connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); | 
|  | connection_->SetEncrypter( | 
|  | ENCRYPTION_FORWARD_SECURE, | 
|  | std::make_unique<NullEncrypter>(connection_->perspective())); | 
|  | session_ = std::make_unique<MockQuicSimpleServerSession>( | 
|  | config_, connection_, &owner_, &stream_helper_, &crypto_config_, | 
|  | &compressed_certs_cache_, &memory_cache_backend_); | 
|  | MockClock clock; | 
|  | handshake_message_ = crypto_config_.AddDefaultConfig( | 
|  | QuicRandom::GetInstance(), &clock, | 
|  | QuicCryptoServerConfig::ConfigOptions()); | 
|  | session_->Initialize(); | 
|  |  | 
|  | if (VersionHasIetfQuicFrames(transport_version())) { | 
|  | EXPECT_CALL(*session_, WriteControlFrame(_, _)) | 
|  | .WillRepeatedly(Invoke(&ClearControlFrameWithTransmissionType)); | 
|  | } | 
|  | session_->OnConfigNegotiated(); | 
|  | } | 
|  |  | 
|  | QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { | 
|  | return GetNthClientInitiatedBidirectionalStreamId(transport_version(), n); | 
|  | } | 
|  |  | 
|  | QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) { | 
|  | return quic::test::GetNthServerInitiatedUnidirectionalStreamId( | 
|  | transport_version(), n); | 
|  | } | 
|  |  | 
|  | ParsedQuicVersion version() const { return GetParam(); } | 
|  |  | 
|  | QuicTransportVersion transport_version() const { | 
|  | return version().transport_version; | 
|  | } | 
|  |  | 
|  | void InjectStopSending(QuicStreamId stream_id, | 
|  | QuicRstStreamErrorCode rst_stream_code) { | 
|  | // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a | 
|  | // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes | 
|  | // a one-way close. | 
|  | if (!VersionHasIetfQuicFrames(transport_version())) { | 
|  | // Only needed for version 99/IETF QUIC. | 
|  | return; | 
|  | } | 
|  | EXPECT_CALL(owner_, OnStopSendingReceived(_)).Times(1); | 
|  | QuicStopSendingFrame stop_sending(kInvalidControlFrameId, stream_id, | 
|  | rst_stream_code); | 
|  | // Expect the RESET_STREAM that is generated in response to receiving a | 
|  | // STOP_SENDING. | 
|  | EXPECT_CALL(*connection_, OnStreamReset(stream_id, rst_stream_code)); | 
|  | session_->OnStopSendingFrame(stop_sending); | 
|  | } | 
|  |  | 
|  | StrictMock<MockQuicSessionVisitor> owner_; | 
|  | StrictMock<MockQuicCryptoServerStreamHelper> stream_helper_; | 
|  | MockQuicConnectionHelper helper_; | 
|  | MockAlarmFactory alarm_factory_; | 
|  | StrictMock<MockQuicConnectionWithSendStreamData>* connection_; | 
|  | QuicConfig config_; | 
|  | QuicCryptoServerConfig crypto_config_; | 
|  | QuicCompressedCertsCache compressed_certs_cache_; | 
|  | QuicMemoryCacheBackend memory_cache_backend_; | 
|  | std::unique_ptr<MockQuicSimpleServerSession> session_; | 
|  | std::unique_ptr<CryptoHandshakeMessage> handshake_message_; | 
|  | }; | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(Tests, | 
|  | QuicSimpleServerSessionTest, | 
|  | ::testing::ValuesIn(AllSupportedVersions()), | 
|  | ::testing::PrintToStringParamName()); | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, CloseStreamDueToReset) { | 
|  | // Send some data open a stream, then reset it. | 
|  | QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, | 
|  | kStreamData); | 
|  | session_->OnStreamFrame(data1); | 
|  | EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  |  | 
|  | // Receive a reset (and send a RST in response). | 
|  | QuicRstStreamFrame rst1(kInvalidControlFrameId, | 
|  | GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_ERROR_PROCESSING_STREAM, 0); | 
|  | EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); | 
|  | EXPECT_CALL(*session_, WriteControlFrame(_, _)); | 
|  |  | 
|  | if (!VersionHasIetfQuicFrames(transport_version())) { | 
|  | // For version 99, this is covered in InjectStopSending() | 
|  | EXPECT_CALL(*connection_, | 
|  | OnStreamReset(GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_RST_ACKNOWLEDGEMENT)); | 
|  | } | 
|  | session_->OnRstStream(rst1); | 
|  | // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a | 
|  | // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes | 
|  | // a one-way close. | 
|  | InjectStopSending(GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_ERROR_PROCESSING_STREAM); | 
|  | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  |  | 
|  | // Send the same two bytes of payload in a new packet. | 
|  | session_->OnStreamFrame(data1); | 
|  |  | 
|  | // The stream should not be re-opened. | 
|  | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | EXPECT_TRUE(connection_->connected()); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, NeverOpenStreamDueToReset) { | 
|  | // Send a reset (and expect the peer to send a RST in response). | 
|  | QuicRstStreamFrame rst1(kInvalidControlFrameId, | 
|  | GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_ERROR_PROCESSING_STREAM, 0); | 
|  | EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); | 
|  | if (!VersionHasIetfQuicFrames(transport_version())) { | 
|  | EXPECT_CALL(*session_, WriteControlFrame(_, _)); | 
|  | // For version 99, this is covered in InjectStopSending() | 
|  | EXPECT_CALL(*connection_, | 
|  | OnStreamReset(GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_RST_ACKNOWLEDGEMENT)); | 
|  | } | 
|  | session_->OnRstStream(rst1); | 
|  | // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a | 
|  | // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes | 
|  | // a one-way close. | 
|  | InjectStopSending(GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_ERROR_PROCESSING_STREAM); | 
|  |  | 
|  | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  |  | 
|  | QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, | 
|  | kStreamData); | 
|  | session_->OnStreamFrame(data1); | 
|  |  | 
|  | // The stream should never be opened, now that the reset is received. | 
|  | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | EXPECT_TRUE(connection_->connected()); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, AcceptClosedStream) { | 
|  | // Send some data to open two streams. | 
|  | QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, | 
|  | kStreamData); | 
|  | QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0, | 
|  | kStreamData); | 
|  | session_->OnStreamFrame(frame1); | 
|  | session_->OnStreamFrame(frame2); | 
|  | EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  |  | 
|  | // Send a reset (and expect the peer to send a RST in response). | 
|  | QuicRstStreamFrame rst(kInvalidControlFrameId, | 
|  | GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_ERROR_PROCESSING_STREAM, 0); | 
|  | EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); | 
|  | if (!VersionHasIetfQuicFrames(transport_version())) { | 
|  | EXPECT_CALL(*session_, WriteControlFrame(_, _)); | 
|  | // For version 99, this is covered in InjectStopSending() | 
|  | EXPECT_CALL(*connection_, | 
|  | OnStreamReset(GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_RST_ACKNOWLEDGEMENT)); | 
|  | } | 
|  | session_->OnRstStream(rst); | 
|  | // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a | 
|  | // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes | 
|  | // a one-way close. | 
|  | InjectStopSending(GetNthClientInitiatedBidirectionalId(0), | 
|  | QUIC_ERROR_PROCESSING_STREAM); | 
|  |  | 
|  | // 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. | 
|  | QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false, 2, | 
|  | kStreamData); | 
|  | QuicStreamFrame frame4(GetNthClientInitiatedBidirectionalId(1), false, 2, | 
|  | kStreamData); | 
|  | session_->OnStreamFrame(frame3); | 
|  | session_->OnStreamFrame(frame4); | 
|  | // The stream should never be opened, now that the reset is received. | 
|  | EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | EXPECT_TRUE(connection_->connected()); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, CreateIncomingStreamDisconnected) { | 
|  | // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. | 
|  | if (version() != AllSupportedVersions()[0]) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Tests that incoming stream creation fails when connection is not connected. | 
|  | size_t initial_num_open_stream = | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()); | 
|  | QuicConnectionPeer::TearDownLocalConnectionState(connection_); | 
|  | EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateIncomingStream( | 
|  | session_.get(), GetNthClientInitiatedBidirectionalId(0)), | 
|  | "ShouldCreateIncomingStream called when disconnected"); | 
|  | EXPECT_EQ(initial_num_open_stream, | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, CreateIncomingStream) { | 
|  | QuicSpdyStream* stream = QuicSimpleServerSessionPeer::CreateIncomingStream( | 
|  | session_.get(), GetNthClientInitiatedBidirectionalId(0)); | 
|  | EXPECT_NE(nullptr, stream); | 
|  | EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0), stream->id()); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamDisconnected) { | 
|  | // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. | 
|  | if (version() != AllSupportedVersions()[0]) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Tests that outgoing stream creation fails when connection is not connected. | 
|  | size_t initial_num_open_stream = | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()); | 
|  | QuicConnectionPeer::TearDownLocalConnectionState(connection_); | 
|  | EXPECT_QUIC_BUG( | 
|  | QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream( | 
|  | session_.get()), | 
|  | "ShouldCreateOutgoingUnidirectionalStream called when disconnected"); | 
|  |  | 
|  | EXPECT_EQ(initial_num_open_stream, | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUnencrypted) { | 
|  | // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. | 
|  | if (version() != AllSupportedVersions()[0]) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Tests that outgoing stream creation fails when encryption has not yet been | 
|  | // established. | 
|  | size_t initial_num_open_stream = | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()); | 
|  | EXPECT_QUIC_BUG( | 
|  | QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream( | 
|  | session_.get()), | 
|  | "Encryption not established so no outgoing stream created."); | 
|  | EXPECT_EQ(initial_num_open_stream, | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) { | 
|  | // Tests that outgoing stream creation should not be affected by existing | 
|  | // incoming stream and vice-versa. But when reaching the limit of max outgoing | 
|  | // stream allowed, creation should fail. | 
|  |  | 
|  | // Receive some data to initiate a incoming stream which should not effect | 
|  | // creating outgoing streams. | 
|  | QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, | 
|  | kStreamData); | 
|  | session_->OnStreamFrame(data1); | 
|  | EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()) - | 
|  | /*incoming=*/1); | 
|  |  | 
|  | if (!VersionUsesHttp3(transport_version())) { | 
|  | session_->UnregisterStreamPriority( | 
|  | QuicUtils::GetHeadersStreamId(transport_version()), | 
|  | /*is_static=*/true); | 
|  | } | 
|  | // Assume encryption already established. | 
|  | QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), nullptr); | 
|  | QuicCryptoServerStreamBase* crypto_stream = | 
|  | CreateMockCryptoServerStream(&crypto_config_, &compressed_certs_cache_, | 
|  | session_.get(), &stream_helper_); | 
|  | QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream); | 
|  | if (!VersionUsesHttp3(transport_version())) { | 
|  | session_->RegisterStreamPriority( | 
|  | QuicUtils::GetHeadersStreamId(transport_version()), | 
|  | /*is_static=*/true, | 
|  | spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)); | 
|  | } | 
|  |  | 
|  | // Create push streams till reaching the upper limit of allowed open streams. | 
|  | for (size_t i = 0; i < kMaxStreamsForTest; ++i) { | 
|  | QuicSpdyStream* created_stream = | 
|  | QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream( | 
|  | session_.get()); | 
|  | if (VersionUsesHttp3(transport_version())) { | 
|  | EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i + 3), | 
|  | created_stream->id()); | 
|  | } else { | 
|  | EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i), created_stream->id()); | 
|  | } | 
|  | EXPECT_EQ(i + 1, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()) - | 
|  | /*incoming=*/1); | 
|  | } | 
|  |  | 
|  | // Continuing creating push stream would fail. | 
|  | EXPECT_EQ(nullptr, | 
|  | QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream( | 
|  | session_.get())); | 
|  | EXPECT_EQ(kMaxStreamsForTest, | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()) - | 
|  | /*incoming=*/1); | 
|  |  | 
|  | // Create peer initiated stream should have no problem. | 
|  | QuicStreamFrame data2(GetNthClientInitiatedBidirectionalId(1), false, 0, | 
|  | kStreamData); | 
|  | session_->OnStreamFrame(data2); | 
|  | EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()) - | 
|  | /*outcoming=*/kMaxStreamsForTest); | 
|  | } | 
|  |  | 
|  | TEST_P(QuicSimpleServerSessionTest, OnStreamFrameWithEvenStreamId) { | 
|  | QuicStreamFrame frame(GetNthServerInitiatedUnidirectionalId(0), false, 0, | 
|  | kStreamData); | 
|  | EXPECT_CALL(*connection_, | 
|  | CloseConnection(QUIC_INVALID_STREAM_ID, | 
|  | "Client sent data on server push stream", _)); | 
|  | session_->OnStreamFrame(frame); | 
|  | } | 
|  |  | 
|  | // Tests that calling GetOrCreateStream() on an outgoing stream not promised yet | 
|  | // should result close connection. | 
|  | TEST_P(QuicSimpleServerSessionTest, GetEvenIncomingError) { | 
|  | const size_t initial_num_open_stream = | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get()); | 
|  | const QuicErrorCode expected_error = VersionUsesHttp3(transport_version()) | 
|  | ? QUIC_HTTP_STREAM_WRONG_DIRECTION | 
|  | : QUIC_INVALID_STREAM_ID; | 
|  | EXPECT_CALL(*connection_, CloseConnection(expected_error, | 
|  | "Data for nonexistent stream", _)); | 
|  | EXPECT_EQ(nullptr, | 
|  | QuicSessionPeer::GetOrCreateStream( | 
|  | session_.get(), GetNthServerInitiatedUnidirectionalId(3))); | 
|  | EXPECT_EQ(initial_num_open_stream, | 
|  | QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace quic |