1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-09 10:00:57 +03:00
Files
mvfst/quic/codec/QuicWriteCodec.cpp
Viktor Chynarov 0ab06b6fc9 Make RetireConnectionIdFrame a QuicSimpleFrame (for now)
Reviewed By: sharma95

Differential Revision: D15264935

fbshipit-source-id: 8c66c4604e24442f4c77e8eb2d10901d1702adc7
2019-09-30 17:40:13 -07:00

679 lines
27 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#include <quic/codec/QuicWriteCodec.h>
#include <algorithm>
#include <quic/QuicConstants.h>
#include <quic/QuicException.h>
#include <quic/codec/QuicInteger.h>
namespace {
bool packetSpaceCheck(uint64_t limit, size_t require);
/**
* A helper function to check if there are enough space to write in the packet.
* Return: true if there is enough space, false otherwise
*/
bool packetSpaceCheck(uint64_t limit, size_t require) {
return (folly::to<uint64_t>(require) <= limit);
}
} // namespace
namespace quic {
folly::Optional<uint64_t> writeStreamFrameHeader(
PacketBuilderInterface& builder,
StreamId id,
uint64_t offset,
uint64_t writeBufferLen,
uint64_t flowControlLen,
bool fin) {
if (builder.remainingSpaceInPkt() == 0) {
return folly::none;
}
if (writeBufferLen == 0 && !fin) {
throw QuicInternalException(
"No data or fin supplied when writing stream.",
LocalErrorCode::INTERNAL_ERROR);
}
StreamTypeField::Builder streamTypeBuilder;
QuicInteger idInt(id);
QuicInteger offsetInt(offset);
// First account for the things that are non-optional: frame type and stream
// id.
uint64_t headerSize = sizeof(FrameType::STREAM) + idInt.getSize();
if (offset != 0) {
streamTypeBuilder.setOffset();
headerSize += offsetInt.getSize();
}
if (builder.remainingSpaceInPkt() < headerSize) {
VLOG(4) << "No space in packet for stream header. stream=" << id
<< " remaining=" << builder.remainingSpaceInPkt();
return folly::none;
}
// Next we have to deal with the data length. This is trickier. The length of
// data we are able to send depends on 3 things: how much we have in the
// buffer, how much flow control we have, and the remaining size in the
// packet. If the amount we want to send is >= the remaining packet size after
// the header so far we can omit the length field and consume the rest of the
// packet. If it is not then we need to use the minimal varint encoding
// possible to avoid sending not-full packets.
// Note: we don't bother with one potential optimization, which is writing
// a zero length fin-only stream frame and omitting the length field.
uint64_t dataLen = std::min(writeBufferLen, flowControlLen);
uint64_t dataLenLen = 0;
if (dataLen > 0 && dataLen >= builder.remainingSpaceInPkt() - headerSize) {
// We can fill this entire packet with the rest of this stream frame.
dataLen = builder.remainingSpaceInPkt() - headerSize;
} else {
if (dataLen <= kOneByteLimit - 1) {
dataLenLen = 1;
} else if (dataLen <= kTwoByteLimit - 2) {
dataLenLen = 2;
} else if (dataLen <= kFourByteLimit - 4) {
dataLenLen = 4;
} else if (dataLen <= kEightByteLimit - 8) {
dataLenLen = 8;
} else {
// This should never really happen as dataLen is bounded by the remaining
// space in the packet which should be << kEightByteLimit.
throw QuicInternalException(
"Stream frame length too large.", LocalErrorCode::INTERNAL_ERROR);
}
}
if (dataLenLen > 0) {
if (dataLen != 0 &&
headerSize + dataLenLen >= builder.remainingSpaceInPkt()) {
VLOG(4) << "No space in packet for stream header. stream=" << id
<< " remaining=" << builder.remainingSpaceInPkt();
return folly::none;
}
// We have to encode the actual data length in the header.
headerSize += dataLenLen;
if (builder.remainingSpaceInPkt() < dataLen + headerSize) {
dataLen = builder.remainingSpaceInPkt() - headerSize;
}
}
bool shouldSetFin = fin && dataLen == writeBufferLen;
if (dataLen == 0 && !shouldSetFin) {
// This would be an empty non-fin stream frame.
return folly::none;
}
if (builder.remainingSpaceInPkt() < headerSize) {
VLOG(4) << "No space in packet for stream header. stream=" << id
<< " remaining=" << builder.remainingSpaceInPkt();
return folly::none;
}
// Done with the accounting, set the bits and write the actual frame header.
if (dataLenLen > 0) {
streamTypeBuilder.setLength();
}
if (shouldSetFin) {
streamTypeBuilder.setFin();
}
auto streamType = streamTypeBuilder.build();
builder.writeBE(streamType.fieldValue());
builder.write(idInt);
if (offset != 0) {
builder.write(offsetInt);
}
if (dataLenLen > 0) {
builder.write(QuicInteger(dataLen));
}
builder.appendFrame(
WriteStreamFrame(id, offset, dataLen, streamType.hasFin()));
DCHECK(dataLen <= builder.remainingSpaceInPkt());
return folly::make_optional(dataLen);
}
void writeStreamFrameData(
PacketBuilderInterface& builder,
const folly::IOBufQueue& writeBuffer,
uint64_t dataLen) {
if (dataLen > 0) {
Buf streamData;
folly::io::Cursor cursor(writeBuffer.front());
cursor.clone(streamData, dataLen);
builder.insert(std::move(streamData));
}
}
void writeStreamFrameData(
PacketBuilderInterface& builder,
Buf writeBuffer,
uint64_t dataLen) {
if (dataLen > 0) {
Buf streamData;
folly::io::Cursor cursor(writeBuffer.get());
cursor.clone(streamData, dataLen);
builder.insert(std::move(streamData));
}
}
folly::Optional<WriteCryptoFrame>
writeCryptoFrame(uint64_t offsetIn, Buf data, PacketBuilderInterface& builder) {
uint64_t spaceLeftInPkt = builder.remainingSpaceInPkt();
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::CRYPTO_FRAME));
QuicInteger offsetInteger(offsetIn);
size_t lengthBytes = 2;
size_t cryptoFrameHeaderSize =
intFrameType.getSize() + offsetInteger.getSize() + lengthBytes;
if (spaceLeftInPkt <= cryptoFrameHeaderSize) {
VLOG(3) << "No space left in packet to write cryptoFrame header of size: "
<< cryptoFrameHeaderSize << ", space left=" << spaceLeftInPkt;
return folly::none;
}
size_t spaceRemaining = spaceLeftInPkt - cryptoFrameHeaderSize;
size_t dataLength = data->computeChainDataLength();
size_t writeableData = std::min(dataLength, spaceRemaining);
QuicInteger lengthVarInt(writeableData);
if (lengthVarInt.getSize() > lengthBytes) {
throw QuicInternalException(
std::string("Length bytes representation"),
LocalErrorCode::CODEC_ERROR);
}
data->coalesce();
data->trimEnd(dataLength - writeableData);
builder.write(intFrameType);
builder.write(offsetInteger);
builder.write(lengthVarInt);
builder.insert(std::move(data));
builder.appendFrame(WriteCryptoFrame(offsetIn, lengthVarInt.getValue()));
return WriteCryptoFrame(offsetIn, lengthVarInt.getValue());
}
size_t fillFrameWithAckBlocks(
const IntervalSet<PacketNum>& ackBlocks,
WriteAckFrame& ackFrame,
uint64_t bytesLimit);
size_t fillFrameWithAckBlocks(
const IntervalSet<PacketNum>& ackBlocks,
WriteAckFrame& ackFrame,
uint64_t bytesLimit) {
PacketNum currentSeqNum = ackBlocks.crbegin()->start;
// starts off with 0 which is what we assumed the initial ack block to be for
// the largest acked.
size_t numAdditionalAckBlocks = 0;
QuicInteger previousNumAckBlockInt(numAdditionalAckBlocks);
for (auto blockItr = ackBlocks.crbegin() + 1; blockItr != ackBlocks.crend();
++blockItr) {
const auto& currBlock = *blockItr;
// These must be true because of the properties of the interval set.
CHECK_GE(currentSeqNum, currBlock.end + 2);
PacketNum gap = currentSeqNum - currBlock.end - 2;
PacketNum currBlockLen = currBlock.end - currBlock.start;
QuicInteger gapInt(gap);
QuicInteger currentBlockLenInt(currBlockLen);
QuicInteger numAckBlocksInt(numAdditionalAckBlocks + 1);
size_t additionalSize = gapInt.getSize() + currentBlockLenInt.getSize() +
(numAckBlocksInt.getSize() - previousNumAckBlockInt.getSize());
if (bytesLimit < additionalSize) {
break;
}
numAdditionalAckBlocks++;
bytesLimit -= additionalSize;
previousNumAckBlockInt = numAckBlocksInt;
currentSeqNum = currBlock.start;
ackFrame.ackBlocks.insert(currBlock.start, currBlock.end);
}
return numAdditionalAckBlocks;
}
folly::Optional<AckFrameWriteResult> writeAckFrame(
const quic::AckFrameMetaData& ackFrameMetaData,
PacketBuilderInterface& builder) {
if (ackFrameMetaData.ackBlocks.empty()) {
return folly::none;
}
// The last block must be the largest block.
auto largestAckedPacket = ackFrameMetaData.ackBlocks.back().end;
// ackBlocks are already an interval set so each value is naturally
// non-overlapping.
auto firstAckBlockLength =
largestAckedPacket - ackFrameMetaData.ackBlocks.back().start;
WriteAckFrame ackFrame;
uint64_t spaceLeft = builder.remainingSpaceInPkt();
uint64_t beginningSpace = spaceLeft;
// We could technically split the range if the size of the representation of
// the integer is too large, but that gets super tricky and is of dubious
// value.
QuicInteger largestAckedPacketInt(largestAckedPacket);
QuicInteger firstAckBlockLengthInt(firstAckBlockLength);
// Convert ackDelay to unsigned value explicitly before right shifting to
// avoid issues with right shifting signed values.
uint64_t encodedAckDelay = ackFrameMetaData.ackDelay.count();
encodedAckDelay = encodedAckDelay >> ackFrameMetaData.ackDelayExponent;
QuicInteger ackDelayInt(encodedAckDelay);
QuicInteger minAdditionalAckBlockCount(0);
// Required fields are Type, LargestAcked, AckDelay, AckBlockCount,
// firstAckBlockLength
QuicInteger encodedintFrameType(static_cast<uint8_t>(FrameType::ACK));
auto headerSize = encodedintFrameType.getSize() +
largestAckedPacketInt.getSize() + ackDelayInt.getSize() +
minAdditionalAckBlockCount.getSize() + firstAckBlockLengthInt.getSize();
if (spaceLeft < headerSize) {
return folly::none;
}
spaceLeft -= headerSize;
auto numAdditionalAckBlocks =
fillFrameWithAckBlocks(ackFrameMetaData.ackBlocks, ackFrame, spaceLeft);
QuicInteger numAdditionalAckBlocksInt(numAdditionalAckBlocks);
builder.write(encodedintFrameType);
builder.write(largestAckedPacketInt);
builder.write(ackDelayInt);
builder.write(numAdditionalAckBlocksInt);
builder.write(firstAckBlockLengthInt);
PacketNum currentSeqNum = ackFrameMetaData.ackBlocks.back().start;
for (auto it = ackFrame.ackBlocks.crbegin(); it != ackFrame.ackBlocks.crend();
++it) {
CHECK_GE(currentSeqNum, it->end + 2);
PacketNum gap = currentSeqNum - it->end - 2;
PacketNum currBlockLen = it->end - it->start;
QuicInteger gapInt(gap);
QuicInteger currentBlockLenInt(currBlockLen);
builder.write(gapInt);
builder.write(currentBlockLenInt);
currentSeqNum = it->start;
}
// also the largest ack block since we already accounted for the space to
// write to it.
ackFrame.ackBlocks.insert(
ackFrameMetaData.ackBlocks.back().start,
ackFrameMetaData.ackBlocks.back().end);
ackFrame.ackDelay = ackFrameMetaData.ackDelay;
builder.appendFrame(std::move(ackFrame));
return AckFrameWriteResult(
beginningSpace - builder.remainingSpaceInPkt(),
1 + numAdditionalAckBlocks);
}
size_t writeSimpleFrame(
QuicSimpleFrame&& frame,
PacketBuilderInterface& builder) {
using FrameTypeType = std::underlying_type<FrameType>::type;
uint64_t spaceLeft = builder.remainingSpaceInPkt();
return folly::variant_match(
frame,
[&](StopSendingFrame& stopSendingFrame) {
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::STOP_SENDING));
QuicInteger streamId(stopSendingFrame.streamId);
QuicInteger errorCode(
static_cast<uint64_t>(stopSendingFrame.errorCode));
auto version = builder.getVersion();
size_t errorSize = version == QuicVersion::MVFST_OLD
? sizeof(ApplicationErrorCode)
: errorCode.getSize();
auto stopSendingFrameSize =
intFrameType.getSize() + streamId.getSize() + errorSize;
if (packetSpaceCheck(spaceLeft, stopSendingFrameSize)) {
builder.write(intFrameType);
builder.write(streamId);
if (version == QuicVersion::MVFST_OLD) {
builder.writeBE(
static_cast<ApplicationErrorCode>(stopSendingFrame.errorCode));
} else {
builder.write(errorCode);
}
builder.appendFrame(std::move(stopSendingFrame));
return stopSendingFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](MinStreamDataFrame& minStreamDataFrame) {
QuicInteger streamId(minStreamDataFrame.streamId);
QuicInteger maximumData(minStreamDataFrame.maximumData);
QuicInteger minimumStreamOffset(minStreamDataFrame.minimumStreamOffset);
QuicInteger frameType(
static_cast<FrameTypeType>(FrameType::MIN_STREAM_DATA));
auto minStreamDataFrameSize = frameType.getSize() + streamId.getSize() +
maximumData.getSize() + minimumStreamOffset.getSize();
if (packetSpaceCheck(spaceLeft, minStreamDataFrameSize)) {
builder.write(frameType);
builder.write(streamId);
builder.write(maximumData);
builder.write(minimumStreamOffset);
builder.appendFrame(std::move(minStreamDataFrame));
return minStreamDataFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](ExpiredStreamDataFrame& expiredStreamDataFrame) {
QuicInteger frameType(
static_cast<FrameTypeType>(FrameType::EXPIRED_STREAM_DATA));
QuicInteger streamId(expiredStreamDataFrame.streamId);
QuicInteger minimumStreamOffset(
expiredStreamDataFrame.minimumStreamOffset);
auto expiredStreamDataFrameSize = frameType.getSize() +
streamId.getSize() + minimumStreamOffset.getSize();
if (packetSpaceCheck(spaceLeft, expiredStreamDataFrameSize)) {
builder.write(frameType);
builder.write(streamId);
builder.write(minimumStreamOffset);
builder.appendFrame(std::move(expiredStreamDataFrame));
return expiredStreamDataFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](PathChallengeFrame& pathChallengeFrame) {
QuicInteger frameType(static_cast<uint8_t>(FrameType::PATH_CHALLENGE));
auto pathChallengeFrameSize =
frameType.getSize() + sizeof(pathChallengeFrame.pathData);
if (packetSpaceCheck(spaceLeft, pathChallengeFrameSize)) {
builder.write(frameType);
builder.writeBE(pathChallengeFrame.pathData);
builder.appendFrame(std::move(pathChallengeFrame));
return pathChallengeFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](PathResponseFrame& pathResponseFrame) {
QuicInteger frameType(static_cast<uint8_t>(FrameType::PATH_RESPONSE));
auto pathResponseFrameSize =
frameType.getSize() + sizeof(pathResponseFrame.pathData);
if (packetSpaceCheck(spaceLeft, pathResponseFrameSize)) {
builder.write(frameType);
builder.writeBE(pathResponseFrame.pathData);
builder.appendFrame(std::move(pathResponseFrame));
return pathResponseFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](NewConnectionIdFrame& newConnectionIdFrame) {
QuicInteger frameType(
static_cast<uint8_t>(FrameType::NEW_CONNECTION_ID));
QuicInteger sequenceNumber(newConnectionIdFrame.sequenceNumber);
QuicInteger retirePriorTo(newConnectionIdFrame.retirePriorTo);
// Include an 8-bit unsigned integer containing the length of the connId
auto newConnectionIdFrameSize = frameType.getSize() +
sequenceNumber.getSize() + retirePriorTo.getSize() +
sizeof(uint8_t) + newConnectionIdFrame.connectionId.size() +
newConnectionIdFrame.token.size();
if (packetSpaceCheck(spaceLeft, newConnectionIdFrameSize)) {
builder.write(frameType);
builder.write(sequenceNumber);
builder.write(retirePriorTo);
builder.writeBE(newConnectionIdFrame.connectionId.size());
builder.push(
newConnectionIdFrame.connectionId.data(),
newConnectionIdFrame.connectionId.size());
builder.push(
newConnectionIdFrame.token.data(),
newConnectionIdFrame.token.size());
builder.appendFrame(std::move(newConnectionIdFrame));
return newConnectionIdFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](MaxStreamsFrame& maxStreamsFrame) {
auto frameType = maxStreamsFrame.isForBidirectionalStream()
? FrameType::MAX_STREAMS_BIDI
: FrameType::MAX_STREAMS_UNI;
QuicInteger intFrameType(static_cast<FrameTypeType>(frameType));
QuicInteger streamCount(maxStreamsFrame.maxStreams);
auto maxStreamsFrameSize =
intFrameType.getSize() + streamCount.getSize();
if (packetSpaceCheck(spaceLeft, maxStreamsFrameSize)) {
builder.write(intFrameType);
builder.write(streamCount);
builder.appendFrame(maxStreamsFrame);
return maxStreamsFrameSize;
}
return size_t(0);
},
[&](RetireConnectionIdFrame& retireConnectionIdFrame) {
QuicInteger frameType(
static_cast<uint8_t>(FrameType::RETIRE_CONNECTION_ID));
QuicInteger sequence(retireConnectionIdFrame.sequenceNumber);
auto retireConnectionIdFrameSize =
frameType.getSize() + sequence.getSize();
if (packetSpaceCheck(spaceLeft, retireConnectionIdFrameSize)) {
builder.write(frameType);
builder.write(sequence);
builder.appendFrame(retireConnectionIdFrame);
return retireConnectionIdFrameSize;
}
// no space left in packet
return size_t(0);
});
}
size_t writeFrame(QuicWriteFrame&& frame, PacketBuilderInterface& builder) {
using FrameTypeType = std::underlying_type<FrameType>::type;
uint64_t spaceLeft = builder.remainingSpaceInPkt();
return folly::variant_match(
frame,
[&](PaddingFrame& paddingFrame) {
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::PADDING));
if (packetSpaceCheck(spaceLeft, intFrameType.getSize())) {
builder.write(intFrameType);
builder.appendFrame(std::move(paddingFrame));
return intFrameType.getSize();
}
return size_t(0);
},
[&](PingFrame& pingFrame) {
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::PING));
if (packetSpaceCheck(spaceLeft, intFrameType.getSize())) {
builder.write(intFrameType);
builder.appendFrame(std::move(pingFrame));
return intFrameType.getSize();
}
// no space left in packet
return size_t(0);
},
[&](RstStreamFrame& rstStreamFrame) {
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::RST_STREAM));
QuicInteger streamId(rstStreamFrame.streamId);
QuicInteger offset(rstStreamFrame.offset);
QuicInteger errorCode(static_cast<uint64_t>(rstStreamFrame.errorCode));
auto version = builder.getVersion();
size_t errorSize = version == QuicVersion::MVFST_OLD
? sizeof(ApplicationErrorCode)
: errorCode.getSize();
auto rstStreamFrameSize = intFrameType.getSize() + errorSize +
streamId.getSize() + offset.getSize();
if (packetSpaceCheck(spaceLeft, rstStreamFrameSize)) {
builder.write(intFrameType);
builder.write(streamId);
if (version == QuicVersion::MVFST_OLD) {
builder.writeBE(
static_cast<ApplicationErrorCode>(rstStreamFrame.errorCode));
} else {
builder.write(errorCode);
}
builder.write(offset);
builder.appendFrame(std::move(rstStreamFrame));
return rstStreamFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](MaxDataFrame& maxDataFrame) {
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::MAX_DATA));
QuicInteger maximumData(maxDataFrame.maximumData);
auto frameSize = intFrameType.getSize() + maximumData.getSize();
if (packetSpaceCheck(spaceLeft, frameSize)) {
builder.write(intFrameType);
builder.write(maximumData);
builder.appendFrame(std::move(maxDataFrame));
return frameSize;
}
// no space left in packet
return size_t(0);
},
[&](MaxStreamDataFrame& maxStreamDataFrame) {
QuicInteger intFrameType(
static_cast<uint8_t>(FrameType::MAX_STREAM_DATA));
QuicInteger streamId(maxStreamDataFrame.streamId);
QuicInteger maximumData(maxStreamDataFrame.maximumData);
auto maxStreamDataFrameSize =
intFrameType.getSize() + streamId.getSize() + maximumData.getSize();
if (packetSpaceCheck(spaceLeft, maxStreamDataFrameSize)) {
builder.write(intFrameType);
builder.write(streamId);
builder.write(maximumData);
builder.appendFrame(std::move(maxStreamDataFrame));
return maxStreamDataFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](DataBlockedFrame& blockedFrame) {
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::DATA_BLOCKED));
QuicInteger dataLimit(blockedFrame.dataLimit);
auto blockedFrameSize = intFrameType.getSize() + dataLimit.getSize();
if (packetSpaceCheck(spaceLeft, blockedFrameSize)) {
builder.write(intFrameType);
builder.write(dataLimit);
builder.appendFrame(std::move(blockedFrame));
return blockedFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](StreamDataBlockedFrame& streamBlockedFrame) {
QuicInteger intFrameType(
static_cast<uint8_t>(FrameType::STREAM_DATA_BLOCKED));
QuicInteger streamId(streamBlockedFrame.streamId);
QuicInteger dataLimit(streamBlockedFrame.dataLimit);
auto blockedFrameSize =
intFrameType.getSize() + streamId.getSize() + dataLimit.getSize();
if (packetSpaceCheck(spaceLeft, blockedFrameSize)) {
builder.write(intFrameType);
builder.write(streamId);
builder.write(dataLimit);
builder.appendFrame(std::move(streamBlockedFrame));
return blockedFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](StreamsBlockedFrame& streamsBlockedFrame) {
auto frameType = streamsBlockedFrame.isForBidirectionalStream()
? FrameType::STREAMS_BLOCKED_BIDI
: FrameType::STREAMS_BLOCKED_UNI;
QuicInteger intFrameType(static_cast<FrameTypeType>(frameType));
QuicInteger streamId(streamsBlockedFrame.streamLimit);
auto streamBlockedFrameSize =
intFrameType.getSize() + streamId.getSize();
if (packetSpaceCheck(spaceLeft, streamBlockedFrameSize)) {
builder.write(intFrameType);
builder.write(streamId);
builder.appendFrame(std::move(streamsBlockedFrame));
return streamBlockedFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](ConnectionCloseFrame& connectionCloseFrame) {
QuicInteger intFrameType(
static_cast<uint8_t>(FrameType::CONNECTION_CLOSE));
QuicInteger reasonLength(connectionCloseFrame.reasonPhrase.size());
QuicInteger closingFrameType(
static_cast<FrameTypeType>(connectionCloseFrame.closingFrameType));
QuicInteger errorCode(
static_cast<uint64_t>(connectionCloseFrame.errorCode));
auto version = builder.getVersion();
size_t errorSize = version == QuicVersion::MVFST_OLD
? sizeof(TransportErrorCode)
: errorCode.getSize();
auto connCloseFrameSize = intFrameType.getSize() + errorSize +
closingFrameType.getSize() + reasonLength.getSize() +
connectionCloseFrame.reasonPhrase.size();
if (packetSpaceCheck(spaceLeft, connCloseFrameSize)) {
builder.write(intFrameType);
if (version == QuicVersion::MVFST_OLD) {
builder.writeBE(
static_cast<std::underlying_type<TransportErrorCode>::type>(
connectionCloseFrame.errorCode));
} else {
builder.write(errorCode);
}
builder.write(closingFrameType);
builder.write(reasonLength);
builder.push(
(const uint8_t*)connectionCloseFrame.reasonPhrase.data(),
connectionCloseFrame.reasonPhrase.size());
builder.appendFrame(std::move(connectionCloseFrame));
return connCloseFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](ApplicationCloseFrame& applicationCloseFrame) {
QuicInteger intFrameType(
static_cast<uint8_t>(FrameType::APPLICATION_CLOSE));
QuicInteger reasonLength(applicationCloseFrame.reasonPhrase.size());
QuicInteger errorCode(
static_cast<uint64_t>(applicationCloseFrame.errorCode));
auto version = builder.getVersion();
size_t errorSize = version == QuicVersion::MVFST_OLD
? sizeof(ApplicationErrorCode)
: errorCode.getSize();
auto applicationCloseFrameSize = intFrameType.getSize() + errorSize +
reasonLength.getSize() + applicationCloseFrame.reasonPhrase.size();
if (packetSpaceCheck(spaceLeft, applicationCloseFrameSize)) {
builder.write(intFrameType);
if (version == QuicVersion::MVFST_OLD) {
builder.writeBE(static_cast<ApplicationErrorCode>(
applicationCloseFrame.errorCode));
} else {
builder.write(errorCode);
}
builder.write(reasonLength);
builder.push(
(const uint8_t*)applicationCloseFrame.reasonPhrase.data(),
applicationCloseFrame.reasonPhrase.size());
builder.appendFrame(std::move(applicationCloseFrame));
return applicationCloseFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](QuicSimpleFrame& simpleFrame) {
return writeSimpleFrame(std::move(simpleFrame), builder);
},
[&](auto&) -> size_t {
// TODO add support for: RETIRE_CONNECTION_ID and NEW_TOKEN frames
auto errorStr = folly::to<std::string>(
"Unknown / unsupported frame type received at ", __func__);
VLOG(2) << errorStr;
throw QuicTransportException(
errorStr, TransportErrorCode::FRAME_ENCODING_ERROR);
});
}
} // namespace quic