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
2011 lines
76 KiB
C++
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
|