mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 23:56:57 +02:00
MINOR: quic: refactor STREAM encoding and splitting
CRYPTO and STREAM frames encoding is similar. If payload is too large, frame will be splitted and only the first payload part will be written in the output QUIC packet. This process is complexified by the presence of a variable-length integer Length field prior to the payload. This commit aims at refactor these operations. Define two functions to simplify the code : * quic_strm_frm_fillbuf() which is used to calculate the optimal frame length of a STREAM/CRYPTO frame with its payload in a buffer * quic_strm_frm_split() which is used to split the frame payload if buffer is too small With this patch, both functions are now implemented for STREAM encoding.
This commit is contained in:
parent
0b9a75e878
commit
f96af8e463
@ -277,5 +277,8 @@ static inline void qc_stream_frm_mv_fwd(struct quic_frame *frm, uint64_t data)
|
|||||||
strm_frm->data = (unsigned char *)b_peek(&cf_buf, data);
|
strm_frm->data = (unsigned char *)b_peek(&cf_buf, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t quic_strm_frm_fillbuf(size_t room, struct quic_frame *frm, size_t *split_size);
|
||||||
|
struct quic_frame *quic_strm_frm_split(struct quic_frame *frm, uint64_t left);
|
||||||
|
|
||||||
#endif /* USE_QUIC */
|
#endif /* USE_QUIC */
|
||||||
#endif /* _HAPROXY_QUIC_FRAME_H */
|
#endif /* _HAPROXY_QUIC_FRAME_H */
|
||||||
|
106
src/quic_frame.c
106
src/quic_frame.c
@ -1286,3 +1286,109 @@ void qc_release_frm(struct quic_conn *qc, struct quic_frame *frm)
|
|||||||
TRACE_LEAVE(QUIC_EV_CONN_PRSAFRM, qc);
|
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;
|
||||||
|
}
|
||||||
|
124
src/quic_tx.c
124
src/quic_tx.c
@ -1532,8 +1532,10 @@ static int qc_build_frms(struct list *outlist, struct list *inlist,
|
|||||||
* in the switch/case block.
|
* in the switch/case block.
|
||||||
*/
|
*/
|
||||||
list_for_each_entry_safe(cf, cfbak, inlist, list) {
|
list_for_each_entry_safe(cf, cfbak, inlist, list) {
|
||||||
|
struct quic_frame *split_frm;
|
||||||
/* header length, data length, frame length. */
|
/* header length, data length, frame length. */
|
||||||
size_t hlen, dlen, flen;
|
size_t hlen, dlen, flen;
|
||||||
|
size_t split_size;
|
||||||
|
|
||||||
if (!room)
|
if (!room)
|
||||||
break;
|
break;
|
||||||
@ -1601,103 +1603,13 @@ static int qc_build_frms(struct list *outlist, struct list *inlist,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Note that these frames are accepted in short packets only without
|
|
||||||
* "Length" packet field. Here, <*len> is used only to compute the
|
flen = quic_strm_frm_fillbuf(room, cf, &split_size);
|
||||||
* sum of the lengths of the already built frames for this packet.
|
if (!flen)
|
||||||
*
|
|
||||||
* 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;
|
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.
|
|
||||||
*/
|
|
||||||
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)",
|
TRACE_DEVEL(" STREAM frame length (flen)",
|
||||||
QUIC_EV_CONN_BCFRMS, qc, &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;
|
|
||||||
|
|
||||||
new_cf = qc_frm_alloc(cf->type);
|
|
||||||
if (!new_cf) {
|
|
||||||
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("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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO the MUX is notified about the frame sending via
|
/* TODO the MUX is notified about the frame sending via
|
||||||
* previous qc_stream_desc_send call. However, the
|
* previous qc_stream_desc_send call. However, the
|
||||||
@ -1707,6 +1619,32 @@ static int qc_build_frms(struct list *outlist, struct list *inlist,
|
|||||||
* bufferize and resent the announced data later.
|
* bufferize and resent the announced data later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_DEVEL("split frame", QUIC_EV_CONN_PRSAFRM, qc, split_frm);
|
||||||
|
if (split_frm->origin)
|
||||||
|
TRACE_DEVEL("duplicated frame", QUIC_EV_CONN_PRSAFRM, qc);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
*len += flen;
|
||||||
|
room -= flen;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
Loading…
Reference in New Issue
Block a user