1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-09 20:42:44 +03:00
Files
mvfst/quic/state/test/QuicStreamFunctionsTest.cpp
Matt Joras 72e677df33 Send windowed stream limit updates
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
2019-09-18 11:33:03 -07:00

2011 lines
76 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <quic/state/QuicStreamFunctions.h>
#include <quic/client/state/ClientStateMachine.h>
#include <quic/common/test/TestUtils.h>
#include <quic/server/state/ServerStateMachine.h>
using namespace folly;
using namespace testing;
namespace quic {
namespace test {
constexpr uint8_t kStreamIncrement = 0x04;
using PeekIterator = std::deque<StreamBuffer>::const_iterator;
class QuicStreamFunctionsTest : public Test {
public:
void SetUp() override {
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiLocal =
kDefaultStreamWindowSize;
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiRemote =
kDefaultStreamWindowSize;
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetUni =
kDefaultStreamWindowSize;
conn.flowControlState.peerAdvertisedMaxOffset =
kDefaultConnectionWindowSize;
conn.streamManager->setMaxLocalBidirectionalStreams(
kDefaultMaxStreamsBidirectional);
conn.streamManager->setMaxLocalUnidirectionalStreams(
kDefaultMaxStreamsUnidirectional);
}
QuicClientConnectionState conn;
};
class QuicServerStreamFunctionsTest : public Test {
public:
void SetUp() override {
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiLocal =
kDefaultStreamWindowSize;
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiRemote =
kDefaultStreamWindowSize;
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetUni =
kDefaultStreamWindowSize;
conn.flowControlState.peerAdvertisedMaxOffset =
kDefaultConnectionWindowSize;
conn.streamManager->setMaxLocalBidirectionalStreams(
kDefaultMaxStreamsBidirectional);
conn.streamManager->setMaxLocalUnidirectionalStreams(
kDefaultMaxStreamsUnidirectional);
}
QuicServerConnectionState conn;
};
TEST_F(QuicStreamFunctionsTest, TestCreateBidirectionalStream) {
const auto stream =
conn.streamManager->createNextBidirectionalStream().value();
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(stream->id, 0x00);
}
TEST_F(QuicStreamFunctionsTest, TestCreateUnidirectionalStream) {
const auto stream =
conn.streamManager->createNextUnidirectionalStream().value();
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(stream->id, 0x02);
}
TEST_F(QuicStreamFunctionsTest, TestCreateBoth) {
for (int i = 0; i < 50; i++) {
auto stream = conn.streamManager->createNextUnidirectionalStream().value();
ASSERT_EQ(conn.streamManager->streamCount(), i + 1);
ASSERT_EQ(stream->id, 0x02 + i * kStreamIncrement);
}
for (int i = 0; i < 50; i++) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
ASSERT_EQ(conn.streamManager->streamCount(), i + 51);
ASSERT_EQ(stream->id, 0x00 + i * kStreamIncrement);
}
}
TEST_F(QuicStreamFunctionsTest, TestWriteStream) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just met you");
auto buf2 = IOBuf::copyBuffer("and this is crazy");
writeDataToQuicStream(*stream, buf1->clone(), false);
writeDataToQuicStream(*stream, buf2->clone(), false);
IOBufEqualTo eq;
buf1->prependChain(std::move(buf2));
EXPECT_TRUE(eq(stream->writeBuffer.move(), buf1));
}
TEST_F(QuicStreamFunctionsTest, TestReadDataWrittenInOrder) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
auto buf1 = IOBuf::copyBuffer("I just met you ");
buf1->prependChain(IOBuf::copyBuffer("and this is crazy. "));
auto buf2 = IOBuf::copyBuffer("Here's my number ");
buf2->prependChain(IOBuf::copyBuffer("so call me maybe"));
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(
*stream,
StreamBuffer(buf2->clone(), buf1->computeChainDataLength(), true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
auto readData1 = readDataFromQuicStream(*stream, 10);
EXPECT_EQ("I just met", readData1.first->moveToFbString().toStdString());
EXPECT_FALSE(readData1.second);
auto readData2 = readDataFromQuicStream(*stream, 30);
EXPECT_EQ(
" you and this is crazy. Here's",
readData2.first->moveToFbString().toStdString());
EXPECT_FALSE(readData2.second);
auto readData3 = readDataFromQuicStream(*stream, 21);
EXPECT_EQ(
" my number so call me", readData3.first->moveToFbString().toStdString());
EXPECT_FALSE(readData3.second);
auto readData4 = readDataFromQuicStream(*stream, 20);
EXPECT_EQ(" maybe", readData4.first->moveToFbString().toStdString());
EXPECT_TRUE(readData4.second);
}
TEST_F(QuicStreamFunctionsTest, TestPeekAndConsumeContiguousData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
auto buf1 = IOBuf::copyBuffer("I just met you ");
buf1->prependChain(IOBuf::copyBuffer("and this is crazy. "));
auto buf2 = IOBuf::copyBuffer("Here's my number ");
buf2->prependChain(IOBuf::copyBuffer("so call me maybe"));
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(
*stream,
StreamBuffer(buf2->clone(), buf1->computeChainDataLength(), true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
bool peekCbCalled = false;
auto peekCallback = [&](StreamId /* unused */,
const folly::Range<PeekIterator>& range) {
peekCbCalled = true;
EXPECT_EQ(range.size(), 1);
for (const auto& streamBuf : range) {
auto bufClone = streamBuf.data.front()->clone();
EXPECT_EQ(
"I just met you and this is crazy. Here's my number so call me maybe",
bufClone->moveToFbString().toStdString());
}
};
peekDataFromQuicStream(*stream, peekCallback);
EXPECT_TRUE(peekCbCalled);
EXPECT_NO_THROW(consumeDataFromQuicStream(*stream, 81));
peekCbCalled = false;
auto peekCallback2 = [&](StreamId /* unused */,
const folly::Range<PeekIterator>& range) {
peekCbCalled = true;
EXPECT_EQ(range.size(), 0);
};
peekDataFromQuicStream(*stream, peekCallback2);
EXPECT_TRUE(peekCbCalled);
}
TEST_F(QuicStreamFunctionsTest, TestPeekAndConsumeNonContiguousData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
auto buf1 = IOBuf::copyBuffer("I just met you ");
buf1->prependChain(IOBuf::copyBuffer("and this is crazy. "));
auto buf2 = IOBuf::copyBuffer("'s my number ");
buf2->prependChain(IOBuf::copyBuffer("so call me maybe"));
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(
*stream,
StreamBuffer(buf2->clone(), buf1->computeChainDataLength() + 4, true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
bool cbCalled = false;
peekDataFromQuicStream(
*stream,
[&](StreamId /* unused */, const folly::Range<PeekIterator>& range) {
cbCalled = true;
EXPECT_EQ(range.size(), 2);
auto bufClone = range[0].data.front()->clone();
EXPECT_EQ(
"I just met you and this is crazy. ",
bufClone->moveToFbString().toStdString());
bufClone = range[1].data.front()->clone();
EXPECT_EQ(
"'s my number so call me maybe",
bufClone->moveToFbString().toStdString());
});
EXPECT_TRUE(cbCalled);
// Consume left side.
EXPECT_NO_THROW(consumeDataFromQuicStream(*stream, 81));
cbCalled = false;
auto peekCallback2 = [&](StreamId /* unused */,
const folly::Range<PeekIterator>& range) {
cbCalled = true;
EXPECT_EQ(range.size(), 1);
auto bufClone = range[0].data.front()->clone();
EXPECT_EQ(
"'s my number so call me maybe",
bufClone->moveToFbString().toStdString());
};
peekDataFromQuicStream(*stream, peekCallback2);
EXPECT_TRUE(cbCalled);
// Try consuming again.
// Nothing has changed since we're missing data in the middle.
EXPECT_NO_THROW(consumeDataFromQuicStream(*stream, 81));
cbCalled = false;
peekDataFromQuicStream(*stream, peekCallback2);
EXPECT_TRUE(cbCalled);
// Add missing middle bytes.
auto buf3 = IOBuf::copyBuffer("Here");
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 0));
appendDataToReadBuffer(
*stream, StreamBuffer(buf3->clone(), buf1->computeChainDataLength()));
cbCalled = false;
peekDataFromQuicStream(
*stream,
[&](StreamId /* unused */, const folly::Range<PeekIterator>& range) {
cbCalled = true;
EXPECT_EQ(range.size(), 1);
auto bufClone = range[0].data.front()->clone();
EXPECT_EQ(
"Here's my number so call me maybe",
bufClone->moveToFbString().toStdString());
});
EXPECT_TRUE(cbCalled);
// Consume the rest of the buffer.
EXPECT_NO_THROW(consumeDataFromQuicStream(*stream, 81));
cbCalled = false;
peekDataFromQuicStream(
*stream,
[&](StreamId /* unused */, const folly::Range<PeekIterator>& range) {
cbCalled = true;
EXPECT_EQ(range.size(), 0);
});
EXPECT_TRUE(cbCalled);
}
TEST_F(QuicStreamFunctionsTest, TestPeekAndConsumeEmptyData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
bool cbCalled = false;
auto peekCallback = [&](StreamId /* unused */,
const folly::Range<PeekIterator>& range) {
cbCalled = true;
EXPECT_EQ(range.size(), 0);
};
peekDataFromQuicStream(*stream, peekCallback);
EXPECT_TRUE(cbCalled);
EXPECT_NO_THROW(consumeDataFromQuicStream(*stream, 81));
cbCalled = false;
peekDataFromQuicStream(*stream, peekCallback);
EXPECT_TRUE(cbCalled);
}
TEST_F(QuicStreamFunctionsTest, TestPeekAndConsumeEmptyDataEof) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
bool cbCalled = false;
auto peekCallback = [&](StreamId /* unused */,
const folly::Range<PeekIterator>& range) {
cbCalled = true;
EXPECT_EQ(range.size(), 0);
};
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 0, true));
peekDataFromQuicStream(*stream, peekCallback);
EXPECT_TRUE(cbCalled);
EXPECT_NO_THROW(consumeDataFromQuicStream(*stream, 42));
cbCalled = false;
peekDataFromQuicStream(*stream, peekCallback);
EXPECT_TRUE(cbCalled);
}
TEST_F(QuicStreamFunctionsTest, TestReadDataFromMultipleBufs) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just met you ");
buf1->prependChain(IOBuf::copyBuffer("and this is crazy. "));
auto buf2 = IOBuf::copyBuffer("Here's my number ");
buf2->prependChain(IOBuf::copyBuffer("so call me maybe"));
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(
*stream,
StreamBuffer(buf2->clone(), buf1->computeChainDataLength(), true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(
"I just met you and this is crazy. Here's my number so call me maybe",
readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.first);
auto readData2 = readDataFromQuicStream(*stream, 30);
EXPECT_EQ(nullptr, readData2.first);
}
TEST_F(QuicStreamFunctionsTest, TestReadDataOutOfOrder) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer(" you ");
buf1->prependChain(IOBuf::copyBuffer("and this is crazy. "));
auto buf2 = IOBuf::copyBuffer("Here's my number ");
buf2->prependChain(IOBuf::copyBuffer("so call me maybe"));
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 10));
appendDataToReadBuffer(
*stream,
StreamBuffer(buf2->clone(), buf1->computeChainDataLength() + 10, true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(nullptr, readData1.first);
appendDataToReadBuffer(
*stream, StreamBuffer(IOBuf::copyBuffer("I just met"), 0));
auto readData2 = readDataFromQuicStream(*stream, 19);
EXPECT_EQ(
"I just met you and ", readData2.first->moveToFbString().toStdString());
EXPECT_FALSE(readData2.second);
auto readData3 = readDataFromQuicStream(*stream, 31);
EXPECT_EQ(
"this is crazy. Here's my number",
readData3.first->moveToFbString().toStdString());
EXPECT_FALSE(readData3.second);
auto readData4 = readDataFromQuicStream(*stream);
EXPECT_EQ(
" so call me maybe", readData4.first->moveToFbString().toStdString());
EXPECT_TRUE(readData4.second);
}
TEST_F(QuicStreamFunctionsTest, TestReadOverlappingData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just met you ");
buf1->prependChain(IOBuf::copyBuffer("and this"));
auto buf2 = IOBuf::copyBuffer("met you and this is crazy. ");
buf2->prependChain(IOBuf::copyBuffer("Here's my number"));
auto buf3 = IOBuf::copyBuffer("Here's my number, ");
buf3->prependChain(IOBuf::copyBuffer("so call me maybe."));
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 7));
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 34, true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
auto readData1 = readDataFromQuicStream(*stream, 100);
auto str = readData1.first->moveToFbString().toStdString();
EXPECT_EQ(
"I just met you and this is crazy. Here's my number, so call me maybe.",
str);
EXPECT_TRUE(readData1.second);
}
TEST_F(QuicStreamFunctionsTest, TestCompleteOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("met you ");
auto buf2 = IOBuf::copyBuffer("this is");
auto buf3 = IOBuf::copyBuffer("I just met you and this is crazy");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 7));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 19));
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 0, true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(
"I just met you and this is crazy",
readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestTotalOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("met you ");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0, true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("met you ", readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestSubsetOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("met you ");
auto buf2 = IOBuf::copyBuffer("you");
auto buf3 = IOBuf::copyBuffer("you ");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 4));
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 4, true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("met you ", readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestLeftOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("met you ");
auto buf2 = IOBuf::copyBuffer("I just met");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 7, true));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("I just met you ", readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestLeftNoOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("met you ");
auto buf2 = IOBuf::copyBuffer("I just");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 7, true));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 2);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("I just", readData1.first->moveToFbString().toStdString());
EXPECT_FALSE(readData1.second);
EXPECT_EQ(stream->readBuffer.size(), 1);
}
TEST_F(QuicStreamFunctionsTest, TestRightOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just");
auto buf2 = IOBuf::copyBuffer(" met you ");
auto buf3 = IOBuf::copyBuffer("you and this is crazy");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 6));
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 11, true));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(
"I just met you and this is crazy",
readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
} // namespace test
TEST_F(QuicStreamFunctionsTest, TestRightNoOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just");
auto buf2 = IOBuf::copyBuffer("met you ");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 7));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 2);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("I just", readData1.first->moveToFbString().toStdString());
EXPECT_FALSE(readData1.second);
EXPECT_EQ(stream->readBuffer.size(), 1);
}
TEST_F(QuicStreamFunctionsTest, TestRightLeftOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just met");
auto buf2 = IOBuf::copyBuffer("met you");
auto buf3 = IOBuf::copyBuffer("you and this is crazy");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 11, true));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 7));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(
"I just met you and this is crazy",
readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestInsertVariations) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("met you ");
auto buf2 = IOBuf::copyBuffer("this is crazy.");
auto buf3 = IOBuf::copyBuffer(" Here's my number");
auto buf4 = IOBuf::copyBuffer("number so call");
auto buf5 = IOBuf::copyBuffer(
"just met you and this is crazy. Here's my number so call");
auto buf6 = IOBuf::copyBuffer(" me maybe");
auto buf7 = IOBuf::copyBuffer("this is crazy. Here's my number so call");
auto buf8 = IOBuf::copyBuffer("I just met you");
buf8->prependChain(IOBuf::copyBuffer(" and this"));
auto buf9 = IOBuf::copyBuffer("Here's my number so call me maybe");
auto buf10 = IOBuf::copyBuffer("I ");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 7));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 19));
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 33));
appendDataToReadBuffer(*stream, StreamBuffer(buf4->clone(), 44));
appendDataToReadBuffer(*stream, StreamBuffer(buf5->clone(), 2));
appendDataToReadBuffer(*stream, StreamBuffer(buf6->clone(), 58));
appendDataToReadBuffer(*stream, StreamBuffer(buf7->clone(), 19));
appendDataToReadBuffer(*stream, StreamBuffer(buf8->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf9->clone(), 34, true));
appendDataToReadBuffer(*stream, StreamBuffer(buf10->clone(), 0));
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
auto readData1 = readDataFromQuicStream(*stream, 100);
auto str = readData1.first->moveToFbString().toStdString();
EXPECT_EQ(
"I just met you and this is crazy. Here's my number so call me maybe",
str);
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestAppendAlreadyReadData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just met you and this is crazy");
auto buf2 = IOBuf::copyBuffer("I just met you and this is");
auto buf3 = IOBuf::copyBuffer("I just met you and this is crazy");
auto buf4 =
IOBuf::copyBuffer("I just met you and this is crazy. Here's my number");
auto buf5 = IOBuf::copyBuffer(
"I just met you and this is crazy. Here's my number so call me");
auto buf6 = IOBuf::copyBuffer(
"I just met you and this is crazy. Here's my number so call me maybe");
auto streamLastMaxOffset = stream->maxOffsetObserved;
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(
"I just met you and this is crazy",
readData1.first->moveToFbString().toStdString());
EXPECT_FALSE(readData1.second);
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
auto readData2 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(readData2.first, nullptr);
EXPECT_FALSE(readData2.second);
EXPECT_TRUE(stream->readBuffer.empty());
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 0));
auto readData3 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(readData3.first, nullptr);
EXPECT_FALSE(readData3.second);
EXPECT_TRUE(stream->readBuffer.empty());
appendDataToReadBuffer(*stream, StreamBuffer(buf4->clone(), 0));
auto readData4 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(
". Here's my number", readData4.first->moveToFbString().toStdString());
EXPECT_FALSE(readData4.second);
EXPECT_TRUE(stream->readBuffer.empty());
appendDataToReadBuffer(*stream, StreamBuffer(buf5->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf6->clone(), 0));
auto readData5 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(
" so call me maybe", readData5.first->moveToFbString().toStdString());
EXPECT_FALSE(readData5.second);
EXPECT_TRUE(stream->readBuffer.empty());
appendDataToReadBuffer(*stream, StreamBuffer(buf6->clone(), 0, true));
auto readData6 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ(readData6.first, nullptr);
EXPECT_TRUE(readData6.second);
EXPECT_EQ(
stream->maxOffsetObserved - streamLastMaxOffset,
conn.flowControlState.sumMaxObservedOffset - connLastMaxOffset);
}
TEST_F(QuicStreamFunctionsTest, TestEmptyEOF) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just");
auto buf2 = IOBuf::copyBuffer("");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 7, true));
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("I just", readData1.first->moveToFbString().toStdString());
EXPECT_FALSE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
auto buf3 = IOBuf::copyBuffer("m");
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 6));
auto readData2 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("m", readData2.first->moveToFbString().toStdString());
EXPECT_TRUE(readData2.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestEmptyEOFOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just");
auto buf2 = IOBuf::copyBuffer("");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0, true));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 6, true));
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("I just", readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestOverlapEOF) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2, true));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
EXPECT_EQ(stream->readBuffer.size(), 1);
auto readData1 = readDataFromQuicStream(*stream, 100);
EXPECT_EQ("I just", readData1.first->moveToFbString().toStdString());
EXPECT_TRUE(readData1.second);
EXPECT_TRUE(stream->readBuffer.empty());
}
TEST_F(QuicStreamFunctionsTest, TestEmptyBuffer) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
}
TEST_F(QuicStreamFunctionsTest, TestInvalidEOFWithAlreadyReadData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just");
auto buf2 = IOBuf::copyBuffer(" met you");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 6));
auto readData1 = readDataFromQuicStream(*stream, 6);
EXPECT_EQ(stream->readBuffer.size(), 1);
EXPECT_THROW(
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0, true)),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, TestInvalidEOFWithSubsetData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I just");
auto buf2 = IOBuf::copyBuffer("I");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
EXPECT_THROW(
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0, true)),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, TestInvalidEOFWithNoOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
EXPECT_THROW(
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0, true)),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, TestInvalidExistingEOFWithCompleteOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I just met");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2, true));
EXPECT_THROW(
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0)),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, TestInvalidExistingEOFNotLastBuffer) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just met");
auto buf2 = IOBuf::copyBuffer("you");
auto buf3 = IOBuf::copyBuffer("I just");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 11));
EXPECT_THROW(
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 0, true)),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, TestInvalidExistingEOFRightOverlap) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just met");
auto buf2 = IOBuf::copyBuffer("met you");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2, true));
EXPECT_THROW(
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 7, true)),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, TestInvalidExistingEOFRightOverlapNotLast) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just met");
auto buf2 = IOBuf::copyBuffer("this is");
auto buf3 = IOBuf::copyBuffer("met you");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 16));
EXPECT_THROW(
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 7, true)),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, SetInvalidMaxStreams) {
conn.streamManager->setMaxLocalBidirectionalStreams(100, true);
conn.streamManager->setMaxLocalUnidirectionalStreams(100, true);
conn.streamManager->setMaxLocalBidirectionalStreams(0);
conn.streamManager->setMaxLocalUnidirectionalStreams(0);
EXPECT_EQ(conn.streamManager->openableLocalBidirectionalStreams(), 100);
EXPECT_EQ(conn.streamManager->openableLocalUnidirectionalStreams(), 100);
EXPECT_THROW(
conn.streamManager->setMaxLocalBidirectionalStreams(kMaxMaxStreams + 1),
QuicTransportException);
EXPECT_THROW(
conn.streamManager->setMaxLocalUnidirectionalStreams(kMaxMaxStreams + 1),
QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, GetOrCreateClientCryptoStream) {
EXPECT_NE(conn.cryptoState, nullptr);
}
TEST_F(QuicServerStreamFunctionsTest, GetOrCreateClientOutOfOrderStream) {
StreamId outOfOrderStream = 100;
StreamId existingStream = 88;
StreamId closedStream = 84;
conn.streamManager->getStream(outOfOrderStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_TRUE(conn.streamManager->streamExists(outOfOrderStream));
// peer stream starts from 0x00
EXPECT_EQ(
conn.streamManager->openBidirectionalPeerStreams().size(),
((outOfOrderStream) / kStreamIncrement) + 1);
conn.streamManager->getStream(existingStream);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_TRUE(conn.streamManager->streamExists(outOfOrderStream));
EXPECT_EQ(
conn.streamManager->openBidirectionalPeerStreams().size(),
((outOfOrderStream) / kStreamIncrement) + 1);
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
closedStream));
EXPECT_EQ(conn.streamManager->getStream(closedStream), nullptr);
}
TEST_F(QuicServerStreamFunctionsTest, GetOrCreateExistingClientStream) {
StreamId outOfOrderStream1 = 100;
StreamId outOfOrderStream2 = 48;
auto stream = conn.streamManager->getStream(outOfOrderStream1);
auto stream2 = conn.streamManager->getStream(outOfOrderStream1);
EXPECT_EQ(stream, stream2);
conn.streamManager->getStream(outOfOrderStream2);
}
TEST_F(QuicStreamFunctionsTest, GetOrCreateExistingServerStream) {
StreamId outOfOrderStream1 = 101;
StreamId outOfOrderStream2 = 49;
auto stream = conn.streamManager->getStream(outOfOrderStream1);
auto stream2 = conn.streamManager->getStream(outOfOrderStream1);
EXPECT_EQ(stream, stream2);
conn.streamManager->getStream(outOfOrderStream2);
}
TEST_F(QuicServerStreamFunctionsTest, GetOrCreateClosedClientStream) {
StreamId outOfOrderStream1 = 100;
StreamId closedStream = 48;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
closedStream));
EXPECT_EQ(conn.streamManager->getStream(closedStream), nullptr);
}
TEST_F(
QuicServerStreamFunctionsTest,
GetOrCreateClientStreamAfterClosingLastStream) {
StreamId outOfOrderStream1 = 96;
StreamId outOfOrderStream2 = 100;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
outOfOrderStream1));
conn.streamManager->getStream(outOfOrderStream2);
EXPECT_EQ(
conn.streamManager->openBidirectionalPeerStreams().size(),
(outOfOrderStream2) / kStreamIncrement);
}
TEST_F(QuicStreamFunctionsTest, GetOrCreateServerStreamAfterClosingLastStream) {
StreamId outOfOrderStream1 = 97;
StreamId outOfOrderStream2 = 101;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
outOfOrderStream1));
conn.streamManager->getStream(outOfOrderStream2);
EXPECT_EQ(
conn.streamManager->openBidirectionalPeerStreams().size(),
(outOfOrderStream2 + 1) / kStreamIncrement);
}
TEST_F(QuicStreamFunctionsTest, GetOrCreateClosedServerStream) {
StreamId outOfOrderStream1 = 97;
StreamId closedStream = 49;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
closedStream));
EXPECT_EQ(conn.streamManager->getStream(closedStream), nullptr);
}
TEST_F(QuicServerStreamFunctionsTest, GetOrCreateServerStreamOnServer) {
StreamId serverStream = 101;
EXPECT_THROW(
conn.streamManager->getStream(serverStream), QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, GetOrCreateClientStreamOnClient) {
StreamId clientStream = 100;
EXPECT_THROW(
conn.streamManager->getStream(clientStream), QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, GetOrCreateNonClientOrServer) {
StreamId streamZero = 0;
EXPECT_THROW(
conn.streamManager->getStream(streamZero), QuicTransportException);
}
TEST_F(QuicServerStreamFunctionsTest, CreateQuicStreamServerOutOfOrder) {
StreamId outOfOrderStream1 = 101;
StreamId outOfOrderStream2 = 49;
conn.streamManager->createStream(outOfOrderStream1).value();
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 26);
conn.streamManager->createStream(outOfOrderStream2).value();
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 26);
}
TEST_F(QuicStreamFunctionsTest, CreateQuicStreamClientOutOfOrder) {
StreamId outOfOrderStream1 = 96;
StreamId outOfOrderStream2 = 48;
conn.streamManager->createStream(outOfOrderStream1);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 25);
conn.streamManager->createStream(outOfOrderStream2).value();
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 25);
}
TEST_F(QuicServerStreamFunctionsTest, CreateClosedServerStream) {
StreamId outOfOrderStream1 = 101;
StreamId outOfOrderStream2 = 49;
conn.streamManager->createStream(outOfOrderStream1);
conn.streamManager->openLocalStreams().erase(std::find(
conn.streamManager->openLocalStreams().begin(),
conn.streamManager->openLocalStreams().end(),
outOfOrderStream2));
EXPECT_FALSE(conn.streamManager->createStream(outOfOrderStream2));
}
TEST_F(QuicStreamFunctionsTest, CreateClosedClientStream) {
StreamId outOfOrderStream1 = 96;
StreamId outOfOrderStream2 = 48;
conn.streamManager->createStream(outOfOrderStream1).value();
conn.streamManager->openLocalStreams().erase(std::find(
conn.streamManager->openLocalStreams().begin(),
conn.streamManager->openLocalStreams().end(),
outOfOrderStream2));
EXPECT_FALSE(conn.streamManager->createStream(outOfOrderStream2));
}
TEST_F(QuicStreamFunctionsTest, CreateInvalidServerStreamOnClient) {
StreamId serverStream = 0x09;
EXPECT_THROW(
conn.streamManager->createStream(serverStream), QuicTransportException);
}
TEST_F(QuicServerStreamFunctionsTest, CreateInvalidClientStreamOnServer) {
StreamId clientStream = 0x04;
EXPECT_THROW(
conn.streamManager->createStream(clientStream), QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, CreateAlreadyExistingStream) {
StreamId stream = 0x08;
conn.streamManager->createStream(stream).value();
EXPECT_THROW(
conn.streamManager->createStream(stream), QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, IsClientStream) {
EXPECT_TRUE(isClientStream(0));
EXPECT_TRUE(isClientStream(0x04));
EXPECT_TRUE(isClientStream(104));
EXPECT_TRUE(isClientStream(0x08));
EXPECT_FALSE(isClientStream(0x01));
EXPECT_FALSE(isClientStream(0x07));
EXPECT_FALSE(isClientStream(0x11));
}
TEST_F(QuicStreamFunctionsTest, IsServerStream) {
EXPECT_TRUE(isServerStream(0x05));
EXPECT_TRUE(isServerStream(105));
EXPECT_TRUE(isServerStream(0x25));
EXPECT_FALSE(isServerStream(0x02));
EXPECT_FALSE(isServerStream(0x04));
EXPECT_FALSE(isServerStream(0));
}
TEST_F(QuicStreamFunctionsTest, IsUnidirectionalStream) {
EXPECT_TRUE(isUnidirectionalStream(0x02));
EXPECT_TRUE(isUnidirectionalStream(0x03));
EXPECT_TRUE(isUnidirectionalStream(0xff));
EXPECT_FALSE(isUnidirectionalStream(0x01));
EXPECT_FALSE(isUnidirectionalStream(0xf0));
EXPECT_FALSE(isUnidirectionalStream(0xf1));
}
TEST_F(QuicStreamFunctionsTest, IsBidirectionalStream) {
EXPECT_TRUE(isBidirectionalStream(0x01));
EXPECT_TRUE(isBidirectionalStream(0xf0));
EXPECT_TRUE(isBidirectionalStream(0xf1));
EXPECT_FALSE(isBidirectionalStream(0x02));
EXPECT_FALSE(isBidirectionalStream(0x03));
EXPECT_FALSE(isBidirectionalStream(0xff));
}
TEST_F(QuicStreamFunctionsTest, IsSendingStream) {
QuicClientConnectionState clientState;
QuicServerConnectionState serverState;
QuicNodeType nodeType;
StreamId id;
QuicStreamState biClientStream(0, clientState);
nodeType = biClientStream.conn.nodeType;
id = biClientStream.id;
EXPECT_FALSE(isSendingStream(nodeType, id));
QuicStreamState biServerStream(0, serverState);
nodeType = biServerStream.conn.nodeType;
id = biServerStream.id;
EXPECT_FALSE(isSendingStream(nodeType, id));
QuicStreamState uniClientSendingStream(0x2, clientState);
nodeType = uniClientSendingStream.conn.nodeType;
id = uniClientSendingStream.id;
EXPECT_TRUE(isSendingStream(nodeType, id));
QuicStreamState uniServerSendingStream(0x3, serverState);
nodeType = uniServerSendingStream.conn.nodeType;
id = uniServerSendingStream.id;
EXPECT_TRUE(isSendingStream(nodeType, id));
}
TEST_F(QuicStreamFunctionsTest, IsReceivingStream) {
QuicClientConnectionState clientState;
QuicServerConnectionState serverState;
QuicNodeType nodeType;
StreamId id;
QuicStreamState biClientStream(0, clientState);
nodeType = biClientStream.conn.nodeType;
id = biClientStream.id;
EXPECT_FALSE(isReceivingStream(nodeType, id));
QuicStreamState biServerStream(0, serverState);
nodeType = biServerStream.conn.nodeType;
id = biServerStream.id;
EXPECT_FALSE(isReceivingStream(nodeType, id));
QuicStreamState uniClientReceivingStream(0x3, clientState);
nodeType = uniClientReceivingStream.conn.nodeType;
id = uniClientReceivingStream.id;
EXPECT_TRUE(isReceivingStream(nodeType, id));
QuicStreamState uniServerReceivingStream(0x2, serverState);
nodeType = uniServerReceivingStream.conn.nodeType;
id = uniServerReceivingStream.id;
EXPECT_TRUE(isReceivingStream(nodeType, id));
}
TEST_F(QuicStreamFunctionsTest, HasReadableDataNoData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
EXPECT_FALSE(stream->hasReadableData());
}
TEST_F(QuicStreamFunctionsTest, HasReadableDataNoDataInBuf) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
EXPECT_FALSE(stream->hasReadableData());
}
TEST_F(QuicStreamFunctionsTest, HasReadableDataEofInBuf) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 1, true));
EXPECT_FALSE(stream->hasReadableData());
appendDataToReadBuffer(*stream, StreamBuffer(IOBuf::copyBuffer("1"), 0));
EXPECT_TRUE(stream->hasReadableData());
auto read1 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read1.second);
EXPECT_FALSE(stream->hasReadableData());
auto read2 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read2.second);
}
TEST_F(QuicStreamFunctionsTest, HasReadableDataEofInEmptyBuf) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 0, true));
EXPECT_TRUE(stream->hasReadableData());
auto read1 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read1.second);
EXPECT_FALSE(stream->hasReadableData());
auto read2 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read2.second);
}
TEST_F(QuicStreamFunctionsTest, HasReadableDataOnlyEof) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 0, true));
EXPECT_TRUE(stream->hasReadableData());
}
TEST_F(QuicStreamFunctionsTest, HasReadableData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I ");
auto buf2 = IOBuf::copyBuffer("met");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 7));
EXPECT_TRUE(stream->hasReadableData());
readDataFromQuicStream(*stream);
EXPECT_FALSE(stream->hasReadableData());
auto buf3 = IOBuf::copyBuffer("just ");
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 2));
EXPECT_TRUE(stream->hasReadableData());
readDataFromQuicStream(*stream, 5);
EXPECT_TRUE(stream->hasReadableData());
readDataFromQuicStream(*stream);
EXPECT_FALSE(stream->hasReadableData());
}
TEST_F(QuicStreamFunctionsTest, HasPeekableDataGappedData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
EXPECT_TRUE(stream->hasPeekableData());
}
TEST_F(QuicStreamFunctionsTest, HasPeekableDataNoDataInBuf) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
EXPECT_FALSE(stream->hasPeekableData());
}
TEST_F(QuicStreamFunctionsTest, HasPeekableDataEofInBuf) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 1, true));
EXPECT_FALSE(stream->hasPeekableData());
appendDataToReadBuffer(*stream, StreamBuffer(IOBuf::copyBuffer("1"), 0));
EXPECT_TRUE(stream->hasPeekableData());
auto read1 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read1.second);
EXPECT_FALSE(stream->hasPeekableData());
auto read2 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read2.second);
}
TEST_F(QuicStreamFunctionsTest, HasPeekableDataEofInEmptyBuf) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 0, true));
EXPECT_FALSE(stream->hasPeekableData());
auto read1 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read1.second);
EXPECT_FALSE(stream->hasPeekableData());
auto read2 = readDataFromQuicStream(*stream, 1);
EXPECT_TRUE(read2.second);
}
TEST_F(QuicStreamFunctionsTest, HasPeekableDataOnlyEof) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 0, true));
EXPECT_FALSE(stream->hasPeekableData());
}
TEST_F(QuicStreamFunctionsTest, HasPeekableData) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("I ");
auto buf2 = IOBuf::copyBuffer("met");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 7));
EXPECT_TRUE(stream->hasPeekableData());
readDataFromQuicStream(*stream);
EXPECT_TRUE(stream->hasPeekableData());
auto buf3 = IOBuf::copyBuffer("just ");
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 2));
EXPECT_TRUE(stream->hasPeekableData());
readDataFromQuicStream(*stream, 5);
EXPECT_TRUE(stream->hasPeekableData());
readDataFromQuicStream(*stream);
EXPECT_FALSE(stream->hasPeekableData());
}
TEST_F(QuicStreamFunctionsTest, UpdatesLastHolbTime) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
// Should not be HOL blocked before data has arrived
EXPECT_FALSE(stream->lastHolbTime);
readDataFromQuicStream(*stream);
// Should be HOL blocked
EXPECT_TRUE(stream->lastHolbTime);
}
TEST_F(QuicStreamFunctionsTest, HolbTimingUpdateReadingListIdempotentWrtHolb) {
// test that calling uRL in succession (without new data or readsd)
// does not affect the HOLB state
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
auto buf3 = IOBuf::copyBuffer("you");
auto buf4 = IOBuf::copyBuffer(" met ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
// Should not be HOL blocked until the readable list has been updated
EXPECT_FALSE(stream->lastHolbTime);
// uRL 0.0 - expected state transition:
// !HOLB => HOLB in progress
conn.streamManager->updateReadableStreams(*stream);
// HOLB state must be detected after the first readable list update
auto lastHolbTimeMark = stream->lastHolbTime;
EXPECT_TRUE(lastHolbTimeMark);
// No total holb time should be recorded
EXPECT_EQ(0us, stream->totalHolbTime);
EXPECT_EQ(1, stream->holbCount);
// uRL 0.1 - expected state transition: none
conn.streamManager->updateReadableStreams(*stream);
EXPECT_EQ(lastHolbTimeMark, stream->lastHolbTime);
EXPECT_EQ(0us, stream->totalHolbTime);
EXPECT_EQ(1, stream->holbCount);
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
// uRL 1.0 - expected state transition:
// HOLB in progress -> !HOLB && holbCount == 1
conn.streamManager->updateReadableStreams(*stream);
auto totalHolbTimeMark = stream->totalHolbTime;
// HOLB state must be cleared by buf2, and total time should be available
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
// uRL 1.1 - expected state transition: none
conn.streamManager->updateReadableStreams(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(totalHolbTimeMark, stream->totalHolbTime);
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 11));
// uRL 2.0 - expected state transition:
// !HOLB && holbCount == 1
// => !HOLB && totalTime == totalTimeMark
// NOTE: the stream is not HOLB since the reading cursor is not
// at the hole
conn.streamManager->updateReadableStreams(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(totalHolbTimeMark, stream->totalHolbTime);
EXPECT_EQ(1, stream->holbCount);
// uRL 2.1 - expected state transition: none
conn.streamManager->updateReadableStreams(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(totalHolbTimeMark, stream->totalHolbTime);
// uRL 3.0 - expected state transition:
// !HOLB && totalTime == totalTimeMark
// => HOLB && holbCount == 2
// && totalHolbTime == totalTimeMark
readDataFromQuicStream(*stream);
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(2, stream->holbCount);
EXPECT_EQ(totalHolbTimeMark, stream->totalHolbTime);
auto lastHolbTimeMark2 = stream->lastHolbTime;
// uRL 3.1 - expected state transition: none
conn.streamManager->updateReadableStreams(*stream);
EXPECT_EQ(lastHolbTimeMark2, stream->lastHolbTime);
EXPECT_EQ(totalHolbTimeMark, stream->totalHolbTime);
EXPECT_EQ(2, stream->holbCount);
// uRL 4.0 - add the rest of the data to the stream.
// HOLB && holbCount == 2
// && totalTime == totalTimeMark
// => !HOLB && holbCount == 2
appendDataToReadBuffer(*stream, StreamBuffer(buf4->clone(), 6));
conn.streamManager->updateReadableStreams(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(2, stream->holbCount);
// uRL 4.1 - read the entire stream - expected state transition:
// !HOLB && holbCount == 2
// => !HOLB && holbCount == 2
readDataFromQuicStream(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(2, stream->holbCount);
// uRL 4.1 - expected state change: none
conn.streamManager->updateReadableStreams(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(2, stream->holbCount);
}
TEST_F(QuicStreamFunctionsTest, HolbTimingFirstBufferHOLBlocked) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
// Should not be HOL blocked until the readable list has been updated
EXPECT_FALSE(stream->lastHolbTime);
conn.streamManager->updateReadableStreams(*stream);
// HOLB state must be detected after the first readable list update
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
EXPECT_EQ(0us, stream->totalHolbTime);
auto lastHolbTimeMark = stream->lastHolbTime;
readDataFromQuicStream(*stream);
// Read data should fail since there is no data available at
// the reading cursor
EXPECT_EQ(lastHolbTimeMark, stream->lastHolbTime);
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
conn.streamManager->updateReadableStreams(*stream);
// HOLB state must be cleared by buf2, and total time should be available
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
}
TEST_F(QuicStreamFunctionsTest, HolbTimingReadingEntireStream) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
conn.streamManager->updateReadableStreams(*stream);
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
conn.streamManager->updateReadableStreams(*stream);
// HOLB state must be cleared by buf2, and total time should be available
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
// Consume the entire stream. This should not change the holb status
readDataFromQuicStream(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
}
TEST_F(QuicStreamFunctionsTest, HolbTimingLockstepScenario) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
auto buf3 = IOBuf::copyBuffer("met you ");
auto buf4 = IOBuf::copyBuffer("and this is crazy");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
// Should not be HOL blocked before data has arrived
EXPECT_FALSE(stream->lastHolbTime);
readDataFromQuicStream(*stream);
// Should be HOL blocked now
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
// At this point, the stream has not been unblocked even once,
// hence the total holb time is expected to be zero
EXPECT_EQ(0us, stream->totalHolbTime);
// Data has arrived
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
appendDataToReadBuffer(*stream, StreamBuffer(buf4->clone(), 15));
// Update readable list has not been called yet, hence
// the total holb time has not been set yet
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
// Update readable list
conn.streamManager->updateReadableStreams(*stream);
// The new data should have unblocked the HOLB
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
auto snapshotHolbTime = stream->totalHolbTime;
// Consume all available data from the stream
readDataFromQuicStream(*stream);
// Should be HOL blocked at missing buf3.
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(2, stream->holbCount);
// The total HOLB time shouldn't have changed since the last update
EXPECT_EQ(snapshotHolbTime, stream->totalHolbTime);
appendDataToReadBuffer(*stream, StreamBuffer(buf3->clone(), 6));
conn.streamManager->updateReadableStreams(*stream);
// Should be not HOLB
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(2, stream->holbCount);
}
TEST_F(QuicStreamFunctionsTest, HolbTimingReadDataCallsUpdateRL) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto buf1 = IOBuf::copyBuffer("just");
auto buf2 = IOBuf::copyBuffer("I ");
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 2));
readDataFromQuicStream(*stream);
// Should be HOL blocked
EXPECT_TRUE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
appendDataToReadBuffer(*stream, StreamBuffer(buf2->clone(), 0));
readDataFromQuicStream(*stream);
EXPECT_FALSE(stream->lastHolbTime);
EXPECT_EQ(1, stream->holbCount);
}
TEST_F(QuicStreamFunctionsTest, RemovedClosedState) {
auto stream = conn.streamManager->createNextBidirectionalStream().value();
auto streamId = stream->id;
conn.streamManager->readableStreams().emplace(streamId);
conn.streamManager->peekableStreams().emplace(streamId);
conn.streamManager->addWritable(streamId);
conn.streamManager->queueBlocked(streamId, 0);
conn.streamManager->addDeliverable(streamId);
conn.streamManager->addLoss(streamId);
conn.streamManager->queueWindowUpdate(streamId);
conn.streamManager->addStopSending(
streamId, GenericApplicationErrorCode::UNKNOWN);
stream->send.state = StreamSendStates::Closed{};
stream->recv.state = StreamReceiveStates::Closed{};
conn.streamManager->removeClosedStream(streamId);
EXPECT_FALSE(conn.streamManager->streamExists(streamId));
EXPECT_TRUE(conn.streamManager->readableStreams().empty());
EXPECT_TRUE(conn.streamManager->peekableStreams().empty());
EXPECT_FALSE(conn.streamManager->writableContains(streamId));
EXPECT_FALSE(conn.streamManager->hasBlocked());
EXPECT_FALSE(conn.streamManager->deliverableContains(streamId));
EXPECT_FALSE(conn.streamManager->hasLoss());
EXPECT_FALSE(conn.streamManager->pendingWindowUpdate(streamId));
EXPECT_TRUE(conn.streamManager->stopSendingStreams().empty());
}
TEST_F(QuicServerStreamFunctionsTest, ServerGetClientQuicStream) {
StreamId clientStream = 0x10;
std::deque<StreamId> newStreams = {0x0, 0x4, 0x8, 0xc, 0x10};
EXPECT_EQ(conn.streamManager->getStream(clientStream)->id, clientStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->newPeerStreams(), newStreams);
StreamId clientStream2 = 0x4;
EXPECT_EQ(conn.streamManager->getStream(clientStream2)->id, clientStream2);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->newPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->newPeerStreams(), newStreams);
StreamId clientStream3 = 0x6;
newStreams = {0x0, 0x2, 0x4, 0x6, 0x8, 0xc, 0x10};
EXPECT_EQ(conn.streamManager->getStream(clientStream3)->id, clientStream3);
EXPECT_EQ(conn.streamManager->streamCount(), 3);
EXPECT_EQ(conn.streamManager->openUnidirectionalPeerStreams().size(), 2);
std::sort(
conn.streamManager->newPeerStreams().begin(),
conn.streamManager->newPeerStreams().end());
EXPECT_EQ(conn.streamManager->newPeerStreams(), newStreams);
}
TEST_F(QuicServerStreamFunctionsTest, ServerGetServerQuicStream) {
StreamId serverStream = 0x09;
conn.streamManager->createStream(serverStream).value();
EXPECT_EQ(conn.streamManager->getStream(serverStream)->id, serverStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 3);
StreamId serverStream2 = 0x05;
EXPECT_EQ(conn.streamManager->getStream(serverStream2)->id, serverStream2);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 3);
StreamId serverStream3 = 0x0D;
EXPECT_THROW(
conn.streamManager->getStream(serverStream3), QuicTransportException);
}
TEST_F(QuicServerStreamFunctionsTest, ServerGetBothDirections) {
StreamId serverBiStream = 0x09;
conn.streamManager->createStream(serverBiStream).value();
EXPECT_EQ(conn.streamManager->getStream(serverBiStream)->id, serverBiStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 3);
StreamId serverUniStream = 0x0B;
conn.streamManager->createStream(serverUniStream).value();
EXPECT_EQ(
conn.streamManager->getStream(serverUniStream)->id, serverUniStream);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 6);
}
TEST_F(QuicServerStreamFunctionsTest, ServerGetCloseBothDirections) {
StreamId serverBiStream = 0x09;
conn.streamManager->createStream(serverBiStream).value();
EXPECT_EQ(conn.streamManager->getStream(serverBiStream)->id, serverBiStream);
StreamId serverUniStream = 0x0B;
auto stream = conn.streamManager->createStream(serverUniStream).value();
stream->send.state = StreamSendStates::Closed{};
conn.streamManager->removeClosedStream(serverUniStream);
EXPECT_TRUE(
conn.streamManager->getStream(serverUniStream - kStreamIncrement));
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_FALSE(conn.streamManager->streamExists(serverUniStream));
EXPECT_TRUE(conn.streamManager->streamExists(serverBiStream));
EXPECT_TRUE(
conn.streamManager->streamExists(serverUniStream - kStreamIncrement));
EXPECT_TRUE(
conn.streamManager->streamExists(serverBiStream - kStreamIncrement));
EXPECT_FALSE(
conn.streamManager->streamExists(serverUniStream + kStreamIncrement));
EXPECT_FALSE(
conn.streamManager->streamExists(serverBiStream + kStreamIncrement));
}
TEST_F(QuicServerStreamFunctionsTest, ServerGetServerUnidirectionalQuicStream) {
StreamId serverStream = 0x0F;
conn.streamManager->createStream(serverStream).value();
EXPECT_EQ(conn.streamManager->getStream(serverStream)->id, serverStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 4);
StreamId serverStream2 = 0x0B;
EXPECT_EQ(conn.streamManager->getStream(serverStream2)->id, serverStream2);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 4);
StreamId serverStream3 = 0x1F;
EXPECT_THROW(
conn.streamManager->getStream(serverStream3), QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, ClientGetServerQuicStream) {
StreamId serverStream = 0x09;
EXPECT_EQ(conn.streamManager->getStream(serverStream)->id, serverStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 3);
StreamId serverStream2 = 0x05;
EXPECT_EQ(conn.streamManager->getStream(serverStream2)->id, serverStream2);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 3);
}
TEST_F(QuicStreamFunctionsTest, ClientGetClientQuicStream) {
StreamId clientStream = 0x0C;
conn.streamManager->createStream(clientStream).value();
EXPECT_EQ(conn.streamManager->getStream(clientStream)->id, clientStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 4);
StreamId clientStream2 = 0x08;
EXPECT_EQ(conn.streamManager->getStream(clientStream2)->id, clientStream2);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openLocalStreams().size(), 4);
StreamId clientStream3 = 0x10;
EXPECT_THROW(
conn.streamManager->getStream(clientStream3), QuicTransportException);
}
TEST_F(QuicStreamFunctionsTest, StreamExists) {
StreamId localStream = 12;
StreamId peerStream = 13;
StreamId localAutoOpened = 8;
StreamId peerAutoOpened = 5;
StreamId peerAutoOpened2 = 9;
StreamId notOpenedLocal = 16;
StreamId notOpenedPeer = 17;
conn.streamManager->createStream(localStream).value();
EXPECT_TRUE(conn.streamManager->streamExists(localStream));
EXPECT_TRUE(conn.streamManager->streamExists(localAutoOpened));
EXPECT_FALSE(conn.streamManager->streamExists(notOpenedLocal));
EXPECT_FALSE(conn.streamManager->streamExists(notOpenedPeer));
EXPECT_FALSE(conn.streamManager->streamExists(peerStream));
EXPECT_FALSE(conn.streamManager->streamExists(peerAutoOpened));
conn.streamManager->getStream(peerStream)->send.state =
StreamSendStates::Closed{};
conn.streamManager->getStream(peerStream)->recv.state =
StreamReceiveStates::Closed{};
EXPECT_TRUE(conn.streamManager->streamExists(localStream));
EXPECT_TRUE(conn.streamManager->streamExists(localAutoOpened));
EXPECT_FALSE(conn.streamManager->streamExists(notOpenedLocal));
EXPECT_FALSE(conn.streamManager->streamExists(notOpenedPeer));
EXPECT_TRUE(conn.streamManager->streamExists(peerStream));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened));
auto it = std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
peerAutoOpened);
conn.streamManager->openBidirectionalPeerStreams().erase(it);
conn.streamManager->removeClosedStream(peerStream);
EXPECT_FALSE(conn.streamManager->streamExists(peerAutoOpened));
EXPECT_FALSE(conn.streamManager->streamExists(peerStream));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened2));
}
TEST_F(QuicStreamFunctionsTest, StreamLimitUpdates) {
StreamId peerStream = 13;
StreamId peerAutoOpened = 5;
StreamId peerAutoOpened2 = 9;
StreamId notOpenedPeer = 17;
conn.streamManager->setStreamLimitWindowingFraction(
conn.transportSettings.advertisedInitialMaxStreamsBidi);
conn.streamManager->getStream(peerStream)->send.state =
StreamSendStates::Closed{};
conn.streamManager->getStream(peerStream)->recv.state =
StreamReceiveStates::Closed{};
EXPECT_FALSE(conn.streamManager->streamExists(notOpenedPeer));
EXPECT_TRUE(conn.streamManager->streamExists(peerStream));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened));
conn.streamManager->removeClosedStream(peerStream);
EXPECT_FALSE(conn.streamManager->streamExists(peerStream));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened2));
auto update = conn.streamManager->remoteBidirectionalStreamLimitUpdate();
ASSERT_TRUE(update);
EXPECT_EQ(
update.value(),
conn.transportSettings.advertisedInitialMaxStreamsBidi + 1);
}
TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAcked) {
StreamId id = 3;
QuicStreamState stream(id, conn);
stream.finalWriteOffset = 1;
stream.currentWriteOffset = 2;
EXPECT_TRUE(allBytesTillFinAcked(stream));
}
TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAckedFinOnly) {
StreamId id = 3;
QuicStreamState stream(id, conn);
stream.finalWriteOffset = 0;
stream.currentWriteOffset = 1;
EXPECT_TRUE(allBytesTillFinAcked(stream));
}
TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAckedNewStream) {
StreamId id = 3;
QuicStreamState stream(id, conn);
EXPECT_FALSE(allBytesTillFinAcked(stream));
}
TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAckedStillLost) {
StreamId id = 3;
QuicStreamState stream(id, conn);
stream.finalWriteOffset = 20;
stream.currentWriteOffset = 21;
stream.lossBuffer.emplace_back(IOBuf::create(10), 10, false);
EXPECT_FALSE(allBytesTillFinAcked(stream));
}
TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAckedStillRetransmitting) {
StreamId id = 3;
QuicStreamState stream(id, conn);
stream.finalWriteOffset = 12;
stream.retransmissionBuffer.emplace_back(IOBuf::create(10), 10, false);
EXPECT_FALSE(allBytesTillFinAcked(stream));
}
TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAckedStillWriting) {
StreamId id = 3;
QuicStreamState stream(id, conn);
stream.finalWriteOffset = 10;
auto buf = IOBuf::create(10);
buf->append(10);
stream.writeBuffer.append(std::move(buf));
EXPECT_FALSE(allBytesTillFinAcked(stream));
}
TEST_F(QuicServerStreamFunctionsTest, TestAppendPendingStreamResetAllData) {
StreamId id = 3;
QuicStreamState stream(id, conn);
auto data = IOBuf::copyBuffer("this is data");
auto len = data->computeChainDataLength();
writeDataToQuicStream(stream, std::move(data), true);
// Simulate all bytes and EOF written on network.
stream.currentWriteOffset = len + 1;
stream.retransmissionBuffer.clear();
appendPendingStreamReset(conn, stream, GenericApplicationErrorCode::UNKNOWN);
auto rst = conn.pendingEvents.resets.at(id);
EXPECT_EQ(rst.errorCode, GenericApplicationErrorCode::UNKNOWN);
EXPECT_EQ(rst.offset, len);
}
TEST_F(
QuicServerStreamFunctionsTest,
TestAppendPendingStreamResetAllDataWithoutFin) {
StreamId id = 3;
QuicStreamState stream(id, conn);
auto data = IOBuf::copyBuffer("this is data");
auto len = data->computeChainDataLength();
writeDataToQuicStream(stream, std::move(data), true);
// Simulate all bytes except EOF written on network.
stream.currentWriteOffset = len;
stream.retransmissionBuffer.clear();
appendPendingStreamReset(conn, stream, GenericApplicationErrorCode::UNKNOWN);
auto rst = conn.pendingEvents.resets.at(id);
EXPECT_EQ(rst.errorCode, GenericApplicationErrorCode::UNKNOWN);
EXPECT_EQ(rst.offset, len);
}
TEST_F(QuicStreamFunctionsTest, LargestWriteOffsetSeenFIN) {
QuicStreamState stream(3, conn);
stream.finalWriteOffset = 100;
EXPECT_EQ(100, getLargestWriteOffsetSeen(stream));
}
TEST_F(QuicStreamFunctionsTest, LargestWriteOffsetSeenNoFIN) {
QuicStreamState stream(3, conn);
stream.currentWriteOffset = 100;
stream.writeBuffer.append(buildRandomInputData(20));
EXPECT_EQ(120, getLargestWriteOffsetSeen(stream));
}
TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliver) {
QuicStreamState stream(3, conn);
stream.currentWriteOffset = 100;
EXPECT_EQ(100, getStreamNextOffsetToDeliver(stream));
}
TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliverRetxBuffer) {
QuicStreamState stream(3, conn);
stream.currentWriteOffset = 100;
stream.retransmissionBuffer.emplace_back(buildRandomInputData(10), 50);
EXPECT_EQ(50, getStreamNextOffsetToDeliver(stream));
}
TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliverRetxAndLossBuffer) {
QuicStreamState stream(3, conn);
stream.currentWriteOffset = 100;
stream.lossBuffer.emplace_back(buildRandomInputData(10), 30);
stream.retransmissionBuffer.emplace_back(buildRandomInputData(10), 50);
EXPECT_EQ(30, getStreamNextOffsetToDeliver(stream));
}
TEST_F(QuicStreamFunctionsTest, LossBufferEmpty) {
StreamId id = 4;
QuicStreamState stream(id, conn);
conn.streamManager->addLoss(id);
conn.streamManager->updateLossStreams(stream);
EXPECT_FALSE(conn.streamManager->hasLoss());
}
TEST_F(QuicStreamFunctionsTest, LossBufferEmptyNoChange) {
StreamId id = 4;
QuicStreamState stream(id, conn);
conn.streamManager->updateLossStreams(stream);
EXPECT_FALSE(conn.streamManager->hasLoss());
}
TEST_F(QuicStreamFunctionsTest, LossBufferHasData) {
StreamId id = 4;
QuicStreamState stream(id, conn);
stream.lossBuffer.emplace_back(IOBuf::create(10), 10, false);
conn.streamManager->updateLossStreams(stream);
EXPECT_TRUE(conn.streamManager->hasLoss());
}
TEST_F(QuicStreamFunctionsTest, LossBufferStillHasData) {
StreamId id = 4;
QuicStreamState stream(id, conn);
conn.streamManager->addLoss(id);
stream.lossBuffer.emplace_back(IOBuf::create(10), 10, false);
conn.streamManager->updateLossStreams(stream);
EXPECT_TRUE(conn.streamManager->hasLoss());
}
TEST_F(QuicStreamFunctionsTest, WritableList) {
StreamId id = 3;
QuicStreamState stream(id, conn);
stream.currentWriteOffset = 100;
stream.flowControlState.peerAdvertisedMaxOffset = 200;
conn.streamManager->updateWritableStreams(stream);
EXPECT_FALSE(stream.conn.streamManager->writableContains(id));
auto buf = IOBuf::create(100);
buf->append(100);
writeDataToQuicStream(stream, std::move(buf), false);
conn.streamManager->updateWritableStreams(stream);
EXPECT_TRUE(stream.conn.streamManager->writableContains(id));
// Flow control
stream.flowControlState.peerAdvertisedMaxOffset = stream.currentWriteOffset;
conn.streamManager->updateWritableStreams(stream);
EXPECT_FALSE(stream.conn.streamManager->writableContains(id));
// Fin
writeDataToQuicStream(stream, nullptr, true);
stream.writeBuffer.clear();
stream.currentWriteOffset += 100;
stream.flowControlState.peerAdvertisedMaxOffset = stream.currentWriteOffset;
conn.streamManager->updateWritableStreams(stream);
EXPECT_TRUE(stream.conn.streamManager->writableContains(id));
// After Fin
stream.currentWriteOffset++;
conn.streamManager->updateWritableStreams(stream);
EXPECT_FALSE(stream.conn.streamManager->writableContains(id));
}
TEST_F(QuicStreamFunctionsTest, AckCryptoStream) {
auto chlo = IOBuf::copyBuffer("CHLO");
conn.cryptoState->handshakeStream.retransmissionBuffer.emplace_back(
StreamBuffer(chlo->clone(), 0));
processCryptoStreamAck(conn.cryptoState->handshakeStream, 0, chlo->length());
EXPECT_EQ(conn.cryptoState->handshakeStream.retransmissionBuffer.size(), 0);
}
TEST_F(QuicStreamFunctionsTest, AckCryptoStreamOffsetLengthMismatch) {
auto chlo = IOBuf::copyBuffer("CHLO");
auto& cryptoStream = conn.cryptoState->handshakeStream;
cryptoStream.retransmissionBuffer.emplace_back(
StreamBuffer(chlo->clone(), 0));
processCryptoStreamAck(cryptoStream, 1, chlo->length());
EXPECT_EQ(cryptoStream.retransmissionBuffer.size(), 1);
processCryptoStreamAck(cryptoStream, 0, chlo->length() - 2);
EXPECT_EQ(cryptoStream.retransmissionBuffer.size(), 1);
processCryptoStreamAck(cryptoStream, 20, chlo->length());
EXPECT_EQ(cryptoStream.retransmissionBuffer.size(), 1);
}
TEST_F(QuicStreamFunctionsTest, AckFrameMatchesRetransmitBufferFullyReliable) {
conn.partialReliabilityEnabled = false;
StreamId id = 4;
QuicStreamState stream(id, conn);
auto data = IOBuf::copyBuffer("Hello");
auto buf = StreamBuffer(data->clone(), 0, true);
WriteStreamFrame ackFrame(
id /* streamId */,
0 /* offset */,
data->length() /* length */,
true /* eof */);
EXPECT_TRUE(ackFrameMatchesRetransmitBuffer(stream, ackFrame, buf));
}
TEST_F(
QuicStreamFunctionsTest,
AckFrameMatchesRetransmitBufferPartiallyReliableNoSkip) {
conn.partialReliabilityEnabled = true;
StreamId id = 4;
QuicStreamState stream(id, conn);
auto data = IOBuf::copyBuffer("Hello");
auto buf = StreamBuffer(data->clone(), 0, true);
WriteStreamFrame ackFrame(
id /* streamId */,
0 /* offset */,
data->length() /* length */,
true /* eof */);
EXPECT_TRUE(ackFrameMatchesRetransmitBuffer(stream, ackFrame, buf));
}
TEST_F(
QuicStreamFunctionsTest,
AckFrameMatchesRetransmitBufferPartiallyReliableFullBufSkipped) {
conn.partialReliabilityEnabled = true;
StreamId id = 4;
QuicStreamState stream(id, conn);
auto data = IOBuf::copyBuffer("Hello");
auto buf = StreamBuffer(data->clone(), 42, true);
WriteStreamFrame ackFrame(
id /* streamId */,
0 /* offset */,
data->length() /* length */,
true /* eof */);
EXPECT_FALSE(ackFrameMatchesRetransmitBuffer(stream, ackFrame, buf));
}
TEST_F(
QuicStreamFunctionsTest,
AckFrameMatchesRetransmitBufferPartiallyReliableHalfBufSkipped) {
conn.partialReliabilityEnabled = true;
StreamId id = 4;
QuicStreamState stream(id, conn);
auto data = IOBuf::copyBuffer("llo");
auto buf = StreamBuffer(data->clone(), 2, true);
WriteStreamFrame ackFrame(
id /* streamId */, 0 /* offset */, 5 /* length */, true /* eof */);
EXPECT_TRUE(ackFrameMatchesRetransmitBuffer(stream, ackFrame, buf));
}
} // namespace test
} // namespace quic