mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-04-18 20:22:48 +02:00
MEDIUM: h3: prevent new streams on GOAWAY reception
Implement the reception of a HTTP/3 GOAWAY frame. This is performed via the new function h3_parse_goaway_frm(). The advertised ID is stored in new <id_shut_r> h3c member. It serves to ensure that a bigger ID is not advertised when receiving multiple GOAWAY frames. GOAWAY frame reception is only really useful on the backend side for haproxy. When this occurs, h3c is now flagged with H3_CF_GOAWAY_RECV. Also, QCC is also updated with new flag QC_CF_CONN_SHUT. This flag indicates that no new stream may be opened on the connection. Callback avail_streams() is thus edited to report 0 in this case.
This commit is contained in:
parent
5c8c9fc528
commit
220b1bf6d9
@ -259,7 +259,7 @@ struct qcc_app_ops {
|
||||
#define QC_CF_ERRL_DONE 0x00000002 /* local error properly handled, connection can be released */
|
||||
#define QC_CF_IS_BACK 0x00000004 /* backend side */
|
||||
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
|
||||
/* unused 0x00000010 */
|
||||
#define QC_CF_CONN_SHUT 0x00000010 /* peer has initiate app layer shutdown - no new stream should be opened locally */
|
||||
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
|
||||
#define QC_CF_WAIT_HS 0x00000040 /* MUX init before QUIC handshake completed (0-RTT) */
|
||||
|
||||
|
||||
@ -43,6 +43,7 @@ int qcc_stream_can_send(const struct qcs *qcs);
|
||||
void qcc_reset_stream(struct qcs *qcs, int err);
|
||||
void qcc_send_stream(struct qcs *qcs, int urg, int count);
|
||||
void qcc_abort_stream_read(struct qcs *qcs);
|
||||
void qcc_update_shut_id(struct qcc *qcc, uint64_t val);
|
||||
int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
|
||||
char fin, char *data);
|
||||
int qcc_recv_max_data(struct qcc *qcc, uint64_t max);
|
||||
|
||||
49
src/h3.c
49
src/h3.c
@ -123,6 +123,7 @@ INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE);
|
||||
#define H3_CF_UNI_QPACK_DEC_SET 0x00000008 /* Remote QPACK decoder stream opened */
|
||||
#define H3_CF_UNI_QPACK_ENC_SET 0x00000010 /* Remote QPACK encoder stream opened */
|
||||
#define H3_CF_GOAWAY_SENT 0x00000020 /* GOAWAY sent on local control stream */
|
||||
#define H3_CF_GOAWAY_RECV 0x00000040 /* GOAWAY received from the peer */
|
||||
|
||||
/* Default settings */
|
||||
static uint64_t h3_settings_qpack_max_table_capacity = 0;
|
||||
@ -141,6 +142,7 @@ struct h3c {
|
||||
uint64_t max_field_section_size;
|
||||
|
||||
uint64_t id_shut_l; /* GOAWAY ID locally emitted */
|
||||
uint64_t id_shut_r; /* GOAWAY ID emitted by the peer */
|
||||
|
||||
struct buffer_wait buf_wait; /* wait list for buffer allocations */
|
||||
/* Stats counters */
|
||||
@ -1681,6 +1683,43 @@ static ssize_t h3_parse_settings_frm(struct h3c *h3c, const struct buffer *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t h3_parse_goaway_frm(struct h3c *h3c, const struct buffer *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct buffer b;
|
||||
uint64_t id;
|
||||
size_t ret = 0;
|
||||
|
||||
TRACE_ENTER(H3_EV_RX_FRAME, h3c->qcc->conn);
|
||||
|
||||
b = b_make(b_orig(buf), b_size(buf), b_head_ofs(buf), len);
|
||||
if (!b_quic_dec_int(&id, &b, &ret)) {
|
||||
h3c->err = H3_ERR_FRAME_ERROR;
|
||||
qcc_report_glitch(h3c->qcc, 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((h3c->flags & H3_CF_GOAWAY_RECV) && id > h3c->id_shut_r) {
|
||||
h3c->err = H3_ERR_ID_ERROR;
|
||||
qcc_report_glitch(h3c->qcc, 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
h3c->flags |= H3_CF_GOAWAY_RECV;
|
||||
h3c->id_shut_r = id;
|
||||
|
||||
/* RFC 9114 5.2. Connection Shutdown
|
||||
*
|
||||
* Endpoints MUST NOT initiate new requests or promise new pushes on the
|
||||
* connection after receipt of a GOAWAY frame from the peer. Clients MAY
|
||||
* establish a new connection to send additional requests.
|
||||
*/
|
||||
h3c->qcc->flags |= QC_CF_CONN_SHUT;
|
||||
|
||||
TRACE_LEAVE(H3_EV_RX_FRAME, h3c->qcc->conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Transcode HTTP/3 payload received in buffer <b> to HTX data for stream
|
||||
* <qcs>. If <fin> is set, it indicates that no more data will arrive after.
|
||||
*
|
||||
@ -1883,10 +1922,17 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||
h3s->st_req = H3S_ST_REQ_TRAILERS;
|
||||
}
|
||||
break;
|
||||
case H3_FT_GOAWAY:
|
||||
ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
|
||||
if (ret < 0) {
|
||||
TRACE_ERROR("error on SETTINGS parsing", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||
qcc_set_error(qcs->qcc, h3c->err, 1);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case H3_FT_CANCEL_PUSH:
|
||||
case H3_FT_PUSH_PROMISE:
|
||||
case H3_FT_MAX_PUSH_ID:
|
||||
case H3_FT_GOAWAY:
|
||||
/* Not supported */
|
||||
ret = flen;
|
||||
break;
|
||||
@ -3201,6 +3247,7 @@ static int h3_init(struct qcc *qcc)
|
||||
h3c->err = 0;
|
||||
h3c->flags = 0;
|
||||
h3c->id_shut_l = 0;
|
||||
h3c->id_shut_r = 0;
|
||||
|
||||
qcc->ctx = h3c;
|
||||
h3c->prx_counters = qc_counters(qcc->conn->target, &h3_stats_module);
|
||||
|
||||
@ -3214,6 +3214,10 @@ static int qmux_avail_streams(struct connection *conn)
|
||||
struct qcc *qcc = conn->ctx;
|
||||
int ret, max_reuse = 0;
|
||||
|
||||
/* Shutdown initiated by the peer - in HTTP/3 this corresponds to a GOAWAY frame received. */
|
||||
if (qcc->flags & QC_CF_CONN_SHUT)
|
||||
return 0;
|
||||
|
||||
ret = qcc_fctl_avail_streams(qcc, 1);
|
||||
|
||||
if (srv->max_reuse >= 0) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user