1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-22 16:02:34 +03:00

Convert IntervalSet from throwing exceptions to using CHECKs with Expected error handling

Summary:
This commit converts IntervalSet to use CHECKs instead of throwing exceptions and provides safe tryInsert methods that return quic::Expected for error handling.

**Core Problem Solved:**
IntervalSet was throwing `std::invalid_argument` exceptions in two scenarios:
1. When constructing an Interval with `start > end`
2. When interval bounds exceed the maximum allowed value

This change eliminates exceptions in favor of CHECKs (for internal validation) and Expected-based error handling (for caller validation).

**Implementation Details:**

**1. IntervalSet Core Changes:**
- Replaced `throw std::invalid_argument` with `CHECK_LE` in Interval constructor
- Replaced `throw std::invalid_argument` with `CHECK_LE` in `insert(start, end)`
- Added `IntervalSetError` enum for error classification
- Added `folly::Expected` include

**2. Safe API Layer:**
- Added `tryInsert(interval)` method returning `Expected<Unit, IntervalSetError>`
- Added `tryInsert(start, end)` method with pre-validation
- Added `tryInsert(point)` method
- Added static `Interval::tryCreate()` method for safe interval construction

**3. Updated  Code:**
- **QuicWriteCodec.cpp**: Updated `fillFrameWithPacketReceiveTimestamps` to use `tryInsert`
  - Returns `QuicError` if interval validation fails
  - Maintains existing error handling patterns
- **QuicTransportFunctions.cpp**: Updated `implicitAckCryptoStream` to use `tryInsert`
  - Logs errors and continues processing other packets
  - Robust error handling for crypto stream implicit acks

Reviewed By: kvtsoy

Differential Revision: D76792362

fbshipit-source-id: 5bd7c22e69a91d60cc41c603a1f2380893f4c8a0
This commit is contained in:
Matt Joras
2025-08-19 10:47:24 -07:00
committed by Facebook GitHub Bot
parent 8f8be8d5d0
commit d3e8fe246a
19 changed files with 262 additions and 51 deletions

View File

