/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include #include #include #include #include #include namespace { /** * 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(require) <= limit); } } // namespace namespace quic { folly::Expected, QuicError> writeStreamFrameHeader( PacketBuilderInterface& builder, StreamId id, uint64_t offset, uint64_t writeBufferLen, uint64_t flowControlLen, bool fin, Optional skipLenHint, OptionalIntegral streamGroupId, bool appendFrame) { if (builder.remainingSpaceInPkt() == 0) { return none; } if (writeBufferLen == 0 && !fin) { return folly::makeUnexpected(QuicError( LocalErrorCode::INTERNAL_ERROR, "No data or fin supplied when writing stream.")); } StreamTypeField::Builder streamTypeBuilder; if (streamGroupId) { streamTypeBuilder.switchToStreamGroups(); } QuicInteger idInt(id); Optional groupIdInt; if (streamGroupId) { groupIdInt = QuicInteger(*streamGroupId); } // First account for the things that are non-optional: frame type, stream id // and (optional) group id. auto idIntSize = idInt.getSize(); if (idIntSize.hasError()) { return folly::makeUnexpected(idIntSize.error()); } uint64_t headerSize = sizeof(uint8_t) + idIntSize.value(); if (groupIdInt) { auto groupIdIntSize = groupIdInt->getSize(); if (groupIdIntSize.hasError()) { return folly::makeUnexpected(groupIdIntSize.error()); } headerSize += groupIdIntSize.value(); } if (builder.remainingSpaceInPkt() < headerSize) { VLOG(4) << "No space in packet for stream header. stream=" << id << " remaining=" << builder.remainingSpaceInPkt(); return none; } QuicInteger offsetInt(offset); if (offset != 0) { streamTypeBuilder.setOffset(); auto offsetIntSize = offsetInt.getSize(); if (offsetIntSize.hasError()) { return folly::makeUnexpected(offsetIntSize.error()); } headerSize += offsetIntSize.value(); } // 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; bool shouldSkipLengthField; if (skipLenHint) { shouldSkipLengthField = *skipLenHint; } else { // Check if we can fill the entire packet with the rest of this stream frame shouldSkipLengthField = dataLen > 0 && dataLen >= builder.remainingSpaceInPkt() - headerSize; } // At most we can write the minimal between data length and what the packet // builder allows us to write. dataLen = std::min(dataLen, builder.remainingSpaceInPkt() - headerSize); if (!shouldSkipLengthField) { 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. LOG(FATAL) << "Stream frame length too large."; } } 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 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 none; } if (builder.remainingSpaceInPkt() < headerSize) { VLOG(4) << "No space in packet for stream header. stream=" << id << " remaining=" << builder.remainingSpaceInPkt(); return 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 (groupIdInt) { builder.write(*groupIdInt); } if (offset != 0) { builder.write(offsetInt); } if (dataLenLen > 0) { builder.write(QuicInteger(dataLen)); } if (appendFrame) { builder.appendFrame(WriteStreamFrame( id, offset, dataLen, streamType.hasFin(), false /* fromBufMetaIn */, streamGroupId)); } else { builder.markNonEmpty(); } return folly::make_optional(dataLen); } void writeStreamFrameData( PacketBuilderInterface& builder, const ChainedByteRangeHead& writeBuffer, uint64_t dataLen) { if (dataLen > 0) { builder.insert(writeBuffer, dataLen); } } folly::Expected, QuicError> writeCryptoFrame( uint64_t offsetIn, const ChainedByteRangeHead& data, PacketBuilderInterface& builder) { uint64_t spaceLeftInPkt = builder.remainingSpaceInPkt(); QuicInteger intFrameType(static_cast(FrameType::CRYPTO_FRAME)); auto intFrameTypeRes = intFrameType.getSize(); if (intFrameTypeRes.hasError()) { return folly::makeUnexpected(intFrameTypeRes.error()); } QuicInteger offsetInteger(offsetIn); auto offsetIntegerRes = offsetInteger.getSize(); if (offsetIntegerRes.hasError()) { return folly::makeUnexpected(offsetIntegerRes.error()); } size_t lengthBytes = 2; size_t cryptoFrameHeaderSize = intFrameTypeRes.value() + offsetIntegerRes.value() + lengthBytes; if (spaceLeftInPkt <= cryptoFrameHeaderSize) { VLOG(3) << "No space left in packet to write cryptoFrame header of size: " << cryptoFrameHeaderSize << ", space left=" << spaceLeftInPkt; return Optional(none); } size_t spaceRemaining = spaceLeftInPkt - cryptoFrameHeaderSize; size_t dataLength = data.chainLength(); size_t writableData = std::min(dataLength, spaceRemaining); QuicInteger lengthVarInt(writableData); auto lengthVarIntSizeRes = lengthVarInt.getSize(); if (lengthVarIntSizeRes.hasError()) { return folly::makeUnexpected(lengthVarIntSizeRes.error()); } CHECK(lengthVarIntSizeRes.value() <= lengthBytes) << "Length bytes representation exceeds allocated space"; builder.write(intFrameType); builder.write(offsetInteger); builder.write(lengthVarInt); builder.insert(data, writableData); builder.appendFrame(WriteCryptoFrame(offsetIn, lengthVarInt.getValue())); return Optional( WriteCryptoFrame(offsetIn, lengthVarInt.getValue())); } /* * This function will fill the parameter ack frame with ack blocks from the * parameter ackBlocks until it runs out of space (bytesLimit). The largest * ack block should have been inserted by the caller. */ [[nodiscard]] static folly::Expected fillFrameWithAckBlocks( const AckBlocks& 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; size_t previousNumAckBlocks = 0; // Skip the largest, as it has already been emplaced. 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; // TODO this is still extra work, as we end up duplicating these // calculations in the caller, we could store the results so they // can be reused by the caller when writing the frame. auto gapSizeRes = getQuicIntegerSize(gap); if (gapSizeRes.hasError()) { return folly::makeUnexpected(gapSizeRes.error()); } auto currBlockLenSizeRes = getQuicIntegerSize(currBlockLen); if (currBlockLenSizeRes.hasError()) { return folly::makeUnexpected(currBlockLenSizeRes.error()); } auto numAdditionalAckBlocksSizeRes = getQuicIntegerSize(numAdditionalAckBlocks + 1); if (numAdditionalAckBlocksSizeRes.hasError()) { return folly::makeUnexpected(numAdditionalAckBlocksSizeRes.error()); } auto previousNumAckBlocksSizeRes = getQuicIntegerSize(previousNumAckBlocks); if (previousNumAckBlocksSizeRes.hasError()) { return folly::makeUnexpected(previousNumAckBlocksSizeRes.error()); } size_t additionalSize = gapSizeRes.value() + currBlockLenSizeRes.value() + (numAdditionalAckBlocksSizeRes.value() - previousNumAckBlocksSizeRes.value()); if (bytesLimit < additionalSize) { break; } numAdditionalAckBlocks++; bytesLimit -= additionalSize; previousNumAckBlocks = numAdditionalAckBlocks; currentSeqNum = currBlock.start; ackFrame.ackBlocks.emplace_back(currBlock.start, currBlock.end); } return numAdditionalAckBlocks; } folly::Expected computeSizeUsedByRecvdTimestamps( WriteAckFrame& ackFrame) { size_t usedSize = 0; for (auto& recvdPacketsTimestampRanges : ackFrame.recvdPacketsTimestampRanges) { auto gapSize = getQuicIntegerSize(recvdPacketsTimestampRanges.gap); if (gapSize.hasError()) { return folly::makeUnexpected(gapSize.error()); } usedSize += gapSize.value(); auto countSize = getQuicIntegerSize(recvdPacketsTimestampRanges.timestamp_delta_count); if (countSize.hasError()) { return folly::makeUnexpected(countSize.error()); } usedSize += countSize.value(); for (auto& timestampDelta : recvdPacketsTimestampRanges.deltas) { auto deltaSize = getQuicIntegerSize(timestampDelta); if (deltaSize.hasError()) { return folly::makeUnexpected(deltaSize.error()); } usedSize += deltaSize.value(); } } return usedSize; } [[nodiscard]] static folly::Expected fillFrameWithPacketReceiveTimestamps( const quic::WriteAckFrameMetaData& ackFrameMetaData, WriteAckFrame& ackFrame, uint64_t largestAckedPacketNum, uint64_t spaceLeft, uint64_t receiveTimestampsExponent, uint64_t maxRecvTimestampsToSend) { if (ackFrameMetaData.ackState.recvdPacketInfos.size() == 0) { return 0; } auto recvdPacketInfos = ackFrameMetaData.ackState.recvdPacketInfos; // Insert all received packet timestamps into an interval set, to identify // contiguous ranges uint64_t pktsAdded = 0; IntervalSet receivedPktNumsIntervalSet; for (auto& recvdPkt : recvdPacketInfos) { // Add up to the peer requested max ack receive timestamps; if (pktsAdded == maxRecvTimestampsToSend) { break; } receivedPktNumsIntervalSet.insert(recvdPkt.pktNum); pktsAdded++; } auto prevPktNum = largestAckedPacketNum; auto timestampIt = recvdPacketInfos.crbegin(); size_t cumUsedSpace = 0; // We start from the latest timestamp intervals bool outOfSpace = false; for (auto timestampIntervalsIt = receivedPktNumsIntervalSet.crbegin(); timestampIntervalsIt != receivedPktNumsIntervalSet.crend(); timestampIntervalsIt++) { RecvdPacketsTimestampsRange nextTimestampRange; size_t nextTimestampRangeUsedSpace = 0; // Compute pktNum gap for each time-stamp range if (ackFrame.recvdPacketsTimestampRanges.empty()) { nextTimestampRange.gap = prevPktNum - timestampIntervalsIt->end; } else { nextTimestampRange.gap = prevPktNum - 2 - timestampIntervalsIt->end; } // Initialize spaced used by the next candidate time-stamp range auto gapSizeResult = getQuicIntegerSize(nextTimestampRange.gap); if (gapSizeResult.hasError()) { return folly::makeUnexpected(gapSizeResult.error()); } nextTimestampRangeUsedSpace += gapSizeResult.value(); while (timestampIt != recvdPacketInfos.crend() && timestampIt->pktNum >= timestampIntervalsIt->start && timestampIt->pktNum <= timestampIntervalsIt->end) { std::chrono::microseconds deltaDuration; if (timestampIt == recvdPacketInfos.crbegin()) { deltaDuration = (timestampIt->timings.receiveTimePoint > ackFrameMetaData.connTime) ? std::chrono::duration_cast( timestampIt->timings.receiveTimePoint - ackFrameMetaData.connTime) : 0us; } else { deltaDuration = std::chrono::duration_cast( (timestampIt - 1)->timings.receiveTimePoint - timestampIt->timings.receiveTimePoint); } auto delta = deltaDuration.count() >> receiveTimestampsExponent; // Check if adding a new time-stamp delta from the current time-stamp // interval Will allow us to run out of space. Since adding a new delta // impacts cumulative counts of deltas these are not already incorporated // into nextTimestampRangeUsedSpace. auto deltaSizeResult = getQuicIntegerSize(delta); if (deltaSizeResult.hasError()) { return folly::makeUnexpected(deltaSizeResult.error()); } auto deltasCountSizeResult = getQuicIntegerSize(nextTimestampRange.deltas.size() + 1); if (deltasCountSizeResult.hasError()) { return folly::makeUnexpected(deltasCountSizeResult.error()); } auto rangesCountSizeResult = getQuicIntegerSize(ackFrame.recvdPacketsTimestampRanges.size() + 1); if (rangesCountSizeResult.hasError()) { return folly::makeUnexpected(rangesCountSizeResult.error()); } if (spaceLeft < (cumUsedSpace + nextTimestampRangeUsedSpace + deltaSizeResult.value() + deltasCountSizeResult.value() + rangesCountSizeResult.value())) { outOfSpace = true; break; } nextTimestampRange.deltas.push_back(delta); nextTimestampRangeUsedSpace += deltaSizeResult.value(); timestampIt++; } if (nextTimestampRange.deltas.size() > 0) { nextTimestampRange.timestamp_delta_count = nextTimestampRange.deltas.size(); auto deltasCountSizeResult = getQuicIntegerSize(nextTimestampRange.deltas.size()); if (deltasCountSizeResult.hasError()) { return folly::makeUnexpected(deltasCountSizeResult.error()); } cumUsedSpace += nextTimestampRangeUsedSpace + deltasCountSizeResult.value(); ackFrame.recvdPacketsTimestampRanges.push_back(nextTimestampRange); prevPktNum = timestampIntervalsIt->start; DCHECK(cumUsedSpace <= spaceLeft); } if (outOfSpace) { break; } } auto computedSizeResult = computeSizeUsedByRecvdTimestamps(ackFrame); if (computedSizeResult.hasError()) { return folly::makeUnexpected(computedSizeResult.error()); } DCHECK(cumUsedSpace == computedSizeResult.value()); return ackFrame.recvdPacketsTimestampRanges.size(); } [[nodiscard]] static folly::Expected, QuicError> maybeWriteAckBaseFields( const quic::WriteAckFrameMetaData& ackFrameMetaData, PacketBuilderInterface& builder, FrameType frameType, uint64_t maxSpaceToUse) { auto spaceLeft = maxSpaceToUse; const WriteAckFrameState& ackState = ackFrameMetaData.ackState; // The last block must be the largest block. auto largestAckedPacket = ackState.acks.back().end; // ackBlocks are already an interval set so each value is naturally // non-overlapping. auto firstAckBlockLength = largestAckedPacket - ackState.acks.back().start; // 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(frameType)); auto largestAckedPacketIntSize = largestAckedPacketInt.getSize(); if (largestAckedPacketIntSize.hasError()) { return folly::makeUnexpected(largestAckedPacketIntSize.error()); } auto ackDelayIntSize = ackDelayInt.getSize(); if (ackDelayIntSize.hasError()) { return folly::makeUnexpected(ackDelayIntSize.error()); } auto minAdditionalAckBlockCountSize = minAdditionalAckBlockCount.getSize(); if (minAdditionalAckBlockCountSize.hasError()) { return folly::makeUnexpected(minAdditionalAckBlockCountSize.error()); } auto firstAckBlockLengthIntSize = firstAckBlockLengthInt.getSize(); if (firstAckBlockLengthIntSize.hasError()) { return folly::makeUnexpected(firstAckBlockLengthIntSize.error()); } auto encodedintFrameTypeSize = encodedintFrameType.getSize(); if (encodedintFrameTypeSize.hasError()) { return folly::makeUnexpected(encodedintFrameTypeSize.error()); } uint64_t headerSize = encodedintFrameTypeSize.value() + largestAckedPacketIntSize.value() + ackDelayIntSize.value() + minAdditionalAckBlockCountSize.value() + firstAckBlockLengthIntSize.value(); if (spaceLeft < headerSize) { return Optional(none); } WriteAckFrame ackFrame; ackFrame.frameType = frameType; // Reserve the number of ack blocks we could fit in the remaining space. ackFrame.ackBlocks.reserve(spaceLeft / 4); // Account for the header size spaceLeft -= headerSize; ackFrame.ackBlocks.push_back(ackState.acks.back()); auto numAdditionalAckBlocksResult = fillFrameWithAckBlocks(ackState.acks, ackFrame, spaceLeft); if (numAdditionalAckBlocksResult.hasError()) { return folly::makeUnexpected(numAdditionalAckBlocksResult.error()); } QuicInteger numAdditionalAckBlocksInt(numAdditionalAckBlocksResult.value()); builder.write(encodedintFrameType); builder.write(largestAckedPacketInt); builder.write(ackDelayInt); builder.write(numAdditionalAckBlocksInt); builder.write(firstAckBlockLengthInt); PacketNum currentSeqNum = ackState.acks.back().start; for (auto it = ackFrame.ackBlocks.cbegin() + 1; it != ackFrame.ackBlocks.cend(); ++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; } ackFrame.ackDelay = ackFrameMetaData.ackDelay; return Optional(std::move(ackFrame)); } [[nodiscard]] static folly::Expected computeEcnRequiredSpace(const quic::WriteAckFrameMetaData& ackFrameMetaData) { QuicInteger ecnECT0Count(ackFrameMetaData.ackState.ecnECT0CountReceived); QuicInteger ecnECT1Count(ackFrameMetaData.ackState.ecnECT1CountReceived); QuicInteger ecnCECount(ackFrameMetaData.ackState.ecnCECountReceived); auto ecnECT0Size = ecnECT0Count.getSize(); if (ecnECT0Size.hasError()) { return folly::makeUnexpected(ecnECT0Size.error()); } auto ecnECT1Size = ecnECT1Count.getSize(); if (ecnECT1Size.hasError()) { return folly::makeUnexpected(ecnECT1Size.error()); } auto ecnCESize = ecnCECount.getSize(); if (ecnCESize.hasError()) { return folly::makeUnexpected(ecnCESize.error()); } return ecnECT0Size.value() + ecnECT1Size.value() + ecnCESize.value(); } [[nodiscard]] static folly::Expected computeReceiveTimestampsMinimumSpace( const quic::WriteAckFrameMetaData& ackFrameMetaData) { // Compute minimum size requirements for 3 fields that must be sent // in every ACK_RECEIVE_TIMESTAMPS frame const WriteAckFrameState& ackState = ackFrameMetaData.ackState; uint64_t countTimestampRanges = 0; uint64_t maybeLastPktNum = 0; std::chrono::microseconds maybeLastPktTsDelta = 0us; if (ackState.lastRecvdPacketInfo) { maybeLastPktNum = ackState.lastRecvdPacketInfo.value().pktNum; maybeLastPktTsDelta = (ackState.lastRecvdPacketInfo.value().timings.receiveTimePoint > ackFrameMetaData.connTime ? std::chrono::duration_cast( ackState.lastRecvdPacketInfo.value() .timings.receiveTimePoint - ackFrameMetaData.connTime) : 0us); } auto countRangesSize = getQuicIntegerSize(countTimestampRanges); auto lastPktNumSize = getQuicIntegerSize(maybeLastPktNum); auto lastPktTsDeltaSize = getQuicIntegerSize(maybeLastPktTsDelta.count()); return countRangesSize.value_or(0) + lastPktNumSize.value_or(0) + lastPktTsDeltaSize.value_or(0); } static void writeECNFieldsToAck( const quic::WriteAckFrameMetaData& ackFrameMetaData, WriteAckFrame& ackFrame, PacketBuilderInterface& builder) { ackFrame.ecnECT0Count = ackFrameMetaData.ackState.ecnECT0CountReceived; ackFrame.ecnECT1Count = ackFrameMetaData.ackState.ecnECT1CountReceived; ackFrame.ecnCECount = ackFrameMetaData.ackState.ecnCECountReceived; QuicInteger ecnECT0Count(ackFrameMetaData.ackState.ecnECT0CountReceived); QuicInteger ecnECT1Count(ackFrameMetaData.ackState.ecnECT1CountReceived); QuicInteger ecnCECount(ackFrameMetaData.ackState.ecnCECountReceived); builder.write(ecnECT0Count); builder.write(ecnECT1Count); builder.write(ecnCECount); } namespace { struct AckReceiveTimesStampsWritten { size_t TimestampRangesWritten{0}; size_t TimestampWritten{0}; }; } // namespace [[nodiscard]] folly::Expected writeReceiveTimestampFieldsToAck( const quic::WriteAckFrameMetaData& ackFrameMetaData, WriteAckFrame& ackFrame, PacketBuilderInterface& builder, const AckReceiveTimestampsConfig& recvTimestampsConfig, uint64_t maxRecvTimestampsToSend) { const WriteAckFrameState& ackState = ackFrameMetaData.ackState; uint64_t spaceLeft = builder.remainingSpaceInPkt(); uint64_t lastPktNum = 0; std::chrono::microseconds lastPktTsDelta = 0us; if (ackState.lastRecvdPacketInfo) { lastPktNum = ackState.lastRecvdPacketInfo.value().pktNum; lastPktTsDelta = (ackState.lastRecvdPacketInfo.value().timings.receiveTimePoint > ackFrameMetaData.connTime ? std::chrono::duration_cast( ackState.lastRecvdPacketInfo.value() .timings.receiveTimePoint - ackFrameMetaData.connTime) : 0us); } QuicInteger lastRecvdPacketNumInt(lastPktNum); builder.write(lastRecvdPacketNumInt); ackFrame.maybeLatestRecvdPacketNum = lastRecvdPacketNumInt.getValue(); QuicInteger lastRecvdPacketTimeInt(lastPktTsDelta.count()); builder.write(lastRecvdPacketTimeInt); ackFrame.maybeLatestRecvdPacketTime = std::chrono::microseconds(lastRecvdPacketTimeInt.getValue()); size_t countTimestampRanges = 0; size_t countTimestamps = 0; spaceLeft = builder.remainingSpaceInPkt(); if (spaceLeft > 0) { auto largestAckedPacket = ackState.acks.back().end; uint64_t receiveTimestampsExponentToUse = recvTimestampsConfig.receiveTimestampsExponent; auto countTimestampRangesResult = fillFrameWithPacketReceiveTimestamps( ackFrameMetaData, ackFrame, largestAckedPacket, spaceLeft, receiveTimestampsExponentToUse, maxRecvTimestampsToSend); if (countTimestampRangesResult.hasError()) { return folly::makeUnexpected(countTimestampRangesResult.error()); } countTimestampRanges = countTimestampRangesResult.value(); if (countTimestampRanges > 0) { QuicInteger timeStampRangeCountInt( ackFrame.recvdPacketsTimestampRanges.size()); builder.write(timeStampRangeCountInt); for (auto& recvdPacketsTimestampRanges : ackFrame.recvdPacketsTimestampRanges) { QuicInteger gapInt(recvdPacketsTimestampRanges.gap); QuicInteger timestampDeltaCountInt( recvdPacketsTimestampRanges.timestamp_delta_count); builder.write(gapInt); builder.write(timestampDeltaCountInt); for (auto& timestamp : recvdPacketsTimestampRanges.deltas) { QuicInteger deltaInt(timestamp); builder.write(deltaInt); countTimestamps++; } } } else { QuicInteger timeStampRangeCountInt(0); builder.write(timeStampRangeCountInt); } } return AckReceiveTimesStampsWritten{countTimestampRanges, countTimestamps}; } folly::Expected, QuicError> writeAckFrame( const quic::WriteAckFrameMetaData& ackFrameMetaData, PacketBuilderInterface& builder, FrameType frameType, const AckReceiveTimestampsConfig& recvTimestampsConfig, uint64_t maxRecvTimestampsToSend, ExtendedAckFeatureMaskType extendedAckFeatures) { if (ackFrameMetaData.ackState.acks.empty()) { return Optional(none); } uint64_t beginningSpace = builder.remainingSpaceInPkt(); uint64_t spaceLeft = beginningSpace; bool ecnEnabled = (frameType == FrameType::ACK_ECN) || (extendedAckFeatures & static_cast( ExtendedAckFeatureMask::ECN_COUNTS)); bool receiveTimestampsEnabled = (frameType == FrameType::ACK_RECEIVE_TIMESTAMPS) || (extendedAckFeatures & static_cast( ExtendedAckFeatureMask::RECEIVE_TIMESTAMPS)); // Reserve space for ACK_EXTENDED header if (frameType == FrameType::ACK_EXTENDED) { auto extendedAckRequiredSpaceResult = QuicInteger(extendedAckFeatures).getSize(); if (extendedAckRequiredSpaceResult.hasError()) { return folly::makeUnexpected(extendedAckRequiredSpaceResult.error()); } auto extendedAckRequiredSpace = extendedAckRequiredSpaceResult.value(); if (spaceLeft < extendedAckRequiredSpace) { return Optional(none); } spaceLeft -= extendedAckRequiredSpace; } // Reserve space for ECN counts if enabled if (ecnEnabled) { auto ecnRequiredSpaceResult = computeEcnRequiredSpace(ackFrameMetaData); if (ecnRequiredSpaceResult.hasError()) { return folly::makeUnexpected(ecnRequiredSpaceResult.error()); } auto ecnRequiredSpace = ecnRequiredSpaceResult.value(); if (spaceLeft < ecnRequiredSpace) { return Optional(none); } spaceLeft -= ecnRequiredSpace; } // Reserve space for receive timestamps if enabled if (receiveTimestampsEnabled) { auto receiveTimestampsMinimumSpaceResult = computeReceiveTimestampsMinimumSpace(ackFrameMetaData); if (receiveTimestampsMinimumSpaceResult.hasError()) { return folly::makeUnexpected(receiveTimestampsMinimumSpaceResult.error()); } auto receiveTimestampsMinimumSpace = receiveTimestampsMinimumSpaceResult.value(); if (spaceLeft < receiveTimestampsMinimumSpace) { return Optional(none); } spaceLeft -= receiveTimestampsMinimumSpace; } // Start writing fields to the builder // 1. Write the base ack fields auto maybeAckFrameResult = maybeWriteAckBaseFields(ackFrameMetaData, builder, frameType, spaceLeft); if (maybeAckFrameResult.hasError()) { return folly::makeUnexpected(maybeAckFrameResult.error()); } auto& maybeAckFrame = maybeAckFrameResult.value(); if (!maybeAckFrame.hasValue()) { return Optional(none); } auto& ackFrame = maybeAckFrame.value(); // 2. Write extended ack header if enabled if (frameType == FrameType::ACK_EXTENDED) { QuicInteger quicExtendedAckFeatures(extendedAckFeatures); builder.write(quicExtendedAckFeatures); } // 3. Write ECN fields if enabled if (ecnEnabled) { writeECNFieldsToAck(ackFrameMetaData, ackFrame, builder); } // 4. Write receive timestamp fields if enabled AckReceiveTimesStampsWritten receiveTimestampsWritten; if (receiveTimestampsEnabled) { auto receiveTimestampsResult = writeReceiveTimestampFieldsToAck( ackFrameMetaData, ackFrame, builder, recvTimestampsConfig, maxRecvTimestampsToSend); if (receiveTimestampsResult.hasError()) { return folly::makeUnexpected(receiveTimestampsResult.error()); } receiveTimestampsWritten = receiveTimestampsResult.value(); } // Everything written auto ackFrameWriteResult = WriteAckFrameResult( beginningSpace - builder.remainingSpaceInPkt(), ackFrame, ackFrame.ackBlocks.size(), receiveTimestampsWritten.TimestampRangesWritten, receiveTimestampsWritten.TimestampWritten, extendedAckFeatures); builder.appendFrame(std::move(ackFrame)); return Optional(std::move(ackFrameWriteResult)); } folly::Expected writeSimpleFrame( QuicSimpleFrame&& frame, PacketBuilderInterface& builder) { using FrameTypeType = std::underlying_type::type; uint64_t spaceLeft = builder.remainingSpaceInPkt(); switch (frame.type()) { case QuicSimpleFrame::Type::StopSendingFrame: { const StopSendingFrame& stopSendingFrame = *frame.asStopSendingFrame(); QuicInteger intFrameType(static_cast(FrameType::STOP_SENDING)); QuicInteger streamId(stopSendingFrame.streamId); QuicInteger errorCode(static_cast(stopSendingFrame.errorCode)); auto errorSizeRes = errorCode.getSize(); if (errorSizeRes.hasError()) { return folly::makeUnexpected(errorSizeRes.error()); } auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto streamIdSize = streamId.getSize(); if (streamIdSize.hasError()) { return folly::makeUnexpected(streamIdSize.error()); } auto stopSendingFrameSize = intFrameTypeSize.value() + streamIdSize.value() + errorSizeRes.value(); if (packetSpaceCheck(spaceLeft, stopSendingFrameSize)) { builder.write(intFrameType); builder.write(streamId); builder.write(errorCode); builder.appendFrame(QuicSimpleFrame(std::move(stopSendingFrame))); return stopSendingFrameSize; } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::PathChallengeFrame: { const PathChallengeFrame& pathChallengeFrame = *frame.asPathChallengeFrame(); QuicInteger frameType(static_cast(FrameType::PATH_CHALLENGE)); auto frameTypeSize = frameType.getSize(); if (frameTypeSize.hasError()) { return folly::makeUnexpected(frameTypeSize.error()); } auto pathChallengeFrameSize = frameTypeSize.value() + sizeof(pathChallengeFrame.pathData); if (packetSpaceCheck(spaceLeft, pathChallengeFrameSize)) { builder.write(frameType); builder.writeBE(pathChallengeFrame.pathData); builder.appendFrame(QuicSimpleFrame(std::move(pathChallengeFrame))); return pathChallengeFrameSize; } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::PathResponseFrame: { const PathResponseFrame& pathResponseFrame = *frame.asPathResponseFrame(); QuicInteger frameType(static_cast(FrameType::PATH_RESPONSE)); auto frameTypeSize = frameType.getSize(); if (frameTypeSize.hasError()) { return folly::makeUnexpected(frameTypeSize.error()); } auto pathResponseFrameSize = frameTypeSize.value() + sizeof(pathResponseFrame.pathData); if (packetSpaceCheck(spaceLeft, pathResponseFrameSize)) { builder.write(frameType); builder.writeBE(pathResponseFrame.pathData); builder.appendFrame(QuicSimpleFrame(std::move(pathResponseFrame))); return pathResponseFrameSize; } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::NewConnectionIdFrame: { const NewConnectionIdFrame& newConnectionIdFrame = *frame.asNewConnectionIdFrame(); QuicInteger frameType(static_cast(FrameType::NEW_CONNECTION_ID)); QuicInteger sequenceNumber(newConnectionIdFrame.sequenceNumber); QuicInteger retirePriorTo(newConnectionIdFrame.retirePriorTo); auto frameTypeSize = frameType.getSize(); if (frameTypeSize.hasError()) { return folly::makeUnexpected(frameTypeSize.error()); } auto sequenceNumberSize = sequenceNumber.getSize(); if (sequenceNumberSize.hasError()) { return folly::makeUnexpected(sequenceNumberSize.error()); } auto retirePriorToSize = retirePriorTo.getSize(); if (retirePriorToSize.hasError()) { return folly::makeUnexpected(retirePriorToSize.error()); } // Include an 8-bit unsigned integer containing the length of the connId auto newConnectionIdFrameSize = frameTypeSize.value() + sequenceNumberSize.value() + retirePriorToSize.value() + 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(QuicSimpleFrame(std::move(newConnectionIdFrame))); return newConnectionIdFrameSize; } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::MaxStreamsFrame: { const MaxStreamsFrame& maxStreamsFrame = *frame.asMaxStreamsFrame(); auto frameType = maxStreamsFrame.isForBidirectionalStream() ? FrameType::MAX_STREAMS_BIDI : FrameType::MAX_STREAMS_UNI; QuicInteger intFrameType(static_cast(frameType)); QuicInteger streamCount(maxStreamsFrame.maxStreams); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto streamCountSize = streamCount.getSize(); if (streamCountSize.hasError()) { return folly::makeUnexpected(streamCountSize.error()); } auto maxStreamsFrameSize = intFrameTypeSize.value() + streamCountSize.value(); if (packetSpaceCheck(spaceLeft, maxStreamsFrameSize)) { builder.write(intFrameType); builder.write(streamCount); builder.appendFrame(QuicSimpleFrame(maxStreamsFrame)); return maxStreamsFrameSize; } return size_t(0); } case QuicSimpleFrame::Type::RetireConnectionIdFrame: { const RetireConnectionIdFrame& retireConnectionIdFrame = *frame.asRetireConnectionIdFrame(); QuicInteger frameType( static_cast(FrameType::RETIRE_CONNECTION_ID)); QuicInteger sequence(retireConnectionIdFrame.sequenceNumber); auto frameTypeSize = frameType.getSize(); if (frameTypeSize.hasError()) { return folly::makeUnexpected(frameTypeSize.error()); } auto sequenceSize = sequence.getSize(); if (sequenceSize.hasError()) { return folly::makeUnexpected(sequenceSize.error()); } auto retireConnectionIdFrameSize = frameTypeSize.value() + sequenceSize.value(); if (packetSpaceCheck(spaceLeft, retireConnectionIdFrameSize)) { builder.write(frameType); builder.write(sequence); builder.appendFrame(QuicSimpleFrame(retireConnectionIdFrame)); return retireConnectionIdFrameSize; } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::HandshakeDoneFrame: { const HandshakeDoneFrame& handshakeDoneFrame = *frame.asHandshakeDoneFrame(); CHECK(builder.getPacketHeader().asShort()); QuicInteger intFrameType(static_cast(FrameType::HANDSHAKE_DONE)); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } if (packetSpaceCheck(spaceLeft, intFrameTypeSize.value())) { builder.write(intFrameType); builder.appendFrame(QuicSimpleFrame(handshakeDoneFrame)); return intFrameTypeSize.value(); } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::KnobFrame: { const KnobFrame& knobFrame = *frame.asKnobFrame(); QuicInteger intFrameType(static_cast(FrameType::KNOB)); QuicInteger intKnobSpace(knobFrame.knobSpace); QuicInteger intKnobId(knobFrame.id); QuicInteger intKnobLen(knobFrame.len); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto intKnobSpaceSize = intKnobSpace.getSize(); if (intKnobSpaceSize.hasError()) { return folly::makeUnexpected(intKnobSpaceSize.error()); } auto intKnobIdSize = intKnobId.getSize(); if (intKnobIdSize.hasError()) { return folly::makeUnexpected(intKnobIdSize.error()); } auto intKnobLenSize = intKnobLen.getSize(); if (intKnobLenSize.hasError()) { return folly::makeUnexpected(intKnobLenSize.error()); } size_t knobFrameLen = intFrameTypeSize.value() + intKnobSpaceSize.value() + intKnobIdSize.value() + intKnobLenSize.value() + intKnobLen.getValue(); if (packetSpaceCheck(spaceLeft, knobFrameLen)) { builder.write(intFrameType); builder.write(intKnobSpace); builder.write(intKnobId); builder.write(intKnobLen); builder.insert(knobFrame.blob->clone()); builder.appendFrame(QuicSimpleFrame(knobFrame)); return knobFrameLen; } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::AckFrequencyFrame: { const auto ackFrequencyFrame = frame.asAckFrequencyFrame(); QuicInteger intFrameType(static_cast(FrameType::ACK_FREQUENCY)); QuicInteger intSequenceNumber(ackFrequencyFrame->sequenceNumber); QuicInteger intPacketTolerance(ackFrequencyFrame->packetTolerance); QuicInteger intUpdateMaxAckDelay(ackFrequencyFrame->updateMaxAckDelay); QuicInteger intReorderThreshold(ackFrequencyFrame->reorderThreshold); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto intSequenceNumberSize = intSequenceNumber.getSize(); if (intSequenceNumberSize.hasError()) { return folly::makeUnexpected(intSequenceNumberSize.error()); } auto intPacketToleranceSize = intPacketTolerance.getSize(); if (intPacketToleranceSize.hasError()) { return folly::makeUnexpected(intPacketToleranceSize.error()); } auto intUpdateMaxAckDelaySize = intUpdateMaxAckDelay.getSize(); if (intUpdateMaxAckDelaySize.hasError()) { return folly::makeUnexpected(intUpdateMaxAckDelaySize.error()); } auto intReorderThresholdSize = intReorderThreshold.getSize(); if (intReorderThresholdSize.hasError()) { return folly::makeUnexpected(intReorderThresholdSize.error()); } size_t ackFrequencyFrameLen = intFrameTypeSize.value() + intSequenceNumberSize.value() + intPacketToleranceSize.value() + intUpdateMaxAckDelaySize.value() + intReorderThresholdSize.value(); if (packetSpaceCheck(spaceLeft, ackFrequencyFrameLen)) { builder.write(intFrameType); builder.write(intSequenceNumber); builder.write(intPacketTolerance); builder.write(intUpdateMaxAckDelay); builder.write(intReorderThreshold); builder.appendFrame(QuicSimpleFrame(*ackFrequencyFrame)); return ackFrequencyFrameLen; } // no space left in packet return size_t(0); } case QuicSimpleFrame::Type::NewTokenFrame: { const auto newTokenFrame = frame.asNewTokenFrame(); QuicInteger intFrameType(static_cast(FrameType::NEW_TOKEN)); auto& token = newTokenFrame->token; QuicInteger tokenLength(token->computeChainDataLength()); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto tokenLengthSize = tokenLength.getSize(); if (tokenLengthSize.hasError()) { return folly::makeUnexpected(tokenLengthSize.error()); } auto newTokenFrameLength = intFrameTypeSize.value() + /*encoding token length*/ tokenLengthSize.value() + tokenLength.getValue(); if (packetSpaceCheck(spaceLeft, newTokenFrameLength)) { builder.write(intFrameType); builder.write(tokenLength); builder.insert(token->clone()); builder.appendFrame(QuicSimpleFrame(*newTokenFrame)); return newTokenFrameLength; } // no space left in packet return size_t(0); } } folly::assume_unreachable(); } folly::Expected writeFrame( QuicWriteFrame&& frame, PacketBuilderInterface& builder) { using FrameTypeType = std::underlying_type::type; uint64_t spaceLeft = builder.remainingSpaceInPkt(); switch (frame.type()) { case QuicWriteFrame::Type::PaddingFrame: { QuicInteger intFrameType(static_cast(FrameType::PADDING)); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } if (packetSpaceCheck(spaceLeft, intFrameTypeSize.value())) { builder.write(intFrameType); builder.appendPaddingFrame(); return intFrameTypeSize.value(); } return size_t(0); } case QuicWriteFrame::Type::RstStreamFrame: { RstStreamFrame& rstStreamFrame = *frame.asRstStreamFrame(); QuicInteger intFrameType(static_cast( rstStreamFrame.reliableSize ? FrameType::RST_STREAM_AT : FrameType::RST_STREAM)); QuicInteger streamId(rstStreamFrame.streamId); QuicInteger finalSize(rstStreamFrame.finalSize); QuicInteger errorCode(static_cast(rstStreamFrame.errorCode)); folly::Optional maybeReliableSize = folly::none; auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto streamIdSize = streamId.getSize(); if (streamIdSize.hasError()) { return folly::makeUnexpected(streamIdSize.error()); } auto finalSizeSize = finalSize.getSize(); if (finalSizeSize.hasError()) { return folly::makeUnexpected(finalSizeSize.error()); } auto errorSize = errorCode.getSize(); if (errorSize.hasError()) { return folly::makeUnexpected(errorSize.error()); } auto rstStreamFrameSize = intFrameTypeSize.value() + errorSize.value() + streamIdSize.value() + finalSizeSize.value(); if (rstStreamFrame.reliableSize) { maybeReliableSize = QuicInteger(*rstStreamFrame.reliableSize); auto reliableSize = maybeReliableSize->getSize(); if (reliableSize.hasError()) { return folly::makeUnexpected(reliableSize.error()); } rstStreamFrameSize += reliableSize.value(); } if (packetSpaceCheck(spaceLeft, rstStreamFrameSize)) { builder.write(intFrameType); builder.write(streamId); builder.write(errorCode); builder.write(finalSize); if (maybeReliableSize) { builder.write(*maybeReliableSize); } builder.appendFrame(std::move(rstStreamFrame)); return rstStreamFrameSize; } // no space left in packet return size_t(0); } case QuicWriteFrame::Type::MaxDataFrame: { MaxDataFrame& maxDataFrame = *frame.asMaxDataFrame(); QuicInteger intFrameType(static_cast(FrameType::MAX_DATA)); QuicInteger maximumData(maxDataFrame.maximumData); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto maximumDataSize = maximumData.getSize(); if (maximumDataSize.hasError()) { return folly::makeUnexpected(maximumDataSize.error()); } auto frameSize = intFrameTypeSize.value() + maximumDataSize.value(); if (packetSpaceCheck(spaceLeft, frameSize)) { builder.write(intFrameType); builder.write(maximumData); builder.appendFrame(std::move(maxDataFrame)); return frameSize; } return size_t(0); } case QuicWriteFrame::Type::MaxStreamDataFrame: { MaxStreamDataFrame& maxStreamDataFrame = *frame.asMaxStreamDataFrame(); QuicInteger intFrameType( static_cast(FrameType::MAX_STREAM_DATA)); QuicInteger streamId(maxStreamDataFrame.streamId); QuicInteger maximumData(maxStreamDataFrame.maximumData); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto streamIdSize = streamId.getSize(); if (streamIdSize.hasError()) { return folly::makeUnexpected(streamIdSize.error()); } auto maximumDataSize = maximumData.getSize(); if (maximumDataSize.hasError()) { return folly::makeUnexpected(maximumDataSize.error()); } auto maxStreamDataFrameSize = intFrameTypeSize.value() + streamIdSize.value() + maximumDataSize.value(); if (packetSpaceCheck(spaceLeft, maxStreamDataFrameSize)) { builder.write(intFrameType); builder.write(streamId); builder.write(maximumData); builder.appendFrame(std::move(maxStreamDataFrame)); return maxStreamDataFrameSize; } return size_t(0); } case QuicWriteFrame::Type::DataBlockedFrame: { DataBlockedFrame& blockedFrame = *frame.asDataBlockedFrame(); QuicInteger intFrameType(static_cast(FrameType::DATA_BLOCKED)); QuicInteger dataLimit(blockedFrame.dataLimit); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto dataLimitSize = dataLimit.getSize(); if (dataLimitSize.hasError()) { return folly::makeUnexpected(dataLimitSize.error()); } auto blockedFrameSize = intFrameTypeSize.value() + dataLimitSize.value(); if (packetSpaceCheck(spaceLeft, blockedFrameSize)) { builder.write(intFrameType); builder.write(dataLimit); builder.appendFrame(std::move(blockedFrame)); return blockedFrameSize; } return size_t(0); } case QuicWriteFrame::Type::StreamDataBlockedFrame: { StreamDataBlockedFrame& streamBlockedFrame = *frame.asStreamDataBlockedFrame(); QuicInteger intFrameType( static_cast(FrameType::STREAM_DATA_BLOCKED)); QuicInteger streamId(streamBlockedFrame.streamId); QuicInteger dataLimit(streamBlockedFrame.dataLimit); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto streamIdSize = streamId.getSize(); if (streamIdSize.hasError()) { return folly::makeUnexpected(streamIdSize.error()); } auto dataLimitSize = dataLimit.getSize(); if (dataLimitSize.hasError()) { return folly::makeUnexpected(dataLimitSize.error()); } auto blockedFrameSize = intFrameTypeSize.value() + streamIdSize.value() + dataLimitSize.value(); if (packetSpaceCheck(spaceLeft, blockedFrameSize)) { builder.write(intFrameType); builder.write(streamId); builder.write(dataLimit); builder.appendFrame(std::move(streamBlockedFrame)); return blockedFrameSize; } return size_t(0); } case QuicWriteFrame::Type::StreamsBlockedFrame: { StreamsBlockedFrame& streamsBlockedFrame = *frame.asStreamsBlockedFrame(); auto frameType = streamsBlockedFrame.isForBidirectionalStream() ? FrameType::STREAMS_BLOCKED_BIDI : FrameType::STREAMS_BLOCKED_UNI; QuicInteger intFrameType(static_cast(frameType)); QuicInteger streamId(streamsBlockedFrame.streamLimit); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto streamIdSize = streamId.getSize(); if (streamIdSize.hasError()) { return folly::makeUnexpected(streamIdSize.error()); } auto streamBlockedFrameSize = intFrameTypeSize.value() + streamIdSize.value(); if (packetSpaceCheck(spaceLeft, streamBlockedFrameSize)) { builder.write(intFrameType); builder.write(streamId); builder.appendFrame(std::move(streamsBlockedFrame)); return streamBlockedFrameSize; } return size_t(0); } case QuicWriteFrame::Type::ConnectionCloseFrame: { ConnectionCloseFrame& connectionCloseFrame = *frame.asConnectionCloseFrame(); const TransportErrorCode* isTransportErrorCode = connectionCloseFrame.errorCode.asTransportErrorCode(); const ApplicationErrorCode* isApplicationErrorCode = connectionCloseFrame.errorCode.asApplicationErrorCode(); QuicInteger intFrameType(static_cast( isTransportErrorCode ? FrameType::CONNECTION_CLOSE : FrameType::CONNECTION_CLOSE_APP_ERR)); QuicInteger reasonLength(connectionCloseFrame.reasonPhrase.size()); Optional closingFrameType; if (isTransportErrorCode) { closingFrameType = QuicInteger( static_cast(connectionCloseFrame.closingFrameType)); } QuicInteger errorCode( isTransportErrorCode ? static_cast(TransportErrorCode(*isTransportErrorCode)) : static_cast( ApplicationErrorCode(*isApplicationErrorCode))); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } auto errorSize = errorCode.getSize(); if (errorSize.hasError()) { return folly::makeUnexpected(errorSize.error()); } auto reasonLengthSize = reasonLength.getSize(); if (reasonLengthSize.hasError()) { return folly::makeUnexpected(reasonLengthSize.error()); } folly::Expected closingFrameTypeSize = [&]() { if (closingFrameType) { auto result = closingFrameType.value().getSize(); if (result.hasError()) { return result; } return result; } else { return folly::Expected(0); } }(); if (closingFrameType && closingFrameTypeSize.hasError()) { return folly::makeUnexpected(closingFrameTypeSize.error()); } size_t closingFrameTypeSizeValue = closingFrameType ? closingFrameTypeSize.value() : 0; auto connCloseFrameSize = intFrameTypeSize.value() + errorSize.value() + closingFrameTypeSizeValue + reasonLengthSize.value() + connectionCloseFrame.reasonPhrase.size(); if (packetSpaceCheck(spaceLeft, connCloseFrameSize)) { builder.write(intFrameType); builder.write(errorCode); if (closingFrameType) { builder.write(closingFrameType.value()); } builder.write(reasonLength); builder.push( (const uint8_t*)connectionCloseFrame.reasonPhrase.data(), connectionCloseFrame.reasonPhrase.size()); builder.appendFrame(std::move(connectionCloseFrame)); return connCloseFrameSize; } return size_t(0); } case QuicWriteFrame::Type::PingFrame: { const PingFrame& pingFrame = *frame.asPingFrame(); QuicInteger intFrameType(static_cast(FrameType::PING)); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } if (packetSpaceCheck(spaceLeft, intFrameTypeSize.value())) { builder.write(intFrameType); builder.appendFrame(pingFrame); return intFrameTypeSize.value(); } return size_t(0); } case QuicWriteFrame::Type::QuicSimpleFrame: { return writeSimpleFrame(std::move(*frame.asQuicSimpleFrame()), builder); } case QuicWriteFrame::Type::DatagramFrame: { const DatagramFrame& datagramFrame = *frame.asDatagramFrame(); QuicInteger frameTypeQuicInt( static_cast(FrameType::DATAGRAM_LEN)); QuicInteger datagramLenInt(datagramFrame.length); auto frameTypeQuicIntSize = frameTypeQuicInt.getSize(); if (frameTypeQuicIntSize.hasError()) { return folly::makeUnexpected(frameTypeQuicIntSize.error()); } auto datagramLenIntSize = datagramLenInt.getSize(); if (datagramLenIntSize.hasError()) { return folly::makeUnexpected(datagramLenIntSize.error()); } auto datagramFrameLength = frameTypeQuicIntSize.value() + datagramFrame.length + datagramLenIntSize.value(); if (packetSpaceCheck(spaceLeft, datagramFrameLength)) { builder.write(frameTypeQuicInt); builder.write(datagramLenInt); builder.insert(std::move(datagramFrame.data), datagramFrame.length); builder.appendFrame(datagramFrame); return datagramFrameLength; } return size_t(0); } case QuicWriteFrame::Type::ImmediateAckFrame: { const ImmediateAckFrame& immediateAckFrame = *frame.asImmediateAckFrame(); QuicInteger intFrameType(static_cast(FrameType::IMMEDIATE_ACK)); auto intFrameTypeSize = intFrameType.getSize(); if (intFrameTypeSize.hasError()) { return folly::makeUnexpected(intFrameTypeSize.error()); } if (packetSpaceCheck(spaceLeft, intFrameTypeSize.value())) { builder.write(intFrameType); builder.appendFrame(immediateAckFrame); return intFrameTypeSize.value(); } return size_t(0); } default: { LOG(FATAL) << "Unknown / unsupported frame type received"; } } } } // namespace quic