Skip to content

Commit f96af8e

Browse files
committed
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.
1 parent 0b9a75e commit f96af8e

File tree

3 files changed

+138
-91
lines changed

3 files changed

+138
-91
lines changed

include/haproxy/quic_frame.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,5 +277,8 @@ static inline void qc_stream_frm_mv_fwd(struct quic_frame *frm, uint64_t data)
277277
strm_frm->data = (unsigned char *)b_peek(&cf_buf, data);
278278
}
279279

280+
size_t quic_strm_frm_fillbuf(size_t room, struct quic_frame *frm, size_t *split_size);
281+
struct quic_frame *quic_strm_frm_split(struct quic_frame *frm, uint64_t left);
282+
280283
#endif /* USE_QUIC */
281284
#endif /* _HAPROXY_QUIC_FRAME_H */

src/quic_frame.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,3 +1286,109 @@ void qc_release_frm(struct quic_conn *qc, struct quic_frame *frm)
12861286
TRACE_LEAVE(QUIC_EV_CONN_PRSAFRM, qc);
12871287
}
12881288

1289+
/* Calculate the length of <frm> frame header and payload using a buffer of
1290+
* <room> size as destination. This helper function can deal both with STREAM
1291+
* and CRYPTO frames. If input frame payload is too big for <room>, it must be
1292+
* truncated to <split> offset output parameter.
1293+
*
1294+
* Returns the total frame length, or 0 if not enough space.
1295+
*/
1296+
size_t quic_strm_frm_fillbuf(size_t room, struct quic_frame *frm, size_t *split)
1297+
{
1298+
size_t total = 0; /* Length of frame header and payload. */
1299+
size_t payload; /* Input payload length. */
1300+
1301+
*split = 0;
1302+
1303+
if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F) {
1304+
total = 1;
1305+
total += quic_int_getsize(frm->stream.id);
1306+
if (frm->type & QUIC_STREAM_FRAME_TYPE_OFF_BIT)
1307+
total += quic_int_getsize(frm->stream.offset);
1308+
payload = frm->stream.len;
1309+
}
1310+
else {
1311+
/* Function must only be used with STREAM or CRYPTO frames. */
1312+
ABORT_NOW();
1313+
}
1314+
1315+
if (total > room) {
1316+
/* Header (without Length) already too large. */
1317+
return 0;
1318+
}
1319+
room -= total;
1320+
1321+
if (payload) {
1322+
/* Payload requested, determine Length field varint size. */
1323+
1324+
/* Optimal length value if whole room is used. */
1325+
const size_t room_data = quic_int_cap_length(room);
1326+
if (!room_data) {
1327+
/* Not enough space to encode a varint length first. */
1328+
return 0;
1329+
}
1330+
1331+
if (payload > room_data) {
1332+
total += quic_int_getsize(room_data);
1333+
total += room_data;
1334+
*split = room_data;
1335+
}
1336+
else {
1337+
total += quic_int_getsize(payload);
1338+
total += payload;
1339+
}
1340+
}
1341+
1342+
return total;
1343+
}
1344+
1345+
/* Split a STREAM or CRYPTO <frm> frame payload at <split> bytes. A newly
1346+
* allocated frame will point to the original payload head up to the split.
1347+
* Input frame payload is reduced to contains the remaining data only.
1348+
*
1349+
* Returns the newly allocated frame or NULL on error.
1350+
*/
1351+
struct quic_frame *quic_strm_frm_split(struct quic_frame *frm, uint64_t split)
1352+
{
1353+
struct quic_frame *new;
1354+
struct buffer stream_buf;
1355+
1356+
new = qc_frm_alloc(frm->type);
1357+
if (!new)
1358+
return NULL;
1359+
1360+
if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F) {
1361+
new->stream.stream = frm->stream.stream;
1362+
new->stream.buf = frm->stream.buf;
1363+
new->stream.id = frm->stream.id;
1364+
new->stream.offset = frm->stream.offset;
1365+
new->stream.len = split;
1366+
new->type |= QUIC_STREAM_FRAME_TYPE_LEN_BIT;
1367+
new->type &= ~QUIC_STREAM_FRAME_TYPE_FIN_BIT;
1368+
new->stream.data = frm->stream.data;
1369+
new->stream.dup = frm->stream.dup;
1370+
1371+
/* Advance original frame to point to the remaining data. */
1372+
frm->type |= QUIC_STREAM_FRAME_TYPE_OFF_BIT;
1373+
stream_buf = b_make(b_orig(frm->stream.buf),
1374+
b_size(frm->stream.buf),
1375+
(char *)frm->stream.data - b_orig(frm->stream.buf), 0);
1376+
frm->stream.len -= split;
1377+
frm->stream.offset += split;
1378+
frm->stream.data = (unsigned char *)b_peek(&stream_buf, split);
1379+
}
1380+
else {
1381+
/* Function must only be used with STREAM or CRYPTO frames. */
1382+
ABORT_NOW();
1383+
}
1384+
1385+
/* Detach <frm> from its origin if it was duplicated. */
1386+
if (frm->origin) {
1387+
LIST_APPEND(&frm->origin->reflist, &new->ref);
1388+
new->origin = frm->origin;
1389+
LIST_DEL_INIT(&frm->ref);
1390+
frm->origin = NULL;
1391+
}
1392+
1393+
return new;
1394+
}

