mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-11-10 21:22:20 +03:00
Summary: Different Builders now want to have its own way of writing or appending write buffer. So let builders handle them. This also add new APIs in BufWriter to copy data from IOBuf/BufQueue directly into a destination IOBuf without cloning inbetween. Reviewed By: mjoras Differential Revision: D20821789 fbshipit-source-id: c0a24eb12378f64cf26c27d4232f610ed80fba84
1563 lines
57 KiB
C++
1563 lines
57 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/QuicWriteCodec.h>
|
|
#include <folly/Random.h>
|
|
#include <folly/io/Cursor.h>
|
|
#include <folly/portability/GMock.h>
|
|
#include <folly/portability/GTest.h>
|
|
#include <quic/QuicException.h>
|
|
#include <quic/codec/Decode.h>
|
|
#include <quic/codec/Types.h>
|
|
#include <quic/codec/test/Mocks.h>
|
|
#include <quic/common/BufUtil.h>
|
|
#include <quic/common/test/TestUtils.h>
|
|
|
|
using namespace quic;
|
|
using namespace quic::test;
|
|
using namespace testing;
|
|
|
|
ShortHeader buildTestShortHeader() {
|
|
return ShortHeader(ProtectionType::KeyPhaseZero, getTestConnectionId(), 0x01);
|
|
}
|
|
|
|
QuicFrame parseQuicFrame(BufQueue& queue) {
|
|
return quic::parseFrame(
|
|
queue,
|
|
buildTestShortHeader(),
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
|
|
}
|
|
|
|
namespace quic {
|
|
namespace test {
|
|
|
|
void setupCommonExpects(MockQuicPacketBuilder& pktBuilder) {
|
|
EXPECT_CALL(pktBuilder, remainingSpaceInPkt()).WillRepeatedly(Invoke([&]() {
|
|
return pktBuilder.remaining_;
|
|
}));
|
|
|
|
EXPECT_CALL(pktBuilder, writeBEUint8(_))
|
|
.WillRepeatedly(WithArgs<0>(Invoke([&](uint8_t value) {
|
|
pktBuilder.appender_.writeBE<uint8_t>(value);
|
|
pktBuilder.remaining_ -= sizeof(uint8_t);
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, writeBEUint16(_))
|
|
.WillRepeatedly(WithArgs<0>(Invoke([&](uint16_t value) {
|
|
pktBuilder.appender_.writeBE<uint16_t>(value);
|
|
pktBuilder.remaining_ -= sizeof(uint16_t);
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, writeBEUint64(_))
|
|
.WillRepeatedly(WithArgs<0>(Invoke([&](uint64_t value) {
|
|
pktBuilder.appender_.writeBE<uint64_t>(value);
|
|
pktBuilder.remaining_ -= sizeof(uint64_t);
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, appendBytes(_, _))
|
|
.WillRepeatedly(
|
|
WithArgs<0, 1>(Invoke([&](PacketNum value, uint8_t byteNumber) {
|
|
pktBuilder.appendBytes(pktBuilder.appender_, value, byteNumber);
|
|
})));
|
|
EXPECT_CALL(pktBuilder, appendBytesWithAppender(_, _, _))
|
|
.WillRepeatedly((Invoke(
|
|
[&](BufAppender& appender, PacketNum value, uint8_t byteNumber) {
|
|
auto bigValue = folly::Endian::big(value);
|
|
appender.push(
|
|
(uint8_t*)&bigValue + sizeof(bigValue) - byteNumber,
|
|
byteNumber);
|
|
pktBuilder.remaining_ -= byteNumber;
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, appendFrame(_))
|
|
.WillRepeatedly(WithArgs<0>(
|
|
Invoke([&](auto frame) { pktBuilder.frames_.push_back(frame); })));
|
|
|
|
EXPECT_CALL(pktBuilder, _insert(_))
|
|
.WillRepeatedly(WithArgs<0>(Invoke([&](Buf& buf) {
|
|
pktBuilder.remaining_ -= buf->computeChainDataLength();
|
|
pktBuilder.appender_.insert(std::move(buf));
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, _insert(_, _))
|
|
.WillRepeatedly(WithArgs<0, 1>(Invoke([&](Buf& buf, size_t limit) {
|
|
pktBuilder.remaining_ -= limit;
|
|
std::unique_ptr<folly::IOBuf> cloneBuf;
|
|
folly::io::Cursor cursor(buf.get());
|
|
cursor.clone(cloneBuf, limit);
|
|
pktBuilder.appender_.insert(std::move(cloneBuf));
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, insert(_, _))
|
|
.WillRepeatedly(
|
|
WithArgs<0, 1>(Invoke([&](const BufQueue& buf, size_t limit) {
|
|
pktBuilder.remaining_ -= limit;
|
|
std::unique_ptr<folly::IOBuf> cloneBuf;
|
|
folly::io::Cursor cursor(buf.front());
|
|
cursor.clone(cloneBuf, limit);
|
|
pktBuilder.appender_.insert(std::move(cloneBuf));
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, push(_, _))
|
|
.WillRepeatedly(
|
|
WithArgs<0, 1>(Invoke([&](const uint8_t* data, size_t len) {
|
|
pktBuilder.appender_.push(data, len);
|
|
pktBuilder.remaining_ -= len;
|
|
})));
|
|
|
|
EXPECT_CALL(pktBuilder, write(_))
|
|
.WillRepeatedly(Invoke([&](const QuicInteger& quicInteger) {
|
|
quicInteger.encode(
|
|
[&](auto val) { pktBuilder.appender_.writeBE(val); });
|
|
pktBuilder.remaining_ -= quicInteger.getSize();
|
|
}));
|
|
}
|
|
|
|
class QuicWriteCodecTest : public Test {};
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamFrameToEmptyPacket) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
auto inputBuf = buildRandomInputData(10);
|
|
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// 1 byte for length
|
|
// => 3 bytes
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = false;
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 10, 10, fin);
|
|
ASSERT_TRUE(dataLen);
|
|
ASSERT_EQ(*dataLen, 10);
|
|
writeStreamFrameData(pktBuilder, inputBuf->clone(), 10);
|
|
auto outputBuf = pktBuilder.data_->clone();
|
|
EXPECT_EQ(13, outputBuf->computeChainDataLength());
|
|
EXPECT_EQ(
|
|
kDefaultUDPSendPacketLen - 3 - 10, pktBuilder.remainingSpaceInPkt());
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = std::move(builtOut.first);
|
|
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto& resultFrame = *regularPacket.frames.back().asWriteStreamFrame();
|
|
EXPECT_EQ(resultFrame.streamId, streamId);
|
|
EXPECT_EQ(resultFrame.offset, offset);
|
|
EXPECT_EQ(resultFrame.len, 10);
|
|
outputBuf->trimStart(3);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, outputBuf));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame quicFrameDecoded = parseQuicFrame(queue);
|
|
auto& decodedStreamFrame = *quicFrameDecoded.asReadStreamFrame();
|
|
EXPECT_EQ(decodedStreamFrame.streamId, streamId);
|
|
EXPECT_EQ(decodedStreamFrame.offset, offset);
|
|
EXPECT_EQ(decodedStreamFrame.data->computeChainDataLength(), 10);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, decodedStreamFrame.data));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamFrameToPartialPacket) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
// 1000 bytes already gone in this packet
|
|
pktBuilder.remaining_ = kDefaultUDPSendPacketLen - 1000;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
StreamId streamId = 200;
|
|
uint64_t offset = 65535;
|
|
bool fin = false;
|
|
auto inputBuf = buildRandomInputData(20);
|
|
// 1 byte for type
|
|
// 2 bytes for stream id
|
|
// 4 bytes offset
|
|
// 1 byte for length
|
|
// => 8 bytes of header
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 20, 20, fin);
|
|
ASSERT_TRUE(dataLen);
|
|
EXPECT_EQ(*dataLen, 20);
|
|
writeStreamFrameData(pktBuilder, inputBuf->clone(), 20);
|
|
auto outputBuf = pktBuilder.data_->clone();
|
|
EXPECT_EQ(28, outputBuf->computeChainDataLength());
|
|
size_t consumedSize = 1000 + 8 + 20;
|
|
EXPECT_EQ(
|
|
kDefaultUDPSendPacketLen - consumedSize,
|
|
pktBuilder.remainingSpaceInPkt());
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = std::move(builtOut.first);
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto& resultFrame = *regularPacket.frames.back().asWriteStreamFrame();
|
|
EXPECT_EQ(resultFrame.streamId, streamId);
|
|
EXPECT_EQ(resultFrame.offset, offset);
|
|
EXPECT_EQ(resultFrame.len, 20);
|
|
outputBuf->trimStart(8);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, outputBuf));
|
|
|
|
// Verify the on wire bytes via decoder:
|
|
// (Awkwardly, this assumes the decoder is correct)
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame quicFrameDecoded = parseQuicFrame(queue);
|
|
auto& decodedStreamFrame = *quicFrameDecoded.asReadStreamFrame();
|
|
EXPECT_EQ(decodedStreamFrame.streamId, streamId);
|
|
EXPECT_EQ(decodedStreamFrame.offset, offset);
|
|
EXPECT_EQ(decodedStreamFrame.data->computeChainDataLength(), 20);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, decodedStreamFrame.data));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteTwoStreamFrames) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = kDefaultUDPSendPacketLen - 1000;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 byte for type
|
|
// 2 bytes for stream id
|
|
// 4 bytes for offset
|
|
// 1 byte for length
|
|
// => 8 bytes
|
|
StreamId streamId1 = 300;
|
|
uint64_t offset1 = 65535;
|
|
bool fin1 = false;
|
|
auto inputBuf = buildRandomInputData(30);
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId1, offset1, 30, 30, fin1);
|
|
ASSERT_TRUE(dataLen);
|
|
ASSERT_EQ(*dataLen, 30);
|
|
writeStreamFrameData(pktBuilder, inputBuf->clone(), 30);
|
|
auto outputBuf = pktBuilder.data_->clone();
|
|
EXPECT_EQ(38, outputBuf->computeChainDataLength());
|
|
size_t consumedSize = 1000 + 8 + 30;
|
|
EXPECT_EQ(
|
|
kDefaultUDPSendPacketLen - consumedSize,
|
|
pktBuilder.remainingSpaceInPkt());
|
|
|
|
StreamId streamId2 = 300;
|
|
uint64_t offset2 = 65565;
|
|
bool fin2 = false;
|
|
uint64_t remainingSpace = pktBuilder.remainingSpaceInPkt();
|
|
auto inputBuf2 = buildRandomInputData(remainingSpace);
|
|
// 1 byte for type
|
|
// 2 bytes for stream
|
|
// 4 bytes for offset
|
|
// => 7 bytes
|
|
dataLen = writeStreamFrameHeader(
|
|
pktBuilder, streamId2, offset2, remainingSpace, remainingSpace, fin2);
|
|
ASSERT_TRUE(dataLen);
|
|
ASSERT_EQ(*dataLen, remainingSpace - 7);
|
|
writeStreamFrameData(pktBuilder, inputBuf2->clone(), remainingSpace - 7);
|
|
auto outputBuf2 = pktBuilder.data_->clone();
|
|
outputBuf2->coalesce();
|
|
consumedSize += remainingSpace;
|
|
EXPECT_EQ(
|
|
kDefaultUDPSendPacketLen - consumedSize,
|
|
pktBuilder.remainingSpaceInPkt());
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = std::move(builtOut.first);
|
|
EXPECT_EQ(regularPacket.frames.size(), 2);
|
|
auto& resultFrame = *regularPacket.frames.front().asWriteStreamFrame();
|
|
EXPECT_EQ(resultFrame.streamId, streamId1);
|
|
EXPECT_EQ(resultFrame.offset, offset1);
|
|
EXPECT_EQ(resultFrame.len, 30);
|
|
outputBuf->trimStart(8);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, outputBuf));
|
|
|
|
auto& resultFrame2 = *regularPacket.frames.back().asWriteStreamFrame();
|
|
EXPECT_EQ(resultFrame2.streamId, streamId2);
|
|
EXPECT_EQ(resultFrame2.offset, offset2);
|
|
EXPECT_EQ(resultFrame2.len, remainingSpace - 7);
|
|
outputBuf2->trimStart(38 + 7);
|
|
inputBuf2->trimEnd(7);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf2, outputBuf2));
|
|
|
|
// Verify the on wire bytes via decoder:
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame streamFrameDecoded1 = quic::parseFrame(
|
|
queue,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
|
|
auto& decodedStreamFrame1 = *streamFrameDecoded1.asReadStreamFrame();
|
|
EXPECT_EQ(decodedStreamFrame1.streamId, streamId1);
|
|
EXPECT_EQ(decodedStreamFrame1.offset, offset1);
|
|
EXPECT_EQ(decodedStreamFrame1.data->computeChainDataLength(), 30);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, decodedStreamFrame1.data));
|
|
// Read another one from wire output:
|
|
QuicFrame streamFrameDecoded2 = quic::parseFrame(
|
|
queue,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
|
|
auto& decodedStreamFrame2 = *streamFrameDecoded2.asReadStreamFrame();
|
|
EXPECT_EQ(decodedStreamFrame2.streamId, streamId2);
|
|
EXPECT_EQ(decodedStreamFrame2.offset, offset2);
|
|
EXPECT_EQ(
|
|
decodedStreamFrame2.data->computeChainDataLength(), remainingSpace - 7);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf2, decodedStreamFrame2.data));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamFramePartialData) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
// Networking bytes are just like your youth, they disappear quick:
|
|
pktBuilder.remaining_ = 40;
|
|
setupCommonExpects(pktBuilder);
|
|
auto inputBuf = buildRandomInputData(50);
|
|
|
|
StreamId streamId = 300;
|
|
uint64_t offset = 65535;
|
|
bool fin = false;
|
|
|
|
// 1 byte for type
|
|
// 2 bytes for stream id
|
|
// 4 bytes for offset
|
|
// => 7 bytes for header
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 50, 50, fin);
|
|
ASSERT_TRUE(dataLen);
|
|
ASSERT_EQ(*dataLen, 33);
|
|
writeStreamFrameData(pktBuilder, inputBuf->clone(), 33);
|
|
auto outputBuf = pktBuilder.data_->clone();
|
|
EXPECT_EQ(40, outputBuf->computeChainDataLength());
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 0);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto& resultFrame = *regularPacket.frames.back().asWriteStreamFrame();
|
|
EXPECT_EQ(resultFrame.streamId, streamId);
|
|
EXPECT_EQ(resultFrame.offset, offset);
|
|
EXPECT_EQ(resultFrame.len, 33);
|
|
|
|
outputBuf->trimStart(7);
|
|
inputBuf->trimEnd(inputBuf->computeChainDataLength() - 33);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, outputBuf));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame quicFrameDecoded = parseQuicFrame(queue);
|
|
auto& decodedStreamFrame = *quicFrameDecoded.asReadStreamFrame();
|
|
EXPECT_EQ(decodedStreamFrame.streamId, streamId);
|
|
EXPECT_EQ(decodedStreamFrame.offset, offset);
|
|
EXPECT_EQ(decodedStreamFrame.data->computeChainDataLength(), 33);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, decodedStreamFrame.data));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamFrameTooSmallForStreamHeader) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 1;
|
|
setupCommonExpects(pktBuilder);
|
|
auto inputBuf = buildRandomInputData(1);
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 65535;
|
|
bool fin = false;
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 1, 1, fin);
|
|
EXPECT_FALSE(dataLen);
|
|
EXPECT_EQ(1, pktBuilder.remainingSpaceInPkt());
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamNoSpaceForData) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 3;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 1;
|
|
bool fin = false;
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// 1 byte for offset
|
|
// => 3 bytes
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 10, 10, fin);
|
|
EXPECT_FALSE(dataLen.has_value());
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 3);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamSpaceForOneByte) {
|
|
// Similar to WriteStreamNoSpaceForData, but this time we
|
|
// do not need the data length field, so we end up actually writing 1 byte
|
|
// real data
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 4;
|
|
setupCommonExpects(pktBuilder);
|
|
auto inputBuf = buildRandomInputData(100);
|
|
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 1;
|
|
bool fin = false;
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// 1 byte for offet
|
|
// => 3 bytes
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 100, 100, fin);
|
|
ASSERT_TRUE(dataLen);
|
|
ASSERT_EQ(*dataLen, 1);
|
|
writeStreamFrameData(pktBuilder, inputBuf->clone(), 1);
|
|
auto outputBuf = pktBuilder.data_->clone();
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 0);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto& resultFrame = *regularPacket.frames.back().asWriteStreamFrame();
|
|
EXPECT_EQ(resultFrame.streamId, streamId);
|
|
EXPECT_EQ(resultFrame.offset, offset);
|
|
EXPECT_EQ(resultFrame.len, 1);
|
|
inputBuf->trimEnd(inputBuf->computeChainDataLength() - 1);
|
|
outputBuf->trimStart(3);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, outputBuf));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = quic::parseFrame(
|
|
queue,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
|
|
auto decodedStreamFrame = *decodedFrame.asReadStreamFrame();
|
|
EXPECT_EQ(decodedStreamFrame.streamId, streamId);
|
|
EXPECT_EQ(decodedStreamFrame.offset, offset);
|
|
EXPECT_EQ(decodedStreamFrame.data->computeChainDataLength(), 1);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, decodedStreamFrame.data));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteFinToEmptyPacket) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
auto inputBuf = buildRandomInputData(10);
|
|
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// 1 byte for length
|
|
// => 3 byte
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = true;
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 10, 10, fin);
|
|
ASSERT_TRUE(dataLen);
|
|
ASSERT_EQ(*dataLen, 10);
|
|
writeStreamFrameData(pktBuilder, inputBuf->clone(), 10);
|
|
auto outputBuf = pktBuilder.data_->clone();
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto& resultFrame = *regularPacket.frames.back().asWriteStreamFrame();
|
|
EXPECT_EQ(resultFrame.streamId, streamId);
|
|
EXPECT_EQ(resultFrame.offset, offset);
|
|
EXPECT_EQ(inputBuf->computeChainDataLength(), resultFrame.len);
|
|
EXPECT_TRUE(resultFrame.fin);
|
|
outputBuf->trimStart(3);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, outputBuf));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = quic::parseFrame(
|
|
queue,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
|
|
auto& decodedStreamFrame = *decodedFrame.asReadStreamFrame();
|
|
EXPECT_EQ(decodedStreamFrame.streamId, streamId);
|
|
EXPECT_EQ(decodedStreamFrame.offset, offset);
|
|
EXPECT_EQ(
|
|
decodedStreamFrame.data->computeChainDataLength(),
|
|
inputBuf->computeChainDataLength());
|
|
EXPECT_TRUE(decodedStreamFrame.fin);
|
|
EXPECT_TRUE(folly::IOBufEqualTo()(inputBuf, decodedStreamFrame.data));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, TestWriteIncompleteDataAndFin) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
// make sure we are writing more than the packet can hold and then some
|
|
auto inDataSize = pktBuilder.remainingSpaceInPkt() + 20;
|
|
auto inputBuf = buildRandomInputData(inDataSize);
|
|
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// 1 byte for length
|
|
// => 3 byte
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = true;
|
|
auto dataLen = writeStreamFrameHeader(
|
|
pktBuilder, streamId, offset, inDataSize, inDataSize, fin);
|
|
ASSERT_TRUE(dataLen);
|
|
EXPECT_LT(*dataLen, inDataSize);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, TestWriteNoDataAndFin) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// => 2 byte
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = true;
|
|
Buf empty;
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 0, 0, fin);
|
|
ASSERT_TRUE(dataLen);
|
|
EXPECT_EQ(*dataLen, 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, TestWriteNoDataAndNoFin) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = false;
|
|
Buf empty;
|
|
EXPECT_THROW(
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 0, 0, fin),
|
|
QuicInternalException);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, PacketOnlyHasSpaceForStreamHeader) {
|
|
// If packet only has space for a stream header, even if FIN is set, we should
|
|
// not write anything if we have data
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 2;
|
|
setupCommonExpects(pktBuilder);
|
|
auto inputBuf = buildRandomInputData(20);
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// => 2 byte
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = true;
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 20, 20, fin);
|
|
EXPECT_FALSE(dataLen.has_value());
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 2);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, PacketOnlyHasSpaceForStreamHeaderWithFin) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 3;
|
|
setupCommonExpects(pktBuilder);
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// 1 byte for length
|
|
// => 3 byte
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = true;
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 0, 0, fin);
|
|
ASSERT_TRUE(dataLen.has_value());
|
|
EXPECT_EQ(*dataLen, 0);
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, PacketNotEnoughSpaceForStreamHeaderWithFin) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 2;
|
|
setupCommonExpects(pktBuilder);
|
|
// 1 byte for type
|
|
// 1 byte for stream id
|
|
// 1 byte for length
|
|
// => 3 byte
|
|
StreamId streamId = 1;
|
|
uint64_t offset = 0;
|
|
bool fin = true;
|
|
auto dataLen =
|
|
writeStreamFrameHeader(pktBuilder, streamId, offset, 0, 0, fin);
|
|
ASSERT_FALSE(dataLen.has_value());
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 2);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, AckFrameGapExceedsRepresentation) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
PacketNum max = std::numeric_limits<uint64_t>::max();
|
|
// Can't use max directly, because it will exceed interval set's
|
|
// representation.
|
|
AckBlocks ackBlocks = {{max - 10, max - 10}, {1, 1}};
|
|
EXPECT_THROW(
|
|
writeAckFrame(
|
|
AckFrameMetaData(ackBlocks, 0us, kDefaultAckDelayExponent),
|
|
pktBuilder),
|
|
QuicTransportException);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, AckFrameVeryLargeAckRange) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 type byte,
|
|
// 8 bytes for largest acked, 1 bytes for ack delay => 9 bytes
|
|
// 1 byte for ack block count
|
|
// There is 1 gap => each represented by 8 bytes => 8 bytes
|
|
// total 11 bytes
|
|
PacketNum largest = (uint64_t)1 << 55;
|
|
AckBlocks ackBlocks = {{1, largest}};
|
|
AckFrameMetaData ackMetadata(ackBlocks, 0us, kDefaultAckDelayExponent);
|
|
|
|
auto ackFrameWriteResult = *writeAckFrame(ackMetadata, pktBuilder);
|
|
|
|
EXPECT_EQ(19, ackFrameWriteResult.bytesWritten);
|
|
EXPECT_EQ(kDefaultUDPSendPacketLen - 19, pktBuilder.remainingSpaceInPkt());
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame& ackFrame = *regularPacket.frames.back().asWriteAckFrame();
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 1);
|
|
|
|
EXPECT_EQ(ackFrame.ackBlocks.back().start, 1);
|
|
EXPECT_EQ(largest, ackFrame.ackBlocks.back().end);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, AckFrameNotEnoughForAnything) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 4;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 type byte,
|
|
// 2 bytes for largest acked, 2 bytes for ack delay => 4 bytes
|
|
// 1 byte for ack block count
|
|
// There are 2 gaps => each represented by 2 bytes => 4 bytes
|
|
// 1 byte for first ack block length, then 2 bytes for each pair => 5 bytes
|
|
// total 15 bytes
|
|
AckBlocks ackBlocks = {{1000, 1000}, {500, 700}, {100, 200}};
|
|
// 4 btyes are just not enough for anything
|
|
AckFrameMetaData ackMetadata(ackBlocks, 555us, kDefaultAckDelayExponent);
|
|
|
|
auto result = writeAckFrame(ackMetadata, pktBuilder);
|
|
EXPECT_FALSE(result.has_value());
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 4);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteSimpleAckFrame) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
auto ackDelay = 111us;
|
|
AckBlocks ackBlocks = {{501, 1000}, {101, 400}};
|
|
AckFrameMetaData meta(ackBlocks, ackDelay, kDefaultAckDelayExponent);
|
|
|
|
// 1 type byte,
|
|
// 2 bytes for largest acked, 1 bytes for ack delay => 3 bytes
|
|
// 1 byte for ack block count
|
|
// There is 1 gap => each represented by 2 bytes => 2 bytes
|
|
// 2 byte for first ack block length, then 2 bytes for the next len => 4 bytes
|
|
// total 11 bytes
|
|
|
|
auto result = *writeAckFrame(meta, pktBuilder);
|
|
|
|
EXPECT_EQ(11, result.bytesWritten);
|
|
EXPECT_EQ(kDefaultUDPSendPacketLen - 11, pktBuilder.remainingSpaceInPkt());
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
WriteAckFrame& ackFrame = *regularPacket.frames.back().asWriteAckFrame();
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 2);
|
|
auto iter = ackFrame.ackBlocks.crbegin();
|
|
EXPECT_EQ(iter->start, 101);
|
|
EXPECT_EQ(iter->end, 400);
|
|
iter++;
|
|
EXPECT_EQ(iter->start, 501);
|
|
EXPECT_EQ(iter->end, 1000);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& decodedAckFrame = *decodedFrame.asReadAckFrame();
|
|
EXPECT_EQ(decodedAckFrame.largestAcked, 1000);
|
|
EXPECT_EQ(
|
|
decodedAckFrame.ackDelay.count(),
|
|
computeExpectedDelay(ackDelay, kDefaultAckDelayExponent));
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks.size(), 2);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[0].startPacket, 501);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[0].endPacket, 1000);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[1].startPacket, 101);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[1].endPacket, 400);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteAckFrameWillSaveAckDelay) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
auto ackDelay = 111us;
|
|
AckBlocks ackBlocks = {{501, 1000}, {101, 400}};
|
|
AckFrameMetaData meta(ackBlocks, ackDelay, kDefaultAckDelayExponent);
|
|
|
|
writeAckFrame(meta, pktBuilder);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
WriteAckFrame& ackFrame = *regularPacket.frames.back().asWriteAckFrame();
|
|
EXPECT_EQ(ackDelay, ackFrame.ackDelay);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, VerifyNumAckBlocksSizeAccounted) {
|
|
// Tests that if we restrict the size to be exactly the size required for a 1
|
|
// byte num blocks size, if the num blocks requires 2 bytes (practically this
|
|
// will never happen), then we won't write the additional blocks.
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 134;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 type byte,
|
|
// 2 byte for largest acked, 1 bytes for ack delay => 3 bytes
|
|
// 2 byte for ack block count, (64 additional blocks)
|
|
// 1 byte for largest ack block len
|
|
// There is 64 blocks => each represented by 2 bytes => 128 bytes
|
|
// total 135 bytes needed, instead only giving 134 bytes.
|
|
auto blockLength = 2;
|
|
auto gap = 2;
|
|
PacketNum largest = 1000;
|
|
PacketNum currentEnd = largest - blockLength - gap;
|
|
AckBlocks ackBlocks;
|
|
for (int i = 0; i < 64; i++) {
|
|
CHECK_GE(currentEnd, blockLength);
|
|
ackBlocks.insert({currentEnd - blockLength, currentEnd});
|
|
currentEnd -= blockLength + gap;
|
|
}
|
|
ackBlocks.insert({largest, largest});
|
|
AckFrameMetaData ackMetadata(ackBlocks, 0us, kDefaultAckDelayExponent);
|
|
|
|
auto ackFrameWriteResult = *writeAckFrame(ackMetadata, pktBuilder);
|
|
|
|
EXPECT_EQ(ackFrameWriteResult.bytesWritten, 132);
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 2);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame& ackFrame = *regularPacket.frames.back().asWriteAckFrame();
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 64);
|
|
|
|
EXPECT_EQ(ackFrame.ackBlocks.back().start, 746);
|
|
EXPECT_EQ(ackFrame.ackBlocks.back().end, 748);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteWithDifferentAckDelayExponent) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
AckBlocks ackBlocks{{1000, 1000}};
|
|
uint8_t ackDelayExponent = 6;
|
|
AckFrameMetaData ackMetadata(ackBlocks, 1240us, ackDelayExponent);
|
|
|
|
writeAckFrame(ackMetadata, pktBuilder);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = quic::parseFrame(
|
|
queue,
|
|
builtOut.first.header,
|
|
CodecParameters(ackDelayExponent, QuicVersion::MVFST));
|
|
auto& decodedAckFrame = *decodedFrame.asReadAckFrame();
|
|
EXPECT_EQ(
|
|
decodedAckFrame.ackDelay.count(),
|
|
computeExpectedDelay(ackMetadata.ackDelay, ackDelayExponent));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteExponentInLongHeaderPacket) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
AckBlocks ackBlocks{{1000, 1000}};
|
|
uint8_t ackDelayExponent = 6;
|
|
AckFrameMetaData ackMetadata(ackBlocks, 1240us, ackDelayExponent);
|
|
|
|
writeAckFrame(ackMetadata, pktBuilder);
|
|
auto builtOut = std::move(pktBuilder).buildLongHeaderPacket();
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = quic::parseFrame(
|
|
queue,
|
|
builtOut.first.header,
|
|
CodecParameters(ackDelayExponent, QuicVersion::MVFST));
|
|
auto& decodedAckFrame = *decodedFrame.asReadAckFrame();
|
|
EXPECT_EQ(
|
|
decodedAckFrame.ackDelay.count(),
|
|
(uint64_t(ackMetadata.ackDelay.count()) >> ackDelayExponent)
|
|
<< kDefaultAckDelayExponent);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, OnlyAckLargestPacket) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 type byte,
|
|
// 2 bytes for largest acked, 2 bytes for ack delay => 4 bytes
|
|
// 1 byte for ack block count
|
|
// 1 byte for first ack block length
|
|
// total 7 bytes
|
|
AckBlocks ackBlocks{{1000, 1000}};
|
|
AckFrameMetaData ackMetadata(ackBlocks, 555us, kDefaultAckDelayExponent);
|
|
|
|
// No AckBlock is added to the metadata. There will still be one block
|
|
// generated as the first block to cover largestAcked => 2 bytes
|
|
auto ackFrameWriteResult = *writeAckFrame(ackMetadata, pktBuilder);
|
|
EXPECT_EQ(7, ackFrameWriteResult.bytesWritten);
|
|
EXPECT_EQ(kDefaultUDPSendPacketLen - 7, pktBuilder.remainingSpaceInPkt());
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame& ackFrame = *regularPacket.frames.back().asWriteAckFrame();
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 1);
|
|
EXPECT_EQ(ackFrame.ackBlocks.front().start, 1000);
|
|
EXPECT_EQ(ackFrame.ackBlocks.front().end, 1000);
|
|
|
|
// Verify the on wire bytes via decoder:
|
|
// (Awkwardly, this assumes the decoder is correct)
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& decodedAckFrame = *decodedFrame.asReadAckFrame();
|
|
EXPECT_EQ(decodedAckFrame.largestAcked, 1000);
|
|
EXPECT_EQ(
|
|
decodedAckFrame.ackDelay.count(),
|
|
computeExpectedDelay(ackMetadata.ackDelay, kDefaultAckDelayExponent));
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks.size(), 1);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[0].startPacket, 1000);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[0].endPacket, 1000);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteSomeAckBlocks) {
|
|
// Too many ack blocks passed in, we can only write some of them
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 36;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 type byte,
|
|
// 2 bytes for largest acked, 2 bytes for ack delay => 4 bytes
|
|
// 1 byte for num ack blocks
|
|
// 1 byte for first ack block length
|
|
// each additional ack block 1 byte gap + 1 byte length => 2 bytes
|
|
// total 7 bytes
|
|
AckBlocks testAckBlocks;
|
|
PacketNum currentEnd = 1000;
|
|
auto blockLength = 5;
|
|
auto gap = 10;
|
|
for (int i = 0; i < 30; i++) {
|
|
testAckBlocks.insert({currentEnd - blockLength + 1, currentEnd});
|
|
currentEnd -= blockLength + gap;
|
|
}
|
|
testAckBlocks.insert({1000, 1000});
|
|
|
|
AckFrameMetaData ackMetadata(testAckBlocks, 555ms, kDefaultAckDelayExponent);
|
|
auto ackFrameWriteResult = *writeAckFrame(ackMetadata, pktBuilder);
|
|
|
|
EXPECT_EQ(ackFrameWriteResult.bytesWritten, 35);
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 1);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame& ackFrame = *regularPacket.frames.back().asWriteAckFrame();
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 14);
|
|
|
|
// Verify the on wire bytes via decoder:
|
|
// (Awkwardly, this assumes the decoder is correct)
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& decodedAckFrame = *decodedFrame.asReadAckFrame();
|
|
EXPECT_EQ(decodedAckFrame.largestAcked, 1000);
|
|
EXPECT_EQ(
|
|
decodedAckFrame.ackDelay.count(),
|
|
computeExpectedDelay(ackMetadata.ackDelay, kDefaultAckDelayExponent));
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks.size(), 14);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForAckBlockSection) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 6;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 type byte,
|
|
// 2 bytes for largest acked, 2 bytes for ack delay => 4 bytes
|
|
// 1 byte for num ack blocks
|
|
// 1 byte for first ack block length
|
|
AckBlocks ackBlocks = {{1000, 1000}, {701, 900}, {501, 600}};
|
|
AckFrameMetaData ackMetadata(ackBlocks, 555us, kDefaultAckDelayExponent);
|
|
auto ackFrameWriteResult = writeAckFrame(ackMetadata, pktBuilder);
|
|
EXPECT_FALSE(ackFrameWriteResult.has_value());
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, OnlyHasSpaceForFirstAckBlock) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 10;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
// 1 type byte,
|
|
// 2 bytes for largest acked, 2 bytes for ack delay => 4 bytes
|
|
// 1 byte for num ack blocks
|
|
// 1 byte for first ack block length
|
|
AckBlocks ackBlocks = {{1000, 1000}, {701, 900}, {501, 600}};
|
|
AckFrameMetaData ackMetadata(ackBlocks, 555us, kDefaultAckDelayExponent);
|
|
auto ackFrameWriteResult = *writeAckFrame(ackMetadata, pktBuilder);
|
|
|
|
EXPECT_EQ(ackFrameWriteResult.bytesWritten, 7);
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 3);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
WriteAckFrame& ackFrame = *builtOut.first.frames.back().asWriteAckFrame();
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 1);
|
|
EXPECT_EQ(ackFrame.ackBlocks.front().start, 1000);
|
|
EXPECT_EQ(ackFrame.ackBlocks.front().end, 1000);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& decodedAckFrame = *decodedFrame.asReadAckFrame();
|
|
EXPECT_EQ(decodedAckFrame.largestAcked, 1000);
|
|
EXPECT_EQ(
|
|
decodedAckFrame.ackDelay.count(),
|
|
computeExpectedDelay(ackMetadata.ackDelay, kDefaultAckDelayExponent));
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks.size(), 1);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[0].startPacket, 1000);
|
|
EXPECT_EQ(decodedAckFrame.ackBlocks[0].endPacket, 1000);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteMaxStreamData) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId id = 1;
|
|
uint64_t offset = 0x08;
|
|
MaxStreamDataFrame maxStreamDataFrame(id, offset);
|
|
auto bytesWritten = writeFrame(maxStreamDataFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 3);
|
|
auto& resultMaxStreamDataFrame =
|
|
*regularPacket.frames[0].asMaxStreamDataFrame();
|
|
EXPECT_EQ(id, resultMaxStreamDataFrame.streamId);
|
|
EXPECT_EQ(offset, resultMaxStreamDataFrame.maximumData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& wireMaxStreamDataFrame = *decodedFrame.asMaxStreamDataFrame();
|
|
EXPECT_EQ(id, wireMaxStreamDataFrame.streamId);
|
|
EXPECT_EQ(offset, wireMaxStreamDataFrame.maximumData);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForMaxStreamData) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 1;
|
|
setupCommonExpects(pktBuilder);
|
|
MaxStreamDataFrame maxStreamDataFrame(1, 0x08);
|
|
EXPECT_EQ(0, writeFrame(maxStreamDataFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteMaxData) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
MaxDataFrame maxDataFrame(1000);
|
|
auto bytesWritten = writeFrame(maxDataFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 3);
|
|
auto& resultMaxDataFrame = *regularPacket.frames[0].asMaxDataFrame();
|
|
EXPECT_EQ(1000, resultMaxDataFrame.maximumData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& wireMaxDataFrame = *decodedFrame.asMaxDataFrame();
|
|
EXPECT_EQ(1000, wireMaxDataFrame.maximumData);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForMaxData) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
MaxDataFrame maxDataFrame(1000);
|
|
EXPECT_EQ(0, writeFrame(maxDataFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteMaxStreamId) {
|
|
for (uint64_t i = 0; i < 100; i++) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
uint64_t maxStream = i;
|
|
bool isBidirectional = true;
|
|
MaxStreamsFrame maxStreamsFrame(maxStream, isBidirectional);
|
|
auto bytesWritten =
|
|
writeFrame(QuicSimpleFrame(maxStreamsFrame), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
auto streamCountSize = i < 64 ? 1 : 2;
|
|
// 1 byte for the type and up to 2 bytes for the stream count.
|
|
EXPECT_EQ(1 + streamCountSize, bytesWritten);
|
|
MaxStreamsFrame& resultMaxStreamIdFrame =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asMaxStreamsFrame();
|
|
EXPECT_EQ(i, resultMaxStreamIdFrame.maxStreams);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
MaxStreamsFrame& wireStreamsFrame = *simpleFrame.asMaxStreamsFrame();
|
|
EXPECT_EQ(i, wireStreamsFrame.maxStreams);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteUniMaxStreamId) {
|
|
for (uint64_t i = 0; i < 100; i++) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
uint64_t maxStream = i;
|
|
bool isBidirectional = false;
|
|
MaxStreamsFrame maxStreamsFrame(maxStream, isBidirectional);
|
|
auto bytesWritten =
|
|
writeFrame(QuicSimpleFrame(maxStreamsFrame), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
auto streamCountSize = i < 64 ? 1 : 2;
|
|
// 1 byte for the type and up to 2 bytes for the stream count.
|
|
EXPECT_EQ(1 + streamCountSize, bytesWritten);
|
|
MaxStreamsFrame& resultMaxStreamIdFrame =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asMaxStreamsFrame();
|
|
EXPECT_EQ(i, resultMaxStreamIdFrame.maxStreams);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
MaxStreamsFrame& wireStreamsFrame = *simpleFrame.asMaxStreamsFrame();
|
|
EXPECT_EQ(i, wireStreamsFrame.maxStreams);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForMaxStreamId) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId maxStream = 0x1234;
|
|
MaxStreamsFrame maxStreamIdFrame(maxStream, true);
|
|
EXPECT_EQ(0, writeFrame(QuicSimpleFrame(maxStreamIdFrame), pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteConnClose) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase("You are fired");
|
|
ConnectionCloseFrame connectionCloseFrame(
|
|
QuicErrorCode(TransportErrorCode::PROTOCOL_VIOLATION), reasonPhrase);
|
|
auto connCloseBytesWritten =
|
|
writeFrame(std::move(connectionCloseFrame), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
// 6 == ErrorCode(2) + FrameType(1) + reasonPhrase-len(2)
|
|
EXPECT_EQ(4 + reasonPhrase.size(), connCloseBytesWritten);
|
|
auto& resultConnCloseFrame =
|
|
*regularPacket.frames[0].asConnectionCloseFrame();
|
|
const TransportErrorCode* transportErrorCode =
|
|
resultConnCloseFrame.errorCode.asTransportErrorCode();
|
|
EXPECT_EQ(TransportErrorCode::PROTOCOL_VIOLATION, *transportErrorCode);
|
|
EXPECT_EQ("You are fired", resultConnCloseFrame.reasonPhrase);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedCloseFrame = parseQuicFrame(queue);
|
|
auto& wireConnCloseFrame = *decodedCloseFrame.asConnectionCloseFrame();
|
|
const TransportErrorCode* protocolViolationCode =
|
|
wireConnCloseFrame.errorCode.asTransportErrorCode();
|
|
EXPECT_EQ(TransportErrorCode::PROTOCOL_VIOLATION, *protocolViolationCode);
|
|
EXPECT_EQ("You are fired", wireConnCloseFrame.reasonPhrase);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, DecodeConnCloseLarge) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase;
|
|
reasonPhrase.resize(kMaxReasonPhraseLength + 10);
|
|
ConnectionCloseFrame connectionCloseFrame(
|
|
QuicErrorCode(TransportErrorCode::PROTOCOL_VIOLATION), reasonPhrase);
|
|
writeFrame(connectionCloseFrame, pktBuilder);
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
auto& resultConnCloseFrame =
|
|
*regularPacket.frames[0].asConnectionCloseFrame();
|
|
const TransportErrorCode* protocolViolationCode =
|
|
resultConnCloseFrame.errorCode.asTransportErrorCode();
|
|
EXPECT_EQ(TransportErrorCode::PROTOCOL_VIOLATION, *protocolViolationCode);
|
|
EXPECT_EQ(resultConnCloseFrame.reasonPhrase, reasonPhrase);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
EXPECT_THROW(parseQuicFrame(queue), QuicTransportException);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceConnClose) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 2;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase("You are all fired");
|
|
ConnectionCloseFrame connCloseFrame(
|
|
QuicErrorCode(TransportErrorCode::PROTOCOL_VIOLATION), reasonPhrase);
|
|
EXPECT_EQ(0, writeFrame(std::move(connCloseFrame), pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, DecodeAppCloseLarge) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase;
|
|
reasonPhrase.resize(kMaxReasonPhraseLength + 10);
|
|
ConnectionCloseFrame applicationCloseFrame(
|
|
QuicErrorCode(GenericApplicationErrorCode::UNKNOWN),
|
|
reasonPhrase,
|
|
quic::FrameType::CONNECTION_CLOSE_APP_ERR);
|
|
writeFrame(std::move(applicationCloseFrame), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
auto& resultAppCloseFrame = *regularPacket.frames[0].asConnectionCloseFrame();
|
|
EXPECT_EQ(
|
|
quic::FrameType::CONNECTION_CLOSE_APP_ERR,
|
|
resultAppCloseFrame.closingFrameType);
|
|
EXPECT_EQ(resultAppCloseFrame.reasonPhrase, reasonPhrase);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
EXPECT_THROW(parseQuicFrame(queue), QuicTransportException);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WritePing) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
auto pingBytesWritten = writeFrame(QuicSimpleFrame(PingFrame()), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(1, pingBytesWritten);
|
|
auto simpleFrame = regularPacket.frames[0].asQuicSimpleFrame();
|
|
EXPECT_NE(simpleFrame->asPingFrame(), nullptr);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto decodedSimpleFrame = decodedFrame.asQuicSimpleFrame();
|
|
EXPECT_NE(decodedSimpleFrame->asPingFrame(), nullptr);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForPing) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
EXPECT_EQ(0, writeFrame(QuicSimpleFrame(PingFrame()), pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WritePadding) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
auto paddingBytesWritten = writeFrame(PaddingFrame(), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(1, paddingBytesWritten);
|
|
EXPECT_NE(regularPacket.frames[0].asPaddingFrame(), nullptr);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
EXPECT_NE(decodedFrame.asPaddingFrame(), nullptr);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForPadding) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
PaddingFrame paddingFrame;
|
|
EXPECT_EQ(0, writeFrame(paddingFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamBlocked) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId blockedId = 0xF00D;
|
|
uint64_t blockedOffset = 0x1111;
|
|
StreamDataBlockedFrame blockedFrame(blockedId, blockedOffset);
|
|
auto blockedBytesWritten = writeFrame(blockedFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(blockedBytesWritten, 7);
|
|
EXPECT_NE(regularPacket.frames[0].asStreamDataBlockedFrame(), nullptr);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& wireBlockedFrame = *decodedFrame.asStreamDataBlockedFrame();
|
|
EXPECT_EQ(blockedId, wireBlockedFrame.streamId);
|
|
EXPECT_EQ(blockedOffset, wireBlockedFrame.dataLimit);
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForBlockedStream) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 1;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId blockedStream = 0x01;
|
|
uint64_t blockedOffset = 0x1111;
|
|
StreamDataBlockedFrame blockedFrame(blockedStream, blockedOffset);
|
|
EXPECT_EQ(0, writeFrame(blockedFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteRstStream) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId id = 0xBAAD;
|
|
ApplicationErrorCode errorCode = GenericApplicationErrorCode::UNKNOWN;
|
|
uint64_t offset = 0xF00D;
|
|
RstStreamFrame rstStreamFrame(id, errorCode, offset);
|
|
auto rstStreamBytesWritten = writeFrame(rstStreamFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(13, rstStreamBytesWritten);
|
|
auto& resultRstStreamFrame = *regularPacket.frames[0].asRstStreamFrame();
|
|
EXPECT_EQ(errorCode, resultRstStreamFrame.errorCode);
|
|
EXPECT_EQ(id, resultRstStreamFrame.streamId);
|
|
EXPECT_EQ(offset, resultRstStreamFrame.offset);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& wireRstStreamFrame = *decodedFrame.asRstStreamFrame();
|
|
EXPECT_EQ(errorCode, wireRstStreamFrame.errorCode);
|
|
EXPECT_EQ(id, wireRstStreamFrame.streamId);
|
|
EXPECT_EQ(offset, wireRstStreamFrame.offset);
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForRst) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 1;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId id = 0xBAAD;
|
|
ApplicationErrorCode errorCode = GenericApplicationErrorCode::UNKNOWN;
|
|
uint64_t offset = 0xF00D;
|
|
RstStreamFrame rstStreamFrame(id, errorCode, offset);
|
|
EXPECT_EQ(0, writeFrame(rstStreamFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteBlockedFrame) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
uint64_t blockedOffset = 0x11111;
|
|
DataBlockedFrame blockedFrame(blockedOffset);
|
|
auto bytesWritten = writeFrame(blockedFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 5);
|
|
EXPECT_NE(regularPacket.frames[0].asDataBlockedFrame(), nullptr);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
auto& wireBlockedFrame = *decodedFrame.asDataBlockedFrame();
|
|
EXPECT_EQ(wireBlockedFrame.dataLimit, blockedOffset);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForBlocked) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
uint64_t blockedOffset = 0x11111;
|
|
DataBlockedFrame blockedFrame(blockedOffset);
|
|
EXPECT_EQ(0, writeFrame(blockedFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStreamIdNeeded) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId blockedStreamId = 0x211;
|
|
MaxStreamsFrame streamIdNeeded(blockedStreamId, true);
|
|
auto bytesWritten = writeFrame(QuicSimpleFrame(streamIdNeeded), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 3);
|
|
EXPECT_NE(
|
|
regularPacket.frames[0].asQuicSimpleFrame()->asMaxStreamsFrame(),
|
|
nullptr);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
auto& writeStreamIdBlocked = *simpleFrame.asMaxStreamsFrame();
|
|
EXPECT_EQ(writeStreamIdBlocked.maxStreams, blockedStreamId);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForStreamIdNeeded) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId blockedStreamId = 0x211;
|
|
MaxStreamsFrame streamIdNeeded(blockedStreamId, true);
|
|
EXPECT_EQ(0, writeFrame(QuicSimpleFrame(streamIdNeeded), pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteNewConnId) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StatelessResetToken token;
|
|
memset(token.data(), 'a', token.size());
|
|
NewConnectionIdFrame newConnId(1, 0, getTestConnectionId(), token);
|
|
auto bytesWritten = writeFrame(QuicSimpleFrame(newConnId), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 28);
|
|
NewConnectionIdFrame& resultNewConnIdFrame =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asNewConnectionIdFrame();
|
|
EXPECT_EQ(resultNewConnIdFrame.sequenceNumber, 1);
|
|
EXPECT_EQ(resultNewConnIdFrame.retirePriorTo, 0);
|
|
EXPECT_EQ(resultNewConnIdFrame.connectionId, getTestConnectionId());
|
|
EXPECT_EQ(resultNewConnIdFrame.token, token);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
NewConnectionIdFrame wireNewConnIdFrame =
|
|
*simpleFrame.asNewConnectionIdFrame();
|
|
EXPECT_EQ(1, wireNewConnIdFrame.sequenceNumber);
|
|
EXPECT_EQ(0, wireNewConnIdFrame.retirePriorTo);
|
|
EXPECT_EQ(getTestConnectionId(), wireNewConnIdFrame.connectionId);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteRetireConnId) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
RetireConnectionIdFrame retireConnId(3);
|
|
auto bytesWritten = writeFrame(QuicSimpleFrame(retireConnId), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 2);
|
|
RetireConnectionIdFrame resultRetireConnIdFrame =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asRetireConnectionIdFrame();
|
|
EXPECT_EQ(resultRetireConnIdFrame.sequenceNumber, 3);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
RetireConnectionIdFrame wireRetireConnIdFrame =
|
|
*simpleFrame.asRetireConnectionIdFrame();
|
|
EXPECT_EQ(3, wireRetireConnIdFrame.sequenceNumber);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteStopSending) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId streamId = 10;
|
|
auto errorCode = GenericApplicationErrorCode::UNKNOWN;
|
|
|
|
StopSendingFrame stopSending(streamId, errorCode);
|
|
auto bytesWritten = writeSimpleFrame(stopSending, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 6);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
StopSendingFrame wireStopSendingFrame = *simpleFrame.asStopSendingFrame();
|
|
EXPECT_EQ(wireStopSendingFrame.streamId, streamId);
|
|
EXPECT_EQ(wireStopSendingFrame.errorCode, errorCode);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForNewConnId) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
NewConnectionIdFrame newConnId(
|
|
1, 0, getTestConnectionId(), StatelessResetToken());
|
|
EXPECT_EQ(0, writeFrame(QuicSimpleFrame(newConnId), pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteExpiredStreamDataFrame) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId id = 10;
|
|
uint64_t offset = 0x08;
|
|
ExpiredStreamDataFrame expiredStreamDataFrame(id, offset);
|
|
auto bytesWritten =
|
|
writeFrame(QuicSimpleFrame(expiredStreamDataFrame), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 4);
|
|
ExpiredStreamDataFrame result =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asExpiredStreamDataFrame();
|
|
EXPECT_EQ(id, result.streamId);
|
|
EXPECT_EQ(offset, result.minimumStreamOffset);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
ExpiredStreamDataFrame wireExpiredStreamDataFrame =
|
|
*simpleFrame.asExpiredStreamDataFrame();
|
|
EXPECT_EQ(id, wireExpiredStreamDataFrame.streamId);
|
|
EXPECT_EQ(offset, wireExpiredStreamDataFrame.minimumStreamOffset);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteMinStreamDataFrame) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId id = 10;
|
|
uint64_t maximumData = 0x64;
|
|
uint64_t offset = 0x08;
|
|
MinStreamDataFrame minStreamDataFrame(id, maximumData, offset);
|
|
auto bytesWritten =
|
|
writeFrame(QuicSimpleFrame(minStreamDataFrame), pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 6);
|
|
MinStreamDataFrame result =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asMinStreamDataFrame();
|
|
EXPECT_EQ(id, result.streamId);
|
|
EXPECT_EQ(maximumData, result.maximumData);
|
|
EXPECT_EQ(offset, result.minimumStreamOffset);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
MinStreamDataFrame wireMinStreamDataFrame =
|
|
*simpleFrame.asMinStreamDataFrame();
|
|
EXPECT_EQ(id, wireMinStreamDataFrame.streamId);
|
|
EXPECT_EQ(maximumData, wireMinStreamDataFrame.maximumData);
|
|
EXPECT_EQ(offset, wireMinStreamDataFrame.minimumStreamOffset);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WritePathChallenge) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
uint64_t pathData = 0x64;
|
|
PathChallengeFrame pathChallenge(pathData);
|
|
auto bytesWritten = writeSimpleFrame(pathChallenge, pktBuilder);
|
|
EXPECT_EQ(bytesWritten, 9);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
|
|
auto regularPacket = builtOut.first;
|
|
PathChallengeFrame result =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asPathChallengeFrame();
|
|
EXPECT_EQ(result.pathData, pathData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
PathChallengeFrame wirePathChallengeFrame =
|
|
*simpleFrame.asPathChallengeFrame();
|
|
EXPECT_EQ(wirePathChallengeFrame.pathData, pathData);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WritePathResponse) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
uint64_t pathData = 0x64;
|
|
PathResponseFrame pathResponse(pathData);
|
|
auto bytesWritten = writeSimpleFrame(pathResponse, pktBuilder);
|
|
EXPECT_EQ(bytesWritten, 9);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildTestPacket();
|
|
|
|
auto regularPacket = builtOut.first;
|
|
PathResponseFrame result =
|
|
*regularPacket.frames[0].asQuicSimpleFrame()->asPathResponseFrame();
|
|
EXPECT_EQ(result.pathData, pathData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
BufQueue queue;
|
|
queue.append(wireBuf->clone());
|
|
QuicFrame decodedFrame = parseQuicFrame(queue);
|
|
QuicSimpleFrame& simpleFrame = *decodedFrame.asQuicSimpleFrame();
|
|
PathResponseFrame wirePathResponseFrame = *simpleFrame.asPathResponseFrame();
|
|
EXPECT_EQ(wirePathResponseFrame.pathData, pathData);
|
|
EXPECT_EQ(queue.chainLength(), 0);
|
|
}
|
|
} // namespace test
|
|
} // namespace quic
|