Skip to content

Commit

Permalink
Create a RST_STREAM_AT frame type and decoding functionality
Browse files Browse the repository at this point in the history
Summary:
You can find the format of the RST_STREAM_AT frame in the [RFC](https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset-06#name-reset_stream_at-frame). In this change, I'm adding RST_STREAM_AT to the FrameType enum and am also adding the functionality to decode such frames.

Note that we ignore the case when we receive RST_STREAM_AT frames (because we haven't implemented their handler yet).

Reviewed By: hanidamlaj

Differential Revision: D64836490

fbshipit-source-id: 5bd742b14c343ce2f26dd29d3a285a6fed60676a
  • Loading branch information
Aman Sharma authored and facebook-github-bot committed Nov 15, 2024
1 parent 0db312e commit efba618
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 10 deletions.
1 change: 1 addition & 0 deletions quic/QuicConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ enum class FrameType : uint64_t {
// CONNECTION_CLOSE_APP_ERR frametype is use to indicate application errors
CONNECTION_CLOSE_APP_ERR = 0x1D,
HANDSHAKE_DONE = 0x1E,
RST_STREAM_AT = 0x24,
DATAGRAM = 0x30,
DATAGRAM_LEN = 0x31,
KNOB = 0x1550,
Expand Down
4 changes: 4 additions & 0 deletions quic/client/QuicClientTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,10 @@ void QuicClientTransport::processUdpPacketData(
}
case QuicFrame::Type::RstStreamFrame: {
RstStreamFrame& frame = *quicFrame.asRstStreamFrame();
if (frame.reliableSize) {
// We're not yet supporting the handling of RESET_STREAM_AT frames
break;
}
VLOG(10) << "Client received reset stream=" << frame.streamId << " "
<< *this;
pktHasRetransmittableData = true;
Expand Down
36 changes: 30 additions & 6 deletions quic/codec/Decode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,14 @@ ReadAckFrame decodeAckFrameWithECN(
return readAckFrame;
}

RstStreamFrame decodeRstStreamFrame(folly::io::Cursor& cursor) {
RstStreamFrame decodeRstStreamFrame(folly::io::Cursor& cursor, bool reliable) {
auto streamId = decodeQuicInteger(cursor);
auto frameType = reliable ? FrameType::RST_STREAM_AT : FrameType::RST_STREAM;
if (!streamId) {
throw QuicTransportException(
"Bad streamId",
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
quic::FrameType::RST_STREAM);
frameType);
}
ApplicationErrorCode errorCode;
auto varCode = decodeQuicInteger(cursor);
Expand All @@ -374,17 +375,38 @@ RstStreamFrame decodeRstStreamFrame(folly::io::Cursor& cursor) {
throw QuicTransportException(
"Cannot decode error code",
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
quic::FrameType::RST_STREAM);
frameType);
}
auto finalSize = decodeQuicInteger(cursor);
if (!finalSize) {
throw QuicTransportException(
"Bad offset",
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
quic::FrameType::RST_STREAM);
frameType);
}
folly::Optional<std::pair<uint64_t, size_t>> reliableSize = folly::none;
if (reliable) {
reliableSize = decodeQuicInteger(cursor);
if (!reliableSize) {
throw QuicTransportException(
"Bad value of reliable size",
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
frameType);
}

if (reliableSize->first > finalSize->first) {
throw QuicTransportException(
"Reliable size is greater than final size",
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
frameType);
}
}
return RstStreamFrame(
folly::to<StreamId>(streamId->first), errorCode, finalSize->first);
folly::to<StreamId>(streamId->first),
errorCode,
finalSize->first,
reliableSize ? folly::Optional<uint64_t>(reliableSize->first)
: folly::none);
}

StopSendingFrame decodeStopSendingFrame(folly::io::Cursor& cursor) {
Expand Down Expand Up @@ -858,7 +880,9 @@ QuicFrame parseFrame(
case FrameType::ACK_ECN:
return QuicFrame(decodeAckFrameWithECN(cursor, header, params));
case FrameType::RST_STREAM:
return QuicFrame(decodeRstStreamFrame(cursor));
case FrameType::RST_STREAM_AT:
return QuicFrame(decodeRstStreamFrame(
cursor, frameType == FrameType::RST_STREAM_AT));
case FrameType::STOP_SENDING:
return QuicFrame(decodeStopSendingFrame(cursor));
case FrameType::CRYPTO_FRAME:
Expand Down
2 changes: 1 addition & 1 deletion quic/codec/Decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ QuicFrame parseFrame(
*/
PaddingFrame decodePaddingFrame(folly::io::Cursor&);

RstStreamFrame decodeRstStreamFrame(folly::io::Cursor& cursor);
RstStreamFrame decodeRstStreamFrame(folly::io::Cursor& cursor, bool reliable);

ConnectionCloseFrame decodeConnectionCloseFrame(folly::io::Cursor& cursor);

Expand Down
2 changes: 2 additions & 0 deletions quic/codec/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ std::string_view toString(FrameType frame) {
return "ACK_ECN";
case FrameType::RST_STREAM:
return "RST_STREAM";
case FrameType::RST_STREAM_AT:
return "RST_STREAM_AT";
case FrameType::STOP_SENDING:
return "STOP_SENDING";
case FrameType::CRYPTO_FRAME:
Expand Down
13 changes: 10 additions & 3 deletions quic/codec/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,20 +295,27 @@ struct WriteAckFrameResult {
timestampsWritten(timestampsWrittenIn) {}
};

// Can represent either a simple RESET_STREAM frame or a RESET_STREAM_AT
// frame, depending on whether reliableSize is set.
struct RstStreamFrame {
StreamId streamId;
ApplicationErrorCode errorCode;
uint64_t finalSize;
folly::Optional<uint64_t> reliableSize;

RstStreamFrame(
StreamId streamIdIn,
ApplicationErrorCode errorCodeIn,
uint64_t finalSizeIn)
: streamId(streamIdIn), errorCode(errorCodeIn), finalSize(finalSizeIn) {}
uint64_t finalSizeIn,
folly::Optional<uint64_t> reliableSizeIn = folly::none)
: streamId(streamIdIn),
errorCode(errorCodeIn),
finalSize(finalSizeIn),
reliableSize(reliableSizeIn) {}

bool operator==(const RstStreamFrame& rhs) const {
return streamId == rhs.streamId && errorCode == rhs.errorCode &&
finalSize == rhs.finalSize;
finalSize == rhs.finalSize && reliableSize == rhs.reliableSize;
}
};

Expand Down
89 changes: 89 additions & 0 deletions quic/codec/test/DecodeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,43 @@ std::unique_ptr<folly::IOBuf> createAckFrame(
return ackFrame;
}

std::unique_ptr<folly::IOBuf> createRstStreamFrame(
StreamId streamId,
ApplicationErrorCode errorCode,
uint64_t finalSize,
folly::Optional<uint64_t> reliableSize = folly::none) {
std::unique_ptr<folly::IOBuf> rstStreamFrame = folly::IOBuf::create(0);
BufAppender wcursor(rstStreamFrame.get(), 10);
auto appenderOp = [&](auto val) { wcursor.writeBE(val); };

FrameType frameType =
reliableSize ? FrameType::RST_STREAM_AT : FrameType::RST_STREAM;

// Write the frame type
QuicInteger frameTypeQuicInt(static_cast<uint8_t>(frameType));
frameTypeQuicInt.encode(appenderOp);

// Write the stream id
QuicInteger streamIdQuicInt(streamId);
streamIdQuicInt.encode(appenderOp);

// Write the error code
QuicInteger errorCodeQuicInt(static_cast<uint64_t>(errorCode));
errorCodeQuicInt.encode(appenderOp);

// Write the final size
QuicInteger finalSizeQuicInt(finalSize);
finalSizeQuicInt.encode(appenderOp);

if (reliableSize) {
// Write the reliable size
QuicInteger reliableSizeQuicInt(*reliableSize);
reliableSizeQuicInt.encode(appenderOp);
}

return rstStreamFrame;
}

template <class StreamIdType = StreamId>
std::unique_ptr<folly::IOBuf> createStreamFrame(
Optional<QuicInteger> streamId,
Expand Down Expand Up @@ -923,4 +960,56 @@ TEST_F(DecodeTest, AckFrequencyFrameDecodeInvalidReserved) {
EXPECT_THROW(decodeAckFrequencyFrame(cursor), QuicTransportException);
}

TEST_F(DecodeTest, RstStreamFrame) {
auto buf = createRstStreamFrame(0, 0, 10);
BufQueue queue(std::move(buf));
auto frame = parseFrame(
queue,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
auto rstStreamFrame = frame.asRstStreamFrame();
EXPECT_EQ(rstStreamFrame->streamId, 0);
EXPECT_EQ(rstStreamFrame->errorCode, 0);
EXPECT_EQ(rstStreamFrame->finalSize, 10);
EXPECT_FALSE(rstStreamFrame->reliableSize.hasValue());
}

TEST_F(DecodeTest, RstStreamAtFrame) {
auto buf = createRstStreamFrame(0, 0, 10, 9);
BufQueue queue(std::move(buf));
auto frame = parseFrame(
queue,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST));
auto rstStreamFrame = frame.asRstStreamFrame();
EXPECT_EQ(rstStreamFrame->streamId, 0);
EXPECT_EQ(rstStreamFrame->errorCode, 0);
EXPECT_EQ(rstStreamFrame->finalSize, 10);
EXPECT_EQ(*rstStreamFrame->reliableSize, 9);
}

TEST_F(DecodeTest, RstStreamAtFrameRelSizeGreaterThanOffset) {
auto buf = createRstStreamFrame(0, 0, 10, 11);
BufQueue queue(std::move(buf));
EXPECT_THROW(
parseFrame(
queue,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}

TEST_F(DecodeTest, RstStreamAtTruncated) {
auto buf = createRstStreamFrame(0, 0, 10, 9);
buf->coalesce();
buf->trimEnd(1);
BufQueue queue(std::move(buf));
EXPECT_THROW(
parseFrame(
queue,
makeHeader(),
CodecParameters(kDefaultAckDelayExponent, QuicVersion::MVFST)),
QuicTransportException);
}

} // namespace quic::test
2 changes: 2 additions & 0 deletions quic/logging/QLoggerConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ folly::StringPiece toQlogString(FrameType frame) {
return "ack_ecn";
case FrameType::RST_STREAM:
return "rst_stream";
case FrameType::RST_STREAM_AT:
return "rst_stream_at";
case FrameType::STOP_SENDING:
return "stop_sending";
case FrameType::CRYPTO_FRAME:
Expand Down
4 changes: 4 additions & 0 deletions quic/server/state/ServerStateMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,10 @@ void onServerReadDataFromOpen(
}
case QuicFrame::Type::RstStreamFrame: {
RstStreamFrame& frame = *quicFrame.asRstStreamFrame();
if (frame.reliableSize) {
// We're not yet supporting the handling of RESET_STREAM_AT frames
break;
}
VLOG(10) << "Server received reset stream=" << frame.streamId << " "
<< conn;
pktHasRetransmittableData = true;
Expand Down

0 comments on commit efba618

Please sign in to comment.