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