1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-25 15:43:13 +03:00
Files
mvfst/quic/codec/test/DecodeTest.cpp
Yang Chi c0707bf996 Make Quic packet header encoding reusable
Summary:
The encoding logic is the same no matter what builder and what IOBuf
manipulater we use. Make these functions reusable.

This requires the QuicInteger encode function to support both BufAppender and
BufWriter. Note that the template is on a callable object. Because later on we
will also need to support wrapped memcpy that isn't a member function.

Reviewed By: mjoras

Differential Revision: D20781979

fbshipit-source-id: 1b8cbdfd7580132c113b12687f932d47207a7cce
2020-04-06 13:59:08 -07:00

742 lines
24 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#include <quic/codec/Decode.h>
#include <folly/Random.h>
#include <folly/container/Array.h>
#include <folly/io/IOBuf.h>
#include <folly/portability/GTest.h>
#include <quic/codec/QuicReadCodec.h>
#include <quic/codec/Types.h>
#include <quic/common/test/TestUtils.h>
using namespace testing;
namespace quic {
namespace test {
using UnderlyingFrameType = std::underlying_type<FrameType>::type;
class DecodeTest : public Test {};
ShortHeader makeHeader() {
PacketNum packetNum = 100;
return ShortHeader(
ProtectionType::KeyPhaseZero, getTestConnectionId(), packetNum);
}
// NormalizedAckBlocks are in order needed.
struct NormalizedAckBlock {
QuicInteger gap; // Gap to previous AckBlock
QuicInteger blockLen;
NormalizedAckBlock(QuicInteger gapIn, QuicInteger blockLenIn)
: gap(gapIn), blockLen(blockLenIn) {}
};
template <class LargestAckedType = uint64_t>
std::unique_ptr<folly::IOBuf> createAckFrame(
folly::Optional<QuicInteger> largestAcked,
folly::Optional<QuicInteger> ackDelay = folly::none,
folly::Optional<QuicInteger> numAdditionalBlocks = folly::none,
folly::Optional<QuicInteger> firstAckBlockLength = folly::none,
std::vector<NormalizedAckBlock> ackBlocks = {},
bool useRealValuesForLargestAcked = false,
bool useRealValuesForAckDelay = false) {
std::unique_ptr<folly::IOBuf> ackFrame = folly::IOBuf::create(0);
BufAppender wcursor(ackFrame.get(), 10);
auto appenderOp = [&](auto val) { wcursor.writeBE(val); };
if (largestAcked) {
if (useRealValuesForLargestAcked) {
wcursor.writeBE<LargestAckedType>(largestAcked->getValue());
} else {
largestAcked->encode(appenderOp);
}
}
if (ackDelay) {
if (useRealValuesForAckDelay) {
wcursor.writeBE(ackDelay->getValue());
} else {
ackDelay->encode(appenderOp);
}
}
if (numAdditionalBlocks) {
numAdditionalBlocks->encode(appenderOp);
}
if (firstAckBlockLength) {
firstAckBlockLength->encode(appenderOp);
}
for (size_t i = 0; i < ackBlocks.size(); ++i) {
ackBlocks[i].gap.encode(appenderOp);
ackBlocks[i].blockLen.encode(appenderOp);
}
return ackFrame;
}
template <class StreamIdType = StreamId>
std::unique_ptr<folly::IOBuf> createStreamFrame(
folly::Optional<QuicInteger> streamId,
folly::Optional<QuicInteger> offset = folly::none,
folly::Optional<QuicInteger> dataLength = folly::none,
Buf data = nullptr,
bool useRealValuesForStreamId = false) {
std::unique_ptr<folly::IOBuf> streamFrame = folly::IOBuf::create(0);
BufAppender wcursor(streamFrame.get(), 10);
auto appenderOp = [&](auto val) { wcursor.writeBE(val); };
if (streamId) {
if (useRealValuesForStreamId) {
wcursor.writeBE<StreamIdType>(streamId->getValue());
} else {
streamId->encode(appenderOp);
}
}
if (offset) {
offset->encode(appenderOp);
}
if (dataLength) {
dataLength->encode(appenderOp);
}
if (data) {
wcursor.insert(std::move(data));
}
return streamFrame;
}
std::unique_ptr<folly::IOBuf> createCryptoFrame(
folly::Optional<QuicInteger> offset = folly::none,
folly::Optional<QuicInteger> dataLength = folly::none,
Buf data = nullptr) {
std::unique_ptr<folly::IOBuf> cryptoFrame = folly::IOBuf::create(0);
BufAppender wcursor(cryptoFrame.get(), 10);
auto appenderOp = [&](auto val) { wcursor.writeBE(val); };
if (offset) {
offset->encode(appenderOp);
}
if (dataLength) {
dataLength->encode(appenderOp);
}
if (data) {
wcursor.insert(std::move(data));
}
return cryptoFrame;
}
TEST_F(DecodeTest, VersionNegotiationPacketDecodeTest) {
ConnectionId srcCid = getTestConnectionId(0),
destCid = getTestConnectionId(1);
std::vector<QuicVersion> versions{{static_cast<QuicVersion>(1234),
static_cast<QuicVersion>(4321),
static_cast<QuicVersion>(2341),
static_cast<QuicVersion>(3412),
static_cast<QuicVersion>(4123)}};
auto packet =
VersionNegotiationPacketBuilder(srcCid, destCid, versions).buildPacket();
auto codec = std::make_unique<QuicReadCodec>(QuicNodeType::Server);
AckStates ackStates;
auto packetQueue = bufToQueue(std::move(packet.second));
auto versionPacket = codec->tryParsingVersionNegotiation(packetQueue);
ASSERT_TRUE(versionPacket.has_value());
EXPECT_EQ(versionPacket->destinationConnectionId, destCid);
EXPECT_EQ(versionPacket->sourceConnectionId, srcCid);
EXPECT_EQ(versionPacket->versions.size(), versions.size());
EXPECT_EQ(versionPacket->versions, versions);
}
TEST_F(DecodeTest, DifferentCIDLength) {
ConnectionId sourceConnectionId = getTestConnectionId();
ConnectionId destinationConnectionId({1, 2, 3, 4, 5, 6});
std::vector<QuicVersion> versions{{static_cast<QuicVersion>(1234),
static_cast<QuicVersion>(4321),
static_cast<QuicVersion>(2341),
static_cast<QuicVersion>(3412),
static_cast<QuicVersion>(4123)}};
auto packet = VersionNegotiationPacketBuilder(
sourceConnectionId, destinationConnectionId, versions)
.buildPacket();
auto codec = std::make_unique<QuicReadCodec>(QuicNodeType::Server);
AckStates ackStates;
auto packetQueue = bufToQueue(std::move(packet.second));
auto versionPacket = codec->tryParsingVersionNegotiation(packetQueue);
ASSERT_TRUE(versionPacket.has_value());
EXPECT_EQ(versionPacket->sourceConnectionId, sourceConnectionId);
EXPECT_EQ(versionPacket->destinationConnectionId, destinationConnectionId);
EXPECT_EQ(versionPacket->versions.size(), versions.size());
EXPECT_EQ(versionPacket->versions, versions);
}
TEST_F(DecodeTest, VersionNegotiationPacketBadPacketTest) {
ConnectionId connId = getTestConnectionId();
QuicVersionType version = static_cast<QuicVersionType>(QuicVersion::MVFST);
auto buf = folly::IOBuf::create(10);
folly::io::Appender appender(buf.get(), 10);
appender.writeBE<uint8_t>(kHeaderFormMask);
appender.push(connId.data(), connId.size());
appender.writeBE<QuicVersionType>(
static_cast<QuicVersionType>(QuicVersion::VERSION_NEGOTIATION));
appender.push((uint8_t*)&version, sizeof(QuicVersion) - 1);
auto codec = std::make_unique<QuicReadCodec>(QuicNodeType::Server);
AckStates ackStates;
auto packetQueue = bufToQueue(std::move(buf));
auto packet = codec->parsePacket(packetQueue, ackStates);
EXPECT_EQ(packet.regularPacket(), nullptr);
buf = folly::IOBuf::create(0);
packetQueue = bufToQueue(std::move(buf));
packet = codec->parsePacket(packetQueue, ackStates);
// Packet with empty versions
EXPECT_EQ(packet.regularPacket(), nullptr);
}
TEST_F(DecodeTest, ValidAckFrame) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(1);
QuicInteger firstAckBlockLength(10);
std::vector<NormalizedAckBlock> ackBlocks;
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
ackBlocks);
folly::io::Cursor cursor(result.get());
auto ackFrame = decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
EXPECT_EQ(ackFrame.ackBlocks.size(), 2);
EXPECT_EQ(ackFrame.largestAcked, 1000);
// Since 100 is the encoded value, we use the decoded value.
EXPECT_EQ(ackFrame.ackDelay.count(), 100 << kDefaultAckDelayExponent);
}
TEST_F(DecodeTest, AckFrameLargestAckExceedsRange) {
// An integer larger than the representable range of quic integer.
QuicInteger largestAcked(std::numeric_limits<uint64_t>::max());
QuicInteger ackDelay(10);
QuicInteger numAdditionalBlocks(0);
QuicInteger firstAckBlockLength(10);
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
{},
true);
folly::io::Cursor cursor(result.get());
auto ackFrame = decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
// it will interpret this as a 8 byte range with the max value.
EXPECT_EQ(ackFrame.largestAcked, 4611686018427387903);
}
TEST_F(DecodeTest, AckFrameLargestAckInvalid) {
// An integer larger than the representable range of quic integer.
QuicInteger largestAcked(std::numeric_limits<uint64_t>::max());
QuicInteger ackDelay(10);
QuicInteger numAdditionalBlocks(0);
QuicInteger firstAckBlockLength(10);
auto result = createAckFrame<uint8_t>(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
{},
true);
folly::io::Cursor cursor(result.get());
EXPECT_THROW(
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameDelayEncodingInvalid) {
QuicInteger largestAcked(1000);
// Maximal representable value by quic integer.
QuicInteger ackDelay(4611686018427387903);
QuicInteger numAdditionalBlocks(0);
QuicInteger firstAckBlockLength(10);
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
{},
false,
true);
folly::io::Cursor cursor(result.get());
EXPECT_THROW(
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameDelayExceedsRange) {
QuicInteger largestAcked(1000);
// Maximal representable value by quic integer.
QuicInteger ackDelay(4611686018427387903);
QuicInteger numAdditionalBlocks(0);
QuicInteger firstAckBlockLength(10);
auto result = createAckFrame(
largestAcked, ackDelay, numAdditionalBlocks, firstAckBlockLength);
folly::io::Cursor cursor(result.get());
EXPECT_THROW(
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameAdditionalBlocksUnderflow) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(2);
QuicInteger firstAckBlockLength(10);
std::vector<NormalizedAckBlock> ackBlocks;
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
ackBlocks);
folly::io::Cursor cursor(result.get());
EXPECT_THROW(
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameAdditionalBlocksOverflow) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(2);
QuicInteger firstAckBlockLength(10);
std::vector<NormalizedAckBlock> ackBlocks;
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
ackBlocks);
folly::io::Cursor cursor(result.get());
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
}
TEST_F(DecodeTest, AckFrameMissingFields) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(2);
QuicInteger firstAckBlockLength(10);
std::vector<NormalizedAckBlock> ackBlocks;
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
auto result1 = createAckFrame(
largestAcked,
folly::none,
numAdditionalBlocks,
firstAckBlockLength,
ackBlocks);
folly::io::Cursor cursor1(result1.get());
EXPECT_THROW(
decodeAckFrame(
cursor1,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
auto result2 = createAckFrame(
largestAcked, ackDelay, folly::none, firstAckBlockLength, ackBlocks);
folly::io::Cursor cursor2(result2.get());
EXPECT_THROW(
decodeAckFrame(
cursor2,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
auto result3 = createAckFrame(
largestAcked, ackDelay, folly::none, firstAckBlockLength, ackBlocks);
folly::io::Cursor cursor3(result3.get());
EXPECT_THROW(
decodeAckFrame(
cursor3,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
auto result4 = createAckFrame(
largestAcked, ackDelay, numAdditionalBlocks, folly::none, ackBlocks);
folly::io::Cursor cursor4(result4.get());
EXPECT_THROW(
decodeAckFrame(
cursor4,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
auto result5 = createAckFrame(
largestAcked, ackDelay, numAdditionalBlocks, firstAckBlockLength, {});
folly::io::Cursor cursor5(result5.get());
EXPECT_THROW(
decodeAckFrame(
cursor5,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameFirstBlockLengthInvalid) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(0);
QuicInteger firstAckBlockLength(2000);
auto result = createAckFrame(
largestAcked, ackDelay, numAdditionalBlocks, firstAckBlockLength);
folly::io::Cursor cursor(result.get());
EXPECT_THROW(
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameBlockLengthInvalid) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(2);
QuicInteger firstAckBlockLength(10);
std::vector<NormalizedAckBlock> ackBlocks;
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(1000));
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
ackBlocks);
folly::io::Cursor cursor(result.get());
EXPECT_THROW(
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameBlockGapInvalid) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(2);
QuicInteger firstAckBlockLength(10);
std::vector<NormalizedAckBlock> ackBlocks;
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
ackBlocks.emplace_back(QuicInteger(1000), QuicInteger(0));
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
ackBlocks);
folly::io::Cursor cursor(result.get());
EXPECT_THROW(
decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}
TEST_F(DecodeTest, AckFrameBlockLengthZero) {
QuicInteger largestAcked(1000);
QuicInteger ackDelay(100);
QuicInteger numAdditionalBlocks(3);
QuicInteger firstAckBlockLength(10);
std::vector<NormalizedAckBlock> ackBlocks;
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(10));
ackBlocks.emplace_back(QuicInteger(10), QuicInteger(0));
ackBlocks.emplace_back(QuicInteger(0), QuicInteger(10));
auto result = createAckFrame(
largestAcked,
ackDelay,
numAdditionalBlocks,
firstAckBlockLength,
ackBlocks);
folly::io::Cursor cursor(result.get());
auto readAckFrame = decodeAckFrame(
cursor,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
EXPECT_EQ(readAckFrame.ackBlocks[0].endPacket, 1000);
EXPECT_EQ(readAckFrame.ackBlocks[0].startPacket, 990);
EXPECT_EQ(readAckFrame.ackBlocks[1].endPacket, 978);
EXPECT_EQ(readAckFrame.ackBlocks[1].startPacket, 968);
EXPECT_EQ(readAckFrame.ackBlocks[2].endPacket, 956);
EXPECT_EQ(readAckFrame.ackBlocks[2].startPacket, 956);
EXPECT_EQ(readAckFrame.ackBlocks[3].endPacket, 954);
EXPECT_EQ(readAckFrame.ackBlocks[3].startPacket, 944);
}
TEST_F(DecodeTest, StreamDecodeSuccess) {
QuicInteger streamId(10);
QuicInteger offset(10);
QuicInteger length(1);
auto streamType =
StreamTypeField::Builder().setFin().setOffset().setLength().build();
auto streamFrame = createStreamFrame(
streamId, offset, length, folly::IOBuf::copyBuffer("a"));
BufQueue queue;
queue.append(streamFrame->clone());
auto decodedFrame = decodeStreamFrame(queue, streamType);
EXPECT_EQ(decodedFrame.offset, 10);
EXPECT_EQ(decodedFrame.data->computeChainDataLength(), 1);
EXPECT_EQ(decodedFrame.streamId, 10);
EXPECT_TRUE(decodedFrame.fin);
}
TEST_F(DecodeTest, StreamLengthStreamIdInvalid) {
QuicInteger streamId(std::numeric_limits<uint64_t>::max());
auto streamType =
StreamTypeField::Builder().setFin().setOffset().setLength().build();
auto streamFrame = createStreamFrame<uint8_t>(
streamId, folly::none, folly::none, nullptr, true);
BufQueue queue;
queue.append(streamFrame->clone());
EXPECT_THROW(decodeStreamFrame(queue, streamType), QuicTransportException);
}
TEST_F(DecodeTest, StreamOffsetNotPresent) {
QuicInteger streamId(10);
QuicInteger length(1);
auto streamType =
StreamTypeField::Builder().setFin().setOffset().setLength().build();
auto streamFrame = createStreamFrame(
streamId, folly::none, length, folly::IOBuf::copyBuffer("a"));
BufQueue queue;
queue.append(streamFrame->clone());
EXPECT_THROW(decodeStreamFrame(queue, streamType), QuicTransportException);
}
TEST_F(DecodeTest, StreamIncorrectDataLength) {
QuicInteger streamId(10);
QuicInteger offset(10);
QuicInteger length(10);
auto streamType =
StreamTypeField::Builder().setFin().setOffset().setLength().build();
auto streamFrame = createStreamFrame(
streamId, offset, length, folly::IOBuf::copyBuffer("a"));
BufQueue queue;
queue.append(streamFrame->clone());
EXPECT_THROW(decodeStreamFrame(queue, streamType), QuicTransportException);
}
TEST_F(DecodeTest, CryptoDecodeSuccess) {
QuicInteger offset(10);
QuicInteger length(1);
auto cryptoFrame =
createCryptoFrame(offset, length, folly::IOBuf::copyBuffer("a"));
folly::io::Cursor cursor(cryptoFrame.get());
auto decodedFrame = decodeCryptoFrame(cursor);
EXPECT_EQ(decodedFrame.offset, 10);
EXPECT_EQ(decodedFrame.data->computeChainDataLength(), 1);
}
TEST_F(DecodeTest, CryptoOffsetNotPresent) {
QuicInteger length(1);
auto cryptoFrame =
createCryptoFrame(folly::none, length, folly::IOBuf::copyBuffer("a"));
folly::io::Cursor cursor(cryptoFrame.get());
EXPECT_THROW(decodeCryptoFrame(cursor), QuicTransportException);
}
TEST_F(DecodeTest, CryptoLengthNotPresent) {
QuicInteger offset(0);
auto cryptoFrame = createCryptoFrame(offset, folly::none, nullptr);
folly::io::Cursor cursor(cryptoFrame.get());
EXPECT_THROW(decodeCryptoFrame(cursor), QuicTransportException);
}
TEST_F(DecodeTest, CryptoIncorrectDataLength) {
QuicInteger offset(10);
QuicInteger length(10);
auto cryptoFrame =
createCryptoFrame(offset, length, folly::IOBuf::copyBuffer("a"));
folly::io::Cursor cursor(cryptoFrame.get());
EXPECT_THROW(decodeCryptoFrame(cursor), QuicTransportException);
}
TEST_F(DecodeTest, PaddingFrameTest) {
auto buf = folly::IOBuf::create(sizeof(UnderlyingFrameType));
buf->append(1);
memset(buf->writableData(), 0, 1);
folly::io::Cursor cursor(buf.get());
decodePaddingFrame(cursor);
}
TEST_F(DecodeTest, PaddingFrameNoBytesTest) {
auto buf = folly::IOBuf::create(sizeof(UnderlyingFrameType));
folly::io::Cursor cursor(buf.get());
decodePaddingFrame(cursor);
}
TEST_F(DecodeTest, DecodeMultiplePaddingInterleavedTest) {
auto buf = folly::IOBuf::create(20);
buf->append(10);
memset(buf->writableData(), 0, 10);
buf->append(1);
// something which is not padding
memset(buf->writableData() + 10, 5, 1);
folly::io::Cursor cursor(buf.get());
decodePaddingFrame(cursor);
// If we encountered an interleaved frame, leave the whole thing
// as is
EXPECT_EQ(cursor.totalLength(), 11);
}
TEST_F(DecodeTest, DecodeMultiplePaddingTest) {
auto buf = folly::IOBuf::create(20);
buf->append(10);
memset(buf->writableData(), 0, 10);
folly::io::Cursor cursor(buf.get());
decodePaddingFrame(cursor);
EXPECT_EQ(cursor.totalLength(), 0);
}
std::unique_ptr<folly::IOBuf> createNewTokenFrame(
folly::Optional<QuicInteger> tokenLength = folly::none,
Buf token = nullptr) {
std::unique_ptr<folly::IOBuf> newTokenFrame = folly::IOBuf::create(0);
BufAppender wcursor(newTokenFrame.get(), 10);
auto appenderOp = [&](auto val) { wcursor.writeBE(val); };
if (tokenLength) {
tokenLength->encode(appenderOp);
}
if (token) {
wcursor.insert(std::move(token));
}
return newTokenFrame;
}
TEST_F(DecodeTest, NewTokenDecodeSuccess) {
QuicInteger length(1);
auto newTokenFrame =
createNewTokenFrame(length, folly::IOBuf::copyBuffer("a"));
folly::io::Cursor cursor(newTokenFrame.get());
auto decodedFrame = decodeNewTokenFrame(cursor);
EXPECT_EQ(decodedFrame.token->computeChainDataLength(), 1);
}
TEST_F(DecodeTest, NewTokenLengthNotPresent) {
auto newTokenFrame =
createNewTokenFrame(folly::none, folly::IOBuf::copyBuffer("a"));
folly::io::Cursor cursor(newTokenFrame.get());
EXPECT_THROW(decodeNewTokenFrame(cursor), QuicTransportException);
}
TEST_F(DecodeTest, NewTokenIncorrectDataLength) {
QuicInteger length(10);
auto newTokenFrame =
createNewTokenFrame(length, folly::IOBuf::copyBuffer("a"));
folly::io::Cursor cursor(newTokenFrame.get());
EXPECT_THROW(decodeNewTokenFrame(cursor), QuicTransportException);
}
std::unique_ptr<folly::IOBuf> createMinOrExpiredStreamDataFrame(
QuicInteger streamId,
folly::Optional<QuicInteger> maximumData = folly::none,
folly::Optional<QuicInteger> minimumStreamOffset = folly::none) {
std::unique_ptr<folly::IOBuf> bufQueue = folly::IOBuf::create(0);
BufAppender wcursor(bufQueue.get(), 10);
auto appenderOp = [&](auto val) { wcursor.writeBE(val); };
streamId.encode(appenderOp);
if (maximumData) {
maximumData->encode(appenderOp);
}
if (minimumStreamOffset) {
minimumStreamOffset->encode(appenderOp);
}
return bufQueue;
}
TEST_F(DecodeTest, DecodeMinStreamDataFrame) {
QuicInteger streamId(10);
QuicInteger maximumData(1000);
QuicInteger minimumStreamOffset(100);
auto noOffset = createMinOrExpiredStreamDataFrame(streamId, maximumData);
folly::io::Cursor cursor0(noOffset.get());
EXPECT_THROW(decodeMinStreamDataFrame(cursor0), QuicTransportException);
auto minStreamDataFrame = createMinOrExpiredStreamDataFrame(
streamId, maximumData, minimumStreamOffset);
folly::io::Cursor cursor(minStreamDataFrame.get());
auto result = decodeMinStreamDataFrame(cursor);
EXPECT_EQ(result.streamId, 10);
EXPECT_EQ(result.maximumData, 1000);
EXPECT_EQ(result.minimumStreamOffset, 100);
}
TEST_F(DecodeTest, DecodeExpiredStreamDataFrame) {
QuicInteger streamId(10);
QuicInteger offset(100);
auto noOffset = createMinOrExpiredStreamDataFrame(streamId);
folly::io::Cursor cursor0(noOffset.get());
EXPECT_THROW(decodeExpiredStreamDataFrame(cursor0), QuicTransportException);
auto expiredStreamDataFrame =
createMinOrExpiredStreamDataFrame(streamId, folly::none, offset);
folly::io::Cursor cursor(expiredStreamDataFrame.get());
auto result = decodeExpiredStreamDataFrame(cursor);
EXPECT_EQ(result.streamId, 10);
EXPECT_EQ(result.minimumStreamOffset, 100);
}
} // namespace test
} // namespace quic