TRACE_LEAVE(QUIC_EV_CONN_PRSAFRM, qc);
}
+/* Calculate the length of <frm> frame header and payload using a buffer of
+ * <room> size as destination. This helper function can deal both with STREAM
+ * and CRYPTO frames. If input frame payload is too big for <room>, it must be
+ * truncated to <split> offset output parameter.
+ *
+ * Returns the total frame length, or 0 if not enough space.
+ */
+size_t quic_strm_frm_fillbuf(size_t room, struct quic_frame *frm, size_t *split)
+{
+ size_t total = 0; /* Length of frame header and payload. */
+ size_t payload; /* Input payload length. */
+
+ *split = 0;
+
+ if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F) {
+ total = 1;
+ total += quic_int_getsize(frm->stream.id);
+ if (frm->type & QUIC_STREAM_FRAME_TYPE_OFF_BIT)
+ total += quic_int_getsize(frm->stream.offset);
+ payload = frm->stream.len;
+ }
+ else {
+ /* Function must only be used with STREAM or CRYPTO frames. */
+ ABORT_NOW();
+ }
+
+ if (total > room) {
+ /* Header (without Length) already too large. */
+ return 0;
+ }
+ room -= total;
+
+ if (payload) {
+ /* Payload requested, determine Length field varint size. */
+
+ /* Optimal length value if whole room is used. */
+ const size_t room_data = quic_int_cap_length(room);
+ if (!room_data) {
+ /* Not enough space to encode a varint length first. */
+ return 0;
+ }
+
+ if (payload > room_data) {
+ total += quic_int_getsize(room_data);
+ total += room_data;
+ *split = room_data;
+ }
+ else {
+ total += quic_int_getsize(payload);
+ total += payload;
+ }
+ }
+
+ return total;
+}
+
+/* Split a STREAM or CRYPTO <frm> frame payload at <split> bytes. A newly
+ * allocated frame will point to the original payload head up to the split.
+ * Input frame payload is reduced to contains the remaining data only.
+ *
+ * Returns the newly allocated frame or NULL on error.
+ */
+struct quic_frame *quic_strm_frm_split(struct quic_frame *frm, uint64_t split)
+{
+ struct quic_frame *new;
+ struct buffer stream_buf;
+
+ new = qc_frm_alloc(frm->type);
+ if (!new)
+ return NULL;
+
+ if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F) {
+ new->stream.stream = frm->stream.stream;
+ new->stream.buf = frm->stream.buf;
+ new->stream.id = frm->stream.id;
+ new->stream.offset = frm->stream.offset;
+ new->stream.len = split;
+ new->type |= QUIC_STREAM_FRAME_TYPE_LEN_BIT;
+ new->type &= ~QUIC_STREAM_FRAME_TYPE_FIN_BIT;
+ new->stream.data = frm->stream.data;
+ new->stream.dup = frm->stream.dup;
+
+ /* Advance original frame to point to the remaining data. */
+ frm->type |= QUIC_STREAM_FRAME_TYPE_OFF_BIT;
+ stream_buf = b_make(b_orig(frm->stream.buf),
+ b_size(frm->stream.buf),
+ (char *)frm->stream.data - b_orig(frm->stream.buf), 0);
+ frm->stream.len -= split;
+ frm->stream.offset += split;
+ frm->stream.data = (unsigned char *)b_peek(&stream_buf, split);
+ }
+ else {
+ /* Function must only be used with STREAM or CRYPTO frames. */
+ ABORT_NOW();
+ }
+
+ /* Detach <frm> from its origin if it was duplicated. */
+ if (frm->origin) {
+ LIST_APPEND(&frm->origin->reflist, &new->ref);
+ new->origin = frm->origin;
+ LIST_DEL_INIT(&frm->ref);
+ frm->origin = NULL;
+ }
+
+ return new;
+}
* in the switch/case block.
*/
list_for_each_entry_safe(cf, cfbak, inlist, list) {
+ struct quic_frame *split_frm;
/* header length, data length, frame length. */
size_t hlen, dlen, flen;
+ size_t split_size;
if (!room)
break;
continue;
}
}
- /* Note that these frames are accepted in short packets only without
- * "Length" packet field. Here, <*len> is used only to compute the
- * sum of the lengths of the already built frames for this packet.
- *
- * Compute the length of this STREAM frame "header" made a all the field
- * excepting the variable ones. Note that +1 is for the type of this frame.
- */
- hlen = 1 + quic_int_getsize(cf->stream.id) +
- ((cf->type & QUIC_STREAM_FRAME_TYPE_OFF_BIT) ? quic_int_getsize(cf->stream.offset) : 0);
- if (room <= hlen)
- continue;
-
- TRACE_DEVEL(" New STREAM frame build (room, len)",
- QUIC_EV_CONN_BCFRMS, qc, &room, len);
- /* hlen contains STREAM id and offset. Ensure there is
- * enough room for length field.
- */
- if (cf->type & QUIC_STREAM_FRAME_TYPE_LEN_BIT) {
- dlen = QUIC_MIN(quic_int_cap_length(room - hlen),
- cf->stream.len);
- flen = hlen + quic_int_getsize(dlen) + dlen;
- }
- else {
- dlen = QUIC_MIN(room - hlen, cf->stream.len);
- flen = hlen + dlen;
- }
-
- if (cf->stream.len && !dlen) {
- /* Only a small gap is left on buffer, not
- * enough to encode the STREAM data length.
- */
+ flen = quic_strm_frm_fillbuf(room, cf, &split_size);
+ if (!flen)
continue;
- }
- TRACE_DEVEL(" STREAM data length (hlen, stream.len, dlen)",
- QUIC_EV_CONN_BCFRMS, qc, &hlen, &cf->stream.len, &dlen);
TRACE_DEVEL(" STREAM frame length (flen)",
QUIC_EV_CONN_BCFRMS, qc, &flen);
- /* Add the STREAM data length and its encoded length to the packet
- * length and the length of this length.
- */
- *len += flen;
- room -= flen;
- if (dlen == cf->stream.len) {
- /* <cf> STREAM data have been consumed. */
- LIST_DEL_INIT(&cf->list);
- LIST_APPEND(outlist, &cf->list);
- qc_stream_desc_send(cf->stream.stream,
- cf->stream.offset,
- cf->stream.len);
- }
- else {
- struct quic_frame *new_cf;
- struct buffer cf_buf;
+ /* TODO the MUX is notified about the frame sending via
+ * previous qc_stream_desc_send call. However, the
+ * sending can fail later, for example if the sendto
+ * system call returns an error. As the MUX has been
+ * notified, the transport layer is responsible to
+ * bufferize and resent the announced data later.
+ */
- new_cf = qc_frm_alloc(cf->type);
- if (!new_cf) {
+ if (split_size) {
+ split_frm = quic_strm_frm_split(cf, split_size);
+ if (!split_frm) {
TRACE_ERROR("No memory for new STREAM frame", QUIC_EV_CONN_BCFRMS, qc);
continue;
}
- new_cf->stream.stream = cf->stream.stream;
- new_cf->stream.buf = cf->stream.buf;
- new_cf->stream.id = cf->stream.id;
- new_cf->stream.offset = cf->stream.offset;
- new_cf->stream.len = dlen;
- new_cf->type |= QUIC_STREAM_FRAME_TYPE_LEN_BIT;
- /* FIN bit reset */
- new_cf->type &= ~QUIC_STREAM_FRAME_TYPE_FIN_BIT;
- new_cf->stream.data = cf->stream.data;
- new_cf->stream.dup = cf->stream.dup;
- TRACE_DEVEL("split frame", QUIC_EV_CONN_PRSAFRM, qc, new_cf);
- if (cf->origin) {
+ TRACE_DEVEL("split frame", QUIC_EV_CONN_PRSAFRM, qc, split_frm);
+ if (split_frm->origin)
TRACE_DEVEL("duplicated frame", QUIC_EV_CONN_PRSAFRM, qc);
- /* This <cf> frame was duplicated */
- LIST_APPEND(&cf->origin->reflist, &new_cf->ref);
- new_cf->origin = cf->origin;
- /* Detach this STREAM frame from its origin */
- LIST_DEL_INIT(&cf->ref);
- cf->origin = NULL;
- }
- LIST_APPEND(outlist, &new_cf->list);
- cf->type |= QUIC_STREAM_FRAME_TYPE_OFF_BIT;
- /* Consume <dlen> bytes of the current frame. */
- cf_buf = b_make(b_orig(cf->stream.buf),
- b_size(cf->stream.buf),
- (char *)cf->stream.data - b_orig(cf->stream.buf), 0);
- cf->stream.len -= dlen;
- cf->stream.offset += dlen;
- cf->stream.data = (unsigned char *)b_peek(&cf_buf, dlen);
-
- qc_stream_desc_send(new_cf->stream.stream,
- new_cf->stream.offset,
- new_cf->stream.len);
+ LIST_APPEND(outlist, &split_frm->list);
+ qc_stream_desc_send(split_frm->stream.stream,
+ split_frm->stream.offset,
+ split_frm->stream.len);
+ }
+ else {
+ LIST_DEL_INIT(&cf->list);
+ LIST_APPEND(outlist, &cf->list);
+ qc_stream_desc_send(cf->stream.stream,
+ cf->stream.offset,
+ cf->stream.len);
}
- /* TODO the MUX is notified about the frame sending via
- * previous qc_stream_desc_send call. However, the
- * sending can fail later, for example if the sendto
- * system call returns an error. As the MUX has been
- * notified, the transport layer is responsible to
- * bufferize and resent the announced data later.
- */
+ *len += flen;
+ room -= flen;
break;