From: Christopher Faulet Date: Tue, 15 Sep 2026 11:59:32 +0000 (+0200) Subject: MEDIUM: htx: Allow tunneled data to be added in a message already ended X-Git-Tag: v3.5-dev8~148 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=78b41149b7842b81ca12b3ce49d6b278759f4f18;p=haproxy.git MEDIUM: htx: Allow tunneled data to be added in a message already ended For a tunneled stream, the end of the HTTP message is reported just after the headers. It is an "early" EOM: from the HTTP point of view the message is finished and only tunneled data may be exchanged then. But nothing may be added in a message already ended, otherwise the end of the message would no longer be reported on the tail block. Now tunneled data have their own block type, they can be added in a message already ended without breaking this rule. htx_add_raw_data() was added for that purpose. It is the only function able to add a block in such message, and the corresponding BUG_ON() were relaxed accordingly. htx_msg_ended() was updated to also report the end of the message when the tail block is a RAW_DATA block. Indeed, tunneled data may only be found after the end of the message. So, if there are some, the end of the message was necessarily seen. Either the block carrying the flag is still in the HTX message, or it was already consumed. In this last case, it is the consumer responsibility to remember it. Finally, the test on the end of the message was removed from htx_xfer() because blocks are transferred one by one, using the safe functions. So invalid additions are detected there, with a better accuracy. --- diff --git a/include/haproxy/htx.h b/include/haproxy/htx.h index d7fd4f36b..6c281ac72 100644 --- a/include/haproxy/htx.h +++ b/include/haproxy/htx.h @@ -54,6 +54,7 @@ struct htx_blk *htx_add_data_type_atonce(struct htx *htx, struct ist data, enum struct htx_blk *htx_add_data_atonce(struct htx *htx, struct ist data); size_t htx_add_data_type(struct htx *htx, const struct ist data, enum htx_blk_type type); size_t htx_add_data(struct htx *htx, const struct ist data); +size_t htx_add_raw_data(struct htx *htx, const struct ist data); struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data); void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref); int htx_append_msg(struct htx *dst, const struct htx *src); @@ -273,9 +274,15 @@ static inline enum htx_blk_type htx_get_tail_type(const struct htx *htx) return (blk ? htx_get_blk_type(blk) : HTX_BLK_UNUSED); } -/* Returns true if the end of the message was reached, i.e. if the HTX message is - * not empty and the HTX_BLK_FL_EOM flag is set on the tail block. Otherwise, - * false is returned. +/* Returns true if the end of the message was reached. Otherwise, false is + * returned. + * + * Note the block carrying the HTX_BLK_FL_EOM flag is not necessarily still in + * the HTX message. It may have already been consumed. In this case, the tail + * block is a RAW_DATA block, because tunneled data may only be found after the + * end of the message. Producers and consumers must remember it by themselves, + * an HTX message being a partial and stateless representation of an HTTP + * message. */ static inline int htx_msg_ended(const struct htx *htx) { @@ -283,7 +290,17 @@ static inline int htx_msg_ended(const struct htx *htx) /* htx_remove_blk() must take care to never leave unused blocks on head and tail of the message */ BUG_ON_HOT(blk && (blk->flags & HTX_BLK_FL_EOM) && htx_get_blk_type(blk) == HTX_BLK_UNUSED); - return (blk != NULL && !!(blk->flags & HTX_BLK_FL_EOM)); + if (!blk) + return 0; + + /* Tunneled data may only be found after the end of the message. So, if + * the tail block is a RAW_DATA block, the end of the message was + * necessarily seen. Either the block carrying the EOM flag is still in + * the HTX message, or it was already consumed. In this last case, it is + * the consumer responsibility to remember it. + */ + return (!!(blk->flags & HTX_BLK_FL_EOM) || + htx_get_blk_type(blk) == HTX_BLK_RAW_DATA); } /* Returns the position of block immediately before the one pointed by . If diff --git a/src/htx.c b/src/htx.c index 4ee527254..12ce2d964 100644 --- a/src/htx.c +++ b/src/htx.c @@ -387,7 +387,8 @@ struct htx_blk *__htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t */ struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t blksz) { - BUG_ON(htx_msg_ended(htx)); + /* Only tunneled data may be added in a message already ended */ + BUG_ON(htx_msg_ended(htx) && type != HTX_BLK_RAW_DATA); return __htx_add_blk(htx, type, blksz); } @@ -841,8 +842,10 @@ size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int fla uint32_t max, last_dstblk_sz; int dst_full = 0; - BUG_ON(htx_msg_ended(dst)); - + /* Note: no test on the end of the message here. Blocks are added one by + * one below, using the safe functions. So invalid additions in a message + * already ended are detected there. + */ last_dstblk = NULL; last_dstblk_sz = 0; for (blk = htx_get_head_blk(src); blk && count > meta_sz; blk = htx_get_next_blk(src, blk)) { @@ -1186,7 +1189,9 @@ size_t htx_add_data_type(struct htx *htx, const struct ist data, enum htx_blk_ty uint32_t flags = 0; BUG_ON(!htx_is_data_type(type)); - BUG_ON(htx_msg_ended(htx)); + + /* Only tunneled data may be added in a message already ended */ + BUG_ON(htx_msg_ended(htx) && type != HTX_BLK_RAW_DATA); /* Not enough space to store data */ if (len > htx_free_data_space(htx)) @@ -1268,6 +1273,15 @@ size_t htx_add_data(struct htx *htx, const struct ist data) return htx_add_data_type(htx, data, HTX_BLK_DATA); } +/* Adds a RAW DATA block in , to store tunneled data. Unlike HTTP data, + * these ones may be added in a message already ended. See htx_add_data_type() + * for details. + */ +size_t htx_add_raw_data(struct htx *htx, const struct ist data) +{ + return htx_add_data_type(htx, data, HTX_BLK_RAW_DATA); +} + /* Adds an HTX block of type DATA in just after all other DATA * blocks. Because it relies on htx_add_data_atonce(), It may be happened to a