mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-09 20:42:44 +03:00
Summary: Implement sending stream limit updates in a windowed fashion, so that as a peer exhausts its streams we will grant it additional credit. This is implemented by having the stream manager check if an update is needed on removing streams, and the api layer potentially sending an update after it initiates the check for closed streams. This also makes some driveby changes to use `std::lower_bound` instead of `std::find` for the sorted collections in the stream manager. Reviewed By: yangchi Differential Revision: D16808229 fbshipit-source-id: f6e3460d43e4d165e362164be00c0cec27cf1e79
1458 lines
53 KiB
C++
1458 lines
53 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/io/IOBufQueue.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/test/TestUtils.h>
|
|
|
|
using namespace quic;
|
|
using namespace quic::test;
|
|
using namespace testing;
|
|
|
|
ShortHeader buildTestShortHeader() {
|
|
return ShortHeader(ProtectionType::KeyPhaseZero, getTestConnectionId(), 0x01);
|
|
}
|
|
|
|
QuicFrame parseQuicFrame(folly::io::Cursor& cursor) {
|
|
return quic::parseFrame(
|
|
cursor,
|
|
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, appendBytes(_, _, _))
|
|
.WillRepeatedly((Invoke([&](folly::io::QueueAppender& appender,
|
|
PacketNum value,
|
|
uint8_t byteNumber) {
|
|
appender.ensure(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, 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(pktBuilder.appender_);
|
|
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.outputQueue_.front()->clone();
|
|
EXPECT_EQ(13, outputBuf->computeChainDataLength());
|
|
EXPECT_EQ(
|
|
kDefaultUDPSendPacketLen - 3 - 10, pktBuilder.remainingSpaceInPkt());
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto resultFrame = boost::get<WriteStreamFrame>(regularPacket.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedStreamFrame = boost::get<ReadStreamFrame>(parseQuicFrame(cursor));
|
|
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.outputQueue_.front()->clone();
|
|
EXPECT_EQ(28, outputBuf->computeChainDataLength());
|
|
size_t consumedSize = 1000 + 8 + 20;
|
|
EXPECT_EQ(
|
|
kDefaultUDPSendPacketLen - consumedSize,
|
|
pktBuilder.remainingSpaceInPkt());
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto resultFrame = boost::get<WriteStreamFrame>(regularPacket.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedStreamFrame = boost::get<ReadStreamFrame>(parseQuicFrame(cursor));
|
|
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.outputQueue_.front()->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.outputQueue_.front()->clone();
|
|
consumedSize += remainingSpace;
|
|
EXPECT_EQ(
|
|
kDefaultUDPSendPacketLen - consumedSize,
|
|
pktBuilder.remainingSpaceInPkt());
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 2);
|
|
auto resultFrame = boost::get<WriteStreamFrame>(regularPacket.frames.front());
|
|
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 = boost::get<WriteStreamFrame>(regularPacket.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedStreamFrame1 = boost::get<ReadStreamFrame>(quic::parseFrame(
|
|
cursor,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)));
|
|
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:
|
|
auto decodedStreamFrame2 = boost::get<ReadStreamFrame>(quic::parseFrame(
|
|
cursor,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)));
|
|
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.outputQueue_.front()->clone();
|
|
EXPECT_EQ(40, outputBuf->computeChainDataLength());
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 0);
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto resultFrame = boost::get<WriteStreamFrame>(regularPacket.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedStreamFrame = boost::get<ReadStreamFrame>(parseQuicFrame(cursor));
|
|
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.hasValue());
|
|
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.outputQueue_.front()->clone();
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 0);
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto resultFrame = boost::get<WriteStreamFrame>(regularPacket.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedStreamFrame = boost::get<ReadStreamFrame>(quic::parseFrame(
|
|
cursor,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)));
|
|
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.outputQueue_.front()->clone();
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
auto resultFrame = boost::get<WriteStreamFrame>(regularPacket.frames.back());
|
|
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());
|
|
auto decodedStreamFrame = boost::get<ReadStreamFrame>(quic::parseFrame(
|
|
cursor,
|
|
regularPacket.header,
|
|
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)));
|
|
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.hasValue());
|
|
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.hasValue());
|
|
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.hasValue());
|
|
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.
|
|
IntervalSet<PacketNum> 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;
|
|
IntervalSet<PacketNum> 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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame ackFrame =
|
|
boost::get<WriteAckFrame>(regularPacket.frames.back());
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 1);
|
|
|
|
EXPECT_EQ(ackFrame.ackBlocks.front().start, 1);
|
|
EXPECT_EQ(largest, ackFrame.ackBlocks.front().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
|
|
IntervalSet<PacketNum> 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.hasValue());
|
|
EXPECT_EQ(pktBuilder.remainingSpaceInPkt(), 4);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteSimpleAckFrame) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
auto ackDelay = 111us;
|
|
IntervalSet<PacketNum> 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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
WriteAckFrame ackFrame =
|
|
boost::get<WriteAckFrame>(regularPacket.frames.back());
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 2);
|
|
auto iter = ackFrame.ackBlocks.cbegin();
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedAckFrame = boost::get<ReadAckFrame>(parseQuicFrame(cursor));
|
|
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;
|
|
IntervalSet<PacketNum> ackBlocks = {{501, 1000}, {101, 400}};
|
|
AckFrameMetaData meta(ackBlocks, ackDelay, kDefaultAckDelayExponent);
|
|
|
|
writeAckFrame(meta, pktBuilder);
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
WriteAckFrame ackFrame =
|
|
boost::get<WriteAckFrame>(regularPacket.frames.back());
|
|
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;
|
|
IntervalSet<PacketNum> 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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame ackFrame =
|
|
boost::get<WriteAckFrame>(regularPacket.frames.back());
|
|
EXPECT_EQ(ackFrame.ackBlocks.size(), 64);
|
|
|
|
EXPECT_EQ(ackFrame.ackBlocks.front().start, 746);
|
|
EXPECT_EQ(ackFrame.ackBlocks.front().end, 748);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteWithDifferentAckDelayExponent) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
IntervalSet<PacketNum> ackBlocks{{1000, 1000}};
|
|
uint8_t ackDelayExponent = 6;
|
|
AckFrameMetaData ackMetadata(ackBlocks, 1240us, ackDelayExponent);
|
|
|
|
writeAckFrame(ackMetadata, pktBuilder);
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedAckFrame = boost::get<ReadAckFrame>(quic::parseFrame(
|
|
cursor,
|
|
builtOut.first.header,
|
|
CodecParameters(ackDelayExponent, QuicVersion::MVFST)));
|
|
EXPECT_EQ(
|
|
decodedAckFrame.ackDelay.count(),
|
|
computeExpectedDelay(ackMetadata.ackDelay, ackDelayExponent));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteExponentInLongHeaderPacket) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
|
|
IntervalSet<PacketNum> 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());
|
|
auto decodedAckFrame = boost::get<ReadAckFrame>(quic::parseFrame(
|
|
cursor,
|
|
builtOut.first.header,
|
|
CodecParameters(ackDelayExponent, QuicVersion::MVFST)));
|
|
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
|
|
IntervalSet<PacketNum> 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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame ackFrame =
|
|
boost::get<WriteAckFrame>(regularPacket.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedAckFrame = boost::get<ReadAckFrame>(parseQuicFrame(cursor));
|
|
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
|
|
IntervalSet<PacketNum> 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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(regularPacket.frames.size(), 1);
|
|
WriteAckFrame ackFrame =
|
|
boost::get<WriteAckFrame>(regularPacket.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedAckFrame = boost::get<ReadAckFrame>(parseQuicFrame(cursor));
|
|
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
|
|
IntervalSet<PacketNum> ackBlocks = {{1000, 1000}, {701, 900}, {501, 600}};
|
|
AckFrameMetaData ackMetadata(ackBlocks, 555us, kDefaultAckDelayExponent);
|
|
auto ackFrameWriteResult = writeAckFrame(ackMetadata, pktBuilder);
|
|
EXPECT_FALSE(ackFrameWriteResult.hasValue());
|
|
}
|
|
|
|
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
|
|
IntervalSet<PacketNum> 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).buildPacket();
|
|
WriteAckFrame ackFrame =
|
|
boost::get<WriteAckFrame>(builtOut.first.frames.back());
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto decodedAckFrame = boost::get<ReadAckFrame>(parseQuicFrame(cursor));
|
|
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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 3);
|
|
auto resultMaxStreamDataFrame =
|
|
boost::get<MaxStreamDataFrame>(regularPacket.frames[0]);
|
|
EXPECT_EQ(id, resultMaxStreamDataFrame.streamId);
|
|
EXPECT_EQ(offset, resultMaxStreamDataFrame.maximumData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireMaxStreamDataFrame =
|
|
boost::get<MaxStreamDataFrame>(parseQuicFrame(cursor));
|
|
EXPECT_EQ(id, wireMaxStreamDataFrame.streamId);
|
|
EXPECT_EQ(offset, wireMaxStreamDataFrame.maximumData);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 3);
|
|
auto resultMaxDataFrame = boost::get<MaxDataFrame>(regularPacket.frames[0]);
|
|
EXPECT_EQ(1000, resultMaxDataFrame.maximumData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireMaxDataFrame = boost::get<MaxDataFrame>(parseQuicFrame(cursor));
|
|
EXPECT_EQ(1000, wireMaxDataFrame.maximumData);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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(maxStreamsFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
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);
|
|
auto resultMaxStreamIdFrame = boost::get<MaxStreamsFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
|
|
EXPECT_EQ(i, resultMaxStreamIdFrame.maxStreams);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireStreamsFrame = boost::get<MaxStreamsFrame>(
|
|
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
|
|
EXPECT_EQ(i, wireStreamsFrame.maxStreams);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
}
|
|
|
|
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(maxStreamsFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
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);
|
|
auto resultMaxStreamIdFrame = boost::get<MaxStreamsFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
|
|
EXPECT_EQ(i, resultMaxStreamIdFrame.maxStreams);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireStreamsFrame = boost::get<MaxStreamsFrame>(
|
|
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
|
|
EXPECT_EQ(i, wireStreamsFrame.maxStreams);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForMaxStreamId) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId maxStream = 0x1234;
|
|
MaxStreamsFrame maxStreamIdFrame(maxStream, true);
|
|
EXPECT_EQ(0, writeFrame(maxStreamIdFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteConnClose) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase("You are fired");
|
|
ConnectionCloseFrame connectionCloseFrame(
|
|
TransportErrorCode::PROTOCOL_VIOLATION, reasonPhrase);
|
|
auto connCloseBytesWritten = writeFrame(connectionCloseFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
// 6 == ErrorCode(2) + FrameType(1) + reasonPhrase-len(2)
|
|
EXPECT_EQ(4 + reasonPhrase.size(), connCloseBytesWritten);
|
|
auto resultConnCloseFrame =
|
|
boost::get<ConnectionCloseFrame>(regularPacket.frames[0]);
|
|
EXPECT_EQ(
|
|
TransportErrorCode::PROTOCOL_VIOLATION, resultConnCloseFrame.errorCode);
|
|
EXPECT_EQ("You are fired", resultConnCloseFrame.reasonPhrase);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireConnCloseFrame =
|
|
boost::get<ConnectionCloseFrame>(parseQuicFrame(cursor));
|
|
EXPECT_EQ(
|
|
TransportErrorCode::PROTOCOL_VIOLATION, wireConnCloseFrame.errorCode);
|
|
EXPECT_EQ("You are fired", wireConnCloseFrame.reasonPhrase);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, DecodeConnCloseLarge) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase;
|
|
reasonPhrase.resize(kMaxReasonPhraseLength + 10);
|
|
ConnectionCloseFrame connectionCloseFrame(
|
|
TransportErrorCode::PROTOCOL_VIOLATION, reasonPhrase);
|
|
writeFrame(connectionCloseFrame, pktBuilder);
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
auto resultConnCloseFrame =
|
|
boost::get<ConnectionCloseFrame>(regularPacket.frames[0]);
|
|
EXPECT_EQ(
|
|
TransportErrorCode::PROTOCOL_VIOLATION, resultConnCloseFrame.errorCode);
|
|
EXPECT_EQ(resultConnCloseFrame.reasonPhrase, reasonPhrase);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
EXPECT_THROW(
|
|
boost::get<ConnectionCloseFrame>(parseQuicFrame(cursor)),
|
|
std::runtime_error);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceConnClose) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 2;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase("You are all fired");
|
|
ConnectionCloseFrame connCloseFrame(
|
|
TransportErrorCode::PROTOCOL_VIOLATION, reasonPhrase);
|
|
EXPECT_EQ(0, writeFrame(connCloseFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, DecodeAppCloseLarge) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
std::string reasonPhrase;
|
|
reasonPhrase.resize(kMaxReasonPhraseLength + 10);
|
|
ApplicationCloseFrame applicationCloseFrame(
|
|
GenericApplicationErrorCode::UNKNOWN, reasonPhrase);
|
|
writeFrame(applicationCloseFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
auto resultAppCloseFrame =
|
|
boost::get<ApplicationCloseFrame>(regularPacket.frames[0]);
|
|
EXPECT_EQ(
|
|
GenericApplicationErrorCode::UNKNOWN, resultAppCloseFrame.errorCode);
|
|
EXPECT_EQ(resultAppCloseFrame.reasonPhrase, reasonPhrase);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
EXPECT_THROW(
|
|
boost::get<ApplicationCloseFrame>(parseQuicFrame(cursor)),
|
|
std::runtime_error);
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WritePing) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
PingFrame pingFrame;
|
|
auto pingBytesWritten = writeFrame(pingFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(1, pingBytesWritten);
|
|
EXPECT_NO_THROW(boost::get<PingFrame>(regularPacket.frames[0]));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
boost::get<PingFrame>(parseQuicFrame(cursor));
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForPing) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
PingFrame pingFrame;
|
|
EXPECT_EQ(0, writeFrame(pingFrame, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WritePadding) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
PaddingFrame paddingFrame;
|
|
auto paddingBytesWritten = writeFrame(paddingFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(1, paddingBytesWritten);
|
|
EXPECT_NO_THROW(boost::get<PaddingFrame>(regularPacket.frames[0]));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
EXPECT_NO_THROW(boost::get<PaddingFrame>(parseQuicFrame(cursor)));
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(blockedBytesWritten, 7);
|
|
auto resultBlockedFrame =
|
|
boost::get<StreamDataBlockedFrame>(regularPacket.frames[0]);
|
|
EXPECT_EQ(blockedId, resultBlockedFrame.streamId);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireBlockedFrame =
|
|
boost::get<StreamDataBlockedFrame>(parseQuicFrame(cursor));
|
|
EXPECT_EQ(blockedId, wireBlockedFrame.streamId);
|
|
EXPECT_EQ(blockedOffset, wireBlockedFrame.dataLimit);
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(13, rstStreamBytesWritten);
|
|
auto resultRstStreamFrame =
|
|
boost::get<RstStreamFrame>(regularPacket.frames[0]);
|
|
EXPECT_EQ(errorCode, resultRstStreamFrame.errorCode);
|
|
EXPECT_EQ(id, resultRstStreamFrame.streamId);
|
|
EXPECT_EQ(offset, resultRstStreamFrame.offset);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireRstStreamFrame = boost::get<RstStreamFrame>(parseQuicFrame(cursor));
|
|
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_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 5);
|
|
EXPECT_NO_THROW(boost::get<DataBlockedFrame>(regularPacket.frames[0]));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireBlockedFrame = boost::get<DataBlockedFrame>(parseQuicFrame(cursor));
|
|
EXPECT_EQ(wireBlockedFrame.dataLimit, blockedOffset);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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(streamIdNeeded, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 3);
|
|
EXPECT_NO_THROW(boost::get<MaxStreamsFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0])));
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto writeStreamIdBlocked = boost::get<MaxStreamsFrame>(
|
|
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
|
|
EXPECT_EQ(writeStreamIdBlocked.maxStreams, blockedStreamId);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForStreamIdNeeded) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId blockedStreamId = 0x211;
|
|
MaxStreamsFrame streamIdNeeded(blockedStreamId, true);
|
|
EXPECT_EQ(0, writeFrame(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(newConnId, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 28);
|
|
auto resultNewConnIdFrame = boost::get<NewConnectionIdFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
|
|
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);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireNewConnIdFrame = boost::get<NewConnectionIdFrame>(
|
|
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
|
|
EXPECT_EQ(1, wireNewConnIdFrame.sequenceNumber);
|
|
EXPECT_EQ(0, wireNewConnIdFrame.retirePriorTo);
|
|
EXPECT_EQ(getTestConnectionId(), wireNewConnIdFrame.connectionId);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 6);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireSimpleFrame = boost::get<QuicSimpleFrame>(parseQuicFrame(cursor));
|
|
auto wireStopSendingFrame = boost::get<StopSendingFrame>(wireSimpleFrame);
|
|
EXPECT_EQ(wireStopSendingFrame.streamId, streamId);
|
|
EXPECT_EQ(wireStopSendingFrame.errorCode, errorCode);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, NoSpaceForNewConnId) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
pktBuilder.remaining_ = 0;
|
|
setupCommonExpects(pktBuilder);
|
|
NewConnectionIdFrame newConnId(
|
|
1, 0, getTestConnectionId(), StatelessResetToken());
|
|
EXPECT_EQ(0, writeFrame(newConnId, pktBuilder));
|
|
}
|
|
|
|
TEST_F(QuicWriteCodecTest, WriteExpiredStreamDataFrame) {
|
|
MockQuicPacketBuilder pktBuilder;
|
|
setupCommonExpects(pktBuilder);
|
|
StreamId id = 10;
|
|
uint64_t offset = 0x08;
|
|
ExpiredStreamDataFrame expiredStreamDataFrame(id, offset);
|
|
auto bytesWritten = writeFrame(expiredStreamDataFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 4);
|
|
auto result = boost::get<ExpiredStreamDataFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
|
|
EXPECT_EQ(id, result.streamId);
|
|
EXPECT_EQ(offset, result.minimumStreamOffset);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireExpiredStreamDataFrame = boost::get<ExpiredStreamDataFrame>(
|
|
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
|
|
EXPECT_EQ(id, wireExpiredStreamDataFrame.streamId);
|
|
EXPECT_EQ(offset, wireExpiredStreamDataFrame.minimumStreamOffset);
|
|
|
|
// At last, verify there is nothing left in the wire format bytes:
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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(minStreamDataFrame, pktBuilder);
|
|
|
|
auto builtOut = std::move(pktBuilder).buildPacket();
|
|
auto regularPacket = builtOut.first;
|
|
EXPECT_EQ(bytesWritten, 6);
|
|
auto result = boost::get<MinStreamDataFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
|
|
EXPECT_EQ(id, result.streamId);
|
|
EXPECT_EQ(maximumData, result.maximumData);
|
|
EXPECT_EQ(offset, result.minimumStreamOffset);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireMinStreamDataFrame = boost::get<MinStreamDataFrame>(
|
|
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
|
|
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_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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).buildPacket();
|
|
|
|
auto regularPacket = builtOut.first;
|
|
auto result = boost::get<PathChallengeFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
|
|
EXPECT_EQ(result.pathData, pathData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireSimpleFrame = boost::get<QuicSimpleFrame>(parseQuicFrame(cursor));
|
|
auto wirePathChallengeFrame = boost::get<PathChallengeFrame>(wireSimpleFrame);
|
|
EXPECT_EQ(wirePathChallengeFrame.pathData, pathData);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
|
|
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).buildPacket();
|
|
|
|
auto regularPacket = builtOut.first;
|
|
auto result = boost::get<PathResponseFrame>(
|
|
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
|
|
EXPECT_EQ(result.pathData, pathData);
|
|
|
|
auto wireBuf = std::move(builtOut.second);
|
|
folly::io::Cursor cursor(wireBuf.get());
|
|
auto wireSimpleFrame = boost::get<QuicSimpleFrame>(parseQuicFrame(cursor));
|
|
auto wirePathResponseFrame = boost::get<PathResponseFrame>(wireSimpleFrame);
|
|
EXPECT_EQ(wirePathResponseFrame.pathData, pathData);
|
|
EXPECT_TRUE(cursor.isAtEnd());
|
|
}
|
|
} // namespace test
|
|
} // namespace quic
|