1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  *
7  */
8 
9 #include <quic/codec/QuicWriteCodec.h>
10 
11 #include <algorithm>
12 
13 #include <quic/QuicConstants.h>
14 #include <quic/QuicException.h>
15 #include <quic/codec/QuicInteger.h>
16 
17 namespace {
18 
19 bool packetSpaceCheck(uint64_t limit, size_t require);
20 
21 /**
22  * A helper function to check if there are enough space to write in the packet.
23  * Return: true if there is enough space, false otherwise
24  */
packetSpaceCheck(uint64_t limit,size_t require)25 bool packetSpaceCheck(uint64_t limit, size_t require) {
26   return (folly::to<uint64_t>(require) <= limit);
27 }
28 } // namespace
29 
30 namespace quic {
31 
writeStreamFrameHeader(PacketBuilderInterface & builder,StreamId id,uint64_t offset,uint64_t writeBufferLen,uint64_t flowControlLen,bool fin,folly::Optional<bool> skipLenHint)32 folly::Optional<uint64_t> writeStreamFrameHeader(
33     PacketBuilderInterface& builder,
34     StreamId id,
35     uint64_t offset,
36     uint64_t writeBufferLen,
37     uint64_t flowControlLen,
38     bool fin,
39     folly::Optional<bool> skipLenHint) {
40   if (builder.remainingSpaceInPkt() == 0) {
41     return folly::none;
42   }
43   if (writeBufferLen == 0 && !fin) {
44     throw QuicInternalException(
45         "No data or fin supplied when writing stream.",
46         LocalErrorCode::INTERNAL_ERROR);
47   }
48   StreamTypeField::Builder streamTypeBuilder;
49   QuicInteger idInt(id);
50   // First account for the things that are non-optional: frame type and stream
51   // id.
52   uint64_t headerSize = sizeof(uint8_t) + idInt.getSize();
53   if (builder.remainingSpaceInPkt() < headerSize) {
54     VLOG(4) << "No space in packet for stream header. stream=" << id
55             << " remaining=" << builder.remainingSpaceInPkt();
56     return folly::none;
57   }
58   QuicInteger offsetInt(offset);
59   if (offset != 0) {
60     streamTypeBuilder.setOffset();
61     headerSize += offsetInt.getSize();
62   }
63   // Next we have to deal with the data length. This is trickier. The length of
64   // data we are able to send depends on 3 things: how much we have in the
65   // buffer, how much flow control we have, and the remaining size in the
66   // packet. If the amount we want to send is >= the remaining packet size after
67   // the header so far we can omit the length field and consume the rest of the
68   // packet. If it is not then we need to use the minimal varint encoding
69   // possible to avoid sending not-full packets.
70   // Note: we don't bother with one potential optimization, which is writing
71   // a zero length fin-only stream frame and omitting the length field.
72   uint64_t dataLen = std::min(writeBufferLen, flowControlLen);
73   uint64_t dataLenLen = 0;
74   bool shouldSkipLengthField;
75   if (skipLenHint) {
76     shouldSkipLengthField = *skipLenHint;
77   } else {
78     // Check if we can fill the entire packet with the rest of this stream frame
79     shouldSkipLengthField =
80         dataLen > 0 && dataLen >= builder.remainingSpaceInPkt() - headerSize;
81   }
82   // At most we can write the minimal between data length and what the packet
83   // builder allows us to write.
84   dataLen = std::min(dataLen, builder.remainingSpaceInPkt() - headerSize);
85   if (!shouldSkipLengthField) {
86     if (dataLen <= kOneByteLimit - 1) {
87       dataLenLen = 1;
88     } else if (dataLen <= kTwoByteLimit - 2) {
89       dataLenLen = 2;
90     } else if (dataLen <= kFourByteLimit - 4) {
91       dataLenLen = 4;
92     } else if (dataLen <= kEightByteLimit - 8) {
93       dataLenLen = 8;
94     } else {
95       // This should never really happen as dataLen is bounded by the remaining
96       // space in the packet which should be << kEightByteLimit.
97       throw QuicInternalException(
98           "Stream frame length too large.", LocalErrorCode::INTERNAL_ERROR);
99     }
100   }
101   if (dataLenLen > 0) {
102     if (dataLen != 0 &&
103         headerSize + dataLenLen >= builder.remainingSpaceInPkt()) {
104       VLOG(4) << "No space in packet for stream header. stream=" << id
105               << " remaining=" << builder.remainingSpaceInPkt();
106       return folly::none;
107     }
108     // We have to encode the actual data length in the header.
109     headerSize += dataLenLen;
110     if (builder.remainingSpaceInPkt() < dataLen + headerSize) {
111       dataLen = builder.remainingSpaceInPkt() - headerSize;
112     }
113   }
114   bool shouldSetFin = fin && dataLen == writeBufferLen;
115   if (dataLen == 0 && !shouldSetFin) {
116     // This would be an empty non-fin stream frame.
117     return folly::none;
118   }
119   if (builder.remainingSpaceInPkt() < headerSize) {
120     VLOG(4) << "No space in packet for stream header. stream=" << id
121             << " remaining=" << builder.remainingSpaceInPkt();
122     return folly::none;
123   }
124 
125   // Done with the accounting, set the bits and write the actual frame header.
126   if (dataLenLen > 0) {
127     streamTypeBuilder.setLength();
128   }
129   if (shouldSetFin) {
130     streamTypeBuilder.setFin();
131   }
132   auto streamType = streamTypeBuilder.build();
133   builder.writeBE(streamType.fieldValue());
134   builder.write(idInt);
135   if (offset != 0) {
136     builder.write(offsetInt);
137   }
138   if (dataLenLen > 0) {
139     builder.write(QuicInteger(dataLen));
140   }
141   builder.appendFrame(
142       WriteStreamFrame(id, offset, dataLen, streamType.hasFin()));
143   DCHECK(dataLen <= builder.remainingSpaceInPkt());
144   return folly::make_optional(dataLen);
145 }
146 
writeStreamFrameData(PacketBuilderInterface & builder,const BufQueue & writeBuffer,uint64_t dataLen)147 void writeStreamFrameData(
148     PacketBuilderInterface& builder,
149     const BufQueue& writeBuffer,
150     uint64_t dataLen) {
151   if (dataLen > 0) {
152     builder.insert(writeBuffer, dataLen);
153   }
154 }
155 
writeStreamFrameData(PacketBuilderInterface & builder,Buf writeBuffer,uint64_t dataLen)156 void writeStreamFrameData(
157     PacketBuilderInterface& builder,
158     Buf writeBuffer,
159     uint64_t dataLen) {
160   if (dataLen > 0) {
161     builder.insert(std::move(writeBuffer), dataLen);
162   }
163 }
164 
writeCryptoFrame(uint64_t offsetIn,const BufQueue & data,PacketBuilderInterface & builder)165 folly::Optional<WriteCryptoFrame> writeCryptoFrame(
166     uint64_t offsetIn,
167     const BufQueue& data,
168     PacketBuilderInterface& builder) {
169   uint64_t spaceLeftInPkt = builder.remainingSpaceInPkt();
170   QuicInteger intFrameType(static_cast<uint8_t>(FrameType::CRYPTO_FRAME));
171   QuicInteger offsetInteger(offsetIn);
172 
173   size_t lengthBytes = 2;
174   size_t cryptoFrameHeaderSize =
175       intFrameType.getSize() + offsetInteger.getSize() + lengthBytes;
176 
177   if (spaceLeftInPkt <= cryptoFrameHeaderSize) {
178     VLOG(3) << "No space left in packet to write cryptoFrame header of size: "
179             << cryptoFrameHeaderSize << ", space left=" << spaceLeftInPkt;
180     return folly::none;
181   }
182   size_t spaceRemaining = spaceLeftInPkt - cryptoFrameHeaderSize;
183   size_t dataLength = data.chainLength();
184   size_t writableData = std::min(dataLength, spaceRemaining);
185   QuicInteger lengthVarInt(writableData);
186 
187   if (lengthVarInt.getSize() > lengthBytes) {
188     throw QuicInternalException(
189         std::string("Length bytes representation"),
190         LocalErrorCode::CODEC_ERROR);
191   }
192   builder.write(intFrameType);
193   builder.write(offsetInteger);
194   builder.write(lengthVarInt);
195   builder.insert(data, writableData);
196   builder.appendFrame(WriteCryptoFrame(offsetIn, lengthVarInt.getValue()));
197   return WriteCryptoFrame(offsetIn, lengthVarInt.getValue());
198 }
199 
200 /*
201  * This function will fill the parameter ack frame with ack blocks from the
202  * parameter ackBlocks until it runs out of space (bytesLimit). The largest
203  * ack block should have been inserted by the caller.
204  */
fillFrameWithAckBlocks(const AckBlocks & ackBlocks,WriteAckFrame & ackFrame,uint64_t bytesLimit)205 static size_t fillFrameWithAckBlocks(
206     const AckBlocks& ackBlocks,
207     WriteAckFrame& ackFrame,
208     uint64_t bytesLimit) {
209   PacketNum currentSeqNum = ackBlocks.crbegin()->start;
210 
211   // starts off with 0 which is what we assumed the initial ack block to be for
212   // the largest acked.
213   size_t numAdditionalAckBlocks = 0;
214   size_t previousNumAckBlocks = 0;
215 
216   // Skip the largest, as it has already been emplaced.
217   for (auto blockItr = ackBlocks.crbegin() + 1; blockItr != ackBlocks.crend();
218        ++blockItr) {
219     const auto& currBlock = *blockItr;
220     // These must be true because of the properties of the interval set.
221     CHECK_GE(currentSeqNum, currBlock.end + 2);
222     PacketNum gap = currentSeqNum - currBlock.end - 2;
223     PacketNum currBlockLen = currBlock.end - currBlock.start;
224 
225     // TODO this is still extra work, as we end up duplicating these
226     // calculations in the caller, we could store the results so they
227     // can be reused by the caller when writing the frame.
228     size_t gapSize = getQuicIntegerSizeThrows(gap);
229     size_t currBlockLenSize = getQuicIntegerSizeThrows(currBlockLen);
230     size_t numAdditionalAckBlocksSize =
231         getQuicIntegerSizeThrows(numAdditionalAckBlocks + 1);
232     size_t previousNumAckBlocksSize =
233         getQuicIntegerSizeThrows(previousNumAckBlocks);
234 
235     size_t additionalSize = gapSize + currBlockLenSize +
236         (numAdditionalAckBlocksSize - previousNumAckBlocksSize);
237     if (bytesLimit < additionalSize) {
238       break;
239     }
240     numAdditionalAckBlocks++;
241     bytesLimit -= additionalSize;
242     previousNumAckBlocks = numAdditionalAckBlocks;
243     currentSeqNum = currBlock.start;
244     ackFrame.ackBlocks.emplace_back(currBlock.start, currBlock.end);
245   }
246   return numAdditionalAckBlocks;
247 }
248 
writeAckFrame(const quic::AckFrameMetaData & ackFrameMetaData,PacketBuilderInterface & builder)249 folly::Optional<AckFrameWriteResult> writeAckFrame(
250     const quic::AckFrameMetaData& ackFrameMetaData,
251     PacketBuilderInterface& builder) {
252   if (ackFrameMetaData.ackBlocks.empty()) {
253     return folly::none;
254   }
255   // The last block must be the largest block.
256   auto largestAckedPacket = ackFrameMetaData.ackBlocks.back().end;
257   // ackBlocks are already an interval set so each value is naturally
258   // non-overlapping.
259   auto firstAckBlockLength =
260       largestAckedPacket - ackFrameMetaData.ackBlocks.back().start;
261 
262   WriteAckFrame ackFrame;
263   uint64_t spaceLeft = builder.remainingSpaceInPkt();
264   uint64_t beginningSpace = spaceLeft;
265   ackFrame.ackBlocks.reserve(spaceLeft / 4);
266 
267   // We could technically split the range if the size of the representation of
268   // the integer is too large, but that gets super tricky and is of dubious
269   // value.
270   QuicInteger largestAckedPacketInt(largestAckedPacket);
271   QuicInteger firstAckBlockLengthInt(firstAckBlockLength);
272   // Convert ackDelay to unsigned value explicitly before right shifting to
273   // avoid issues with right shifting signed values.
274   uint64_t encodedAckDelay = ackFrameMetaData.ackDelay.count();
275   encodedAckDelay = encodedAckDelay >> ackFrameMetaData.ackDelayExponent;
276   QuicInteger ackDelayInt(encodedAckDelay);
277   QuicInteger minAdditionalAckBlockCount(0);
278 
279   // Required fields are Type, LargestAcked, AckDelay, AckBlockCount,
280   // firstAckBlockLength
281   QuicInteger encodedintFrameType(static_cast<uint8_t>(FrameType::ACK));
282   auto headerSize = encodedintFrameType.getSize() +
283       largestAckedPacketInt.getSize() + ackDelayInt.getSize() +
284       minAdditionalAckBlockCount.getSize() + firstAckBlockLengthInt.getSize();
285   if (spaceLeft < headerSize) {
286     return folly::none;
287   }
288   spaceLeft -= headerSize;
289 
290   ackFrame.ackBlocks.push_back(ackFrameMetaData.ackBlocks.back());
291   auto numAdditionalAckBlocks =
292       fillFrameWithAckBlocks(ackFrameMetaData.ackBlocks, ackFrame, spaceLeft);
293 
294   QuicInteger numAdditionalAckBlocksInt(numAdditionalAckBlocks);
295   builder.write(encodedintFrameType);
296   builder.write(largestAckedPacketInt);
297   builder.write(ackDelayInt);
298   builder.write(numAdditionalAckBlocksInt);
299   builder.write(firstAckBlockLengthInt);
300 
301   PacketNum currentSeqNum = ackFrameMetaData.ackBlocks.back().start;
302   for (auto it = ackFrame.ackBlocks.cbegin() + 1;
303        it != ackFrame.ackBlocks.cend();
304        ++it) {
305     CHECK_GE(currentSeqNum, it->end + 2);
306     PacketNum gap = currentSeqNum - it->end - 2;
307     PacketNum currBlockLen = it->end - it->start;
308     QuicInteger gapInt(gap);
309     QuicInteger currentBlockLenInt(currBlockLen);
310     builder.write(gapInt);
311     builder.write(currentBlockLenInt);
312     currentSeqNum = it->start;
313   }
314   ackFrame.ackDelay = ackFrameMetaData.ackDelay;
315   builder.appendFrame(std::move(ackFrame));
316   return AckFrameWriteResult(
317       beginningSpace - builder.remainingSpaceInPkt(),
318       1 + numAdditionalAckBlocks);
319 }
320 
writeSimpleFrame(QuicSimpleFrame && frame,PacketBuilderInterface & builder)321 size_t writeSimpleFrame(
322     QuicSimpleFrame&& frame,
323     PacketBuilderInterface& builder) {
324   using FrameTypeType = std::underlying_type<FrameType>::type;
325 
326   uint64_t spaceLeft = builder.remainingSpaceInPkt();
327 
328   switch (frame.type()) {
329     case QuicSimpleFrame::Type::StopSendingFrame: {
330       const StopSendingFrame& stopSendingFrame = *frame.asStopSendingFrame();
331       QuicInteger intFrameType(static_cast<uint8_t>(FrameType::STOP_SENDING));
332       QuicInteger streamId(stopSendingFrame.streamId);
333       QuicInteger errorCode(static_cast<uint64_t>(stopSendingFrame.errorCode));
334       size_t errorSize = errorCode.getSize();
335       auto stopSendingFrameSize =
336           intFrameType.getSize() + streamId.getSize() + errorSize;
337       if (packetSpaceCheck(spaceLeft, stopSendingFrameSize)) {
338         builder.write(intFrameType);
339         builder.write(streamId);
340         builder.write(errorCode);
341         builder.appendFrame(QuicSimpleFrame(std::move(stopSendingFrame)));
342         return stopSendingFrameSize;
343       }
344       // no space left in packet
345       return size_t(0);
346     }
347     case QuicSimpleFrame::Type::PathChallengeFrame: {
348       const PathChallengeFrame& pathChallengeFrame =
349           *frame.asPathChallengeFrame();
350       QuicInteger frameType(static_cast<uint8_t>(FrameType::PATH_CHALLENGE));
351       auto pathChallengeFrameSize =
352           frameType.getSize() + sizeof(pathChallengeFrame.pathData);
353       if (packetSpaceCheck(spaceLeft, pathChallengeFrameSize)) {
354         builder.write(frameType);
355         builder.writeBE(pathChallengeFrame.pathData);
356         builder.appendFrame(QuicSimpleFrame(std::move(pathChallengeFrame)));
357         return pathChallengeFrameSize;
358       }
359       // no space left in packet
360       return size_t(0);
361     }
362     case QuicSimpleFrame::Type::PathResponseFrame: {
363       const PathResponseFrame& pathResponseFrame = *frame.asPathResponseFrame();
364       QuicInteger frameType(static_cast<uint8_t>(FrameType::PATH_RESPONSE));
365       auto pathResponseFrameSize =
366           frameType.getSize() + sizeof(pathResponseFrame.pathData);
367       if (packetSpaceCheck(spaceLeft, pathResponseFrameSize)) {
368         builder.write(frameType);
369         builder.writeBE(pathResponseFrame.pathData);
370         builder.appendFrame(QuicSimpleFrame(std::move(pathResponseFrame)));
371         return pathResponseFrameSize;
372       }
373       // no space left in packet
374       return size_t(0);
375     }
376     case QuicSimpleFrame::Type::NewConnectionIdFrame: {
377       const NewConnectionIdFrame& newConnectionIdFrame =
378           *frame.asNewConnectionIdFrame();
379       QuicInteger frameType(static_cast<uint8_t>(FrameType::NEW_CONNECTION_ID));
380       QuicInteger sequenceNumber(newConnectionIdFrame.sequenceNumber);
381       QuicInteger retirePriorTo(newConnectionIdFrame.retirePriorTo);
382       // Include an 8-bit unsigned integer containing the length of the connId
383       auto newConnectionIdFrameSize = frameType.getSize() +
384           sequenceNumber.getSize() + retirePriorTo.getSize() + sizeof(uint8_t) +
385           newConnectionIdFrame.connectionId.size() +
386           newConnectionIdFrame.token.size();
387       if (packetSpaceCheck(spaceLeft, newConnectionIdFrameSize)) {
388         builder.write(frameType);
389         builder.write(sequenceNumber);
390         builder.write(retirePriorTo);
391         builder.writeBE(newConnectionIdFrame.connectionId.size());
392         builder.push(
393             newConnectionIdFrame.connectionId.data(),
394             newConnectionIdFrame.connectionId.size());
395         builder.push(
396             newConnectionIdFrame.token.data(),
397             newConnectionIdFrame.token.size());
398         builder.appendFrame(QuicSimpleFrame(std::move(newConnectionIdFrame)));
399         return newConnectionIdFrameSize;
400       }
401       // no space left in packet
402       return size_t(0);
403     }
404     case QuicSimpleFrame::Type::MaxStreamsFrame: {
405       const MaxStreamsFrame& maxStreamsFrame = *frame.asMaxStreamsFrame();
406       auto frameType = maxStreamsFrame.isForBidirectionalStream()
407           ? FrameType::MAX_STREAMS_BIDI
408           : FrameType::MAX_STREAMS_UNI;
409       QuicInteger intFrameType(static_cast<FrameTypeType>(frameType));
410       QuicInteger streamCount(maxStreamsFrame.maxStreams);
411       auto maxStreamsFrameSize = intFrameType.getSize() + streamCount.getSize();
412       if (packetSpaceCheck(spaceLeft, maxStreamsFrameSize)) {
413         builder.write(intFrameType);
414         builder.write(streamCount);
415         builder.appendFrame(QuicSimpleFrame(maxStreamsFrame));
416         return maxStreamsFrameSize;
417       }
418       return size_t(0);
419     }
420     case QuicSimpleFrame::Type::RetireConnectionIdFrame: {
421       const RetireConnectionIdFrame& retireConnectionIdFrame =
422           *frame.asRetireConnectionIdFrame();
423       QuicInteger frameType(
424           static_cast<uint8_t>(FrameType::RETIRE_CONNECTION_ID));
425       QuicInteger sequence(retireConnectionIdFrame.sequenceNumber);
426       auto retireConnectionIdFrameSize =
427           frameType.getSize() + sequence.getSize();
428       if (packetSpaceCheck(spaceLeft, retireConnectionIdFrameSize)) {
429         builder.write(frameType);
430         builder.write(sequence);
431         builder.appendFrame(QuicSimpleFrame(retireConnectionIdFrame));
432         return retireConnectionIdFrameSize;
433       }
434       // no space left in packet
435       return size_t(0);
436     }
437     case QuicSimpleFrame::Type::HandshakeDoneFrame: {
438       const HandshakeDoneFrame& handshakeDoneFrame =
439           *frame.asHandshakeDoneFrame();
440       CHECK(builder.getPacketHeader().asShort());
441       QuicInteger intFrameType(static_cast<uint8_t>(FrameType::HANDSHAKE_DONE));
442       if (packetSpaceCheck(spaceLeft, intFrameType.getSize())) {
443         builder.write(intFrameType);
444         builder.appendFrame(QuicSimpleFrame(handshakeDoneFrame));
445         return intFrameType.getSize();
446       }
447       // no space left in packet
448       return size_t(0);
449     }
450     case QuicSimpleFrame::Type::KnobFrame: {
451       const KnobFrame& knobFrame = *frame.asKnobFrame();
452       QuicInteger intFrameType(static_cast<uint64_t>(FrameType::KNOB));
453       QuicInteger intKnobSpace(knobFrame.knobSpace);
454       QuicInteger intKnobId(knobFrame.id);
455       QuicInteger intKnobLen(knobFrame.len);
456       size_t knobFrameLen = intFrameType.getSize() + intKnobSpace.getSize() +
457           intKnobId.getSize() + intKnobLen.getSize() + intKnobLen.getValue();
458       if (packetSpaceCheck(spaceLeft, knobFrameLen)) {
459         builder.write(intFrameType);
460         builder.write(intKnobSpace);
461         builder.write(intKnobId);
462         builder.write(intKnobLen);
463         builder.insert(knobFrame.blob->clone());
464         builder.appendFrame(QuicSimpleFrame(knobFrame));
465         return knobFrameLen;
466       }
467       // no space left in packet
468       return size_t(0);
469     }
470     case QuicSimpleFrame::Type::AckFrequencyFrame: {
471       const auto ackFrequencyFrame = frame.asAckFrequencyFrame();
472       QuicInteger intFrameType(static_cast<uint64_t>(FrameType::ACK_FREQUENCY));
473       QuicInteger intSequenceNumber(ackFrequencyFrame->sequenceNumber);
474       QuicInteger intPacketTolerance(ackFrequencyFrame->packetTolerance);
475       QuicInteger intUpdateMaxAckDelay(ackFrequencyFrame->updateMaxAckDelay);
476       size_t ackFrequencyFrameLen = intFrameType.getSize() +
477           intSequenceNumber.getSize() + intPacketTolerance.getSize() +
478           intUpdateMaxAckDelay.getSize() + 1 /* ignoreOrder */;
479       if (packetSpaceCheck(spaceLeft, ackFrequencyFrameLen)) {
480         builder.write(intFrameType);
481         builder.write(intSequenceNumber);
482         builder.write(intPacketTolerance);
483         builder.write(intUpdateMaxAckDelay);
484         builder.writeBE(ackFrequencyFrame->ignoreOrder);
485         builder.appendFrame(QuicSimpleFrame(*ackFrequencyFrame));
486         return ackFrequencyFrameLen;
487       }
488       // no space left in packet
489       return size_t(0);
490     }
491   }
492   folly::assume_unreachable();
493 }
494 
writeFrame(QuicWriteFrame && frame,PacketBuilderInterface & builder)495 size_t writeFrame(QuicWriteFrame&& frame, PacketBuilderInterface& builder) {
496   using FrameTypeType = std::underlying_type<FrameType>::type;
497 
498   uint64_t spaceLeft = builder.remainingSpaceInPkt();
499 
500   switch (frame.type()) {
501     case QuicWriteFrame::Type::PaddingFrame: {
502       PaddingFrame& paddingFrame = *frame.asPaddingFrame();
503       QuicInteger intFrameType(static_cast<uint8_t>(FrameType::PADDING));
504       if (packetSpaceCheck(spaceLeft, intFrameType.getSize())) {
505         builder.write(intFrameType);
506         builder.appendFrame(std::move(paddingFrame));
507         return intFrameType.getSize();
508       }
509       return size_t(0);
510     }
511     case QuicWriteFrame::Type::RstStreamFrame: {
512       RstStreamFrame& rstStreamFrame = *frame.asRstStreamFrame();
513       QuicInteger intFrameType(static_cast<uint8_t>(FrameType::RST_STREAM));
514       QuicInteger streamId(rstStreamFrame.streamId);
515       QuicInteger offset(rstStreamFrame.offset);
516       QuicInteger errorCode(static_cast<uint64_t>(rstStreamFrame.errorCode));
517       size_t errorSize = errorCode.getSize();
518       auto rstStreamFrameSize = intFrameType.getSize() + errorSize +
519           streamId.getSize() + offset.getSize();
520       if (packetSpaceCheck(spaceLeft, rstStreamFrameSize)) {
521         builder.write(intFrameType);
522         builder.write(streamId);
523         builder.write(errorCode);
524         builder.write(offset);
525         builder.appendFrame(std::move(rstStreamFrame));
526         return rstStreamFrameSize;
527       }
528       // no space left in packet
529       return size_t(0);
530     }
531     case QuicWriteFrame::Type::MaxDataFrame: {
532       MaxDataFrame& maxDataFrame = *frame.asMaxDataFrame();
533       QuicInteger intFrameType(static_cast<uint8_t>(FrameType::MAX_DATA));
534       QuicInteger maximumData(maxDataFrame.maximumData);
535       auto frameSize = intFrameType.getSize() + maximumData.getSize();
536       if (packetSpaceCheck(spaceLeft, frameSize)) {
537         builder.write(intFrameType);
538         builder.write(maximumData);
539         builder.appendFrame(std::move(maxDataFrame));
540         return frameSize;
541       }
542       // no space left in packet
543       return size_t(0);
544     }
545     case QuicWriteFrame::Type::MaxStreamDataFrame: {
546       MaxStreamDataFrame& maxStreamDataFrame = *frame.asMaxStreamDataFrame();
547       QuicInteger intFrameType(
548           static_cast<uint8_t>(FrameType::MAX_STREAM_DATA));
549       QuicInteger streamId(maxStreamDataFrame.streamId);
550       QuicInteger maximumData(maxStreamDataFrame.maximumData);
551       auto maxStreamDataFrameSize =
552           intFrameType.getSize() + streamId.getSize() + maximumData.getSize();
553       if (packetSpaceCheck(spaceLeft, maxStreamDataFrameSize)) {
554         builder.write(intFrameType);
555         builder.write(streamId);
556         builder.write(maximumData);
557         builder.appendFrame(std::move(maxStreamDataFrame));
558         return maxStreamDataFrameSize;
559       }
560       // no space left in packet
561       return size_t(0);
562     }
563     case QuicWriteFrame::Type::DataBlockedFrame: {
564       DataBlockedFrame& blockedFrame = *frame.asDataBlockedFrame();
565       QuicInteger intFrameType(static_cast<uint8_t>(FrameType::DATA_BLOCKED));
566       QuicInteger dataLimit(blockedFrame.dataLimit);
567       auto blockedFrameSize = intFrameType.getSize() + dataLimit.getSize();
568       if (packetSpaceCheck(spaceLeft, blockedFrameSize)) {
569         builder.write(intFrameType);
570         builder.write(dataLimit);
571         builder.appendFrame(std::move(blockedFrame));
572         return blockedFrameSize;
573       }
574       // no space left in packet
575       return size_t(0);
576     }
577     case QuicWriteFrame::Type::StreamDataBlockedFrame: {
578       StreamDataBlockedFrame& streamBlockedFrame =
579           *frame.asStreamDataBlockedFrame();
580       QuicInteger intFrameType(
581           static_cast<uint8_t>(FrameType::STREAM_DATA_BLOCKED));
582       QuicInteger streamId(streamBlockedFrame.streamId);
583       QuicInteger dataLimit(streamBlockedFrame.dataLimit);
584       auto blockedFrameSize =
585           intFrameType.getSize() + streamId.getSize() + dataLimit.getSize();
586       if (packetSpaceCheck(spaceLeft, blockedFrameSize)) {
587         builder.write(intFrameType);
588         builder.write(streamId);
589         builder.write(dataLimit);
590         builder.appendFrame(std::move(streamBlockedFrame));
591         return blockedFrameSize;
592       }
593       // no space left in packet
594       return size_t(0);
595     }
596     case QuicWriteFrame::Type::StreamsBlockedFrame: {
597       StreamsBlockedFrame& streamsBlockedFrame = *frame.asStreamsBlockedFrame();
598       auto frameType = streamsBlockedFrame.isForBidirectionalStream()
599           ? FrameType::STREAMS_BLOCKED_BIDI
600           : FrameType::STREAMS_BLOCKED_UNI;
601       QuicInteger intFrameType(static_cast<FrameTypeType>(frameType));
602       QuicInteger streamId(streamsBlockedFrame.streamLimit);
603       auto streamBlockedFrameSize = intFrameType.getSize() + streamId.getSize();
604       if (packetSpaceCheck(spaceLeft, streamBlockedFrameSize)) {
605         builder.write(intFrameType);
606         builder.write(streamId);
607         builder.appendFrame(std::move(streamsBlockedFrame));
608         return streamBlockedFrameSize;
609       }
610       // no space left in packet
611       return size_t(0);
612     }
613     case QuicWriteFrame::Type::ConnectionCloseFrame: {
614       ConnectionCloseFrame& connectionCloseFrame =
615           *frame.asConnectionCloseFrame();
616       // Need to distinguish between CONNECTION_CLOSE & CONNECTINO_CLOSE_APP_ERR
617       const TransportErrorCode* isTransportErrorCode =
618           connectionCloseFrame.errorCode.asTransportErrorCode();
619       const ApplicationErrorCode* isApplicationErrorCode =
620           connectionCloseFrame.errorCode.asApplicationErrorCode();
621 
622       QuicInteger intFrameType(static_cast<uint8_t>(
623           isTransportErrorCode ? FrameType::CONNECTION_CLOSE
624                                : FrameType::CONNECTION_CLOSE_APP_ERR));
625 
626       QuicInteger reasonLength(connectionCloseFrame.reasonPhrase.size());
627       folly::Optional<QuicInteger> closingFrameType;
628       if (isTransportErrorCode) {
629         closingFrameType = QuicInteger(
630             static_cast<FrameTypeType>(connectionCloseFrame.closingFrameType));
631       }
632 
633       QuicInteger errorCode(
634           isTransportErrorCode
635               ? static_cast<uint64_t>(TransportErrorCode(*isTransportErrorCode))
636               : static_cast<uint64_t>(
637                     ApplicationErrorCode(*isApplicationErrorCode)));
638       size_t errorSize = errorCode.getSize();
639       auto connCloseFrameSize = intFrameType.getSize() + errorSize +
640           (closingFrameType ? closingFrameType.value().getSize() : 0) +
641           reasonLength.getSize() + connectionCloseFrame.reasonPhrase.size();
642       if (packetSpaceCheck(spaceLeft, connCloseFrameSize)) {
643         builder.write(intFrameType);
644         builder.write(errorCode);
645         if (closingFrameType) {
646           builder.write(closingFrameType.value());
647         }
648         builder.write(reasonLength);
649         builder.push(
650             (const uint8_t*)connectionCloseFrame.reasonPhrase.data(),
651             connectionCloseFrame.reasonPhrase.size());
652         builder.appendFrame(std::move(connectionCloseFrame));
653         return connCloseFrameSize;
654       }
655       // no space left in packet
656       return size_t(0);
657     }
658     case QuicWriteFrame::Type::PingFrame: {
659       const PingFrame& pingFrame = *frame.asPingFrame();
660       QuicInteger intFrameType(static_cast<uint8_t>(FrameType::PING));
661       if (packetSpaceCheck(spaceLeft, intFrameType.getSize())) {
662         builder.write(intFrameType);
663         builder.appendFrame(pingFrame);
664         return intFrameType.getSize();
665       }
666       // no space left in packet
667       return size_t(0);
668     }
669     case QuicWriteFrame::Type::QuicSimpleFrame: {
670       return writeSimpleFrame(std::move(*frame.asQuicSimpleFrame()), builder);
671     }
672     case QuicWriteFrame::Type::DatagramFrame: {
673       const DatagramFrame& datagramFrame = *frame.asDatagramFrame();
674       QuicInteger frameTypeQuicInt(
675           static_cast<uint8_t>(FrameType::DATAGRAM_LEN));
676       QuicInteger datagramLenInt(datagramFrame.length);
677       auto datagramFrameLength = frameTypeQuicInt.getSize() +
678           datagramFrame.length + datagramLenInt.getSize();
679       if (packetSpaceCheck(spaceLeft, datagramFrameLength)) {
680         builder.write(frameTypeQuicInt);
681         builder.write(datagramLenInt);
682         builder.insert(std::move(datagramFrame.data), datagramFrame.length);
683         builder.appendFrame(datagramFrame);
684         return datagramFrameLength;
685       }
686       // no space left in packet
687       return size_t(0);
688     }
689     default: {
690       // TODO add support for: RETIRE_CONNECTION_ID and NEW_TOKEN frames
691       auto errorStr = folly::to<std::string>(
692           "Unknown / unsupported frame type received at ", __func__);
693       VLOG(2) << errorStr;
694       throw QuicTransportException(
695           errorStr, TransportErrorCode::FRAME_ENCODING_ERROR);
696     }
697   }
698 }
699 } // namespace quic
700