@@ -2147,7 +2147,15 @@ void implicitAckCryptoStream(
implicitAck.implicit = true; implicitAck.implicit = true;
for (const auto& op : conn.outstandings.packets) { for (const auto& op : conn.outstandings.packets) {
if (op.packet.header.getPacketNumberSpace() == packetNumSpace) { if (op.packet.header.getPacketNumberSpace() == packetNumSpace) {
ackBlocks.insert(op.packet.header.getPacketSequenceNum()); auto insertResult =
ackBlocks.tryInsert(op.packet.header.getPacketSequenceNum());
if (insertResult.hasError()) {
LOG(ERROR) << "Failed to insert packet number into ack blocks: "
<< static_cast<int>(insertResult.error());
// Continue processing other packets - this shouldn't happen in normal
// operation
continue;
}
} }
} }
if (ackBlocks.empty()) { if (ackBlocks.empty()) {

View File

@@ -410,8 +410,13 @@ quic::Expected<void, QuicError> QuicClientTransportLite::processUdpPacketData(
// Add the packet to the AckState associated with the packet number space. // Add the packet to the AckState associated with the packet number space.
auto& ackState = getAckState(*conn_, pnSpace); auto& ackState = getAckState(*conn_, pnSpace);
uint64_t distanceFromExpectedPacketNum = auto addResult = addPacketToAckState(*conn_, ackState, packetNum, udpPacket);
addPacketToAckState(*conn_, ackState, packetNum, udpPacket); if (!addResult.has_value()) {
return quic::make_unexpected(QuicError(
TransportErrorCode::INTERNAL_ERROR,
"Failed to add packet to ack state"));
}
uint64_t distanceFromExpectedPacketNum = addResult.value();
if (distanceFromExpectedPacketNum > 0) { if (distanceFromExpectedPacketNum > 0) {
QUIC_STATS(conn_->statsCallback, onOutOfOrderPacketReceived); QUIC_STATS(conn_->statsCallback, onOutOfOrderPacketReceived);
} }

View File

@@ -347,7 +347,12 @@ fillFrameWithPacketReceiveTimestamps(
if (pktsAdded == maxRecvTimestampsToSend) { if (pktsAdded == maxRecvTimestampsToSend) {
break; break;
} }
receivedPktNumsIntervalSet.insert(recvdPkt.pktNum); auto insertResult = receivedPktNumsIntervalSet.tryInsert(recvdPkt.pktNum);
if (insertResult.hasError()) {
return quic::make_unexpected(QuicError(
QuicErrorCode(TransportErrorCode::INTERNAL_ERROR),
"Failed to insert packet number into interval set"));
}
pktsAdded++; pktsAdded++;
} }
auto prevPktNum = largestAckedPacketNum; auto prevPktNum = largestAckedPacketNum;

View File

@@ -9,9 +9,13 @@ mvfst_cpp_library(
"IntervalSet-inl.h", "IntervalSet-inl.h",
], ],
exported_deps = [ exported_deps = [
":expected",
":optional", ":optional",
"//folly:likely", "//folly:likely",
], ],
exported_external_deps = [
"glog",
],
) )
mvfst_cpp_library( mvfst_cpp_library(

View File

@@ -94,9 +94,7 @@ bool IntervalSet<T, Unit, Container>::contains(const T& start, const T& end)
template <typename T, T Unit, template <typename... I> class Container> template <typename T, T Unit, template <typename... I> class Container>
void IntervalSet<T, Unit, Container>::insert(const T& startIt, const T& endIt) { void IntervalSet<T, Unit, Container>::insert(const T& startIt, const T& endIt) {
if (startIt > endIt) { CHECK_LE(startIt, endIt) << "Trying to insert invalid interval";
throw std::invalid_argument("Trying to insert invalid interval");
}
insert(Interval<T, Unit>(startIt, endIt)); insert(Interval<T, Unit>(startIt, endIt));
} }
@@ -128,4 +126,37 @@ template <typename T, T Unit, template <typename... I> class Container>
uint64_t IntervalSet<T, Unit, Container>::insertVersion() const { uint64_t IntervalSet<T, Unit, Container>::insertVersion() const {
return insertVersion_; return insertVersion_;
} }
template <typename T, T Unit, template <typename... I> class Container>
Expected<void, IntervalSetError> IntervalSet<T, Unit, Container>::tryInsert(
const Interval<T, Unit>& interval) {
// The interval constructor already validated the bounds, so we can safely
// insert
insert(interval);
return {};
}
template <typename T, T Unit, template <typename... I> class Container>
Expected<void, IntervalSetError> IntervalSet<T, Unit, Container>::tryInsert(
const T& start,
const T& end) {
// Validate the bounds before creating the interval
if (start > end) {
return quic::make_unexpected(IntervalSetError::InvalidInterval);
}
if (end > std::numeric_limits<T>::max() - interval_type::unitValue()) {
return quic::make_unexpected(IntervalSetError::IntervalBoundTooLarge);
}
// Safe to create and insert the interval
insert(Interval<T, Unit>(start, end));
return {};
}
template <typename T, T Unit, template <typename... I> class Container>
Expected<void, IntervalSetError> IntervalSet<T, Unit, Container>::tryInsert(
const T& point) {
return tryInsert(point, point);
}
} // namespace quic } // namespace quic

View File

@@ -9,17 +9,25 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <exception>
#include <limits> #include <limits>
#include <queue> #include <queue>
#include <stdexcept>
#include <folly/Likely.h> #include <folly/Likely.h>
#include <glog/logging.h>
#include <quic/common/Expected.h>
namespace quic { namespace quic {
constexpr uint64_t kDefaultIntervalSetVersion = 0; constexpr uint64_t kDefaultIntervalSetVersion = 0;
/**
* Error codes for IntervalSet operations
*/
enum class IntervalSetError : uint8_t {
InvalidInterval, // start > end
IntervalBoundTooLarge // interval bound exceeds max allowed value
};
template <typename T, T Unit = (T)1> template <typename T, T Unit = (T)1>
struct Interval { struct Interval {
T start; T start;
@@ -30,12 +38,21 @@ struct Interval {
} }
Interval(const T& s, const T& e) : start(s), end(e) { Interval(const T& s, const T& e) : start(s), end(e) {
if (start > end) { CHECK_LE(start, end) << "Trying to construct invalid interval";
throw std::invalid_argument("Trying to construct invalid interval"); CHECK_LE(end, std::numeric_limits<T>::max() - unitValue())
<< "Interval bound too large";
} }
if (end > std::numeric_limits<T>::max() - unitValue()) {
throw std::invalid_argument("Interval bound too large"); // Safe constructor that returns Expected instead of CHECKing
[[nodiscard]] static quic::Expected<Interval<T, Unit>, IntervalSetError>
tryCreate(const T& s, const T& e) {
if (s > e) {
return quic::make_unexpected(IntervalSetError::InvalidInterval);
} }
if (e > std::numeric_limits<T>::max() - unitValue()) {
return quic::make_unexpected(IntervalSetError::IntervalBoundTooLarge);
}
return Interval<T, Unit>{s, e};
} }
bool operator==(Interval& rhs) const { bool operator==(Interval& rhs) const {
@@ -95,6 +112,16 @@ class IntervalSet : private Container<Interval<T, Unit>> {
void insert(const T& point); void insert(const T& point);
// Safe versions that return Expected instead of CHECKing
[[nodiscard]] Expected<void, IntervalSetError> tryInsert(
const Interval<T, Unit>& interval);
[[nodiscard]] Expected<void, IntervalSetError> tryInsert(
const T& start,
const T& end);
[[nodiscard]] Expected<void, IntervalSetError> tryInsert(const T& point);
void withdraw(const Interval<T, Unit>& interval); void withdraw(const Interval<T, Unit>& interval);
[[nodiscard]] bool contains(const T& start, const T& end) const; [[nodiscard]] bool contains(const T& start, const T& end) const;

View File

@@ -158,9 +158,10 @@ TEST(IntervalSet, insertWithMergeAtEdge) {
TEST(IntervalSet, insertBoundTooLarge) { TEST(IntervalSet, insertBoundTooLarge) {
IntervalSet<uint32_t, 10> set; IntervalSet<uint32_t, 10> set;
EXPECT_THROW( // This should CHECK-fail since end - start > 10
set.insert(0, std::numeric_limits<uint32_t>::max() - 9), EXPECT_DEATH(
std::invalid_argument); set.insert(0, std::numeric_limits<uint32_t>::max() - 9), "Check failed");
// This should work fine since end - start == 10
set.insert(0, std::numeric_limits<uint32_t>::max() - 10); set.insert(0, std::numeric_limits<uint32_t>::max() - 10);
} }
@@ -402,3 +403,77 @@ TEST(IntervalSet, equalityComparatorNotEqualDiffIntervals2) {
EXPECT_FALSE(set1 == set2); EXPECT_FALSE(set1 == set2);
EXPECT_TRUE(set1 != set2); EXPECT_TRUE(set1 != set2);
} }
TEST(IntervalSet, tryInsertValidInterval) {
IntervalSet<int> set;
auto result = set.tryInsert(1, 5);
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1, set.size());
EXPECT_TRUE(set.contains(1, 5));
}
TEST(IntervalSet, tryInsertValidPoint) {
IntervalSet<int> set;
auto result = set.tryInsert(10);
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1, set.size());
EXPECT_TRUE(set.contains(10, 10));
}
TEST(IntervalSet, tryInsertInvalidInterval) {
IntervalSet<int> set;
// start > end should return error
auto result = set.tryInsert(10, 5);
EXPECT_TRUE(result.hasError());
EXPECT_EQ(IntervalSetError::InvalidInterval, result.error());
EXPECT_EQ(0, set.size());
}
TEST(IntervalSet, tryInsertBoundTooLarge) {
IntervalSet<uint32_t, 10> set;
// This should return error instead of CHECK-failing
auto result = set.tryInsert(0, std::numeric_limits<uint32_t>::max() - 9);
EXPECT_TRUE(result.hasError());
EXPECT_EQ(IntervalSetError::IntervalBoundTooLarge, result.error());
EXPECT_EQ(0, set.size());
// This should succeed
auto result2 = set.tryInsert(0, std::numeric_limits<uint32_t>::max() - 10);
EXPECT_TRUE(result2.has_value());
EXPECT_EQ(1, set.size());
}
TEST(IntervalSet, tryInsertWithMerging) {
IntervalSet<int> set;
auto result1 = set.tryInsert(1, 3);
EXPECT_TRUE(result1.has_value());
auto result2 = set.tryInsert(5, 7);
EXPECT_TRUE(result2.has_value());
// Insert overlapping interval
auto result3 = set.tryInsert(2, 6);
EXPECT_TRUE(result3.has_value());
EXPECT_EQ(1, set.size());
EXPECT_TRUE(set.contains(1, 7));
}
TEST(IntervalSet, tryCreateInterval) {
// Test the static tryCreate method
auto result1 = Interval<int>::tryCreate(1, 5);
EXPECT_TRUE(result1.has_value());
EXPECT_EQ(1, result1->start);
EXPECT_EQ(5, result1->end);
// Test invalid interval
auto result2 = Interval<int>::tryCreate(10, 5);
EXPECT_TRUE(result2.hasError());
EXPECT_EQ(IntervalSetError::InvalidInterval, result2.error());
// Test bound too large
auto result3 = Interval<uint32_t, 10>::tryCreate(
0, std::numeric_limits<uint32_t>::max() - 9);
EXPECT_TRUE(result3.hasError());
EXPECT_EQ(IntervalSetError::IntervalBoundTooLarge, result3.error());
}

View File

@@ -541,8 +541,13 @@ void updateAckState(
TimePoint receiveTimePoint) { TimePoint receiveTimePoint) {
ReceivedUdpPacket packet; ReceivedUdpPacket packet;
packet.timings.receiveTimePoint = receiveTimePoint; packet.timings.receiveTimePoint = receiveTimePoint;
uint64_t distance = auto addResult =
addPacketToAckState(conn, getAckState(conn, pnSpace), packetNum, packet); addPacketToAckState(conn, getAckState(conn, pnSpace), packetNum, packet);
if (!addResult.has_value()) {
LOG(FATAL) << "Failed to add packet to ack state in test: "
<< static_cast<int>(addResult.error());
}
uint64_t distance = addResult.value();
updateAckSendStateOnRecvPacket( updateAckSendStateOnRecvPacket(
conn, conn,
getAckState(conn, pnSpace), getAckState(conn, pnSpace),

View File

@@ -1262,8 +1262,14 @@ quic::Expected<void, QuicError> onServerReadDataFromOpen(
} }
auto& ackState = getAckState(conn, packetNumberSpace); auto& ackState = getAckState(conn, packetNumberSpace);
uint64_t distanceFromExpectedPacketNum = auto addResult =
addPacketToAckState(conn, ackState, packetNum, readData.udpPacket); addPacketToAckState(conn, ackState, packetNum, readData.udpPacket);
if (!addResult.has_value()) {
return quic::make_unexpected(QuicError(
TransportErrorCode::INTERNAL_ERROR,
"Failed to add packet to ack state"));
}
uint64_t distanceFromExpectedPacketNum = addResult.value();
if (distanceFromExpectedPacketNum > 0) { if (distanceFromExpectedPacketNum > 0) {
QUIC_STATS(conn.statsCallback, onOutOfOrderPacketReceived); QUIC_STATS(conn.statsCallback, onOutOfOrderPacketReceived);
} }

View File

@@ -210,7 +210,13 @@ quic::Expected<AckEvent, QuicError> processAckFrame(
// If we hit a packet which has been declared lost we need to count the // If we hit a packet which has been declared lost we need to count the
// spurious loss and ignore all other processing. // spurious loss and ignore all other processing.
if (ackedPacketIterator->declaredLost) { if (ackedPacketIterator->declaredLost) {
auto modifyResult =
modifyStateForSpuriousLoss(conn, *ackedPacketIterator); modifyStateForSpuriousLoss(conn, *ackedPacketIterator);
if (!modifyResult.has_value()) {
return quic::make_unexpected(QuicError(
TransportErrorCode::INTERNAL_ERROR,
"Failed to modify state for spurious loss"));
}
QUIC_STATS(conn.statsCallback, onPacketSpuriousLoss); QUIC_STATS(conn.statsCallback, onPacketSpuriousLoss);
if (spuriousLossEvent) { if (spuriousLossEvent) {
spuriousLossEvent->addSpuriousPacket( spuriousLossEvent->addSpuriousPacket(
@@ -705,7 +711,7 @@ void updateEcnCountEchoed(
std::max(ackState.ecnCECountEchoed, readAckFrame.ecnCECount); std::max(ackState.ecnCECountEchoed, readAckFrame.ecnCECount);
} }
void modifyStateForSpuriousLoss( Expected<void, IntervalSetError> modifyStateForSpuriousLoss(
QuicConnectionStateBase& conn, QuicConnectionStateBase& conn,
OutstandingPacketWrapper& spuriouslyLostPacket) { OutstandingPacketWrapper& spuriouslyLostPacket) {
CHECK_GT(conn.outstandings.declaredLostCount, 0); CHECK_GT(conn.outstandings.declaredLostCount, 0);
@@ -734,8 +740,11 @@ void modifyStateForSpuriousLoss(
if (stream) { if (stream) {
stream->removeFromLossBuffer( stream->removeFromLossBuffer(
streamFrame->offset, streamFrame->len, streamFrame->fin); streamFrame->offset, streamFrame->len, streamFrame->fin);
stream->updateAckedIntervals( auto updateResult = stream->updateAckedIntervals(
streamFrame->offset, streamFrame->len, streamFrame->fin); streamFrame->offset, streamFrame->len, streamFrame->fin);
if (!updateResult.has_value()) {
return quic::make_unexpected(updateResult.error());
}
conn.streamManager->updateWritableStreams(*stream); conn.streamManager->updateWritableStreams(*stream);
} }
} }
@@ -743,5 +752,6 @@ void modifyStateForSpuriousLoss(
} }
CHECK_GT(conn.outstandings.declaredLostCount, 0); CHECK_GT(conn.outstandings.declaredLostCount, 0);
conn.outstandings.declaredLostCount--; conn.outstandings.declaredLostCount--;
return {};
} }
} // namespace quic } // namespace quic

View File

@@ -10,6 +10,7 @@
#include <quic/QuicConstants.h> #include <quic/QuicConstants.h>
#include <quic/codec/Types.h> #include <quic/codec/Types.h>
#include <quic/common/Expected.h> #include <quic/common/Expected.h>
#include <quic/common/IntervalSet.h>
#include <quic/state/StateData.h> #include <quic/state/StateData.h>
#include <functional> #include <functional>
@@ -124,7 +125,7 @@ void updateEcnCountEchoed(
* Modifies the state in the QuicConnectionStateBase when a packet that * Modifies the state in the QuicConnectionStateBase when a packet that
* was marked as lost is acked. * was marked as lost is acked.
*/ */
void modifyStateForSpuriousLoss( [[nodiscard]] Expected<void, IntervalSetError> modifyStateForSpuriousLoss(
QuicConnectionStateBase& conn, QuicConnectionStateBase& conn,
OutstandingPacketWrapper& spuriouslyLostPacket); OutstandingPacketWrapper& spuriouslyLostPacket);
} // namespace quic } // namespace quic

View File

@@ -132,6 +132,7 @@ mvfst_cpp_library(
"//quic/common:buf_accessor", "//quic/common:buf_accessor",
"//quic/common:circular_deque", "//quic/common:circular_deque",
"//quic/common:expected", "//quic/common:expected",
"//quic/common:interval_set",
"//quic/common:optional", "//quic/common:optional",
"//quic/congestion_control:congestion_controller", "//quic/congestion_control:congestion_controller",
"//quic/congestion_control:packet_processor", "//quic/congestion_control:packet_processor",
@@ -167,6 +168,7 @@ mvfst_cpp_library(
"//quic:constants", "//quic:constants",
"//quic/codec:types", "//quic/codec:types",
"//quic/common:expected", "//quic/common:expected",
"//quic/common:interval_set",
"//quic/common:optional", "//quic/common:optional",
"//quic/congestion_control:congestion_controller", "//quic/congestion_control:congestion_controller",
], ],
@@ -207,6 +209,8 @@ mvfst_cpp_library(
"//quic:constants", "//quic:constants",
"//quic/codec:pktbuilder", "//quic/codec:pktbuilder",
"//quic/codec:types", "//quic/codec:types",
"//quic/common:expected",
"//quic/common:interval_set",
"//quic/common:network_data", "//quic/common:network_data",
], ],
) )

View File

@@ -395,7 +395,7 @@ uint64_t maximumConnectionIdsToIssue(const QuicConnectionStateBase& conn) {
return maximumIdsToIssue; return maximumIdsToIssue;
} }
uint64_t addPacketToAckState( Expected<uint64_t, IntervalSetError> addPacketToAckState(
QuicConnectionStateBase& conn, QuicConnectionStateBase& conn,
AckState& ackState, AckState& ackState,
const PacketNum packetNum, const PacketNum packetNum,
@@ -407,7 +407,10 @@ uint64_t addPacketToAckState(
ackState.largestRecvdPacketNum = std::max<PacketNum>( ackState.largestRecvdPacketNum = std::max<PacketNum>(
ackState.largestRecvdPacketNum.value_or(packetNum), packetNum); ackState.largestRecvdPacketNum.value_or(packetNum), packetNum);
auto preInsertVersion = ackState.acks.insertVersion(); auto preInsertVersion = ackState.acks.insertVersion();
ackState.acks.insert(packetNum); auto insertResult = ackState.acks.tryInsert(packetNum);
if (!insertResult.has_value()) {
return quic::make_unexpected(insertResult.error());
}
if (preInsertVersion == ackState.acks.insertVersion()) { if (preInsertVersion == ackState.acks.insertVersion()) {
QUIC_STATS(conn.statsCallback, onDuplicatedPacketReceived); QUIC_STATS(conn.statsCallback, onDuplicatedPacketReceived);
} }
@@ -446,7 +449,7 @@ uint64_t addPacketToAckState(
return (packetNum > expectedNextPacket) ? packetNum - expectedNextPacket return (packetNum > expectedNextPacket) ? packetNum - expectedNextPacket
: expectedNextPacket - packetNum; : expectedNextPacket - packetNum;
} else { } else {
return 0; return uint64_t{0};
} }
} }

View File

@@ -10,6 +10,8 @@
#include <quic/QuicConstants.h> #include <quic/QuicConstants.h>
#include <quic/codec/QuicPacketBuilder.h> #include <quic/codec/QuicPacketBuilder.h>
#include <quic/codec/Types.h> #include <quic/codec/Types.h>
#include <quic/common/Expected.h>
#include <quic/common/IntervalSet.h>
#include <quic/common/NetworkData.h> #include <quic/common/NetworkData.h>
#include <quic/state/StateData.h> #include <quic/state/StateData.h>
@@ -65,7 +67,7 @@ void increaseNextPacketNum(
* Update largestReceivedUdpPacketNum in ackState with packetNum. Return the * Update largestReceivedUdpPacketNum in ackState with packetNum. Return the
* distance from the next packet number we expect to receive. * distance from the next packet number we expect to receive.
*/ */
uint64_t addPacketToAckState( [[nodiscard]] Expected<uint64_t, IntervalSetError> addPacketToAckState(
QuicConnectionStateBase& conn, QuicConnectionStateBase& conn,
AckState& ackState, AckState& ackState,
const PacketNum packetNum, const PacketNum packetNum,

View File

@@ -11,6 +11,8 @@
#include <quic/QuicConstants.h> #include <quic/QuicConstants.h>
#include <quic/codec/Types.h> #include <quic/codec/Types.h>
#include <quic/common/Expected.h>
#include <quic/common/IntervalSet.h>
#include <quic/dsr/DSRPacketizationRequestSender.h> #include <quic/dsr/DSRPacketizationRequestSender.h>
#include <quic/mvfst-config.h> #include <quic/mvfst-config.h>
#include <quic/priority/PriorityQueue.h> #include <quic/priority/PriorityQueue.h>
@@ -211,7 +213,8 @@ struct QuicStreamLike {
// egress packets that contains a *new* STREAM frame for this stream. // egress packets that contains a *new* STREAM frame for this stream.
uint64_t numPacketsTxWithNewData{0}; uint64_t numPacketsTxWithNewData{0};
void updateAckedIntervals(uint64_t offset, uint64_t len, bool eof) { [[nodiscard]] Expected<void, IntervalSetError>
updateAckedIntervals(uint64_t offset, uint64_t len, bool eof) {
// When there's an EOF we count the byte of 1 past the end as having been // When there's an EOF we count the byte of 1 past the end as having been
// ACKed, since this is useful for delivery APIs. // ACKed, since this is useful for delivery APIs.
int lenAdjustment = [eof]() { int lenAdjustment = [eof]() {
@@ -224,7 +227,7 @@ struct QuicStreamLike {
if (lenAdjustment && len == 0) { if (lenAdjustment && len == 0) {
LOG(FATAL) << "ACK for empty stream frame with no fin."; LOG(FATAL) << "ACK for empty stream frame with no fin.";
} }
ackedIntervals.insert(offset, offset + len - lenAdjustment); return ackedIntervals.tryInsert(offset, offset + len - lenAdjustment);
} }
/* /*

View File

@@ -145,10 +145,15 @@ quic::Expected<void, QuicError> sendAckSMHandler(
<< " offset=" << ackedBuffer->second->offset << " offset=" << ackedBuffer->second->offset
<< " len=" << ackedBuffer->second->data.chainLength() << " len=" << ackedBuffer->second->data.chainLength()
<< " eof=" << ackedBuffer->second->eof << " " << stream.conn; << " eof=" << ackedBuffer->second->eof << " " << stream.conn;
stream.updateAckedIntervals( auto updateResult = stream.updateAckedIntervals(
ackedBuffer->second->offset, ackedBuffer->second->offset,
ackedBuffer->second->data.chainLength(), ackedBuffer->second->data.chainLength(),
ackedBuffer->second->eof); ackedBuffer->second->eof);
if (!updateResult.has_value()) {
return quic::make_unexpected(QuicError(
TransportErrorCode::INTERNAL_ERROR,
"Failed to update acked intervals"));
}
stream.retransmissionBuffer.erase(ackedBuffer); stream.retransmissionBuffer.erase(ackedBuffer);
} }
} else { } else {
@@ -162,10 +167,15 @@ quic::Expected<void, QuicError> sendAckSMHandler(
<< " offset=" << ackedBuffer->second.offset << " offset=" << ackedBuffer->second.offset
<< " len=" << ackedBuffer->second.length << " len=" << ackedBuffer->second.length
<< " eof=" << ackedBuffer->second.eof << " " << stream.conn; << " eof=" << ackedBuffer->second.eof << " " << stream.conn;
stream.updateAckedIntervals( auto updateResult = stream.updateAckedIntervals(
ackedBuffer->second.offset, ackedBuffer->second.offset,
ackedBuffer->second.length, ackedBuffer->second.length,
ackedBuffer->second.eof); ackedBuffer->second.eof);
if (!updateResult.has_value()) {
return quic::make_unexpected(QuicError(
TransportErrorCode::INTERNAL_ERROR,
"Failed to update acked intervals"));
}
stream.retransmissionBufMetas.erase(ackedBuffer); stream.retransmissionBufMetas.erase(ackedBuffer);
} }
} }

View File

@@ -345,7 +345,7 @@ TEST_F(QuicResetSentStateTest, ReliableRstAckNoReduction) {
stream.readBuffer.emplace_back( stream.readBuffer.emplace_back(
folly::IOBuf::copyBuffer("One more thing"), 0xABCD, false); folly::IOBuf::copyBuffer("One more thing"), 0xABCD, false);
RstStreamFrame frame(id, GenericApplicationErrorCode::UNKNOWN, 0); RstStreamFrame frame(id, GenericApplicationErrorCode::UNKNOWN, 0);
stream.updateAckedIntervals(0, 3, false); ASSERT_TRUE(stream.updateAckedIntervals(0, 3, false).has_value());
auto result = sendRstAckSMHandler(stream, 5); auto result = sendRstAckSMHandler(stream, 5);
ASSERT_FALSE(result.hasError()); ASSERT_FALSE(result.hasError());
@@ -370,7 +370,7 @@ TEST_F(QuicResetSentStateTest, ReliableRstAckReduction) {
stream.readBuffer.emplace_back( stream.readBuffer.emplace_back(
folly::IOBuf::copyBuffer("One more thing"), 0xABCD, false); folly::IOBuf::copyBuffer("One more thing"), 0xABCD, false);
RstStreamFrame frame(id, GenericApplicationErrorCode::UNKNOWN, 0); RstStreamFrame frame(id, GenericApplicationErrorCode::UNKNOWN, 0);
stream.updateAckedIntervals(0, 1, false); ASSERT_TRUE(stream.updateAckedIntervals(0, 1, false).has_value());
auto result = sendRstAckSMHandler(stream, 1); auto result = sendRstAckSMHandler(stream, 1);
ASSERT_FALSE(result.hasError()); ASSERT_FALSE(result.hasError());
@@ -434,7 +434,7 @@ TEST_F(QuicResetSentStateTest, ResetSentToClosedTransition1) {
StreamId id = 5; StreamId id = 5;
QuicStreamState stream(id, *conn); QuicStreamState stream(id, *conn);
stream.sendState = StreamSendState::ResetSent; stream.sendState = StreamSendState::ResetSent;
stream.updateAckedIntervals(0, 5, false); ASSERT_TRUE(stream.updateAckedIntervals(0, 5, false).has_value());
auto result = sendRstAckSMHandler(stream, 5); auto result = sendRstAckSMHandler(stream, 5);
ASSERT_FALSE(result.hasError()); ASSERT_FALSE(result.hasError());
EXPECT_EQ(stream.sendState, StreamSendState::Closed); EXPECT_EQ(stream.sendState, StreamSendState::Closed);
@@ -447,7 +447,7 @@ TEST_F(QuicResetSentStateTest, ResetSentToClosedTransition2) {
StreamId id = 5; StreamId id = 5;
QuicStreamState stream(id, *conn); QuicStreamState stream(id, *conn);
stream.sendState = StreamSendState::ResetSent; stream.sendState = StreamSendState::ResetSent;
stream.updateAckedIntervals(0, 4, false); ASSERT_TRUE(stream.updateAckedIntervals(0, 4, false).has_value());
auto result = sendRstAckSMHandler(stream, 5); auto result = sendRstAckSMHandler(stream, 5);
ASSERT_FALSE(result.hasError()); ASSERT_FALSE(result.hasError());
EXPECT_EQ(stream.sendState, StreamSendState::ResetSent); EXPECT_EQ(stream.sendState, StreamSendState::ResetSent);

View File

@@ -73,12 +73,14 @@ TEST_P(AddPacketToAckStateTest, FirstPacketNotOutOfOrder) {
/** /**
* We skip setting the getAckState(conn, * We skip setting the getAckState(conn,
* GetParam()).largestReceivedUdpPacketNum to simulate that we haven't * GetParam()).largestReceivedUdpPacketNum to simulate that we haven't
* received any packets yet. `addPacketToAckState()` should return false for * received any packets yet. `addPacketToAckState()` should return distance 0
* the first packet received. * for the first packet received.
*/ */
PacketNum firstPacket = folly::Random::rand32(1, 100); PacketNum firstPacket = folly::Random::rand32(1, 100);
EXPECT_FALSE(addPacketToAckState( auto result = addPacketToAckState(
conn, getAckState(conn, GetParam()), firstPacket, buildPacketMinimal())); conn, getAckState(conn, GetParam()), firstPacket, buildPacketMinimal());
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), 0);
} }
TEST_P(AddPacketToAckStateTest, ReceiveNew) { TEST_P(AddPacketToAckStateTest, ReceiveNew) {
@@ -90,7 +92,8 @@ TEST_P(AddPacketToAckStateTest, ReceiveNew) {
PacketNum newReceived = currentLargestReceived + 1; PacketNum newReceived = currentLargestReceived + 1;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal()); conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal());
EXPECT_EQ(distance, 0); ASSERT_TRUE(distance.has_value());
EXPECT_EQ(distance.value(), 0);
EXPECT_GT( EXPECT_GT(
*getAckState(conn, GetParam()).largestRecvdPacketNum, *getAckState(conn, GetParam()).largestRecvdPacketNum,
currentLargestReceived); currentLargestReceived);
@@ -105,7 +108,8 @@ TEST_P(AddPacketToAckStateTest, ReceiveNewWithGap) {
PacketNum newReceived = currentLargestReceived + 3; PacketNum newReceived = currentLargestReceived + 3;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal()); conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal());
EXPECT_EQ(distance, 2); // newReceived is 2 after the expected pkt num ASSERT_TRUE(distance.has_value());
EXPECT_EQ(distance.value(), 2); // newReceived is 2 after the expected pkt num
EXPECT_GT( EXPECT_GT(
*getAckState(conn, GetParam()).largestRecvdPacketNum, *getAckState(conn, GetParam()).largestRecvdPacketNum,
currentLargestReceived); currentLargestReceived);
@@ -120,7 +124,9 @@ TEST_P(AddPacketToAckStateTest, ReceiveOld) {
PacketNum newReceived = currentLargestReceived - 1; PacketNum newReceived = currentLargestReceived - 1;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal()); conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal());
EXPECT_EQ(distance, 2); // newReceived is 2 before the expected pkt num ASSERT_TRUE(distance.has_value());
EXPECT_EQ(
distance.value(), 2); // newReceived is 2 before the expected pkt num
EXPECT_EQ( EXPECT_EQ(
*getAckState(conn, GetParam()).largestRecvdPacketNum, *getAckState(conn, GetParam()).largestRecvdPacketNum,
currentLargestReceived); currentLargestReceived);
@@ -135,7 +141,9 @@ TEST_P(AddPacketToAckStateTest, ReceiveOldWithGap) {
PacketNum newReceived = currentLargestReceived - 5; PacketNum newReceived = currentLargestReceived - 5;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal()); conn, getAckState(conn, GetParam()), newReceived, buildPacketMinimal());
EXPECT_EQ(distance, 6); // newReceived is 6 before the expected pkt num ASSERT_TRUE(distance.has_value());
EXPECT_EQ(
distance.value(), 6); // newReceived is 6 before the expected pkt num
EXPECT_EQ( EXPECT_EQ(
*getAckState(conn, GetParam()).largestRecvdPacketNum, *getAckState(conn, GetParam()).largestRecvdPacketNum,
currentLargestReceived); currentLargestReceived);
@@ -159,7 +167,8 @@ TEST_P(AddPacketToAckStateTest, ReceiveWithECN) {
packet.tosValue = kEcnECT0; packet.tosValue = kEcnECT0;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), ++nextPacketNum, packet); conn, getAckState(conn, GetParam()), ++nextPacketNum, packet);
EXPECT_EQ(distance, 0); ASSERT_TRUE(distance.has_value());
EXPECT_EQ(distance.value(), 0);
// Seen 1 ECT0, 0 ECT1, 0 CE. // Seen 1 ECT0, 0 ECT1, 0 CE.
EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 0); EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 0);
@@ -172,7 +181,8 @@ TEST_P(AddPacketToAckStateTest, ReceiveWithECN) {
packet.tosValue = kEcnECT1; packet.tosValue = kEcnECT1;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), ++nextPacketNum, packet); conn, getAckState(conn, GetParam()), ++nextPacketNum, packet);
EXPECT_EQ(distance, 0); ASSERT_TRUE(distance.has_value());
EXPECT_EQ(distance.value(), 0);
// Seen 1 ECT0, 1 ECT1, 0 CE. // Seen 1 ECT0, 1 ECT1, 0 CE.
EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 0); EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 0);
@@ -185,7 +195,8 @@ TEST_P(AddPacketToAckStateTest, ReceiveWithECN) {
packet.tosValue = kEcnCE; packet.tosValue = kEcnCE;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), ++nextPacketNum, packet); conn, getAckState(conn, GetParam()), ++nextPacketNum, packet);
EXPECT_EQ(distance, 0); ASSERT_TRUE(distance.has_value());
EXPECT_EQ(distance.value(), 0);
// Seen 1 ECT0, 1 ECT1, 1 CE. // Seen 1 ECT0, 1 ECT1, 1 CE.
EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 1); EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 1);
@@ -198,7 +209,8 @@ TEST_P(AddPacketToAckStateTest, ReceiveWithECN) {
packet.tosValue = kEcnCE; packet.tosValue = kEcnCE;
auto distance = addPacketToAckState( auto distance = addPacketToAckState(
conn, getAckState(conn, GetParam()), ++nextPacketNum, packet); conn, getAckState(conn, GetParam()), ++nextPacketNum, packet);
EXPECT_EQ(distance, 0); ASSERT_TRUE(distance.has_value());
EXPECT_EQ(distance.value(), 0);
// Seen 1 ECT0, 1 ECT1, 2 CE. // Seen 1 ECT0, 1 ECT1, 2 CE.
EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 2); EXPECT_EQ(getAckState(conn, GetParam()).ecnCECountReceived, 2);

