| // 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_packet_creator.h" |
| |
| #include <cstdint> |
| #include <limits> |
| #include <memory> |
| #include <ostream> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/base/macros.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "quic/core/crypto/null_decrypter.h" |
| #include "quic/core/crypto/null_encrypter.h" |
| #include "quic/core/crypto/quic_decrypter.h" |
| #include "quic/core/crypto/quic_encrypter.h" |
| #include "quic/core/frames/quic_stream_frame.h" |
| #include "quic/core/quic_connection_id.h" |
| #include "quic/core/quic_data_writer.h" |
| #include "quic/core/quic_simple_buffer_allocator.h" |
| #include "quic/core/quic_types.h" |
| #include "quic/core/quic_utils.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/quic_framer_peer.h" |
| #include "quic/test_tools/quic_packet_creator_peer.h" |
| #include "quic/test_tools/quic_test_utils.h" |
| #include "quic/test_tools/simple_data_producer.h" |
| #include "quic/test_tools/simple_quic_framer.h" |
| #include "common/test_tools/quiche_test_utils.h" |
| |
| using ::testing::_; |
| using ::testing::AtLeast; |
| using ::testing::DoAll; |
| using ::testing::InSequence; |
| using ::testing::Invoke; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| using ::testing::StrictMock; |
| |
| namespace quic { |
| namespace test { |
| namespace { |
| |
| const QuicPacketNumber kPacketNumber = QuicPacketNumber(UINT64_C(0x12345678)); |
| // Use fields in which each byte is distinct to ensure that every byte is |
| // framed correctly. The values are otherwise arbitrary. |
| QuicConnectionId CreateTestConnectionId() { |
| return TestConnectionId(UINT64_C(0xFEDCBA9876543210)); |
| } |
| |
| // Run tests with combinations of {ParsedQuicVersion, |
| // ToggleVersionSerialization}. |
| struct TestParams { |
| TestParams(ParsedQuicVersion version, bool version_serialization) |
| : version(version), version_serialization(version_serialization) {} |
| |
| ParsedQuicVersion version; |
| bool version_serialization; |
| }; |
| |
| // Used by ::testing::PrintToStringParamName(). |
| std::string PrintToString(const TestParams& p) { |
| return absl::StrCat(ParsedQuicVersionToString(p.version), "_", |
| (p.version_serialization ? "Include" : "No"), "Version"); |
| } |
| |
| // Constructs various test permutations. |
| std::vector<TestParams> GetTestParams() { |
| std::vector<TestParams> params; |
| ParsedQuicVersionVector all_supported_versions = AllSupportedVersions(); |
| for (size_t i = 0; i < all_supported_versions.size(); ++i) { |
| params.push_back(TestParams(all_supported_versions[i], true)); |
| params.push_back(TestParams(all_supported_versions[i], false)); |
| } |
| return params; |
| } |
| |
| class MockDebugDelegate : public QuicPacketCreator::DebugDelegate { |
| public: |
| ~MockDebugDelegate() override = default; |
| |
| MOCK_METHOD(void, OnFrameAddedToPacket, (const QuicFrame& frame), (override)); |
| |
| MOCK_METHOD(void, |
| OnStreamFrameCoalesced, |
| (const QuicStreamFrame& frame), |
| (override)); |
| }; |
| |
| class TestPacketCreator : public QuicPacketCreator { |
| public: |
| TestPacketCreator(QuicConnectionId connection_id, |
| QuicFramer* framer, |
| DelegateInterface* delegate, |
| SimpleDataProducer* producer) |
| : QuicPacketCreator(connection_id, framer, delegate), |
| producer_(producer), |
| version_(framer->version()) {} |
| |
| bool ConsumeDataToFillCurrentPacket(QuicStreamId id, |
| const struct iovec* iov, |
| int iov_count, |
| size_t total_length, |
| size_t iov_offset, |
| QuicStreamOffset offset, |
| bool fin, |
| bool needs_full_padding, |
| TransmissionType transmission_type, |
| QuicFrame* frame) { |
| // Save data before data is consumed. |
| QuicByteCount data_length = total_length - iov_offset; |
| if (data_length > 0) { |
| producer_->SaveStreamData(id, iov, iov_count, iov_offset, data_length); |
| } |
| return QuicPacketCreator::ConsumeDataToFillCurrentPacket( |
| id, data_length - iov_offset, offset, fin, needs_full_padding, |
| transmission_type, frame); |
| } |
| |
| void StopSendingVersion() { |
| if (version_.HasIetfInvariantHeader()) { |
| set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| return; |
| } |
| QuicPacketCreator::StopSendingVersion(); |
| } |
| |
| SimpleDataProducer* producer_; |
| ParsedQuicVersion version_; |
| }; |
| |
| class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { |
| public: |
| void ClearSerializedPacketForTests(SerializedPacket /*serialized_packet*/) { |
| // serialized packet self-clears on destruction. |
| } |
| |
| void SaveSerializedPacket(SerializedPacket serialized_packet) { |
| serialized_packet_.reset(CopySerializedPacket( |
| serialized_packet, &allocator_, /*copy_buffer=*/true)); |
| } |
| |
| void DeleteSerializedPacket() { serialized_packet_ = nullptr; } |
| |
| protected: |
| QuicPacketCreatorTest() |
| : connection_id_(TestConnectionId(2)), |
| server_framer_(SupportedVersions(GetParam().version), |
| QuicTime::Zero(), |
| Perspective::IS_SERVER, |
| connection_id_.length()), |
| client_framer_(SupportedVersions(GetParam().version), |
| QuicTime::Zero(), |
| Perspective::IS_CLIENT, |
| connection_id_.length()), |
| data_("foo"), |
| creator_(connection_id_, &client_framer_, &delegate_, &producer_) { |
| EXPECT_CALL(delegate_, GetPacketBuffer()) |
| .WillRepeatedly(Return(QuicPacketBuffer())); |
| EXPECT_CALL(delegate_, GetSerializedPacketFate(_, _)) |
| .WillRepeatedly(Return(SEND_TO_WRITER)); |
| creator_.SetEncrypter(ENCRYPTION_INITIAL, std::make_unique<NullEncrypter>( |
| Perspective::IS_CLIENT)); |
| creator_.SetEncrypter(ENCRYPTION_HANDSHAKE, std::make_unique<NullEncrypter>( |
| Perspective::IS_CLIENT)); |
| creator_.SetEncrypter(ENCRYPTION_ZERO_RTT, std::make_unique<NullEncrypter>( |
| Perspective::IS_CLIENT)); |
| creator_.SetEncrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); |
| client_framer_.set_visitor(&framer_visitor_); |
| server_framer_.set_visitor(&framer_visitor_); |
| client_framer_.set_data_producer(&producer_); |
| if (server_framer_.version().KnowsWhichDecrypterToUse()) { |
| server_framer_.InstallDecrypter( |
| ENCRYPTION_INITIAL, |
| std::make_unique<NullDecrypter>(Perspective::IS_SERVER)); |
| server_framer_.InstallDecrypter( |
| ENCRYPTION_ZERO_RTT, |
| std::make_unique<NullDecrypter>(Perspective::IS_SERVER)); |
| server_framer_.InstallDecrypter( |
| ENCRYPTION_HANDSHAKE, |
| std::make_unique<NullDecrypter>(Perspective::IS_SERVER)); |
| server_framer_.InstallDecrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullDecrypter>(Perspective::IS_SERVER)); |
| } else { |
| server_framer_.SetDecrypter( |
| ENCRYPTION_INITIAL, |
| std::make_unique<NullDecrypter>(Perspective::IS_SERVER)); |
| } |
| } |
| |
| ~QuicPacketCreatorTest() override {} |
| |
| SerializedPacket SerializeAllFrames(const QuicFrames& frames) { |
| SerializedPacket packet = QuicPacketCreatorPeer::SerializeAllFrames( |
| &creator_, frames, buffer_, kMaxOutgoingPacketSize); |
| EXPECT_EQ(QuicPacketCreatorPeer::GetEncryptionLevel(&creator_), |
| packet.encryption_level); |
| return packet; |
| } |
| |
| void ProcessPacket(const SerializedPacket& packet) { |
| QuicEncryptedPacket encrypted_packet(packet.encrypted_buffer, |
| packet.encrypted_length); |
| server_framer_.ProcessPacket(encrypted_packet); |
| } |
| |
| void CheckStreamFrame(const QuicFrame& frame, |
| QuicStreamId stream_id, |
| const std::string& data, |
| QuicStreamOffset offset, |
| bool fin) { |
| EXPECT_EQ(STREAM_FRAME, frame.type); |
| EXPECT_EQ(stream_id, frame.stream_frame.stream_id); |
| char buf[kMaxOutgoingPacketSize]; |
| QuicDataWriter writer(kMaxOutgoingPacketSize, buf, quiche::HOST_BYTE_ORDER); |
| if (frame.stream_frame.data_length > 0) { |
| producer_.WriteStreamData(stream_id, frame.stream_frame.offset, |
| frame.stream_frame.data_length, &writer); |
| } |
| EXPECT_EQ(data, absl::string_view(buf, frame.stream_frame.data_length)); |
| EXPECT_EQ(offset, frame.stream_frame.offset); |
| EXPECT_EQ(fin, frame.stream_frame.fin); |
| } |
| |
| // Returns the number of bytes consumed by the header of packet, including |
| // the version. |
| size_t GetPacketHeaderOverhead(QuicTransportVersion version) { |
| return GetPacketHeaderSize( |
| version, creator_.GetDestinationConnectionIdLength(), |
| creator_.GetSourceConnectionIdLength(), |
| QuicPacketCreatorPeer::SendVersionInPacket(&creator_), |
| !kIncludeDiversificationNonce, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), |
| QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), 0, |
| QuicPacketCreatorPeer::GetLengthLength(&creator_)); |
| } |
| |
| // Returns the number of bytes of overhead that will be added to a packet |
| // of maximum length. |
| size_t GetEncryptionOverhead() { |
| return creator_.max_packet_length() - |
| client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); |
| } |
| |
| // Returns the number of bytes consumed by the non-data fields of a stream |
| // frame, assuming it is the last frame in the packet |
| size_t GetStreamFrameOverhead(QuicTransportVersion version) { |
| return QuicFramer::GetMinStreamFrameSize( |
| version, GetNthClientInitiatedStreamId(1), kOffset, true, |
| /* data_length= */ 0); |
| } |
| |
| bool IsDefaultTestConfiguration() { |
| TestParams p = GetParam(); |
| return p.version == AllSupportedVersions()[0] && p.version_serialization; |
| } |
| |
| QuicStreamId GetNthClientInitiatedStreamId(int n) const { |
| return QuicUtils::GetFirstBidirectionalStreamId( |
| creator_.transport_version(), Perspective::IS_CLIENT) + |
| n * 2; |
| } |
| |
| void TestChaosProtection(bool enabled); |
| |
| static constexpr QuicStreamOffset kOffset = 0u; |
| |
| char buffer_[kMaxOutgoingPacketSize]; |
| QuicConnectionId connection_id_; |
| QuicFrames frames_; |
| QuicFramer server_framer_; |
| QuicFramer client_framer_; |
| StrictMock<MockFramerVisitor> framer_visitor_; |
| StrictMock<MockPacketCreatorDelegate> delegate_; |
| std::string data_; |
| struct iovec iov_; |
| TestPacketCreator creator_; |
| std::unique_ptr<SerializedPacket> serialized_packet_; |
| SimpleDataProducer producer_; |
| SimpleBufferAllocator allocator_; |
| }; |
| |
| // Run all packet creator tests with all supported versions of QUIC, and with |
| // and without version in the packet header, as well as doing a run for each |
| // length of truncated connection id. |
| INSTANTIATE_TEST_SUITE_P(QuicPacketCreatorTests, |
| QuicPacketCreatorTest, |
| ::testing::ValuesIn(GetTestParams()), |
| ::testing::PrintToStringParamName()); |
| |
| TEST_P(QuicPacketCreatorTest, SerializeFrames) { |
| for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { |
| EncryptionLevel level = static_cast<EncryptionLevel>(i); |
| creator_.set_encryption_level(level); |
| if (level != ENCRYPTION_ZERO_RTT) { |
| frames_.push_back(QuicFrame(new QuicAckFrame(InitAckFrame(1)))); |
| } |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| if (level != ENCRYPTION_INITIAL && level != ENCRYPTION_HANDSHAKE) { |
| frames_.push_back(QuicFrame( |
| QuicStreamFrame(stream_id, false, 0u, absl::string_view()))); |
| } |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| EXPECT_EQ(level, serialized.encryption_level); |
| if (level != ENCRYPTION_ZERO_RTT) { |
| delete frames_[0].ack_frame; |
| } |
| frames_.clear(); |
| |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| if (level != ENCRYPTION_ZERO_RTT) { |
| EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(framer_visitor_, |
| OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(framer_visitor_, OnAckFrameEnd(QuicPacketNumber(1))) |
| .WillOnce(Return(true)); |
| } |
| if (level != ENCRYPTION_INITIAL && level != ENCRYPTION_HANDSHAKE) { |
| EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); |
| } |
| if (client_framer_.version().HasHeaderProtection()) { |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)) |
| .Times(testing::AnyNumber()); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| ProcessPacket(serialized); |
| } |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) { |
| QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame( |
| creator_.transport_version(), QUIC_NO_ERROR, NO_IETF_QUIC_ERROR, "error", |
| /*transport_close_frame_type=*/0); |
| |
| QuicFrames frames; |
| frames.push_back(QuicFrame(frame)); |
| SerializedPacket serialized = SerializeAllFrames(frames); |
| EXPECT_EQ(ENCRYPTION_INITIAL, serialized.encryption_level); |
| ASSERT_EQ(QuicPacketNumber(1u), serialized.packet_number); |
| ASSERT_EQ(QuicPacketNumber(1u), creator_.packet_number()); |
| |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| |
| ProcessPacket(serialized); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ConsumeCryptoDataToFillCurrentPacket) { |
| std::string data = "crypto data"; |
| QuicFrame frame; |
| ASSERT_TRUE(creator_.ConsumeCryptoDataToFillCurrentPacket( |
| ENCRYPTION_INITIAL, data.length(), 0, |
| /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); |
| EXPECT_EQ(frame.crypto_frame->data_length, data.length()); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ConsumeDataToFillCurrentPacket) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| QuicFrame frame; |
| MakeIOVector("test", &iov_); |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false, |
| NOT_RETRANSMISSION, &frame)); |
| size_t consumed = frame.stream_frame.data_length; |
| EXPECT_EQ(4u, consumed); |
| CheckStreamFrame(frame, stream_id, "test", 0u, false); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ConsumeDataFin) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| QuicFrame frame; |
| MakeIOVector("test", &iov_); |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, true, false, |
| NOT_RETRANSMISSION, &frame)); |
| size_t consumed = frame.stream_frame.data_length; |
| EXPECT_EQ(4u, consumed); |
| CheckStreamFrame(frame, stream_id, "test", 0u, true); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ConsumeDataFinOnly) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| QuicFrame frame; |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, nullptr, 0u, 0u, 0u, 0u, true, false, NOT_RETRANSMISSION, |
| &frame)); |
| size_t consumed = frame.stream_frame.data_length; |
| EXPECT_EQ(0u, consumed); |
| CheckStreamFrame(frame, stream_id, std::string(), 0u, true); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| EXPECT_TRUE(absl::StartsWith(creator_.GetPendingFramesInfo(), |
| "type { STREAM_FRAME }")); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| const size_t overhead = |
| GetPacketHeaderOverhead(client_framer_.transport_version()) + |
| GetEncryptionOverhead(); |
| for (size_t i = overhead + QuicPacketCreator::MinPlaintextPacketSize( |
| client_framer_.version()); |
| i < overhead + 100; ++i) { |
| SCOPED_TRACE(i); |
| creator_.SetMaxPacketLength(i); |
| const bool should_have_room = |
| i > |
| overhead + GetStreamFrameOverhead(client_framer_.transport_version()); |
| ASSERT_EQ(should_have_room, |
| creator_.HasRoomForStreamFrame(GetNthClientInitiatedStreamId(1), |
| kOffset, /* data_size=*/0xffff)); |
| if (should_have_room) { |
| QuicFrame frame; |
| MakeIOVector("testdata", &iov_); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillRepeatedly(Invoke( |
| this, &QuicPacketCreatorTest::ClearSerializedPacketForTests)); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, |
| kOffset, false, false, NOT_RETRANSMISSION, &frame)); |
| size_t bytes_consumed = frame.stream_frame.data_length; |
| EXPECT_LT(0u, bytes_consumed); |
| creator_.FlushCurrentPacket(); |
| } |
| } |
| } |
| |
| TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| // Compute the total overhead for a single frame in packet. |
| const size_t overhead = |
| GetPacketHeaderOverhead(client_framer_.transport_version()) + |
| GetEncryptionOverhead() + |
| GetStreamFrameOverhead(client_framer_.transport_version()); |
| size_t capacity = kDefaultMaxPacketSize - overhead; |
| // Now, test various sizes around this size. |
| for (int delta = -5; delta <= 5; ++delta) { |
| std::string data(capacity + delta, 'A'); |
| size_t bytes_free = delta > 0 ? 0 : 0 - delta; |
| QuicFrame frame; |
| MakeIOVector(data, &iov_); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, kOffset, |
| false, false, NOT_RETRANSMISSION, &frame)); |
| |
| // BytesFree() returns bytes available for the next frame, which will |
| // be two bytes smaller since the stream frame would need to be grown. |
| EXPECT_EQ(2u, creator_.ExpansionOnNewFrame()); |
| size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2; |
| EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta; |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| creator_.FlushCurrentPacket(); |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| DeleteSerializedPacket(); |
| } |
| } |
| |
| TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { |
| // This test serializes crypto payloads slightly larger than a packet, which |
| // Causes the multi-packet ClientHello check to fail. |
| SetQuicFlag(FLAGS_quic_enforce_single_packet_chlo, false); |
| // Compute the total overhead for a single frame in packet. |
| size_t overhead = |
| GetPacketHeaderOverhead(client_framer_.transport_version()) + |
| GetEncryptionOverhead(); |
| if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| overhead += |
| QuicFramer::GetMinCryptoFrameSize(kOffset, kMaxOutgoingPacketSize); |
| } else { |
| overhead += GetStreamFrameOverhead(client_framer_.transport_version()); |
| } |
| ASSERT_GT(kMaxOutgoingPacketSize, overhead); |
| size_t capacity = kDefaultMaxPacketSize - overhead; |
| // Now, test various sizes around this size. |
| for (int delta = -5; delta <= 5; ++delta) { |
| SCOPED_TRACE(delta); |
| std::string data(capacity + delta, 'A'); |
| size_t bytes_free = delta > 0 ? 0 : 0 - delta; |
| |
| QuicFrame frame; |
| MakeIOVector(data, &iov_); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillRepeatedly( |
| Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| if (client_framer_.version().CanSendCoalescedPackets()) { |
| EXPECT_CALL(delegate_, GetSerializedPacketFate(_, _)) |
| .WillRepeatedly(Return(COALESCE)); |
| } |
| if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), |
| &iov_, 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION, |
| &frame)); |
| size_t bytes_consumed = frame.stream_frame.data_length; |
| EXPECT_LT(0u, bytes_consumed); |
| } else { |
| producer_.SaveCryptoData(ENCRYPTION_INITIAL, kOffset, data); |
| ASSERT_TRUE(creator_.ConsumeCryptoDataToFillCurrentPacket( |
| ENCRYPTION_INITIAL, data.length(), kOffset, |
| /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); |
| size_t bytes_consumed = frame.crypto_frame->data_length; |
| EXPECT_LT(0u, bytes_consumed); |
| } |
| creator_.FlushCurrentPacket(); |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| // If there is not enough space in the packet to fit a padding frame |
| // (1 byte) and to expand the stream frame (another 2 bytes) the packet |
| // will not be padded. |
| // Padding is skipped when we try to send coalesced packets. |
| if ((bytes_free < 3 && |
| !QuicVersionUsesCryptoFrames(client_framer_.transport_version())) || |
| client_framer_.version().CanSendCoalescedPackets()) { |
| EXPECT_EQ(kDefaultMaxPacketSize - bytes_free, |
| serialized_packet_->encrypted_length); |
| } else { |
| EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_->encrypted_length); |
| } |
| DeleteSerializedPacket(); |
| } |
| } |
| |
| TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| // Compute the total overhead for a single frame in packet. |
| const size_t overhead = |
| GetPacketHeaderOverhead(client_framer_.transport_version()) + |
| GetEncryptionOverhead() + |
| GetStreamFrameOverhead(client_framer_.transport_version()); |
| ASSERT_GT(kDefaultMaxPacketSize, overhead); |
| size_t capacity = kDefaultMaxPacketSize - overhead; |
| // Now, test various sizes around this size. |
| for (int delta = -5; delta <= 5; ++delta) { |
| std::string data(capacity + delta, 'A'); |
| size_t bytes_free = delta > 0 ? 0 : 0 - delta; |
| |
| QuicFrame frame; |
| MakeIOVector(data, &iov_); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, kOffset, |
| false, false, NOT_RETRANSMISSION, &frame)); |
| size_t bytes_consumed = frame.stream_frame.data_length; |
| EXPECT_LT(0u, bytes_consumed); |
| creator_.FlushCurrentPacket(); |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| if (bytes_free > 0) { |
| EXPECT_EQ(kDefaultMaxPacketSize - bytes_free, |
| serialized_packet_->encrypted_length); |
| } else { |
| EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_->encrypted_length); |
| } |
| DeleteSerializedPacket(); |
| } |
| } |
| |
| // Test that the path challenge connectivity probing packet is serialized |
| // correctly as a padded PATH CHALLENGE packet. |
| TEST_P(QuicPacketCreatorTest, BuildPathChallengePacket) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| // This frame is only for IETF QUIC. |
| return; |
| } |
| |
| QuicPacketHeader header; |
| header.destination_connection_id = CreateTestConnectionId(); |
| header.reset_flag = false; |
| header.version_flag = false; |
| header.packet_number = kPacketNumber; |
| MockRandom randomizer; |
| QuicPathFrameBuffer payload; |
| randomizer.RandBytes(payload.data(), payload.size()); |
| |
| // clang-format off |
| unsigned char packet[] = { |
| // type (short header, 4 byte packet number) |
| 0x43, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // Path Challenge Frame type (IETF_PATH_CHALLENGE) |
| 0x1a, |
| // 8 "random" bytes, MockRandom makes lots of r's |
| 'r', 'r', 'r', 'r', 'r', 'r', 'r', 'r', |
| // frame type (padding frame) |
| 0x00, |
| 0x00, 0x00, 0x00, 0x00 |
| }; |
| // clang-format on |
| |
| std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); |
| |
| size_t length = creator_.BuildPaddedPathChallengePacket( |
| header, buffer.get(), ABSL_ARRAYSIZE(packet), payload, |
| ENCRYPTION_INITIAL); |
| EXPECT_EQ(length, ABSL_ARRAYSIZE(packet)); |
| |
| // Payload has the random bytes that were generated. Copy them into packet, |
| // above, before checking that the generated packet is correct. |
| EXPECT_EQ(kQuicPathFrameBufferSize, payload.size()); |
| |
| QuicPacket data(creator_.transport_version(), buffer.release(), length, true, |
| header); |
| |
| quiche::test::CompareCharArraysWithHexError( |
| "constructed packet", data.data(), data.length(), |
| reinterpret_cast<char*>(packet), ABSL_ARRAYSIZE(packet)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, BuildConnectivityProbingPacket) { |
| QuicPacketHeader header; |
| header.destination_connection_id = CreateTestConnectionId(); |
| header.reset_flag = false; |
| header.version_flag = false; |
| header.packet_number = kPacketNumber; |
| |
| // clang-format off |
| unsigned char packet[] = { |
| // public flags (8 byte connection_id) |
| 0x2C, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // frame type (ping frame) |
| 0x07, |
| // frame type (padding frame) |
| 0x00, |
| 0x00, 0x00, 0x00, 0x00 |
| }; |
| |
| unsigned char packet46[] = { |
| // type (short header, 4 byte packet number) |
| 0x43, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // frame type |
| 0x07, |
| // frame type (padding frame) |
| 0x00, |
| 0x00, 0x00, 0x00, 0x00 |
| }; |
| |
| unsigned char packet99[] = { |
| // type (short header, 4 byte packet number) |
| 0x43, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // frame type (IETF_PING frame) |
| 0x01, |
| // frame type (padding frame) |
| 0x00, |
| 0x00, 0x00, 0x00, 0x00 |
| }; |
| // clang-format on |
| |
| unsigned char* p = packet; |
| size_t packet_size = ABSL_ARRAYSIZE(packet); |
| if (creator_.version().HasIetfQuicFrames()) { |
| p = packet99; |
| packet_size = ABSL_ARRAYSIZE(packet99); |
| } else if (creator_.version().HasIetfInvariantHeader()) { |
| p = packet46; |
| packet_size = ABSL_ARRAYSIZE(packet46); |
| } |
| |
| std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); |
| |
| size_t length = creator_.BuildConnectivityProbingPacket( |
| header, buffer.get(), packet_size, ENCRYPTION_INITIAL); |
| |
| EXPECT_NE(0u, length); |
| QuicPacket data(creator_.transport_version(), buffer.release(), length, true, |
| header); |
| |
| quiche::test::CompareCharArraysWithHexError( |
| "constructed packet", data.data(), data.length(), |
| reinterpret_cast<char*>(p), packet_size); |
| } |
| |
| // Several tests that the path response connectivity probing packet is |
| // serialized correctly as either a padded and unpadded PATH RESPONSE |
| // packet. Also generates packets with 1 and 3 PATH_RESPONSES in them to |
| // exercised the single- and multiple- payload cases. |
| TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket1ResponseUnpadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| // This frame is only for IETF QUIC. |
| return; |
| } |
| |
| QuicPacketHeader header; |
| header.destination_connection_id = CreateTestConnectionId(); |
| header.reset_flag = false; |
| header.version_flag = false; |
| header.packet_number = kPacketNumber; |
| QuicPathFrameBuffer payload0 = { |
| {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; |
| |
| // Build 1 PATH RESPONSE, not padded |
| // clang-format off |
| unsigned char packet[] = { |
| // type (short header, 4 byte packet number) |
| 0x43, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // Path Response Frame type (IETF_PATH_RESPONSE) |
| 0x1b, |
| // 8 "random" bytes |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| }; |
| // clang-format on |
| std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| size_t length = creator_.BuildPathResponsePacket( |
| header, buffer.get(), ABSL_ARRAYSIZE(packet), payloads, |
| /*is_padded=*/false, ENCRYPTION_INITIAL); |
| EXPECT_EQ(length, ABSL_ARRAYSIZE(packet)); |
| QuicPacket data(creator_.transport_version(), buffer.release(), length, true, |
| header); |
| |
| quiche::test::CompareCharArraysWithHexError( |
| "constructed packet", data.data(), data.length(), |
| reinterpret_cast<char*>(packet), ABSL_ARRAYSIZE(packet)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket1ResponsePadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| // This frame is only for IETF QUIC. |
| return; |
| } |
| |
| QuicPacketHeader header; |
| header.destination_connection_id = CreateTestConnectionId(); |
| header.reset_flag = false; |
| header.version_flag = false; |
| header.packet_number = kPacketNumber; |
| QuicPathFrameBuffer payload0 = { |
| {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; |
| |
| // Build 1 PATH RESPONSE, padded |
| // clang-format off |
| unsigned char packet[] = { |
| // type (short header, 4 byte packet number) |
| 0x43, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // Path Response Frame type (IETF_PATH_RESPONSE) |
| 0x1b, |
| // 8 "random" bytes |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| // Padding type and pad |
| 0x00, 0x00, 0x00, 0x00, 0x00 |
| }; |
| // clang-format on |
| std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| size_t length = creator_.BuildPathResponsePacket( |
| header, buffer.get(), ABSL_ARRAYSIZE(packet), payloads, |
| /*is_padded=*/true, ENCRYPTION_INITIAL); |
| EXPECT_EQ(length, ABSL_ARRAYSIZE(packet)); |
| QuicPacket data(creator_.transport_version(), buffer.release(), length, true, |
| header); |
| |
| quiche::test::CompareCharArraysWithHexError( |
| "constructed packet", data.data(), data.length(), |
| reinterpret_cast<char*>(packet), ABSL_ARRAYSIZE(packet)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket3ResponsesUnpadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| // This frame is only for IETF QUIC. |
| return; |
| } |
| |
| QuicPacketHeader header; |
| header.destination_connection_id = CreateTestConnectionId(); |
| header.reset_flag = false; |
| header.version_flag = false; |
| header.packet_number = kPacketNumber; |
| QuicPathFrameBuffer payload0 = { |
| {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; |
| QuicPathFrameBuffer payload1 = { |
| {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; |
| QuicPathFrameBuffer payload2 = { |
| {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; |
| |
| // Build one packet with 3 PATH RESPONSES, no padding |
| // clang-format off |
| unsigned char packet[] = { |
| // type (short header, 4 byte packet number) |
| 0x43, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // 3 path response frames (IETF_PATH_RESPONSE type byte and payload) |
| 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, |
| 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, |
| }; |
| // clang-format on |
| |
| std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| payloads.push_back(payload1); |
| payloads.push_back(payload2); |
| size_t length = creator_.BuildPathResponsePacket( |
| header, buffer.get(), ABSL_ARRAYSIZE(packet), payloads, |
| /*is_padded=*/false, ENCRYPTION_INITIAL); |
| EXPECT_EQ(length, ABSL_ARRAYSIZE(packet)); |
| QuicPacket data(creator_.transport_version(), buffer.release(), length, true, |
| header); |
| |
| quiche::test::CompareCharArraysWithHexError( |
| "constructed packet", data.data(), data.length(), |
| reinterpret_cast<char*>(packet), ABSL_ARRAYSIZE(packet)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket3ResponsesPadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| // This frame is only for IETF QUIC. |
| return; |
| } |
| |
| QuicPacketHeader header; |
| header.destination_connection_id = CreateTestConnectionId(); |
| header.reset_flag = false; |
| header.version_flag = false; |
| header.packet_number = kPacketNumber; |
| QuicPathFrameBuffer payload0 = { |
| {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; |
| QuicPathFrameBuffer payload1 = { |
| {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; |
| QuicPathFrameBuffer payload2 = { |
| {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; |
| |
| // Build one packet with 3 PATH RESPONSES, with padding |
| // clang-format off |
| unsigned char packet[] = { |
| // type (short header, 4 byte packet number) |
| 0x43, |
| // connection_id |
| 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, |
| // packet number |
| 0x12, 0x34, 0x56, 0x78, |
| |
| // 3 path response frames (IETF_PATH_RESPONSE byte and payload) |
| 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, |
| 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, |
| // Padding |
| 0x00, 0x00, 0x00, 0x00, 0x00 |
| }; |
| // clang-format on |
| |
| std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| payloads.push_back(payload1); |
| payloads.push_back(payload2); |
| size_t length = creator_.BuildPathResponsePacket( |
| header, buffer.get(), ABSL_ARRAYSIZE(packet), payloads, |
| /*is_padded=*/true, ENCRYPTION_INITIAL); |
| EXPECT_EQ(length, ABSL_ARRAYSIZE(packet)); |
| QuicPacket data(creator_.transport_version(), buffer.release(), length, true, |
| header); |
| |
| quiche::test::CompareCharArraysWithHexError( |
| "constructed packet", data.data(), data.length(), |
| reinterpret_cast<char*>(packet), ABSL_ARRAYSIZE(packet)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializeConnectivityProbingPacket) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| std::unique_ptr<SerializedPacket> encrypted; |
| if (VersionHasIetfQuicFrames(creator_.transport_version())) { |
| QuicPathFrameBuffer payload = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; |
| encrypted = |
| creator_.SerializePathChallengeConnectivityProbingPacket(payload); |
| } else { |
| encrypted = creator_.SerializeConnectivityProbingPacket(); |
| } |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| if (VersionHasIetfQuicFrames(creator_.transport_version())) { |
| EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| } else { |
| EXPECT_CALL(framer_visitor_, OnPingFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializePathChallengeProbePacket) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| return; |
| } |
| QuicPathFrameBuffer payload = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| std::unique_ptr<SerializedPacket> encrypted( |
| creator_.SerializePathChallengeConnectivityProbingPacket(payload)); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadPadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| return; |
| } |
| QuicPathFrameBuffer payload0 = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| |
| std::unique_ptr<SerializedPacket> encrypted( |
| creator_.SerializePathResponseConnectivityProbingPacket(payloads, true)); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, |
| SerializePathResponseProbePacket1PayloadUnPadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| return; |
| } |
| QuicPathFrameBuffer payload0 = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| |
| std::unique_ptr<SerializedPacket> encrypted( |
| creator_.SerializePathResponseConnectivityProbingPacket(payloads, false)); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsPadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| return; |
| } |
| QuicPathFrameBuffer payload0 = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; |
| QuicPathFrameBuffer payload1 = { |
| {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| payloads.push_back(payload1); |
| |
| std::unique_ptr<SerializedPacket> encrypted( |
| creator_.SerializePathResponseConnectivityProbingPacket(payloads, true)); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, |
| SerializePathResponseProbePacket2PayloadsUnPadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| return; |
| } |
| QuicPathFrameBuffer payload0 = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; |
| QuicPathFrameBuffer payload1 = { |
| {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| payloads.push_back(payload1); |
| |
| std::unique_ptr<SerializedPacket> encrypted( |
| creator_.SerializePathResponseConnectivityProbingPacket(payloads, false)); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsPadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| return; |
| } |
| QuicPathFrameBuffer payload0 = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; |
| QuicPathFrameBuffer payload1 = { |
| {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; |
| QuicPathFrameBuffer payload2 = { |
| {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}}; |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| payloads.push_back(payload1); |
| payloads.push_back(payload2); |
| |
| std::unique_ptr<SerializedPacket> encrypted( |
| creator_.SerializePathResponseConnectivityProbingPacket(payloads, true)); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, |
| SerializePathResponseProbePacket3PayloadsUnpadded) { |
| if (!VersionHasIetfQuicFrames(creator_.transport_version())) { |
| return; |
| } |
| QuicPathFrameBuffer payload0 = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; |
| QuicPathFrameBuffer payload1 = { |
| {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; |
| QuicPathFrameBuffer payload2 = { |
| {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}}; |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; |
| payloads.push_back(payload0); |
| payloads.push_back(payload1); |
| payloads.push_back(payload2); |
| |
| std::unique_ptr<SerializedPacket> encrypted( |
| creator_.SerializePathResponseConnectivityProbingPacket(payloads, false)); |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| |
| server_framer_.ProcessPacket(QuicEncryptedPacket( |
| encrypted->encrypted_buffer, encrypted->encrypted_length)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { |
| if (creator_.version().HasIetfInvariantHeader() && |
| !GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) { |
| EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| } else { |
| EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| } |
| |
| QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64); |
| creator_.UpdatePacketNumberLength(QuicPacketNumber(2), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256); |
| creator_.UpdatePacketNumberLength(QuicPacketNumber(2), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256 * 256); |
| creator_.UpdatePacketNumberLength(QuicPacketNumber(2), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| QuicPacketCreatorPeer::SetPacketNumber(&creator_, |
| UINT64_C(64) * 256 * 256 * 256 * 256); |
| creator_.UpdatePacketNumberLength(QuicPacketNumber(2), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthCwnd) { |
| QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1); |
| if (creator_.version().HasIetfInvariantHeader() && |
| !GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) { |
| EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| } else { |
| EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| } |
| |
| creator_.UpdatePacketNumberLength(QuicPacketNumber(1), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| creator_.UpdatePacketNumberLength(QuicPacketNumber(1), |
| 10000 * 256 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| creator_.UpdatePacketNumberLength(QuicPacketNumber(1), |
| 10000 * 256 * 256 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| creator_.UpdatePacketNumberLength( |
| QuicPacketNumber(1), |
| UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize); |
| EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SkipNPacketNumbers) { |
| QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1); |
| if (creator_.version().HasIetfInvariantHeader() && |
| !GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) { |
| EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| } else { |
| EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| } |
| creator_.SkipNPacketNumbers(63, QuicPacketNumber(2), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(QuicPacketNumber(64), creator_.packet_number()); |
| EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| creator_.SkipNPacketNumbers(64 * 255, QuicPacketNumber(2), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(QuicPacketNumber(64 * 256), creator_.packet_number()); |
| EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| |
| creator_.SkipNPacketNumbers(64 * 256 * 255, QuicPacketNumber(2), |
| 10000 / kDefaultMaxPacketSize); |
| EXPECT_EQ(QuicPacketNumber(64 * 256 * 256), creator_.packet_number()); |
| EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializeFrame) { |
| if (!GetParam().version_serialization) { |
| creator_.StopSendingVersion(); |
| } |
| std::string data("test data"); |
| if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| QuicStreamFrame stream_frame( |
| QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), |
| /*fin=*/false, 0u, absl::string_view()); |
| frames_.push_back(QuicFrame(stream_frame)); |
| } else { |
| producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data); |
| frames_.push_back( |
| QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length()))); |
| } |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| |
| QuicPacketHeader header; |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)) |
| .WillOnce(DoAll(SaveArg<0>(&header), Return(true))); |
| if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)); |
| } else { |
| EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| ProcessPacket(serialized); |
| EXPECT_EQ(GetParam().version_serialization, header.version_flag); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializeFrameShortData) { |
| if (!GetParam().version_serialization) { |
| creator_.StopSendingVersion(); |
| } |
| std::string data("Hello World!"); |
| if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| QuicStreamFrame stream_frame( |
| QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), |
| /*fin=*/false, 0u, absl::string_view()); |
| frames_.push_back(QuicFrame(stream_frame)); |
| } else { |
| producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data); |
| frames_.push_back( |
| QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length()))); |
| } |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| |
| QuicPacketHeader header; |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)) |
| .WillOnce(DoAll(SaveArg<0>(&header), Return(true))); |
| if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)); |
| } else { |
| EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| ProcessPacket(serialized); |
| EXPECT_EQ(GetParam().version_serialization, header.version_flag); |
| } |
| |
| void QuicPacketCreatorTest::TestChaosProtection(bool enabled) { |
| if (!GetParam().version.UsesCryptoFrames()) { |
| return; |
| } |
| MockRandom mock_random(2); |
| QuicPacketCreatorPeer::SetRandom(&creator_, &mock_random); |
| creator_.set_chaos_protection_enabled(enabled); |
| std::string data("ChAoS_ThEoRy!"); |
| producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data); |
| frames_.push_back( |
| QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length()))); |
| frames_.push_back(QuicFrame(QuicPaddingFrame(33))); |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| if (enabled) { |
| EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)).Times(AtLeast(2)); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(AtLeast(2)); |
| EXPECT_CALL(framer_visitor_, OnPingFrame(_)).Times(AtLeast(1)); |
| } else { |
| EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)).Times(1); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(1); |
| EXPECT_CALL(framer_visitor_, OnPingFrame(_)).Times(0); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| ProcessPacket(serialized); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ChaosProtectionEnabled) { |
| TestChaosProtection(/*enabled=*/true); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ChaosProtectionDisabled) { |
| TestChaosProtection(/*enabled=*/false); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ConsumeDataLargerThanOneStreamFrame) { |
| if (!GetParam().version_serialization) { |
| creator_.StopSendingVersion(); |
| } |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| // A string larger than fits into a frame. |
| QuicFrame frame; |
| size_t payload_length = creator_.max_packet_length(); |
| const std::string too_long_payload(payload_length, 'a'); |
| MakeIOVector(too_long_payload, &iov_); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, true, false, |
| NOT_RETRANSMISSION, &frame)); |
| size_t consumed = frame.stream_frame.data_length; |
| // The entire payload could not be consumed. |
| EXPECT_GT(payload_length, consumed); |
| creator_.FlushCurrentPacket(); |
| DeleteSerializedPacket(); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, AddFrameAndFlush) { |
| if (!GetParam().version_serialization) { |
| creator_.StopSendingVersion(); |
| } |
| const size_t max_plaintext_size = |
| client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| stream_id = |
| QuicUtils::GetCryptoStreamId(client_framer_.transport_version()); |
| } |
| EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(stream_id)); |
| EXPECT_EQ(max_plaintext_size - |
| GetPacketHeaderSize( |
| client_framer_.transport_version(), |
| creator_.GetDestinationConnectionIdLength(), |
| creator_.GetSourceConnectionIdLength(), |
| QuicPacketCreatorPeer::SendVersionInPacket(&creator_), |
| !kIncludeDiversificationNonce, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), |
| QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), |
| 0, QuicPacketCreatorPeer::GetLengthLength(&creator_)), |
| creator_.BytesFree()); |
| StrictMock<MockDebugDelegate> debug; |
| creator_.set_debug_delegate(&debug); |
| |
| // Add a variety of frame types and then a padding frame. |
| QuicAckFrame ack_frame(InitAckFrame(10u)); |
| EXPECT_CALL(debug, OnFrameAddedToPacket(_)); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(stream_id)); |
| |
| QuicFrame frame; |
| MakeIOVector("test", &iov_); |
| EXPECT_CALL(debug, OnFrameAddedToPacket(_)); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false, |
| NOT_RETRANSMISSION, &frame)); |
| size_t consumed = frame.stream_frame.data_length; |
| EXPECT_EQ(4u, consumed); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| EXPECT_TRUE(creator_.HasPendingStreamFramesOfStream(stream_id)); |
| |
| QuicPaddingFrame padding_frame; |
| EXPECT_CALL(debug, OnFrameAddedToPacket(_)); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(padding_frame), NOT_RETRANSMISSION)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| EXPECT_EQ(0u, creator_.BytesFree()); |
| |
| // Packet is full. Creator will flush. |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| EXPECT_FALSE(creator_.AddFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); |
| |
| // Ensure the packet is successfully created. |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| ASSERT_FALSE(serialized_packet_->retransmittable_frames.empty()); |
| const QuicFrames& retransmittable = |
| serialized_packet_->retransmittable_frames; |
| ASSERT_EQ(1u, retransmittable.size()); |
| EXPECT_EQ(STREAM_FRAME, retransmittable[0].type); |
| EXPECT_TRUE(serialized_packet_->has_ack); |
| EXPECT_EQ(QuicPacketNumber(10u), serialized_packet_->largest_acked); |
| DeleteSerializedPacket(); |
| |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(stream_id)); |
| EXPECT_EQ(max_plaintext_size - |
| GetPacketHeaderSize( |
| client_framer_.transport_version(), |
| creator_.GetDestinationConnectionIdLength(), |
| creator_.GetSourceConnectionIdLength(), |
| QuicPacketCreatorPeer::SendVersionInPacket(&creator_), |
| !kIncludeDiversificationNonce, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), |
| QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), |
| 0, QuicPacketCreatorPeer::GetLengthLength(&creator_)), |
| creator_.BytesFree()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializeAndSendStreamFrame) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| if (!GetParam().version_serialization) { |
| creator_.StopSendingVersion(); |
| } |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| |
| MakeIOVector("test", &iov_); |
| producer_.SaveStreamData(GetNthClientInitiatedStreamId(0), &iov_, 1u, 0u, |
| iov_.iov_len); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| size_t num_bytes_consumed; |
| StrictMock<MockDebugDelegate> debug; |
| creator_.set_debug_delegate(&debug); |
| EXPECT_CALL(debug, OnFrameAddedToPacket(_)); |
| creator_.CreateAndSerializeStreamFrame( |
| GetNthClientInitiatedStreamId(0), iov_.iov_len, 0, 0, true, |
| NOT_RETRANSMISSION, &num_bytes_consumed); |
| EXPECT_EQ(4u, num_bytes_consumed); |
| |
| // Ensure the packet is successfully created. |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| ASSERT_FALSE(serialized_packet_->retransmittable_frames.empty()); |
| const QuicFrames& retransmittable = |
| serialized_packet_->retransmittable_frames; |
| ASSERT_EQ(1u, retransmittable.size()); |
| EXPECT_EQ(STREAM_FRAME, retransmittable[0].type); |
| DeleteSerializedPacket(); |
| |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializeStreamFrameWithPadding) { |
| // Regression test to check that CreateAndSerializeStreamFrame uses a |
| // correctly formatted stream frame header when appending padding. |
| |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| if (!GetParam().version_serialization) { |
| creator_.StopSendingVersion(); |
| } |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| |
| // Send one byte of stream data. |
| MakeIOVector("a", &iov_); |
| producer_.SaveStreamData(GetNthClientInitiatedStreamId(0), &iov_, 1u, 0u, |
| iov_.iov_len); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| size_t num_bytes_consumed; |
| creator_.CreateAndSerializeStreamFrame( |
| GetNthClientInitiatedStreamId(0), iov_.iov_len, 0, 0, true, |
| NOT_RETRANSMISSION, &num_bytes_consumed); |
| EXPECT_EQ(1u, num_bytes_consumed); |
| |
| // Check that a packet is created. |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| ASSERT_FALSE(serialized_packet_->retransmittable_frames.empty()); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); |
| if (client_framer_.version().HasHeaderProtection()) { |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| ProcessPacket(*serialized_packet_); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, AddUnencryptedStreamDataClosesConnection) { |
| // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. |
| if (!IsDefaultTestConfiguration()) { |
| return; |
| } |
| |
| creator_.set_encryption_level(ENCRYPTION_INITIAL); |
| EXPECT_CALL(delegate_, OnUnrecoverableError(_, _)); |
| QuicStreamFrame stream_frame(GetNthClientInitiatedStreamId(0), |
| /*fin=*/false, 0u, absl::string_view()); |
| EXPECT_QUIC_BUG( |
| creator_.AddFrame(QuicFrame(stream_frame), NOT_RETRANSMISSION), |
| "Cannot send stream data with level: ENCRYPTION_INITIAL"); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SendStreamDataWithEncryptionHandshake) { |
| // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. |
| if (!IsDefaultTestConfiguration()) { |
| return; |
| } |
| |
| creator_.set_encryption_level(ENCRYPTION_HANDSHAKE); |
| EXPECT_CALL(delegate_, OnUnrecoverableError(_, _)); |
| QuicStreamFrame stream_frame(GetNthClientInitiatedStreamId(0), |
| /*fin=*/false, 0u, absl::string_view()); |
| EXPECT_QUIC_BUG( |
| creator_.AddFrame(QuicFrame(stream_frame), NOT_RETRANSMISSION), |
| "Cannot send stream data with level: ENCRYPTION_HANDSHAKE"); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ChloTooLarge) { |
| // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. |
| if (!IsDefaultTestConfiguration()) { |
| return; |
| } |
| |
| // This test only matters when the crypto handshake is sent in stream frames. |
| // TODO(b/128596274): Re-enable when this check is supported for CRYPTO |
| // frames. |
| if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| return; |
| } |
| |
| CryptoHandshakeMessage message; |
| message.set_tag(kCHLO); |
| message.set_minimum_size(kMaxOutgoingPacketSize); |
| CryptoFramer framer; |
| std::unique_ptr<QuicData> message_data; |
| message_data = framer.ConstructHandshakeMessage(message); |
| |
| struct iovec iov; |
| MakeIOVector(absl::string_view(message_data->data(), message_data->length()), |
| &iov); |
| QuicFrame frame; |
| EXPECT_CALL(delegate_, OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, _)); |
| EXPECT_QUIC_BUG( |
| creator_.ConsumeDataToFillCurrentPacket( |
| QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), |
| &iov, 1u, iov.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, |
| &frame), |
| "Client hello won't fit in a single packet."); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, PendingPadding) { |
| EXPECT_EQ(0u, creator_.pending_padding_bytes()); |
| creator_.AddPendingPadding(kMaxNumRandomPaddingBytes * 10); |
| EXPECT_EQ(kMaxNumRandomPaddingBytes * 10, creator_.pending_padding_bytes()); |
| |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillRepeatedly( |
| Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| // Flush all paddings. |
| while (creator_.pending_padding_bytes() > 0) { |
| creator_.FlushCurrentPacket(); |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| // Packet only contains padding. |
| ProcessPacket(*serialized_packet_); |
| } |
| EXPECT_EQ(0u, creator_.pending_padding_bytes()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, FullPaddingDoesNotConsumePendingPadding) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| creator_.AddPendingPadding(kMaxNumRandomPaddingBytes); |
| QuicFrame frame; |
| MakeIOVector("test", &iov_); |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, |
| /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| creator_.FlushCurrentPacket(); |
| EXPECT_EQ(kMaxNumRandomPaddingBytes, creator_.pending_padding_bytes()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ConsumeDataAndRandomPadding) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| const QuicByteCount kStreamFramePayloadSize = 100u; |
| // Set the packet size be enough for one stream frame with 0 stream offset + |
| // 1. |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| size_t length = |
| GetPacketHeaderOverhead(client_framer_.transport_version()) + |
| GetEncryptionOverhead() + |
| QuicFramer::GetMinStreamFrameSize( |
| client_framer_.transport_version(), stream_id, 0, |
| /*last_frame_in_packet=*/false, kStreamFramePayloadSize + 1) + |
| kStreamFramePayloadSize + 1; |
| creator_.SetMaxPacketLength(length); |
| creator_.AddPendingPadding(kMaxNumRandomPaddingBytes); |
| QuicByteCount pending_padding_bytes = creator_.pending_padding_bytes(); |
| QuicFrame frame; |
| char buf[kStreamFramePayloadSize + 1] = {}; |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillRepeatedly( |
| Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| // Send stream frame of size kStreamFramePayloadSize. |
| MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize), &iov_); |
| creator_.ConsumeDataToFillCurrentPacket(stream_id, &iov_, 1u, iov_.iov_len, |
| 0u, 0u, false, false, |
| NOT_RETRANSMISSION, &frame); |
| creator_.FlushCurrentPacket(); |
| // 1 byte padding is sent. |
| EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes()); |
| // Send stream frame of size kStreamFramePayloadSize + 1. |
| MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize + 1), &iov_); |
| creator_.ConsumeDataToFillCurrentPacket(stream_id, &iov_, 1u, iov_.iov_len, |
| 0u, kStreamFramePayloadSize, false, |
| false, NOT_RETRANSMISSION, &frame); |
| // No padding is sent. |
| creator_.FlushCurrentPacket(); |
| EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes()); |
| // Flush all paddings. |
| while (creator_.pending_padding_bytes() > 0) { |
| creator_.FlushCurrentPacket(); |
| } |
| EXPECT_EQ(0u, creator_.pending_padding_bytes()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, FlushWithExternalBuffer) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| char* buffer = new char[kMaxOutgoingPacketSize]; |
| QuicPacketBuffer external_buffer = {buffer, |
| [](const char* p) { delete[] p; }}; |
| EXPECT_CALL(delegate_, GetPacketBuffer()).WillOnce(Return(external_buffer)); |
| |
| QuicFrame frame; |
| MakeIOVector("test", &iov_); |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, |
| /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); |
| |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke([&external_buffer](SerializedPacket serialized_packet) { |
| EXPECT_EQ(external_buffer.buffer, serialized_packet.encrypted_buffer); |
| })); |
| creator_.FlushCurrentPacket(); |
| } |
| |
| // Test for error found in |
| // https://bugs.chromium.org/p/chromium/issues/detail?id=859949 where a gap |
| // length that crosses an IETF VarInt length boundary would cause a |
| // failure. While this test is not applicable to versions other than version 99, |
| // it should still work. Hence, it is not made version-specific. |
| TEST_P(QuicPacketCreatorTest, IetfAckGapErrorRegression) { |
| QuicAckFrame ack_frame = |
| InitAckFrame({{QuicPacketNumber(60), QuicPacketNumber(61)}, |
| {QuicPacketNumber(125), QuicPacketNumber(126)}}); |
| frames_.push_back(QuicFrame(&ack_frame)); |
| SerializeAllFrames(frames_); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, AddMessageFrame) { |
| if (!VersionSupportsMessageFrames(client_framer_.transport_version())) { |
| return; |
| } |
| if (client_framer_.version().UsesTls()) { |
| creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize); |
| } |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .Times(3) |
| .WillRepeatedly( |
| Invoke(this, &QuicPacketCreatorTest::ClearSerializedPacketForTests)); |
| // Verify that there is enough room for the largest message payload. |
| EXPECT_TRUE(creator_.HasRoomForMessageFrame( |
| creator_.GetCurrentLargestMessagePayload())); |
| std::string large_message(creator_.GetCurrentLargestMessagePayload(), 'a'); |
| QuicMessageFrame* message_frame = |
| new QuicMessageFrame(1, MemSliceFromString(large_message)); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(message_frame), NOT_RETRANSMISSION)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| creator_.FlushCurrentPacket(); |
| |
| QuicMessageFrame* frame2 = |
| new QuicMessageFrame(2, MemSliceFromString("message")); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame2), NOT_RETRANSMISSION)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| // Verify if a new frame is added, 1 byte message length will be added. |
| EXPECT_EQ(1u, creator_.ExpansionOnNewFrame()); |
| QuicMessageFrame* frame3 = |
| new QuicMessageFrame(3, MemSliceFromString("message2")); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame3), NOT_RETRANSMISSION)); |
| EXPECT_EQ(1u, creator_.ExpansionOnNewFrame()); |
| creator_.FlushCurrentPacket(); |
| |
| QuicFrame frame; |
| MakeIOVector("test", &iov_); |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| EXPECT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false, |
| NOT_RETRANSMISSION, &frame)); |
| QuicMessageFrame* frame4 = |
| new QuicMessageFrame(4, MemSliceFromString("message")); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame4), NOT_RETRANSMISSION)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| // Verify there is not enough room for largest payload. |
| EXPECT_FALSE(creator_.HasRoomForMessageFrame( |
| creator_.GetCurrentLargestMessagePayload())); |
| // Add largest message will causes the flush of the stream frame. |
| QuicMessageFrame frame5(5, MemSliceFromString(large_message)); |
| EXPECT_FALSE(creator_.AddFrame(QuicFrame(&frame5), NOT_RETRANSMISSION)); |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, MessageFrameConsumption) { |
| if (!VersionSupportsMessageFrames(client_framer_.transport_version())) { |
| return; |
| } |
| if (client_framer_.version().UsesTls()) { |
| creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize); |
| } |
| std::string message_data(kDefaultMaxPacketSize, 'a'); |
| // Test all possible encryption levels of message frames. |
| for (EncryptionLevel level : |
| {ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { |
| creator_.set_encryption_level(level); |
| // Test all possible sizes of message frames. |
| for (size_t message_size = 0; |
| message_size <= creator_.GetCurrentLargestMessagePayload(); |
| ++message_size) { |
| QuicMessageFrame* frame = |
| new QuicMessageFrame(0, MemSliceFromString(absl::string_view( |
| message_data.data(), message_size))); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame), NOT_RETRANSMISSION)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| |
| size_t expansion_bytes = message_size >= 64 ? 2 : 1; |
| EXPECT_EQ(expansion_bytes, creator_.ExpansionOnNewFrame()); |
| // Verify BytesFree returns bytes available for the next frame, which |
| // should subtract the message length. |
| size_t expected_bytes_free = |
| creator_.GetCurrentLargestMessagePayload() - message_size < |
| expansion_bytes |
| ? 0 |
| : creator_.GetCurrentLargestMessagePayload() - expansion_bytes - |
| message_size; |
| EXPECT_EQ(expected_bytes_free, creator_.BytesFree()); |
| EXPECT_LE(creator_.GetGuaranteedLargestMessagePayload(), |
| creator_.GetCurrentLargestMessagePayload()); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| creator_.FlushCurrentPacket(); |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| DeleteSerializedPacket(); |
| } |
| } |
| } |
| |
| TEST_P(QuicPacketCreatorTest, GetGuaranteedLargestMessagePayload) { |
| ParsedQuicVersion version = GetParam().version; |
| if (!version.SupportsMessageFrames()) { |
| return; |
| } |
| if (version.UsesTls()) { |
| creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize); |
| } |
| QuicPacketLength expected_largest_payload = 1219; |
| if (version.HasLongHeaderLengths()) { |
| expected_largest_payload -= 2; |
| } |
| if (version.HasLengthPrefixedConnectionIds()) { |
| expected_largest_payload -= 1; |
| } |
| EXPECT_EQ(expected_largest_payload, |
| creator_.GetGuaranteedLargestMessagePayload()); |
| EXPECT_TRUE(creator_.HasRoomForMessageFrame( |
| creator_.GetGuaranteedLargestMessagePayload())); |
| |
| // Now test whether SetMaxDatagramFrameSize works. |
| creator_.SetMaxDatagramFrameSize(expected_largest_payload + 1 + |
| kQuicFrameTypeSize); |
| EXPECT_EQ(expected_largest_payload, |
| creator_.GetGuaranteedLargestMessagePayload()); |
| EXPECT_TRUE(creator_.HasRoomForMessageFrame( |
| creator_.GetGuaranteedLargestMessagePayload())); |
| |
| creator_.SetMaxDatagramFrameSize(expected_largest_payload + |
| kQuicFrameTypeSize); |
| EXPECT_EQ(expected_largest_payload, |
| creator_.GetGuaranteedLargestMessagePayload()); |
| EXPECT_TRUE(creator_.HasRoomForMessageFrame( |
| creator_.GetGuaranteedLargestMessagePayload())); |
| |
| creator_.SetMaxDatagramFrameSize(expected_largest_payload - 1 + |
| kQuicFrameTypeSize); |
| EXPECT_EQ(expected_largest_payload - 1, |
| creator_.GetGuaranteedLargestMessagePayload()); |
| EXPECT_TRUE(creator_.HasRoomForMessageFrame( |
| creator_.GetGuaranteedLargestMessagePayload())); |
| |
| constexpr QuicPacketLength kFrameSizeLimit = 1000; |
| constexpr QuicPacketLength kPayloadSizeLimit = |
| kFrameSizeLimit - kQuicFrameTypeSize; |
| creator_.SetMaxDatagramFrameSize(kFrameSizeLimit); |
| EXPECT_EQ(creator_.GetGuaranteedLargestMessagePayload(), kPayloadSizeLimit); |
| EXPECT_TRUE(creator_.HasRoomForMessageFrame(kPayloadSizeLimit)); |
| EXPECT_FALSE(creator_.HasRoomForMessageFrame(kPayloadSizeLimit + 1)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, GetCurrentLargestMessagePayload) { |
| ParsedQuicVersion version = GetParam().version; |
| if (!version.SupportsMessageFrames()) { |
| return; |
| } |
| if (version.UsesTls()) { |
| creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize); |
| } |
| QuicPacketLength expected_largest_payload = 1219; |
| if (version.SendsVariableLengthPacketNumberInLongHeader()) { |
| expected_largest_payload += 3; |
| } |
| if (version.HasLongHeaderLengths()) { |
| expected_largest_payload -= 2; |
| } |
| if (version.HasLengthPrefixedConnectionIds()) { |
| expected_largest_payload -= 1; |
| } |
| EXPECT_EQ(expected_largest_payload, |
| creator_.GetCurrentLargestMessagePayload()); |
| |
| // Now test whether SetMaxDatagramFrameSize works. |
| creator_.SetMaxDatagramFrameSize(expected_largest_payload + 1 + |
| kQuicFrameTypeSize); |
| EXPECT_EQ(expected_largest_payload, |
| creator_.GetCurrentLargestMessagePayload()); |
| |
| creator_.SetMaxDatagramFrameSize(expected_largest_payload + |
| kQuicFrameTypeSize); |
| EXPECT_EQ(expected_largest_payload, |
| creator_.GetCurrentLargestMessagePayload()); |
| |
| creator_.SetMaxDatagramFrameSize(expected_largest_payload - 1 + |
| kQuicFrameTypeSize); |
| EXPECT_EQ(expected_largest_payload - 1, |
| creator_.GetCurrentLargestMessagePayload()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, PacketTransmissionType) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| |
| QuicAckFrame temp_ack_frame = InitAckFrame(1); |
| QuicFrame ack_frame(&temp_ack_frame); |
| ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(ack_frame.type)); |
| |
| QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| QuicFrame stream_frame(QuicStreamFrame(stream_id, |
| /*fin=*/false, 0u, |
| absl::string_view())); |
| ASSERT_TRUE(QuicUtils::IsRetransmittableFrame(stream_frame.type)); |
| |
| QuicFrame padding_frame{QuicPaddingFrame()}; |
| ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(padding_frame.type)); |
| |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| |
| EXPECT_TRUE(creator_.AddFrame(ack_frame, LOSS_RETRANSMISSION)); |
| ASSERT_EQ(serialized_packet_, nullptr); |
| |
| EXPECT_TRUE(creator_.AddFrame(stream_frame, RTO_RETRANSMISSION)); |
| ASSERT_EQ(serialized_packet_, nullptr); |
| |
| EXPECT_TRUE(creator_.AddFrame(padding_frame, TLP_RETRANSMISSION)); |
| creator_.FlushCurrentPacket(); |
| ASSERT_TRUE(serialized_packet_->encrypted_buffer); |
| |
| // The last retransmittable frame on packet is a stream frame, the packet's |
| // transmission type should be the same as the stream frame's. |
| EXPECT_EQ(serialized_packet_->transmission_type, RTO_RETRANSMISSION); |
| DeleteSerializedPacket(); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, RetryToken) { |
| if (!GetParam().version_serialization || |
| !QuicVersionHasLongHeaderLengths(client_framer_.transport_version())) { |
| return; |
| } |
| |
| char retry_token_bytes[] = {1, 2, 3, 4, 5, 6, 7, 8, |
| 9, 10, 11, 12, 13, 14, 15, 16}; |
| |
| creator_.SetRetryToken( |
| std::string(retry_token_bytes, sizeof(retry_token_bytes))); |
| |
| frames_.push_back(QuicFrame(QuicPingFrame())); |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| |
| QuicPacketHeader header; |
| { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)) |
| .WillOnce(DoAll(SaveArg<0>(&header), Return(true))); |
| EXPECT_CALL(framer_visitor_, OnPingFrame(_)); |
| if (client_framer_.version().HasHeaderProtection()) { |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| } |
| ProcessPacket(serialized); |
| ASSERT_TRUE(header.version_flag); |
| ASSERT_EQ(header.long_packet_type, INITIAL); |
| ASSERT_EQ(header.retry_token.length(), sizeof(retry_token_bytes)); |
| quiche::test::CompareCharArraysWithHexError( |
| "retry token", header.retry_token.data(), header.retry_token.length(), |
| retry_token_bytes, sizeof(retry_token_bytes)); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, GetConnectionId) { |
| EXPECT_EQ(TestConnectionId(2), creator_.GetDestinationConnectionId()); |
| EXPECT_EQ(EmptyQuicConnectionId(), creator_.GetSourceConnectionId()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, ClientConnectionId) { |
| if (!client_framer_.version().SupportsClientConnectionIds()) { |
| return; |
| } |
| EXPECT_EQ(TestConnectionId(2), creator_.GetDestinationConnectionId()); |
| EXPECT_EQ(EmptyQuicConnectionId(), creator_.GetSourceConnectionId()); |
| creator_.SetClientConnectionId(TestConnectionId(0x33)); |
| EXPECT_EQ(TestConnectionId(2), creator_.GetDestinationConnectionId()); |
| EXPECT_EQ(TestConnectionId(0x33), creator_.GetSourceConnectionId()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, CoalesceStreamFrames) { |
| InSequence s; |
| if (!GetParam().version_serialization) { |
| creator_.StopSendingVersion(); |
| } |
| const size_t max_plaintext_size = |
| client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| QuicStreamId stream_id1 = QuicUtils::GetFirstBidirectionalStreamId( |
| client_framer_.transport_version(), Perspective::IS_CLIENT); |
| QuicStreamId stream_id2 = GetNthClientInitiatedStreamId(1); |
| EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(stream_id1)); |
| EXPECT_EQ(max_plaintext_size - |
| GetPacketHeaderSize( |
| client_framer_.transport_version(), |
| creator_.GetDestinationConnectionIdLength(), |
| creator_.GetSourceConnectionIdLength(), |
| QuicPacketCreatorPeer::SendVersionInPacket(&creator_), |
| !kIncludeDiversificationNonce, |
| QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), |
| QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), |
| 0, QuicPacketCreatorPeer::GetLengthLength(&creator_)), |
| creator_.BytesFree()); |
| StrictMock<MockDebugDelegate> debug; |
| creator_.set_debug_delegate(&debug); |
| |
| MakeIOVector("test", &iov_); |
| QuicFrame frame; |
| EXPECT_CALL(debug, OnFrameAddedToPacket(_)); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id1, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false, |
| NOT_RETRANSMISSION, &frame)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| EXPECT_TRUE(creator_.HasPendingStreamFramesOfStream(stream_id1)); |
| |
| MakeIOVector("coalesce", &iov_); |
| // frame will be coalesced with the first frame. |
| const auto previous_size = creator_.PacketSize(); |
| QuicStreamFrame target(stream_id1, true, 0, 12); |
| EXPECT_CALL(debug, OnStreamFrameCoalesced(target)); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id1, &iov_, 1u, iov_.iov_len, 0u, 4u, true, false, |
| NOT_RETRANSMISSION, &frame)); |
| EXPECT_EQ(frame.stream_frame.data_length, |
| creator_.PacketSize() - previous_size); |
| |
| // frame is for another stream, so it won't be coalesced. |
| const auto length = creator_.BytesFree() - 10u; |
| std::string large_data(length, 'x'); |
| MakeIOVector(large_data, &iov_); |
| EXPECT_CALL(debug, OnFrameAddedToPacket(_)); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id2, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false, |
| NOT_RETRANSMISSION, &frame)); |
| EXPECT_TRUE(creator_.HasPendingStreamFramesOfStream(stream_id2)); |
| |
| // The packet doesn't have enough free bytes for all data, but will still be |
| // able to consume and coalesce part of them. |
| EXPECT_CALL(debug, OnStreamFrameCoalesced(_)); |
| MakeIOVector("somerandomdata", &iov_); |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| stream_id2, &iov_, 1u, iov_.iov_len, 0u, length, false, false, |
| NOT_RETRANSMISSION, &frame)); |
| |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| creator_.FlushCurrentPacket(); |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| // The packet should only have 2 stream frames. |
| EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| ProcessPacket(*serialized_packet_); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SaveNonRetransmittableFrames) { |
| QuicAckFrame ack_frame(InitAckFrame(1)); |
| frames_.push_back(QuicFrame(&ack_frame)); |
| frames_.push_back(QuicFrame(QuicPaddingFrame(-1))); |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| ASSERT_EQ(2u, serialized.nonretransmittable_frames.size()); |
| EXPECT_EQ(ACK_FRAME, serialized.nonretransmittable_frames[0].type); |
| EXPECT_EQ(PADDING_FRAME, serialized.nonretransmittable_frames[1].type); |
| // Verify full padding frame is translated to a padding frame with actual |
| // bytes of padding. |
| EXPECT_LT( |
| 0, |
| serialized.nonretransmittable_frames[1].padding_frame.num_padding_bytes); |
| frames_.clear(); |
| |
| // Serialize another packet with the same frames. |
| SerializedPacket packet = QuicPacketCreatorPeer::SerializeAllFrames( |
| &creator_, serialized.nonretransmittable_frames, buffer_, |
| kMaxOutgoingPacketSize); |
| // Verify the packet length of both packets are equal. |
| EXPECT_EQ(serialized.encrypted_length, packet.encrypted_length); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SerializeCoalescedPacket) { |
| QuicCoalescedPacket coalesced; |
| SimpleBufferAllocator allocator; |
| QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1); |
| QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2); |
| for (size_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { |
| EncryptionLevel level = static_cast<EncryptionLevel>(i); |
| creator_.set_encryption_level(level); |
| QuicAckFrame ack_frame(InitAckFrame(1)); |
| if (level != ENCRYPTION_ZERO_RTT) { |
| frames_.push_back(QuicFrame(&ack_frame)); |
| } |
| if (level != ENCRYPTION_INITIAL && level != ENCRYPTION_HANDSHAKE) { |
| frames_.push_back( |
| QuicFrame(QuicStreamFrame(1, false, 0u, absl::string_view()))); |
| } |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| EXPECT_EQ(level, serialized.encryption_level); |
| frames_.clear(); |
| ASSERT_TRUE(coalesced.MaybeCoalescePacket(serialized, self_address, |
| peer_address, &allocator, |
| creator_.max_packet_length())); |
| } |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t coalesced_length = creator_.SerializeCoalescedPacket( |
| coalesced, buffer, kMaxOutgoingPacketSize); |
| // Verify packet is padded to full. |
| ASSERT_EQ(coalesced.max_packet_length(), coalesced_length); |
| if (!QuicVersionHasLongHeaderLengths(server_framer_.transport_version())) { |
| return; |
| } |
| // Verify packet process. |
| std::unique_ptr<QuicEncryptedPacket> packets[NUM_ENCRYPTION_LEVELS]; |
| packets[ENCRYPTION_INITIAL] = |
| std::make_unique<QuicEncryptedPacket>(buffer, coalesced_length); |
| for (size_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { |
| InSequence s; |
| EXPECT_CALL(framer_visitor_, OnPacket()); |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); |
| if (i < ENCRYPTION_FORWARD_SECURE) { |
| // Save coalesced packet. |
| EXPECT_CALL(framer_visitor_, OnCoalescedPacket(_)) |
| .WillOnce(Invoke([i, &packets](const QuicEncryptedPacket& packet) { |
| packets[i + 1] = packet.Clone(); |
| })); |
| } |
| EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); |
| EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); |
| EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); |
| if (i != ENCRYPTION_ZERO_RTT) { |
| EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(framer_visitor_, |
| OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(framer_visitor_, OnAckFrameEnd(_)).WillOnce(Return(true)); |
| } |
| if (i == ENCRYPTION_INITIAL) { |
| // Verify padding is added. |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| } else if (i != ENCRYPTION_ZERO_RTT) { |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(testing::AtMost(1)); |
| } |
| if (i != ENCRYPTION_INITIAL && i != ENCRYPTION_HANDSHAKE) { |
| EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); |
| } |
| if (i == ENCRYPTION_ZERO_RTT) { |
| EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); |
| } |
| EXPECT_CALL(framer_visitor_, OnPacketComplete()); |
| |
| server_framer_.ProcessPacket(*packets[i]); |
| } |
| } |
| |
| TEST_P(QuicPacketCreatorTest, SoftMaxPacketLength) { |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| QuicByteCount previous_max_packet_length = creator_.max_packet_length(); |
| const size_t overhead = |
| GetPacketHeaderOverhead(client_framer_.transport_version()) + |
| QuicPacketCreator::MinPlaintextPacketSize(client_framer_.version()) + |
| GetEncryptionOverhead(); |
| // Make sure a length which cannot accommodate header (includes header |
| // protection minimal length) gets rejected. |
| creator_.SetSoftMaxPacketLength(overhead - 1); |
| EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length()); |
| |
| creator_.SetSoftMaxPacketLength(overhead); |
| EXPECT_EQ(overhead, creator_.max_packet_length()); |
| |
| // Verify creator has room for stream frame because max_packet_length_ gets |
| // restored. |
| ASSERT_TRUE(creator_.HasRoomForStreamFrame( |
| GetNthClientInitiatedStreamId(1), kMaxIetfVarInt, |
| std::numeric_limits<uint32_t>::max())); |
| EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length()); |
| |
| // Same for message frame. |
| if (VersionSupportsMessageFrames(client_framer_.transport_version())) { |
| creator_.SetSoftMaxPacketLength(overhead); |
| if (client_framer_.version().UsesTls()) { |
| creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize); |
| } |
| // Verify GetCurrentLargestMessagePayload is based on the actual |
| // max_packet_length. |
| EXPECT_LT(1u, creator_.GetCurrentLargestMessagePayload()); |
| EXPECT_EQ(overhead, creator_.max_packet_length()); |
| ASSERT_TRUE(creator_.HasRoomForMessageFrame( |
| creator_.GetCurrentLargestMessagePayload())); |
| EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length()); |
| } |
| |
| // Verify creator can consume crypto data because max_packet_length_ gets |
| // restored. |
| creator_.SetSoftMaxPacketLength(overhead); |
| EXPECT_EQ(overhead, creator_.max_packet_length()); |
| std::string data = "crypto data"; |
| MakeIOVector(data, &iov_); |
| QuicFrame frame; |
| if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { |
| ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( |
| QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, |
| 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION, |
| &frame)); |
| size_t bytes_consumed = frame.stream_frame.data_length; |
| EXPECT_LT(0u, bytes_consumed); |
| } else { |
| producer_.SaveCryptoData(ENCRYPTION_INITIAL, kOffset, data); |
| ASSERT_TRUE(creator_.ConsumeCryptoDataToFillCurrentPacket( |
| ENCRYPTION_INITIAL, data.length(), kOffset, |
| /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); |
| size_t bytes_consumed = frame.crypto_frame->data_length; |
| EXPECT_LT(0u, bytes_consumed); |
| } |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); |
| creator_.FlushCurrentPacket(); |
| |
| // Verify ACK frame can be consumed. |
| creator_.SetSoftMaxPacketLength(overhead); |
| EXPECT_EQ(overhead, creator_.max_packet_length()); |
| QuicAckFrame ack_frame(InitAckFrame(10u)); |
| EXPECT_TRUE(creator_.AddFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| } |
| |
| TEST_P(QuicPacketCreatorTest, |
| ChangingEncryptionLevelRemovesSoftMaxPacketLength) { |
| if (!client_framer_.version().CanSendCoalescedPackets()) { |
| return; |
| } |
| // First set encryption level to forward secure which has the shortest header. |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| const QuicByteCount previous_max_packet_length = creator_.max_packet_length(); |
| const size_t min_acceptable_packet_size = |
| GetPacketHeaderOverhead(client_framer_.transport_version()) + |
| QuicPacketCreator::MinPlaintextPacketSize(client_framer_.version()) + |
| GetEncryptionOverhead(); |
| // Then set the soft max packet length to the lowest allowed value. |
| creator_.SetSoftMaxPacketLength(min_acceptable_packet_size); |
| // Make sure that the low value was accepted. |
| EXPECT_EQ(creator_.max_packet_length(), min_acceptable_packet_size); |
| // Now set the encryption level to handshake which increases the header size. |
| creator_.set_encryption_level(ENCRYPTION_HANDSHAKE); |
| // Make sure that adding a frame removes the the soft max packet length. |
| QuicAckFrame ack_frame(InitAckFrame(1)); |
| frames_.push_back(QuicFrame(&ack_frame)); |
| SerializedPacket serialized = SerializeAllFrames(frames_); |
| EXPECT_EQ(serialized.encryption_level, ENCRYPTION_HANDSHAKE); |
| EXPECT_EQ(creator_.max_packet_length(), previous_max_packet_length); |
| } |
| |
| class MockDelegate : public QuicPacketCreator::DelegateInterface { |
| public: |
| MockDelegate() {} |
| MockDelegate(const MockDelegate&) = delete; |
| MockDelegate& operator=(const MockDelegate&) = delete; |
| ~MockDelegate() override {} |
| |
| MOCK_METHOD(bool, |
| ShouldGeneratePacket, |
| (HasRetransmittableData retransmittable, IsHandshake handshake), |
| (override)); |
| MOCK_METHOD(const QuicFrames, |
| MaybeBundleAckOpportunistically, |
| (), |
| (override)); |
| MOCK_METHOD(QuicPacketBuffer, GetPacketBuffer, (), (override)); |
| MOCK_METHOD(void, OnSerializedPacket, (SerializedPacket), (override)); |
| MOCK_METHOD(void, |
| OnUnrecoverableError, |
| (QuicErrorCode, const std::string&), |
| (override)); |
| MOCK_METHOD(SerializedPacketFate, |
| GetSerializedPacketFate, |
| (bool, EncryptionLevel), |
| (override)); |
| |
| void SetCanWriteAnything() { |
| EXPECT_CALL(*this, ShouldGeneratePacket(_, _)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _)) |
| .WillRepeatedly(Return(true)); |
| } |
| |
| void SetCanNotWrite() { |
| EXPECT_CALL(*this, ShouldGeneratePacket(_, _)) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _)) |
| .WillRepeatedly(Return(false)); |
| } |
| |
| // Use this when only ack frames should be allowed to be written. |
| void SetCanWriteOnlyNonRetransmittable() { |
| EXPECT_CALL(*this, ShouldGeneratePacket(_, _)) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _)) |
| .WillRepeatedly(Return(true)); |
| } |
| }; |
| |
| // Simple struct for describing the contents of a packet. |
| // Useful in conjunction with a SimpleQuicFrame for validating that a packet |
| // contains the expected frames. |
| struct PacketContents { |
| PacketContents() |
| : num_ack_frames(0), |
| num_connection_close_frames(0), |
| num_goaway_frames(0), |
| num_rst_stream_frames(0), |
| num_stop_waiting_frames(0), |
| num_stream_frames(0), |
| num_crypto_frames(0), |
| num_ping_frames(0), |
| num_mtu_discovery_frames(0), |
| num_padding_frames(0) {} |
| |
| size_t num_ack_frames; |
| size_t num_connection_close_frames; |
| size_t num_goaway_frames; |
| size_t num_rst_stream_frames; |
| size_t num_stop_waiting_frames; |
| size_t num_stream_frames; |
| size_t num_crypto_frames; |
| size_t num_ping_frames; |
| size_t num_mtu_discovery_frames; |
| size_t num_padding_frames; |
| }; |
| |
| class MultiplePacketsTestPacketCreator : public QuicPacketCreator { |
| public: |
| MultiplePacketsTestPacketCreator( |
| QuicConnectionId connection_id, |
| QuicFramer* framer, |
| QuicRandom* random_generator, |
| QuicPacketCreator::DelegateInterface* delegate, |
| SimpleDataProducer* producer) |
| : QuicPacketCreator(connection_id, framer, random_generator, delegate), |
| ack_frame_(InitAckFrame(1)), |
| delegate_(static_cast<MockDelegate*>(delegate)), |
| producer_(producer) {} |
| |
| bool ConsumeRetransmittableControlFrame(const QuicFrame& frame, |
| bool bundle_ack) { |
| if (!has_ack()) { |
| QuicFrames frames; |
| if (bundle_ack) { |
| frames.push_back(QuicFrame(&ack_frame_)); |
| } |
| if (delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, |
| NOT_HANDSHAKE)) { |
| EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()) |
| .WillOnce(Return(frames)); |
| } |
| } |
| return QuicPacketCreator::ConsumeRetransmittableControlFrame(frame); |
| } |
| |
| QuicConsumedData ConsumeDataFastPath(QuicStreamId id, |
| const struct iovec* iov, |
| int iov_count, |
| size_t total_length, |
| QuicStreamOffset offset, |
| bool fin) { |
| // Save data before data is consumed. |
| if (total_length > 0) { |
| producer_->SaveStreamData(id, iov, iov_count, 0, total_length); |
| } |
| return QuicPacketCreator::ConsumeDataFastPath(id, total_length, offset, fin, |
| 0); |
| } |
| |
| QuicConsumedData ConsumeData(QuicStreamId id, |
| const struct iovec* iov, |
| int iov_count, |
| size_t total_length, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| // Save data before data is consumed. |
| if (total_length > 0) { |
| producer_->SaveStreamData(id, iov, iov_count, 0, total_length); |
| } |
| if (!has_ack() && delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, |
| NOT_HANDSHAKE)) { |
| EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1); |
| } |
| return QuicPacketCreator::ConsumeData(id, total_length, offset, state); |
| } |
| |
| MessageStatus AddMessageFrame(QuicMessageId message_id, |
| QuicMemSlice message) { |
| if (!has_ack() && delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, |
| NOT_HANDSHAKE)) { |
| EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1); |
| } |
| return QuicPacketCreator::AddMessageFrame(message_id, |
| absl::MakeSpan(&message, 1)); |
| } |
| |
| size_t ConsumeCryptoData(EncryptionLevel level, |
| absl::string_view data, |
| QuicStreamOffset offset) { |
| producer_->SaveCryptoData(level, offset, data); |
| if (!has_ack() && delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, |
| NOT_HANDSHAKE)) { |
| EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1); |
| } |
| return QuicPacketCreator::ConsumeCryptoData(level, data.length(), offset); |
| } |
| |
| QuicAckFrame ack_frame_; |
| MockDelegate* delegate_; |
| SimpleDataProducer* producer_; |
| }; |
| |
| class QuicPacketCreatorMultiplePacketsTest : public QuicTest { |
| public: |
| QuicPacketCreatorMultiplePacketsTest() |
| : framer_(AllSupportedVersions(), |
| QuicTime::Zero(), |
| Perspective::IS_CLIENT, |
| kQuicDefaultConnectionIdLength), |
| creator_(TestConnectionId(), |
| &framer_, |
| &random_creator_, |
| &delegate_, |
| &producer_), |
| ack_frame_(InitAckFrame(1)) { |
| EXPECT_CALL(delegate_, GetPacketBuffer()) |
| .WillRepeatedly(Return(QuicPacketBuffer())); |
| EXPECT_CALL(delegate_, GetSerializedPacketFate(_, _)) |
| .WillRepeatedly(Return(SEND_TO_WRITER)); |
| creator_.SetEncrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); |
| creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| framer_.set_data_producer(&producer_); |
| if (simple_framer_.framer()->version().KnowsWhichDecrypterToUse()) { |
| simple_framer_.framer()->InstallDecrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullDecrypter>(Perspective::IS_SERVER)); |
| } |
| creator_.AttachPacketFlusher(); |
| } |
| |
| ~QuicPacketCreatorMultiplePacketsTest() override {} |
| |
| void SavePacket(SerializedPacket packet) { |
| QUICHE_DCHECK(packet.release_encrypted_buffer == nullptr); |
| packet.encrypted_buffer = CopyBuffer(packet); |
| packet.release_encrypted_buffer = [](const char* p) { delete[] p; }; |
| packets_.push_back(std::move(packet)); |
| } |
| |
| protected: |
| QuicRstStreamFrame* CreateRstStreamFrame() { |
| return new QuicRstStreamFrame(1, 1, QUIC_STREAM_NO_ERROR, 0); |
| } |
| |
| QuicGoAwayFrame* CreateGoAwayFrame() { |
| return new QuicGoAwayFrame(2, QUIC_NO_ERROR, 1, std::string()); |
| } |
| |
| void CheckPacketContains(const PacketContents& contents, |
| size_t packet_index) { |
| ASSERT_GT(packets_.size(), packet_index); |
| const SerializedPacket& packet = packets_[packet_index]; |
| size_t num_retransmittable_frames = |
| contents.num_connection_close_frames + contents.num_goaway_frames + |
| contents.num_rst_stream_frames + contents.num_stream_frames + |
| contents.num_crypto_frames + contents.num_ping_frames; |
| size_t num_frames = |
| contents.num_ack_frames + contents.num_stop_waiting_frames + |
| contents.num_mtu_discovery_frames + contents.num_padding_frames + |
| num_retransmittable_frames; |
| |
| if (num_retransmittable_frames == 0) { |
| ASSERT_TRUE(packet.retransmittable_frames.empty()); |
| } else { |
| ASSERT_FALSE(packet.retransmittable_frames.empty()); |
| EXPECT_EQ(num_retransmittable_frames, |
| packet.retransmittable_frames.size()); |
| } |
| |
| ASSERT_TRUE(packet.encrypted_buffer != nullptr); |
| ASSERT_TRUE(simple_framer_.ProcessPacket( |
| QuicEncryptedPacket(packet.encrypted_buffer, packet.encrypted_length))); |
| size_t num_padding_frames = 0; |
| if (contents.num_padding_frames == 0) { |
| num_padding_frames = simple_framer_.padding_frames().size(); |
| } |
| EXPECT_EQ(num_frames + num_padding_frames, simple_framer_.num_frames()); |
| EXPECT_EQ(contents.num_ack_frames, simple_framer_.ack_frames().size()); |
| EXPECT_EQ(contents.num_connection_close_frames, |
| simple_framer_.connection_close_frames().size()); |
| EXPECT_EQ(contents.num_goaway_frames, |
| simple_framer_.goaway_frames().size()); |
| EXPECT_EQ(contents.num_rst_stream_frames, |
| simple_framer_.rst_stream_frames().size()); |
| EXPECT_EQ(contents.num_stream_frames, |
| simple_framer_.stream_frames().size()); |
| EXPECT_EQ(contents.num_crypto_frames, |
| simple_framer_.crypto_frames().size()); |
| EXPECT_EQ(contents.num_stop_waiting_frames, |
| simple_framer_.stop_waiting_frames().size()); |
| if (contents.num_padding_frames != 0) { |
| EXPECT_EQ(contents.num_padding_frames, |
| simple_framer_.padding_frames().size()); |
| } |
| |
| // From the receiver's perspective, MTU discovery frames are ping frames. |
| EXPECT_EQ(contents.num_ping_frames + contents.num_mtu_discovery_frames, |
| simple_framer_.ping_frames().size()); |
| } |
| |
| void CheckPacketHasSingleStreamFrame(size_t packet_index) { |
| ASSERT_GT(packets_.size(), packet_index); |
| const SerializedPacket& packet = packets_[packet_index]; |
| ASSERT_FALSE(packet.retransmittable_frames.empty()); |
| EXPECT_EQ(1u, packet.retransmittable_frames.size()); |
| ASSERT_TRUE(packet.encrypted_buffer != nullptr); |
| ASSERT_TRUE(simple_framer_.ProcessPacket( |
| QuicEncryptedPacket(packet.encrypted_buffer, packet.encrypted_length))); |
| EXPECT_EQ(1u, simple_framer_.num_frames()); |
| EXPECT_EQ(1u, simple_framer_.stream_frames().size()); |
| } |
| |
| void CheckAllPacketsHaveSingleStreamFrame() { |
| for (size_t i = 0; i < packets_.size(); i++) { |
| CheckPacketHasSingleStreamFrame(i); |
| } |
| } |
| |
| void CreateData(size_t len) { |
| data_array_.reset(new char[len]); |
| memset(data_array_.get(), '?', len); |
| iov_.iov_base = data_array_.get(); |
| iov_.iov_len = len; |
| } |
| |
| QuicFramer framer_; |
| MockRandom random_creator_; |
| StrictMock<MockDelegate> delegate_; |
| MultiplePacketsTestPacketCreator creator_; |
| SimpleQuicFramer simple_framer_; |
| std::vector<SerializedPacket> packets_; |
| QuicAckFrame ack_frame_; |
| struct iovec iov_; |
| SimpleBufferAllocator allocator_; |
| |
| private: |
| std::unique_ptr<char[]> data_array_; |
| SimpleDataProducer producer_; |
| }; |
| |
| TEST_F(QuicPacketCreatorMultiplePacketsTest, AddControlFrame_NotWritable) { |
| delegate_.SetCanNotWrite(); |
| |
| QuicRstStreamFrame* rst_frame = CreateRstStreamFrame(); |
| const bool consumed = |
| creator_.ConsumeRetransmittableControlFrame(QuicFrame(rst_frame), |
| /*bundle_ack=*/false); |
| EXPECT_FALSE(consumed); |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| EXPECT_FALSE(creator_.HasPendingRetransmittableFrames()); |
| delete rst_frame; |
| } |
| |
| TEST_F(QuicPacketCreatorMultiplePacketsTest, |
| WrongEncryptionLevelForStreamDataFastPath) { |
| creator_.set_encryption_level(ENCRYPTION_HANDSHAKE); |
| delegate_.SetCanWriteAnything(); |
| // Create a 10000 byte IOVector. |
| CreateData(10000); |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)).Times(0); |
| EXPECT_CALL(delegate_, OnUnrecoverableError(_, _)); |
| EXPECT_QUIC_BUG(creator_.ConsumeDataFastPath( |
| QuicUtils::GetFirstBidirectionalStreamId( |
| framer_.transport_version(), Perspective::IS_CLIENT), |
| &iov_, 1u, iov_.iov_len, 0, true), |
| ""); |
| } |
| |
| TEST_F(QuicPacketCreatorMultiplePacketsTest, AddControlFrame_OnlyAckWritable) { |
| delegate_.SetCanWriteOnlyNonRetransmittable(); |
| |
| QuicRstStreamFrame* rst_frame = CreateRstStreamFrame(); |
| const bool consumed = |
| creator_.ConsumeRetransmittableControlFrame(QuicFrame(rst_frame), |
| /*bundle_ack=*/false); |
| EXPECT_FALSE(consumed); |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| EXPECT_FALSE(creator_.HasPendingRetransmittableFrames()); |
| delete rst_frame; |
| } |
| |
| TEST_F(QuicPacketCreatorMultiplePacketsTest, |
| AddControlFrame_WritableAndShouldNotFlush) { |
| delegate_.SetCanWriteAnything(); |
| |
| creator_.ConsumeRetransmittableControlFrame(QuicFrame(CreateRstStreamFrame()), |
| /*bundle_ack=*/false); |
| EXPECT_TRUE(creator_.HasPendingFrames()); |
| EXPECT_TRUE(creator_.HasPendingRetransmittableFrames()); |
| } |
| |
| TEST_F(QuicPacketCreatorMultiplePacketsTest, |
| AddControlFrame_NotWritableBatchThenFlush) { |
| delegate_.SetCanNotWrite(); |
| |
| QuicRstStreamFrame* rst_frame = CreateRstStreamFrame(); |
| const bool consumed = |
| creator_.ConsumeRetransmittableControlFrame(QuicFrame(rst_frame), |
| /*bundle_ack=*/false); |
| EXPECT_FALSE(consumed); |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| EXPECT_FALSE(creator_.HasPendingRetransmittableFrames()); |
| delete rst_frame; |
| } |
| |
| TEST_F(QuicPacketCreatorMultiplePacketsTest, |
| AddControlFrame_WritableAndShouldFlush) { |
| delegate_.SetCanWriteAnything(); |
| |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce( |
| Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket)); |
| |
| creator_.ConsumeRetransmittableControlFrame(QuicFrame(CreateRstStreamFrame()), |
| /*bundle_ack=*/false); |
| creator_.Flush(); |
| EXPECT_FALSE(creator_.HasPendingFrames()); |
| EXPECT_FALSE(creator_.HasPendingRetransmittableFrames()); |
| |
| PacketContents contents; |
| contents.num_rst_stream_frames = 1; |
| CheckPacketContains(contents, 0); |
| } |
| |
| TEST_F(QuicPacketCreatorMultiplePacketsTest, ConsumeCryptoData) { |
| delegate_.SetCanWriteAnything(); |
| |
| EXPECT_CALL(delegate_, OnSerializedPacket(_)) |
| .WillOnce( |
| Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket)); |
| std::string data =<
|