src/quic_tx.c

Lines changed: 29 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,8 +1532,10 @@ static int qc_build_frms(struct list *outlist, struct list *inlist,
15321532
* in the switch/case block.
15331533
*/
15341534
list_for_each_entry_safe(cf, cfbak, inlist, list) {
1535+
struct quic_frame *split_frm;
15351536
/* header length, data length, frame length. */
15361537
size_t hlen, dlen, flen;
1538+
size_t split_size;
15371539

15381540
if (!room)
15391541
break;
@@ -1601,111 +1603,47 @@ static int qc_build_frms(struct list *outlist, struct list *inlist,
16011603
continue;
16021604
}
16031605
}
1604-
/* Note that these frames are accepted in short packets only without
1605-
* "Length" packet field. Here, <*len> is used only to compute the
1606-
* sum of the lengths of the already built frames for this packet.
1607-
*
1608-
* Compute the length of this STREAM frame "header" made a all the field
1609-
* excepting the variable ones. Note that +1 is for the type of this frame.
1610-
*/
1611-
hlen = 1 + quic_int_getsize(cf->stream.id) +
1612-
((cf->type & QUIC_STREAM_FRAME_TYPE_OFF_BIT) ? quic_int_getsize(cf->stream.offset) : 0);
1613-
if (room <= hlen)
1614-
continue;
1615-
1616-
TRACE_DEVEL(" New STREAM frame build (room, len)",
1617-
QUIC_EV_CONN_BCFRMS, qc, &room, len);
16181606

1619-
/* hlen contains STREAM id and offset. Ensure there is
1620-
* enough room for length field.
1621-
*/
1622-
if (cf->type & QUIC_STREAM_FRAME_TYPE_LEN_BIT) {
1623-
dlen = QUIC_MIN(quic_int_cap_length(room - hlen),
1624-
cf->stream.len);
1625-
flen = hlen + quic_int_getsize(dlen) + dlen;
1626-
}
1627-
else {
1628-
dlen = QUIC_MIN(room - hlen, cf->stream.len);
1629-
flen = hlen + dlen;
1630-
}
1631-
1632-
if (cf->stream.len && !dlen) {
1633-
/* Only a small gap is left on buffer, not
1634-
* enough to encode the STREAM data length.
1635-
*/
1607+
flen = quic_strm_frm_fillbuf(room, cf, &split_size);
1608+
if (!flen)
16361609
continue;
1637-
}
16381610

1639-
TRACE_DEVEL(" STREAM data length (hlen, stream.len, dlen)",
1640-
QUIC_EV_CONN_BCFRMS, qc, &hlen, &cf->stream.len, &dlen);
16411611
TRACE_DEVEL(" STREAM frame length (flen)",
16421612
QUIC_EV_CONN_BCFRMS, qc, &flen);
1643-
/* Add the STREAM data length and its encoded length to the packet
1644-
* length and the length of this length.
1645-
*/
1646-
*len += flen;
1647-
room -= flen;
1648-
if (dlen == cf->stream.len) {
1649-
/* <cf> STREAM data have been consumed. */
1650-
LIST_DEL_INIT(&cf->list);
1651-
LIST_APPEND(outlist, &cf->list);
16521613

1653-
qc_stream_desc_send(cf->stream.stream,
1654-
cf->stream.offset,
1655-
cf->stream.len);
1656-
}
1657-
else {
1658-
struct quic_frame *new_cf;
1659-
struct buffer cf_buf;
1614+
/* TODO the MUX is notified about the frame sending via
1615+
* previous qc_stream_desc_send call. However, the
1616+
* sending can fail later, for example if the sendto
1617+
* system call returns an error. As the MUX has been
1618+
* notified, the transport layer is responsible to
1619+
* bufferize and resent the announced data later.
1620+
*/
16601621

1661-
new_cf = qc_frm_alloc(cf->type);
1662-
if (!new_cf) {
1622+
if (split_size) {
1623+
split_frm = quic_strm_frm_split(cf, split_size);
1624+
if (!split_frm) {
16631625
TRACE_ERROR("No memory for new STREAM frame", QUIC_EV_CONN_BCFRMS, qc);
16641626
continue;
16651627
}
16661628

1667-
new_cf->stream.stream = cf->stream.stream;
1668-
new_cf->stream.buf = cf->stream.buf;
1669-
new_cf->stream.id = cf->stream.id;
1670-
new_cf->stream.offset = cf->stream.offset;
1671-
new_cf->stream.len = dlen;
1672-
new_cf->type |= QUIC_STREAM_FRAME_TYPE_LEN_BIT;
1673-
/* FIN bit reset */
1674-
new_cf->type &= ~QUIC_STREAM_FRAME_TYPE_FIN_BIT;
1675-
new_cf->stream.data = cf->stream.data;
1676-
new_cf->stream.dup = cf->stream.dup;
1677-
TRACE_DEVEL("split frame", QUIC_EV_CONN_PRSAFRM, qc, new_cf);
1678-
if (cf->origin) {
1629+
TRACE_DEVEL("split frame", QUIC_EV_CONN_PRSAFRM, qc, split_frm);
1630+
if (split_frm->origin)
16791631
TRACE_DEVEL("duplicated frame", QUIC_EV_CONN_PRSAFRM, qc);
1680-
/* This <cf> frame was duplicated */
1681-
LIST_APPEND(&cf->origin->reflist, &new_cf->ref);
1682-
new_cf->origin = cf->origin;
1683-
/* Detach this STREAM frame from its origin */
1684-
LIST_DEL_INIT(&cf->ref);
1685-
cf->origin = NULL;
1686-
}
1687-
LIST_APPEND(outlist, &new_cf->list);
1688-
cf->type |= QUIC_STREAM_FRAME_TYPE_OFF_BIT;
1689-
/* Consume <dlen> bytes of the current frame. */
1690-
cf_buf = b_make(b_orig(cf->stream.buf),
1691-
b_size(cf->stream.buf),
1692-
(char *)cf->stream.data - b_orig(cf->stream.buf), 0);
1693-
cf->stream.len -= dlen;
1694-
cf->stream.offset += dlen;
1695-
cf->stream.data = (unsigned char *)b_peek(&cf_buf, dlen);
1696-
1697-
qc_stream_desc_send(new_cf->stream.stream,
1698-
new_cf->stream.offset,
1699-
new_cf->stream.len);
1632+
LIST_APPEND(outlist, &split_frm->list);
1633+
qc_stream_desc_send(split_frm->stream.stream,
1634+
split_frm->stream.offset,
1635+
split_frm->stream.len);
1636+
}
1637+
else {
1638+
LIST_DEL_INIT(&cf->list);
1639+
LIST_APPEND(outlist, &cf->list);
1640+
qc_stream_desc_send(cf->stream.stream,
1641+
cf->stream.offset,
1642+
cf->stream.len);
17001643
}
17011644

1702-
/* TODO the MUX is notified about the frame sending via
1703-
* previous qc_stream_desc_send call. However, the
1704-
* sending can fail later, for example if the sendto
1705-
* system call returns an error. As the MUX has been
1706-
* notified, the transport layer is responsible to
1707-
* bufferize and resent the announced data later.
1708-
*/
1645+
*len += flen;
1646+
room -= flen;
17091647

17101648
break;
17111649

0 commit comments

Comments
 (0)