View File

@@ -703,7 +703,7 @@ TEST(StreamDataTest, AllBytesAckedTillNotStartAtZero) {
QuicStreamState state(0, qcsb); QuicStreamState state(0, qcsb);
EXPECT_TRUE(state.ackedIntervals.empty()); EXPECT_TRUE(state.ackedIntervals.empty());
state.updateAckedIntervals(1, 5, false); ASSERT_TRUE(state.updateAckedIntervals(1, 5, false).has_value());
EXPECT_EQ(state.allBytesAckedTill(5), false); EXPECT_EQ(state.allBytesAckedTill(5), false);
} }
@@ -712,7 +712,7 @@ TEST(StreamDataTest, AllBytesAckedTillNotEnoughLength) {
QuicStreamState state(0, qcsb); QuicStreamState state(0, qcsb);
EXPECT_TRUE(state.ackedIntervals.empty()); EXPECT_TRUE(state.ackedIntervals.empty());
state.updateAckedIntervals(0, 5, false); ASSERT_TRUE(state.updateAckedIntervals(0, 5, false).has_value());
EXPECT_EQ(state.allBytesAckedTill(5), false); EXPECT_EQ(state.allBytesAckedTill(5), false);
} }
@@ -721,7 +721,7 @@ TEST(StreamDataTest, AllBytesAckedPass) {
QuicStreamState state(0, qcsb); QuicStreamState state(0, qcsb);
EXPECT_TRUE(state.ackedIntervals.empty()); EXPECT_TRUE(state.ackedIntervals.empty());
state.updateAckedIntervals(0, 6, false); ASSERT_TRUE(state.updateAckedIntervals(0, 6, false).has_value());
EXPECT_EQ(state.allBytesAckedTill(5), true); EXPECT_EQ(state.allBytesAckedTill(5), true);
} }
@@ -730,8 +730,8 @@ TEST(StreamDataTest, AllBytesAckedDisjointIntervals) {
QuicStreamState state(0, qcsb); QuicStreamState state(0, qcsb);
EXPECT_TRUE(state.ackedIntervals.empty()); EXPECT_TRUE(state.ackedIntervals.empty());
state.updateAckedIntervals(0, 2, false); ASSERT_TRUE(state.updateAckedIntervals(0, 2, false).has_value());
state.updateAckedIntervals(3, 5, false); ASSERT_TRUE(state.updateAckedIntervals(3, 5, false).has_value());
EXPECT_EQ(state.allBytesAckedTill(5), false); EXPECT_EQ(state.allBytesAckedTill(5), false);
} }