mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-09 20:42:44 +03:00
Summary: All the functionality is a duplicated from `updateWritableStreams`. Btw the only callsites were inside UT. Reviewed By: kvtsoy Differential Revision: D68351972 fbshipit-source-id: 7e17f38ffcffecea23a64f5d2d5c1dec7a8c43f5
2428 lines
91 KiB
C++
2428 lines
91 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and 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/QuicStreamUtilities.h>
|
|
|
|
#include <quic/state/QuicStreamFunctions.h>
|
|
#include <quic/state/QuicStreamUtilities.h>
|
|
|
|
#include <quic/client/state/ClientStateMachine.h>
|
|
#include <quic/common/test/TestUtils.h>
|
|
#include <quic/fizz/client/handshake/FizzClientQuicHandshakeContext.h>
|
|
#include <quic/fizz/server/handshake/FizzServerQuicHandshakeContext.h>
|
|
#include <quic/server/state/ServerStateMachine.h>
|
|
|
|
using namespace folly;
|
|
using namespace testing;
|
|
|
|
namespace quic {
|
|
namespace test {
|
|
|
|
constexpr uint8_t kStreamIncrement = 0x04;
|
|
|
|
using PeekIterator = CircularDeque<StreamBuffer>::const_iterator;
|
|
|
|
class QuicStreamFunctionsTest : public Test {
|
|
public:
|
|
QuicStreamFunctionsTest()
|
|
: conn(FizzClientQuicHandshakeContext::Builder().build()) {}
|
|
|
|
void SetUp() override {
|
|
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiLocal =
|
|
kDefaultStreamFlowControlWindow;
|
|
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiRemote =
|
|
kDefaultStreamFlowControlWindow;
|
|
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetUni =
|
|
kDefaultStreamFlowControlWindow;
|
|
conn.flowControlState.peerAdvertisedMaxOffset =
|
|
kDefaultConnectionFlowControlWindow;
|
|
conn.streamManager->setMaxLocalBidirectionalStreams(
|
|
kDefaultMaxStreamsBidirectional);
|
|
conn.streamManager->setMaxLocalUnidirectionalStreams(
|
|
kDefaultMaxStreamsUnidirectional);
|
|
maybeSetReadCoalescing();
|
|
}
|
|
|
|
virtual void maybeSetReadCoalescing() {}
|
|
|
|
QuicClientConnectionState conn;
|
|
};
|
|
|
|
class QuicServerStreamFunctionsTest : public Test {
|
|
public:
|
|
QuicServerStreamFunctionsTest()
|
|
: conn(FizzServerQuicHandshakeContext::Builder().build()) {}
|
|
|
|
void SetUp() override {
|
|
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiLocal =
|
|
kDefaultStreamFlowControlWindow;
|
|
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiRemote =
|
|
kDefaultStreamFlowControlWindow;
|
|
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetUni =
|
|
kDefaultStreamFlowControlWindow;
|
|
conn.flowControlState.peerAdvertisedMaxOffset =
|
|
kDefaultConnectionFlowControlWindow;
|
|
conn.streamManager->setMaxLocalBidirectionalStreams(
|
|
kDefaultMaxStreamsBidirectional);
|
|
conn.streamManager->setMaxLocalUnidirectionalStreams(
|
|
kDefaultMaxStreamsUnidirectional);
|
|
}
|
|
|
|
QuicServerConnectionState conn;
|
|
};
|
|
|
|
struct ReadCoalescingParam {
|
|
size_t readCoalescingSize;
|
|
};
|
|
|
|
class QuicStreamFunctionsTestBase
|
|
: public QuicStreamFunctionsTest,
|
|
public WithParamInterface<ReadCoalescingParam> {
|
|
void maybeSetReadCoalescing() override {
|
|
conn.transportSettings.readCoalescingSize = GetParam().readCoalescingSize;
|
|
}
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
QuicStreamFunctionsTestBase,
|
|
QuicStreamFunctionsTestBase,
|
|
::testing::Values(
|
|
ReadCoalescingParam{.readCoalescingSize = 0},
|
|
ReadCoalescingParam{.readCoalescingSize = 8196}));
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, TestCreateBidirectionalStream) {
|
|
const auto stream =
|
|
conn.streamManager->createNextBidirectionalStream().value();
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 1);
|
|
EXPECT_EQ(stream->id, 0x00);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, TestCreateUnidirectionalStream) {
|
|
const auto stream =
|
|
conn.streamManager->createNextUnidirectionalStream().value();
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 1);
|
|
EXPECT_EQ(stream->id, 0x02);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_FALSE(readData1.second);
|
|
|
|
auto readData2 = readDataFromQuicStream(*stream, 30);
|
|
EXPECT_EQ(
|
|
" you and this is crazy. Here's", readData2.first->to<std::string>());
|
|
EXPECT_FALSE(readData2.second);
|
|
|
|
auto readData3 = readDataFromQuicStream(*stream, 21);
|
|
EXPECT_EQ(" my number so call me", readData3.first->to<std::string>());
|
|
EXPECT_FALSE(readData3.second);
|
|
|
|
auto readData4 = readDataFromQuicStream(*stream, 20);
|
|
EXPECT_EQ(" maybe", readData4.first->to<std::string>());
|
|
EXPECT_TRUE(readData4.second);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
}
|
|
};
|
|
|
|
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_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
|
|
bufClone = range[1].data.front()->clone();
|
|
EXPECT_EQ("'s my number so call me maybe", bufClone->to<std::string>());
|
|
});
|
|
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->to<std::string>());
|
|
};
|
|
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->to<std::string>());
|
|
});
|
|
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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.first);
|
|
|
|
auto readData2 = readDataFromQuicStream(*stream, 30);
|
|
EXPECT_EQ(nullptr, readData2.first);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, TestReadDataFromMultipleBufsShared) {
|
|
auto stream = conn.streamManager->createNextBidirectionalStream().value();
|
|
auto buf1 = IOBuf::copyBuffer("I just met you ");
|
|
buf1->prependChain(IOBuf::copyBuffer("and this is crazy. "));
|
|
buf1->coalesceWithHeadroomTailroom(0, 8000);
|
|
|
|
auto buf2 = IOBuf::copyBuffer("Here's my number ");
|
|
buf2->prependChain(IOBuf::copyBuffer("so call me maybe"));
|
|
|
|
// Manually share the buffers like multiple stream frames in a packet.
|
|
auto buf3 = buf1->clone();
|
|
buf3->trimStart(buf3->length());
|
|
memcpy(buf3->writableTail(), " it's hard to look right at you", 31);
|
|
buf3->append(31);
|
|
|
|
auto streamLastMaxOffset = stream->maxOffsetObserved;
|
|
auto connLastMaxOffset = conn.flowControlState.sumMaxObservedOffset;
|
|
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
|
|
appendDataToReadBuffer(
|
|
*stream,
|
|
StreamBuffer(std::move(buf2), buf1->computeChainDataLength(), false));
|
|
appendDataToReadBuffer(
|
|
*stream,
|
|
StreamBuffer(std::move(buf3), buf1->computeChainDataLength() + 33, 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 it's hard to look right at you",
|
|
readData1.first->to<std::string>());
|
|
EXPECT_TRUE(readData1.first);
|
|
|
|
auto readData2 = readDataFromQuicStream(*stream, 30);
|
|
EXPECT_EQ(nullptr, readData2.first);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_FALSE(readData2.second);
|
|
|
|
auto readData3 = readDataFromQuicStream(*stream, 31);
|
|
EXPECT_EQ(
|
|
"this is crazy. Here's my number", readData3.first->to<std::string>());
|
|
EXPECT_FALSE(readData3.second);
|
|
|
|
auto readData4 = readDataFromQuicStream(*stream);
|
|
EXPECT_EQ(" so call me maybe", readData4.first->to<std::string>());
|
|
EXPECT_TRUE(readData4.second);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>();
|
|
EXPECT_EQ(
|
|
"I just met you and this is crazy. Here's my number, so call me maybe.",
|
|
str);
|
|
EXPECT_TRUE(readData1.second);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_FALSE(readData1.second);
|
|
EXPECT_EQ(stream->readBuffer.size(), 1);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
} // namespace test
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_FALSE(readData1.second);
|
|
EXPECT_EQ(stream->readBuffer.size(), 1);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>();
|
|
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_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
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->to<std::string>());
|
|
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->to<std::string>());
|
|
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_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
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->to<std::string>());
|
|
EXPECT_TRUE(readData2.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->to<std::string>());
|
|
EXPECT_TRUE(readData1.second);
|
|
EXPECT_TRUE(stream->readBuffer.empty());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, TestEmptyBuffer) {
|
|
auto stream = conn.streamManager->createNextBidirectionalStream().value();
|
|
auto buf1 = IOBuf::copyBuffer("");
|
|
appendDataToReadBuffer(*stream, StreamBuffer(buf1->clone(), 0));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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().remove(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_P(QuicStreamFunctionsTestBase, 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().remove(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().remove(outOfOrderStream1);
|
|
conn.streamManager->getStream(outOfOrderStream2);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openBidirectionalPeerStreams().size(),
|
|
(outOfOrderStream2) / kStreamIncrement);
|
|
}
|
|
|
|
TEST_P(
|
|
QuicStreamFunctionsTestBase,
|
|
GetOrCreateServerStreamAfterClosingLastStream) {
|
|
StreamId outOfOrderStream1 = 97;
|
|
StreamId outOfOrderStream2 = 101;
|
|
conn.streamManager->getStream(outOfOrderStream1);
|
|
conn.streamManager->openBidirectionalPeerStreams().remove(outOfOrderStream1);
|
|
conn.streamManager->getStream(outOfOrderStream2);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openBidirectionalPeerStreams().size(),
|
|
(outOfOrderStream2 + 1) / kStreamIncrement);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, GetOrCreateClosedServerStream) {
|
|
StreamId outOfOrderStream1 = 97;
|
|
StreamId closedStream = 49;
|
|
conn.streamManager->getStream(outOfOrderStream1);
|
|
conn.streamManager->openBidirectionalPeerStreams().remove(closedStream);
|
|
EXPECT_EQ(conn.streamManager->getStream(closedStream), nullptr);
|
|
}
|
|
|
|
TEST_F(QuicServerStreamFunctionsTest, GetOrCreateServerStreamOnServer) {
|
|
StreamId serverStream = 101;
|
|
EXPECT_THROW(
|
|
conn.streamManager->getStream(serverStream), QuicTransportException);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, GetOrCreateClientStreamOnClient) {
|
|
StreamId clientStream = 100;
|
|
EXPECT_THROW(
|
|
conn.streamManager->getStream(clientStream), QuicTransportException);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
26);
|
|
conn.streamManager->createStream(outOfOrderStream2).value();
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
26);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, CreateQuicStreamClientOutOfOrder) {
|
|
StreamId outOfOrderStream1 = 96;
|
|
StreamId outOfOrderStream2 = 48;
|
|
conn.streamManager->createStream(outOfOrderStream1);
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 1);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
25);
|
|
conn.streamManager->createStream(outOfOrderStream2).value();
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
25);
|
|
}
|
|
|
|
TEST_F(QuicServerStreamFunctionsTest, CreateClosedServerStream) {
|
|
StreamId outOfOrderStream1 = 101;
|
|
StreamId outOfOrderStream2 = 49;
|
|
conn.streamManager->createStream(outOfOrderStream1);
|
|
conn.streamManager->openBidirectionalLocalStreams().remove(outOfOrderStream2);
|
|
EXPECT_FALSE(conn.streamManager->createStream(outOfOrderStream2));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, CreateClosedClientStream) {
|
|
StreamId outOfOrderStream1 = 96;
|
|
StreamId outOfOrderStream2 = 48;
|
|
conn.streamManager->createStream(outOfOrderStream1).value();
|
|
conn.streamManager->openBidirectionalLocalStreams().remove(outOfOrderStream2);
|
|
EXPECT_FALSE(conn.streamManager->createStream(outOfOrderStream2));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, CreateAlreadyExistingStream) {
|
|
StreamId stream = 0x08;
|
|
conn.streamManager->createStream(stream).value();
|
|
EXPECT_THROW(
|
|
conn.streamManager->createStream(stream), QuicTransportException);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, IsServerUnidirectionalStream) {
|
|
EXPECT_TRUE(isServerUnidirectionalStream(0x03));
|
|
EXPECT_TRUE(isServerUnidirectionalStream(0x07));
|
|
EXPECT_TRUE(isServerUnidirectionalStream(0x0f));
|
|
EXPECT_TRUE(isServerUnidirectionalStream(0xf3));
|
|
EXPECT_TRUE(isServerUnidirectionalStream(0xff));
|
|
EXPECT_FALSE(isServerUnidirectionalStream(0x01));
|
|
EXPECT_FALSE(isServerUnidirectionalStream(0x02));
|
|
EXPECT_FALSE(isServerUnidirectionalStream(0x0e));
|
|
EXPECT_FALSE(isServerUnidirectionalStream(0xf1));
|
|
EXPECT_FALSE(isServerUnidirectionalStream(0xfd));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, IsClientBidirectionalStream) {
|
|
EXPECT_TRUE(isClientBidirectionalStream(0x00));
|
|
EXPECT_TRUE(isClientBidirectionalStream(0x04));
|
|
EXPECT_TRUE(isClientBidirectionalStream(0x08));
|
|
EXPECT_TRUE(isClientBidirectionalStream(0xf0));
|
|
EXPECT_TRUE(isClientBidirectionalStream(0xfc));
|
|
EXPECT_FALSE(isClientBidirectionalStream(0x01));
|
|
EXPECT_FALSE(isClientBidirectionalStream(0x02));
|
|
EXPECT_FALSE(isClientBidirectionalStream(0x03));
|
|
EXPECT_FALSE(isClientBidirectionalStream(0xf1));
|
|
EXPECT_FALSE(isClientBidirectionalStream(0xff));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, GetStreamDirectionality) {
|
|
EXPECT_EQ(StreamDirectionality::Bidirectional, getStreamDirectionality(0x01));
|
|
EXPECT_EQ(StreamDirectionality::Bidirectional, getStreamDirectionality(0xf0));
|
|
EXPECT_EQ(StreamDirectionality::Bidirectional, getStreamDirectionality(0xf1));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional, getStreamDirectionality(0x02));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional, getStreamDirectionality(0x03));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional, getStreamDirectionality(0xff));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, IsSendingStream) {
|
|
QuicClientConnectionState clientState(
|
|
FizzClientQuicHandshakeContext::Builder().build());
|
|
QuicServerConnectionState serverState(
|
|
FizzServerQuicHandshakeContext::Builder().build());
|
|
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_P(QuicStreamFunctionsTestBase, IsReceivingStream) {
|
|
QuicClientConnectionState clientState(
|
|
FizzClientQuicHandshakeContext::Builder().build());
|
|
QuicServerConnectionState serverState(
|
|
FizzServerQuicHandshakeContext::Builder().build());
|
|
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_P(QuicStreamFunctionsTestBase, GetStreamInitiatorBidirectional) {
|
|
const auto clientStream1Id =
|
|
conn.streamManager->createNextBidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 1);
|
|
const auto clientStream2Id =
|
|
conn.streamManager->createNextBidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(clientStream1Id, 0x00);
|
|
EXPECT_EQ(clientStream2Id, 0x04);
|
|
|
|
const auto serverStream1Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(clientStream1Id + 1))->id;
|
|
const auto serverStream2Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(clientStream2Id + 1))->id;
|
|
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(clientStream2Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(serverStream2Id));
|
|
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, clientStream2Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, serverStream2Id));
|
|
}
|
|
|
|
TEST_F(QuicServerStreamFunctionsTest, GetStreamInitiatorBidirectional) {
|
|
const auto serverStream1Id =
|
|
conn.streamManager->createNextBidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 1);
|
|
const auto serverStream2Id =
|
|
conn.streamManager->createNextBidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(serverStream1Id, 0x01);
|
|
EXPECT_EQ(serverStream2Id, 0x05);
|
|
|
|
const auto clientStream1Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(serverStream1Id - 1))->id;
|
|
const auto clientStream2Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(serverStream2Id - 1))->id;
|
|
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(serverStream2Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Bidirectional,
|
|
getStreamDirectionality(clientStream2Id));
|
|
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, serverStream2Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, clientStream2Id));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, GetStreamInitiatorUnidirectional) {
|
|
const auto clientStream1Id =
|
|
conn.streamManager->createNextUnidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 1);
|
|
const auto clientStream2Id =
|
|
conn.streamManager->createNextUnidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(clientStream1Id, 0x02);
|
|
EXPECT_EQ(clientStream2Id, 0x06);
|
|
|
|
const auto serverStream1Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(clientStream1Id + 1))->id;
|
|
const auto serverStream2Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(clientStream2Id + 1))->id;
|
|
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(clientStream2Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(serverStream2Id));
|
|
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, clientStream2Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, serverStream2Id));
|
|
}
|
|
|
|
TEST_F(QuicServerStreamFunctionsTest, GetStreamInitiatorUnidirectional) {
|
|
const auto serverStream1Id =
|
|
conn.streamManager->createNextUnidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 1);
|
|
const auto serverStream2Id =
|
|
conn.streamManager->createNextUnidirectionalStream().value()->id;
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(serverStream1Id, 0x03);
|
|
EXPECT_EQ(serverStream2Id, 0x07);
|
|
|
|
const auto clientStream1Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(serverStream1Id - 1))->id;
|
|
const auto clientStream2Id =
|
|
CHECK_NOTNULL(conn.streamManager->getStream(serverStream2Id - 1))->id;
|
|
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(serverStream2Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamDirectionality::Unidirectional,
|
|
getStreamDirectionality(clientStream2Id));
|
|
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, serverStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Local,
|
|
getStreamInitiator(conn.nodeType, serverStream2Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, clientStream1Id));
|
|
EXPECT_EQ(
|
|
StreamInitiator::Remote,
|
|
getStreamInitiator(conn.nodeType, clientStream2Id));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, HasReadableDataNoDataInBuf) {
|
|
auto stream = conn.streamManager->createNextBidirectionalStream().value();
|
|
EXPECT_FALSE(stream->hasReadableData());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, HasReadableDataOnlyEof) {
|
|
auto stream = conn.streamManager->createNextBidirectionalStream().value();
|
|
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 0, true));
|
|
EXPECT_TRUE(stream->hasReadableData());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, HasPeekableDataNoDataInBuf) {
|
|
auto stream = conn.streamManager->createNextBidirectionalStream().value();
|
|
EXPECT_FALSE(stream->hasPeekableData());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, HasPeekableDataOnlyEof) {
|
|
auto stream = conn.streamManager->createNextBidirectionalStream().value();
|
|
appendDataToReadBuffer(*stream, StreamBuffer(nullptr, 0, true));
|
|
EXPECT_FALSE(stream->hasPeekableData());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(
|
|
QuicStreamFunctionsTestBase,
|
|
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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, RemovedClosedState) {
|
|
auto stream = conn.streamManager->createNextBidirectionalStream().value();
|
|
auto streamId = stream->id;
|
|
conn.streamManager->readableStreams().emplace(streamId);
|
|
conn.streamManager->peekableStreams().emplace(streamId);
|
|
writeDataToQuicStream(*stream, folly::IOBuf::copyBuffer("write data"), true);
|
|
conn.streamManager->updateWritableStreams(*stream);
|
|
conn.streamManager->queueBlocked(streamId, 0);
|
|
conn.streamManager->addDeliverable(streamId);
|
|
conn.streamManager->addLoss(streamId);
|
|
conn.streamManager->queueWindowUpdate(streamId);
|
|
conn.streamManager->addStopSending(
|
|
streamId, GenericApplicationErrorCode::UNKNOWN);
|
|
conn.streamManager->queueFlowControlUpdated(streamId);
|
|
stream->sendState = StreamSendState::Closed;
|
|
stream->recvState = StreamRecvState::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(writableContains(*conn.streamManager, 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());
|
|
EXPECT_FALSE(conn.streamManager->flowControlUpdatedContains(streamId));
|
|
}
|
|
|
|
TEST_F(QuicServerStreamFunctionsTest, ServerGetClientQuicStream) {
|
|
StreamId clientStream = 0x10;
|
|
std::vector<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->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
3);
|
|
|
|
StreamId serverStream2 = 0x05;
|
|
EXPECT_EQ(conn.streamManager->getStream(serverStream2)->id, serverStream2);
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().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->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().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->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().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->sendState = StreamSendState::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->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
4);
|
|
|
|
StreamId serverStream2 = 0x0B;
|
|
EXPECT_EQ(conn.streamManager->getStream(serverStream2)->id, serverStream2);
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
4);
|
|
|
|
StreamId serverStream3 = 0x1F;
|
|
EXPECT_THROW(
|
|
conn.streamManager->getStream(serverStream3), QuicTransportException);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, 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->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
4);
|
|
|
|
StreamId clientStream2 = 0x08;
|
|
EXPECT_EQ(conn.streamManager->getStream(clientStream2)->id, clientStream2);
|
|
EXPECT_EQ(conn.streamManager->streamCount(), 2);
|
|
EXPECT_EQ(
|
|
conn.streamManager->openUnidirectionalLocalStreams().size() +
|
|
conn.streamManager->openBidirectionalLocalStreams().size(),
|
|
4);
|
|
|
|
StreamId clientStream3 = 0x10;
|
|
EXPECT_THROW(
|
|
conn.streamManager->getStream(clientStream3), QuicTransportException);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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)->sendState =
|
|
StreamSendState::Closed;
|
|
conn.streamManager->getStream(peerStream)->recvState =
|
|
StreamRecvState::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));
|
|
|
|
conn.streamManager->openBidirectionalPeerStreams().remove(peerAutoOpened);
|
|
|
|
conn.streamManager->removeClosedStream(peerStream);
|
|
|
|
EXPECT_FALSE(conn.streamManager->streamExists(peerAutoOpened));
|
|
EXPECT_FALSE(conn.streamManager->streamExists(peerStream));
|
|
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened2));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, StreamLimitUpdates) {
|
|
StreamId peerStream = 13;
|
|
StreamId peerAutoOpened = 5;
|
|
StreamId peerAutoOpened2 = 9;
|
|
StreamId notOpenedPeer = 17;
|
|
|
|
conn.streamManager->setStreamLimitWindowingFraction(
|
|
conn.transportSettings.advertisedInitialMaxStreamsBidi);
|
|
conn.streamManager->getStream(peerStream)->sendState =
|
|
StreamSendState::Closed;
|
|
conn.streamManager->getStream(peerStream)->recvState =
|
|
StreamRecvState::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_P(QuicStreamFunctionsTestBase, AllBytesTillFinAcked) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 1;
|
|
stream.currentWriteOffset = 2;
|
|
EXPECT_TRUE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedDSR) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 1;
|
|
stream.writeBufMeta.offset = 2;
|
|
EXPECT_TRUE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedFinOnly) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 0;
|
|
stream.currentWriteOffset = 1;
|
|
EXPECT_TRUE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedFinOnlyDSR) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 0;
|
|
stream.writeBufMeta.offset = 1;
|
|
EXPECT_TRUE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedNewStream) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
EXPECT_FALSE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedStillLost) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 20;
|
|
stream.currentWriteOffset = 21;
|
|
auto dataBuf = IOBuf::create(10);
|
|
stream.lossBuffer.emplace_back(ChainedByteRangeHead(dataBuf), 10, false);
|
|
EXPECT_FALSE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedStillLostDSR) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 20;
|
|
stream.writeBufMeta.offset = 21;
|
|
WriteBufferMeta::Builder b;
|
|
b.setLength(10);
|
|
b.setOffset(10);
|
|
b.setEOF(false);
|
|
stream.lossBufMetas.emplace_back(b.build());
|
|
EXPECT_FALSE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedStillRetransmitting) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 12;
|
|
|
|
auto retxBufData = IOBuf::create(10);
|
|
stream.retransmissionBuffer.emplace(
|
|
0,
|
|
std::make_unique<WriteStreamBuffer>(
|
|
ChainedByteRangeHead(retxBufData), 10, false));
|
|
EXPECT_FALSE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(
|
|
QuicStreamFunctionsTestBase,
|
|
AllBytesTillFinAckedStillRetransmittingDSR) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 12;
|
|
WriteBufferMeta::Builder b;
|
|
b.setLength(10);
|
|
b.setOffset(10);
|
|
b.setEOF(false);
|
|
stream.retransmissionBufMetas.emplace(0, b.build());
|
|
EXPECT_FALSE(allBytesTillFinAcked(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, 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_P(QuicStreamFunctionsTestBase, AllBytesTillFinAckedStillWritingDSR) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.finalWriteOffset = 10;
|
|
stream.writeBufMeta.length = 10;
|
|
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.finalSize, 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.finalSize, len);
|
|
}
|
|
|
|
// This tests the scenario in which the reliable size is greater than the
|
|
// current write offset.
|
|
TEST_F(QuicServerStreamFunctionsTest, TestAppendPendingStreamReliableReset1) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
auto data = IOBuf::copyBuffer("this is data");
|
|
auto len = data->computeChainDataLength();
|
|
writeDataToQuicStream(stream, std::move(data), false);
|
|
|
|
stream.currentWriteOffset = len - 5;
|
|
|
|
appendPendingStreamReset(
|
|
conn, stream, GenericApplicationErrorCode::UNKNOWN, len - 3);
|
|
auto rst = conn.pendingEvents.resets.at(id);
|
|
EXPECT_EQ(rst.errorCode, GenericApplicationErrorCode::UNKNOWN);
|
|
EXPECT_EQ(rst.finalSize, len - 3);
|
|
}
|
|
|
|
// This tests the scenario in which the reliable size is less than the
|
|
// current write offset.
|
|
TEST_F(QuicServerStreamFunctionsTest, TestAppendPendingStreamReliableReset2) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
auto data = IOBuf::copyBuffer("this is data");
|
|
auto len = data->computeChainDataLength();
|
|
writeDataToQuicStream(stream, std::move(data), false);
|
|
|
|
stream.currentWriteOffset = len - 5;
|
|
|
|
appendPendingStreamReset(
|
|
conn, stream, GenericApplicationErrorCode::UNKNOWN, len - 7);
|
|
auto rst = conn.pendingEvents.resets.at(id);
|
|
EXPECT_EQ(rst.errorCode, GenericApplicationErrorCode::UNKNOWN);
|
|
EXPECT_EQ(rst.finalSize, len - 5);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LargestWriteOffsetSeenFIN) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.finalWriteOffset = 100;
|
|
EXPECT_EQ(100, getLargestWriteOffsetSeen(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LargestWriteOffsetSeenNoFIN) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 100;
|
|
auto randomInputData = buildRandomInputData(20);
|
|
stream.pendingWrites.append(randomInputData);
|
|
stream.writeBuffer.append(std::move(randomInputData));
|
|
EXPECT_EQ(120, getLargestWriteOffsetSeen(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LargestWriteOffsetSeenDSRNoFIN) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 10;
|
|
stream.writeBufMeta.offset = 100;
|
|
stream.writeBufMeta.length = 20;
|
|
EXPECT_EQ(120, getLargestWriteOffsetSeen(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, StreamLargestWriteOffsetTxedNothingTxed) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 0;
|
|
stream.writeBufMeta.offset = 0;
|
|
EXPECT_EQ(none, getLargestWriteOffsetTxed(stream));
|
|
}
|
|
|
|
TEST_F(
|
|
QuicStreamFunctionsTest,
|
|
StreamLargestWriteOffsetTxedNothingTxedDSRTxed) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 0;
|
|
stream.writeBufMeta.offset = 55;
|
|
EXPECT_EQ(54, getLargestWriteOffsetTxed(stream).value());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, StreamLargestWriteOffsetTxedOneByteTxed) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 1;
|
|
ASSERT_TRUE(getLargestWriteOffsetTxed(stream).has_value());
|
|
EXPECT_EQ(0, getLargestWriteOffsetTxed(stream).value());
|
|
}
|
|
|
|
TEST_P(
|
|
QuicStreamFunctionsTestBase,
|
|
StreamLargestWriteOffsetTxedOneByteDSRTxed) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 1;
|
|
stream.writeBufMeta.offset = 2;
|
|
ASSERT_TRUE(getLargestWriteOffsetTxed(stream).has_value());
|
|
EXPECT_EQ(1, getLargestWriteOffsetTxed(stream).value());
|
|
}
|
|
|
|
TEST_P(
|
|
QuicStreamFunctionsTestBase,
|
|
StreamLargestWriteOffsetTxedHundredBytesTxed) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 100;
|
|
ASSERT_TRUE(getLargestWriteOffsetTxed(stream).has_value());
|
|
EXPECT_EQ(99, getLargestWriteOffsetTxed(stream).value());
|
|
}
|
|
|
|
TEST_F(
|
|
QuicStreamFunctionsTest,
|
|
StreamLargestWriteOffsetTxedHundredBytesDSRTxed) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 10;
|
|
stream.writeBufMeta.offset = 100;
|
|
ASSERT_TRUE(getLargestWriteOffsetTxed(stream).has_value());
|
|
EXPECT_EQ(99, getLargestWriteOffsetTxed(stream).value());
|
|
}
|
|
|
|
TEST_F(
|
|
QuicStreamFunctionsTest,
|
|
StreamLargestWriteOffsetTxedIgnoreFinalWriteOffset) {
|
|
// finalWriteOffset is set when writeChain is called with EoR, but we should
|
|
// always use currentWriteOffset to determine how many bytes have been TXed
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 10;
|
|
stream.finalWriteOffset = 100;
|
|
ASSERT_TRUE(getLargestWriteOffsetTxed(stream).has_value());
|
|
EXPECT_EQ(9, getLargestWriteOffsetTxed(stream).value());
|
|
}
|
|
|
|
TEST_F(
|
|
QuicStreamFunctionsTest,
|
|
StreamLargestWriteOffsetTxedDSRIgnoreFinalWriteOffset) {
|
|
// finalWriteOffset is set when writeChain is called with EoR, but we should
|
|
// always use currentWriteOffset to determine how many bytes have been TXed
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 1;
|
|
stream.writeBufMeta.offset = 10;
|
|
stream.finalWriteOffset = 100;
|
|
ASSERT_TRUE(getLargestWriteOffsetTxed(stream).has_value());
|
|
EXPECT_EQ(9, getLargestWriteOffsetTxed(stream).value());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, StreamNextOffsetToDeliverNothingAcked) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 100;
|
|
EXPECT_EQ(none, getLargestDeliverableOffset(stream));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, StreamNextOffsetToDeliverAllAcked) {
|
|
QuicStreamState stream(3, conn);
|
|
stream.currentWriteOffset = 100;
|
|
stream.ackedIntervals.insert(0, 99);
|
|
EXPECT_EQ(99, getLargestDeliverableOffset(stream).value());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LossBufferEmpty) {
|
|
StreamId id = 4;
|
|
QuicStreamState stream(id, conn);
|
|
conn.streamManager->addLoss(id);
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_FALSE(conn.streamManager->hasLoss());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LossBufferEmptyNoChange) {
|
|
StreamId id = 4;
|
|
QuicStreamState stream(id, conn);
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_FALSE(conn.streamManager->hasLoss());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LossBufferHasData) {
|
|
StreamId id = 4;
|
|
QuicStreamState stream(id, conn);
|
|
auto dataBuf = IOBuf::create(10);
|
|
stream.lossBuffer.emplace_back(ChainedByteRangeHead(dataBuf), 10, false);
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_TRUE(conn.streamManager->hasLoss());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LossBufferMetaHasData) {
|
|
StreamId id = 4;
|
|
QuicStreamState stream(id, conn);
|
|
WriteBufferMeta::Builder b;
|
|
b.setLength(10);
|
|
b.setOffset(10);
|
|
b.setEOF(false);
|
|
stream.lossBufMetas.emplace_back(b.build());
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_TRUE(conn.streamManager->hasLoss());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LossBufferMetaStillHasData) {
|
|
StreamId id = 4;
|
|
QuicStreamState stream(id, conn);
|
|
conn.streamManager->addLoss(id);
|
|
WriteBufferMeta::Builder b;
|
|
b.setLength(10);
|
|
b.setOffset(10);
|
|
b.setEOF(false);
|
|
stream.lossBufMetas.emplace_back(b.build());
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_TRUE(conn.streamManager->hasLoss());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, LossBufferStillHasData) {
|
|
StreamId id = 4;
|
|
QuicStreamState stream(id, conn);
|
|
conn.streamManager->addLoss(id);
|
|
auto dataBuf = IOBuf::create(10);
|
|
stream.lossBuffer.emplace_back(ChainedByteRangeHead(dataBuf), 10, false);
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_TRUE(conn.streamManager->hasLoss());
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, WritableList) {
|
|
StreamId id = 3;
|
|
QuicStreamState stream(id, conn);
|
|
stream.currentWriteOffset = 100;
|
|
stream.flowControlState.peerAdvertisedMaxOffset = 200;
|
|
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_FALSE(writableContains(*stream.conn.streamManager, id));
|
|
|
|
auto buf = IOBuf::create(100);
|
|
buf->append(100);
|
|
writeDataToQuicStream(stream, std::move(buf), false);
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_TRUE(writableContains(*stream.conn.streamManager, id));
|
|
|
|
// Flow control
|
|
stream.flowControlState.peerAdvertisedMaxOffset = stream.currentWriteOffset;
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_FALSE(writableContains(*stream.conn.streamManager, id));
|
|
|
|
// Fin
|
|
writeDataToQuicStream(stream, nullptr, true);
|
|
stream.writeBuffer.move();
|
|
ChainedByteRangeHead(std::move(stream.pendingWrites));
|
|
stream.currentWriteOffset += 100;
|
|
stream.flowControlState.peerAdvertisedMaxOffset = stream.currentWriteOffset;
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_TRUE(writableContains(*stream.conn.streamManager, id));
|
|
|
|
// After Fin
|
|
stream.currentWriteOffset++;
|
|
conn.streamManager->updateWritableStreams(stream);
|
|
EXPECT_FALSE(writableContains(*stream.conn.streamManager, id));
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AckCryptoStream) {
|
|
auto chlo = IOBuf::copyBuffer("CHLO");
|
|
conn.cryptoState->handshakeStream.retransmissionBuffer.emplace(
|
|
0, std::make_unique<WriteStreamBuffer>(ChainedByteRangeHead(chlo), 0));
|
|
processCryptoStreamAck(conn.cryptoState->handshakeStream, 0, chlo->length());
|
|
EXPECT_EQ(conn.cryptoState->handshakeStream.retransmissionBuffer.size(), 0);
|
|
}
|
|
|
|
TEST_P(QuicStreamFunctionsTestBase, AckCryptoStreamOffsetLengthMismatch) {
|
|
auto chlo = IOBuf::copyBuffer("CHLO");
|
|
auto& cryptoStream = conn.cryptoState->handshakeStream;
|
|
cryptoStream.retransmissionBuffer.emplace(
|
|
0, std::make_unique<WriteStreamBuffer>(ChainedByteRangeHead(chlo), 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);
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace quic
|