| // Copyright (c) 2012 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/core/quic_crypto_stream.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "quic/core/crypto/crypto_handshake.h" |
| #include "quic/core/crypto/crypto_protocol.h" |
| #include "quic/core/crypto/null_encrypter.h" |
| #include "quic/core/quic_utils.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/quic_stream_peer.h" |
| #include "quic/test_tools/quic_test_utils.h" |
| |
| using testing::_; |
| using testing::InSequence; |
| using testing::Invoke; |
| using testing::InvokeWithoutArgs; |
| using testing::Return; |
| |
| namespace quic { |
| namespace test { |
| namespace { |
| |
| class MockQuicCryptoStream : public QuicCryptoStream, |
| public QuicCryptoHandshaker { |
| public: |
| explicit MockQuicCryptoStream(QuicSession* session) |
| : QuicCryptoStream(session), |
| QuicCryptoHandshaker(this, session), |
| params_(new QuicCryptoNegotiatedParameters) {} |
| MockQuicCryptoStream(const MockQuicCryptoStream&) = delete; |
| MockQuicCryptoStream& operator=(const MockQuicCryptoStream&) = delete; |
| |
| void OnHandshakeMessage(const CryptoHandshakeMessage& message) override { |
| messages_.push_back(message); |
| } |
| |
| std::vector<CryptoHandshakeMessage>* messages() { return &messages_; } |
| |
| ssl_early_data_reason_t EarlyDataReason() const override { |
| return ssl_early_data_unknown; |
| } |
| bool encryption_established() const override { return false; } |
| bool one_rtt_keys_available() const override { return false; } |
| |
| const QuicCryptoNegotiatedParameters& crypto_negotiated_params() |
| const override { |
| return *params_; |
| } |
| CryptoMessageParser* crypto_message_parser() override { |
| return QuicCryptoHandshaker::crypto_message_parser(); |
| } |
| void OnPacketDecrypted(EncryptionLevel /*level*/) override {} |
| void OnOneRttPacketAcknowledged() override {} |
| void OnHandshakePacketSent() override {} |
| void OnHandshakeDoneReceived() override {} |
| void OnNewTokenReceived(absl::string_view /*token*/) override {} |
| std::string GetAddressToken() const override { return ""; } |
| bool ValidateAddressToken(absl::string_view /*token*/) const override { |
| return true; |
| } |
| HandshakeState GetHandshakeState() const override { return HANDSHAKE_START; } |
| void SetServerApplicationStateForResumption( |
| std::unique_ptr<ApplicationState> /*application_state*/) override {} |
| bool KeyUpdateSupportedLocally() const override { return false; } |
| std::unique_ptr<QuicDecrypter> AdvanceKeysAndCreateCurrentOneRttDecrypter() |
| override { |
| return nullptr; |
| } |
| std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override { |
| return nullptr; |
| } |
| bool ExportKeyingMaterial(absl::string_view /*label*/, |
| absl::string_view /*context*/, |
| size_t /*result_len*/, |
| std::string* /*result*/) override { |
| return false; |
| } |
| SSL* GetSsl() const override { return nullptr; } |
| |
| private: |
| QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; |
| std::vector<CryptoHandshakeMessage> messages_; |
| }; |
| |
| class QuicCryptoStreamTest : public QuicTest { |
| public: |
| QuicCryptoStreamTest() |
| : connection_(new MockQuicConnection(&helper_, |
| &alarm_factory_, |
| Perspective::IS_CLIENT)), |
| session_(connection_, /*create_mock_crypto_stream=*/false) { |
| stream_ = new MockQuicCryptoStream(&session_); |
| session_.SetCryptoStream(stream_); |
| session_.Initialize(); |
| message_.set_tag(kSHLO); |
| message_.SetStringPiece(1, "abc"); |
| message_.SetStringPiece(2, "def"); |
| ConstructHandshakeMessage(); |
| } |
| QuicCryptoStreamTest(const QuicCryptoStreamTest&) = delete; |
| QuicCryptoStreamTest& operator=(const QuicCryptoStreamTest&) = delete; |
| |
| void ConstructHandshakeMessage() { |
| CryptoFramer framer; |
| message_data_ = framer.ConstructHandshakeMessage(message_); |
| } |
| |
| protected: |
| MockQuicConnectionHelper helper_; |
| MockAlarmFactory alarm_factory_; |
| MockQuicConnection* connection_; |
| MockQuicSpdySession session_; |
| MockQuicCryptoStream* stream_; |
| CryptoHandshakeMessage message_; |
| std::unique_ptr<QuicData> message_data_; |
| }; |
| |
| TEST_F(QuicCryptoStreamTest, NotInitiallyConected) { |
| EXPECT_FALSE(stream_->encryption_established()); |
| EXPECT_FALSE(stream_->one_rtt_keys_available()); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, ProcessRawData) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| stream_->OnStreamFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| /*fin=*/false, |
| /*offset=*/0, message_data_->AsStringPiece())); |
| } else { |
| stream_->OnCryptoFrame(QuicCryptoFrame(ENCRYPTION_INITIAL, /*offset*/ 0, |
| message_data_->AsStringPiece())); |
| } |
| ASSERT_EQ(1u, stream_->messages()->size()); |
| const CryptoHandshakeMessage& message = (*stream_->messages())[0]; |
| EXPECT_EQ(kSHLO, message.tag()); |
| EXPECT_EQ(2u, message.tag_value_map().size()); |
| EXPECT_EQ("abc", crypto_test_utils::GetValueForTag(message, 1)); |
| EXPECT_EQ("def", crypto_test_utils::GetValueForTag(message, 2)); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, ProcessBadData) { |
| std::string bad(message_data_->data(), message_data_->length()); |
| const int kFirstTagIndex = sizeof(uint32_t) + // message tag |
| sizeof(uint16_t) + // number of tag-value pairs |
| sizeof(uint16_t); // padding |
| EXPECT_EQ(1, bad[kFirstTagIndex]); |
| bad[kFirstTagIndex] = 0x7F; // out of order tag |
| |
| EXPECT_CALL(*connection_, CloseConnection(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, |
| testing::_, testing::_)); |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| stream_->OnStreamFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| /*fin=*/false, /*offset=*/0, bad)); |
| } else { |
| stream_->OnCryptoFrame( |
| QuicCryptoFrame(ENCRYPTION_INITIAL, /*offset*/ 0, bad)); |
| } |
| } |
| |
| TEST_F(QuicCryptoStreamTest, NoConnectionLevelFlowControl) { |
| EXPECT_FALSE( |
| QuicStreamPeer::StreamContributesToConnectionFlowControl(stream_)); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, RetransmitCryptoData) { |
| if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| InSequence s; |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 0, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->WriteOrBufferData(data, false, nullptr); |
| // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 1350, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->WriteOrBufferData(data, false, nullptr); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| // Lost [0, 1000). |
| stream_->OnStreamFrameLost(0, 1000, false); |
| EXPECT_TRUE(stream_->HasPendingRetransmission()); |
| // Lost [1200, 2000). |
| stream_->OnStreamFrameLost(1200, 800, false); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1000, 0, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| // Verify [1200, 2000) are sent in [1200, 1350) and [1350, 2000) because of |
| // they are in different encryption levels. |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 150, 1200, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 650, 1350, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->OnCanWrite(); |
| EXPECT_FALSE(stream_->HasPendingRetransmission()); |
| // Verify connection's encryption level has restored. |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, RetransmitCryptoDataInCryptoFrames) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); |
| InSequence s; |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); |
| // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. |
| std::unique_ptr<NullEncrypter> encrypter = |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT); |
| connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); |
| connection_->SetEncrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| // Lost [0, 1000). |
| QuicCryptoFrame lost_frame(ENCRYPTION_INITIAL, 0, 1000); |
| stream_->OnCryptoFrameLost(&lost_frame); |
| EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); |
| // Lost [1200, 2000). |
| lost_frame = QuicCryptoFrame(ENCRYPTION_INITIAL, 1200, 150); |
| stream_->OnCryptoFrameLost(&lost_frame); |
| lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650); |
| stream_->OnCryptoFrameLost(&lost_frame); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1000, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| // Verify [1200, 2000) are sent in [1200, 1350) and [1350, 2000) because of |
| // they are in different encryption levels. |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 150, 1200)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WritePendingCryptoRetransmission(); |
| EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); |
| // Verify connection's encryption level has restored. |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| } |
| |
| // Regression test for handling the missing ENCRYPTION_HANDSHAKE in |
| // quic_crypto_stream.cc. This test is essentially the same as |
| // RetransmitCryptoDataInCryptoFrames, except it uses ENCRYPTION_HANDSHAKE in |
| // place of ENCRYPTION_ZERO_RTT. |
| TEST_F(QuicCryptoStreamTest, RetransmitEncryptionHandshakeLevelCryptoFrames) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); |
| InSequence s; |
| // Send [0, 1000) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1000, 'a'); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1000, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); |
| // Send [1000, 2000) in ENCRYPTION_HANDSHAKE. |
| std::unique_ptr<NullEncrypter> encrypter = |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT); |
| connection_->SetEncrypter(ENCRYPTION_HANDSHAKE, std::move(encrypter)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); |
| EXPECT_EQ(ENCRYPTION_HANDSHAKE, connection_->encryption_level()); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_HANDSHAKE, 1000, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_HANDSHAKE, data); |
| connection_->SetEncrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| // Lost [1000, 1200). |
| QuicCryptoFrame lost_frame(ENCRYPTION_HANDSHAKE, 0, 200); |
| stream_->OnCryptoFrameLost(&lost_frame); |
| EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); |
| // Verify [1000, 1200) is sent. |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_HANDSHAKE, 200, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WritePendingCryptoRetransmission(); |
| EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, NeuterUnencryptedStreamData) { |
| if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 0, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->WriteOrBufferData(data, false, nullptr); |
| // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 1350, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->WriteOrBufferData(data, false, nullptr); |
| |
| // Lost [0, 1350). |
| stream_->OnStreamFrameLost(0, 1350, false); |
| EXPECT_TRUE(stream_->HasPendingRetransmission()); |
| // Neuters [0, 1350). |
| stream_->NeuterUnencryptedStreamData(); |
| EXPECT_FALSE(stream_->HasPendingRetransmission()); |
| // Lost [0, 1350) again. |
| stream_->OnStreamFrameLost(0, 1350, false); |
| EXPECT_FALSE(stream_->HasPendingRetransmission()); |
| |
| // Lost [1350, 2000). |
| stream_->OnStreamFrameLost(1350, 650, false); |
| EXPECT_TRUE(stream_->HasPendingRetransmission()); |
| stream_->NeuterUnencryptedStreamData(); |
| EXPECT_TRUE(stream_->HasPendingRetransmission()); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, NeuterUnencryptedCryptoData) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); |
| // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. |
| connection_->SetEncrypter( |
| ENCRYPTION_ZERO_RTT, |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); |
| std::unique_ptr<NullEncrypter> encrypter = |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT); |
| connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter)); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); |
| |
| // Lost [0, 1350). |
| QuicCryptoFrame lost_frame(ENCRYPTION_INITIAL, 0, 1350); |
| stream_->OnCryptoFrameLost(&lost_frame); |
| EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); |
| // Neuters [0, 1350). |
| stream_->NeuterUnencryptedStreamData(); |
| EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); |
| // Lost [0, 1350) again. |
| stream_->OnCryptoFrameLost(&lost_frame); |
| EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); |
| |
| // Lost [1350, 2000), which starts at offset 0 at the ENCRYPTION_ZERO_RTT |
| // level. |
| lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650); |
| stream_->OnCryptoFrameLost(&lost_frame); |
| EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); |
| stream_->NeuterUnencryptedStreamData(); |
| EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, RetransmitStreamData) { |
| if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| InSequence s; |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 0, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->WriteOrBufferData(data, false, nullptr); |
| // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 1350, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->WriteOrBufferData(data, false, nullptr); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| // Ack [2000, 2500). |
| QuicByteCount newly_acked_length = 0; |
| stream_->OnStreamFrameAcked(2000, 500, false, QuicTime::Delta::Zero(), |
| QuicTime::Zero(), &newly_acked_length); |
| EXPECT_EQ(500u, newly_acked_length); |
| |
| // Force crypto stream to send [1350, 2700) and only [1350, 1500) is consumed. |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 650, 1350, _, _, _)) |
| .WillOnce(InvokeWithoutArgs([this]() { |
| return session_.ConsumeData( |
| QuicUtils::GetCryptoStreamId(connection_->transport_version()), 150, |
| 1350, NO_FIN, HANDSHAKE_RETRANSMISSION, absl::nullopt); |
| })); |
| |
| EXPECT_FALSE(stream_->RetransmitStreamData(1350, 1350, false, |
| HANDSHAKE_RETRANSMISSION)); |
| // Verify connection's encryption level has restored. |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| // Force session to send [1350, 1500) again and all data is consumed. |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 650, 1350, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 200, 2500, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| EXPECT_TRUE(stream_->RetransmitStreamData(1350, 1350, false, |
| HANDSHAKE_RETRANSMISSION)); |
| // Verify connection's encryption level has restored. |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(0); |
| // Force to send an empty frame. |
| EXPECT_TRUE( |
| stream_->RetransmitStreamData(0, 0, false, HANDSHAKE_RETRANSMISSION)); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, RetransmitStreamDataWithCryptoFrames) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| InSequence s; |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); |
| // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. |
| std::unique_ptr<NullEncrypter> encrypter = |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT); |
| connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); |
| connection_->SetEncrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| // Ack [2000, 2500). |
| QuicCryptoFrame acked_frame(ENCRYPTION_ZERO_RTT, 650, 500); |
| EXPECT_TRUE( |
| stream_->OnCryptoFrameAcked(acked_frame, QuicTime::Delta::Zero())); |
| |
| // Retransmit only [1350, 1500). |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 150, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| QuicCryptoFrame frame_to_retransmit(ENCRYPTION_ZERO_RTT, 0, 150); |
| stream_->RetransmitData(&frame_to_retransmit, HANDSHAKE_RETRANSMISSION); |
| |
| // Verify connection's encryption level has restored. |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| // Retransmit [1350, 2700) again and all data is sent. |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 200, 1150)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| frame_to_retransmit = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 1350); |
| stream_->RetransmitData(&frame_to_retransmit, HANDSHAKE_RETRANSMISSION); |
| // Verify connection's encryption level has restored. |
| EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); |
| |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); |
| // Force to send an empty frame. |
| QuicCryptoFrame empty_frame(ENCRYPTION_FORWARD_SECURE, 0, 0); |
| stream_->RetransmitData(&empty_frame, HANDSHAKE_RETRANSMISSION); |
| } |
| |
| // Regression test for b/115926584. |
| TEST_F(QuicCryptoStreamTest, HasUnackedCryptoData) { |
| if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| std::string data(1350, 'a'); |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 0, _, _, _)) |
| .WillOnce(testing::Return(QuicConsumedData(0, false))); |
| stream_->WriteOrBufferData(data, false, nullptr); |
| EXPECT_FALSE(stream_->IsWaitingForAcks()); |
| // Although there is no outstanding data, verify session has pending crypto |
| // data. |
| EXPECT_TRUE(session_.HasUnackedCryptoData()); |
| |
| EXPECT_CALL( |
| session_, |
| WritevData(QuicUtils::GetCryptoStreamId(connection_->transport_version()), |
| 1350, 0, _, _, _)) |
| .WillOnce(Invoke(&session_, &MockQuicSpdySession::ConsumeData)); |
| stream_->OnCanWrite(); |
| EXPECT_TRUE(stream_->IsWaitingForAcks()); |
| EXPECT_TRUE(session_.HasUnackedCryptoData()); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, HasUnackedCryptoDataWithCryptoFrames) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); |
| EXPECT_TRUE(stream_->IsWaitingForAcks()); |
| EXPECT_TRUE(session_.HasUnackedCryptoData()); |
| } |
| |
| // Regression test for bugfix of GetPacketHeaderSize. |
| TEST_F(QuicCryptoStreamTest, CryptoMessageFramingOverhead) { |
| for (const ParsedQuicVersion& version : |
| AllSupportedVersionsWithQuicCrypto()) { |
| SCOPED_TRACE(version); |
| QuicByteCount expected_overhead = 48; |
| if (version.HasIetfInvariantHeader()) { |
| expected_overhead += 4; |
| } |
| if (version.HasLongHeaderLengths()) { |
| expected_overhead += 3; |
| } |
| if (version.HasLengthPrefixedConnectionIds()) { |
| expected_overhead += 1; |
| } |
| EXPECT_EQ(expected_overhead, |
| QuicCryptoStream::CryptoMessageFramingOverhead( |
| version.transport_version, TestConnectionId())); |
| } |
| } |
| |
| TEST_F(QuicCryptoStreamTest, WriteBufferedCryptoFrames) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| EXPECT_FALSE(stream_->HasBufferedCryptoFrames()); |
| InSequence s; |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| // Only consumed 1000 bytes. |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) |
| .WillOnce(Return(1000)); |
| stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); |
| EXPECT_TRUE(stream_->HasBufferedCryptoFrames()); |
| |
| // Send [1350, 2700) in ENCRYPTION_ZERO_RTT and verify no write is attempted |
| // because there is buffered data. |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); |
| connection_->SetEncrypter( |
| ENCRYPTION_ZERO_RTT, |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); |
| connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); |
| stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 350, 1000)) |
| .WillOnce(Return(350)); |
| // Partial write of ENCRYPTION_ZERO_RTT data. |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) |
| .WillOnce(Return(1000)); |
| stream_->WriteBufferedCryptoFrames(); |
| EXPECT_TRUE(stream_->HasBufferedCryptoFrames()); |
| EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); |
| |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 350, 1000)) |
| .WillOnce(Return(350)); |
| stream_->WriteBufferedCryptoFrames(); |
| EXPECT_FALSE(stream_->HasBufferedCryptoFrames()); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, LimitBufferedCryptoData) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| |
| EXPECT_CALL(*connection_, |
| CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); |
| std::string large_frame(2 * GetQuicFlag(FLAGS_quic_max_buffered_crypto_bytes), |
| 'a'); |
| |
| // Set offset to 1 so that we guarantee the data gets buffered instead of |
| // immediately processed. |
| QuicStreamOffset offset = 1; |
| stream_->OnCryptoFrame( |
| QuicCryptoFrame(ENCRYPTION_INITIAL, offset, large_frame)); |
| } |
| |
| TEST_F(QuicCryptoStreamTest, RetransmitCryptoFramesAndPartialWrite) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); |
| InSequence s; |
| // Send [0, 1350) in ENCRYPTION_INITIAL. |
| EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); |
| std::string data(1350, 'a'); |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); |
| |
| // Lost [0, 1000). |
| QuicCryptoFrame lost_frame(ENCRYPTION_INITIAL, 0, 1000); |
| stream_->OnCryptoFrameLost(&lost_frame); |
| EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); |
| // Simulate connection is constrained by amplification restriction. |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1000, 0)) |
| .WillOnce(Return(0)); |
| stream_->WritePendingCryptoRetransmission(); |
| EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); |
| // Connection gets unblocked. |
| EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1000, 0)) |
| .WillOnce(Invoke(connection_, |
| &MockQuicConnection::QuicConnection_SendCryptoData)); |
| stream_->WritePendingCryptoRetransmission(); |
| EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); |
| } |
| |
| // Regression test for b/203199510 |
| TEST_F(QuicCryptoStreamTest, EmptyCryptoFrame) { |
| if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { |
| return; |
| } |
| if (GetQuicReloadableFlag(quic_accept_empty_crypto_frame)) { |
| EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); |
| } else { |
| EXPECT_CALL(*connection_, |
| CloseConnection(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _, _)); |
| } |
| QuicCryptoFrame empty_crypto_frame(ENCRYPTION_INITIAL, 0, nullptr, 0); |
| stream_->OnCryptoFrame(empty_crypto_frame); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace quic |