Files
haproxy/src/qmux_http.c
Amaury Denoyelle f3003d1508 BUG/MAJOR: Revert "MEDIUM: mux-quic: add BUG_ON if sending on locally closed QCS"
This reverts commit 235e8f1afd.

Prior to the above commit, snd_buf callback for QUIC MUX was able to
deal with data even after stream closure. The excess was simply
discarded, as no STREAM frame can be emitted after FIN/RESET_STREAM.
This code was later removed and replaced by a BUG_ON() to ensure snd_buf
is never called after stream closure.

However, this approach is too strict. Indeed, there is nothing in the
haproxy stream architecture which forbids this scheduling, in part
because QUIC MUX is the sole responsible of the stream closure. As such,
it is preferable to revert to the old code to prevent any triggering of
a BUG_ON() failure.

Note that nego_ff does not implement data draining if called after
stream closure. This will be done in a future patch.

Thanks to Mike Walker for his investigation on the subject.

This must be backported up to 2.8.
2026-02-17 18:18:44 +01:00

125 lines
3.1 KiB
C

#include <haproxy/qmux_http.h>
#include <haproxy/api-t.h>
#include <haproxy/htx.h>
#include <haproxy/qmux_trace.h>
/* QUIC MUX rcv_buf operation using HTX data. Received data from stream <qcs>
* will be transferred as HTX in <buf>. Output buffer is expected to be of
* length <count>. <fin> will be set to signal the last data to receive on this
* stream.
*
* Return the size in bytes of transferred data.
*/
size_t qcs_http_rcv_buf(struct qcs *qcs, struct buffer *buf, size_t count,
char *fin)
{
struct htx *qcs_htx = NULL;
struct htx *cs_htx = NULL;
size_t ret = 0;
TRACE_ENTER(QMUX_EV_STRM_RECV, qcs->qcc->conn, qcs);
*fin = 0;
qcs_htx = htx_from_buf(&qcs->rx.app_buf);
if (htx_is_empty(qcs_htx)) {
/* Set buffer data to 0 as HTX is empty. */
htx_to_buf(qcs_htx, &qcs->rx.app_buf);
goto end;
}
ret = qcs_htx->data;
cs_htx = htx_from_buf(buf);
if (htx_is_empty(cs_htx) && htx_used_space(qcs_htx) <= count) {
/* EOM will be copied to cs_htx via b_xfer(). */
if ((qcs_htx->flags & HTX_FL_EOM) &&
!(qcs->flags & QC_SF_EOI_SUSPENDED)) {
*fin = 1;
}
htx_to_buf(cs_htx, buf);
htx_to_buf(qcs_htx, &qcs->rx.app_buf);
b_xfer(buf, &qcs->rx.app_buf, b_data(&qcs->rx.app_buf));
goto end;
}
htx_xfer_blks(cs_htx, qcs_htx, count, HTX_BLK_UNUSED);
BUG_ON(qcs_htx->flags & HTX_FL_PARSING_ERROR);
/* Copy EOM from src to dst buffer if all data copied. */
if (htx_is_empty(qcs_htx) && (qcs_htx->flags & HTX_FL_EOM)) {
cs_htx->flags |= HTX_FL_EOM;
if (!(qcs->flags & QC_SF_EOI_SUSPENDED))
*fin = 1;
}
htx_to_buf(cs_htx, buf);
htx_to_buf(qcs_htx, &qcs->rx.app_buf);
ret -= qcs_htx->data;
end:
TRACE_LEAVE(QMUX_EV_STRM_RECV, qcs->qcc->conn, qcs);
return ret;
}
int qcs_http_handle_standalone_fin(struct qcs *qcs)
{
struct buffer *appbuf;
struct htx *htx;
int eom;
if (!(appbuf = qcc_get_stream_rxbuf(qcs)))
goto err;
htx = htx_from_buf(appbuf);
eom = htx_set_eom(htx);
htx_to_buf(htx, appbuf);
if (!eom)
goto err;
return 0;
err:
return -1;
}
/* QUIC MUX snd_buf operation using HTX data. HTX data will be transferred from
* <buf> to <qcs> stream buffer. Input buffer is expected to be of length
* <count>. <fin> will be set to signal the last data to send for this stream.
*
* Return the size in bytes of transferred data.
*/
size_t qcs_http_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count,
char *fin)
{
size_t ret;
TRACE_ENTER(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
ret = qcs->qcc->app_ops->snd_buf(qcs, buf, count, fin);
TRACE_LEAVE(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
return ret;
}
/* QUIC MUX snd_buf reset. HTX data stored in <buf> of length <count> will be
* cleared. This can be used when data should not be transmitted any longer.
*
* Return the size in bytes of cleared data.
*/
size_t qcs_http_reset_buf(struct qcs *qcs, struct buffer *buf, size_t count)
{
struct htx *htx;
TRACE_ENTER(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
htx = htx_from_buf(buf);
htx_reset(htx);
htx_to_buf(htx, buf);
TRACE_LEAVE(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
return count;
}