From: Amaury Denoyelle Date: Mon, 28 Mar 2022 12:53:45 +0000 (+0200) Subject: MINOR: h3: implement graceful shutdown with GOAWAY X-Git-Tag: v2.7-dev2~54 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=114c9c87ce8826cc920a27de6524d9cff07d837b;p=thirdparty%2Fhaproxy.git MINOR: h3: implement graceful shutdown with GOAWAY Implement graceful shutdown as specified in RFC 9114. A GOAWAY frame is generated with stream ID to indicate range of processed requests. This process is done via the release app protocol operation. The MUX is responsible to emit the generated GOAWAY frame after app release. A CONNECTION_CLOSE will be emitted once there is no unacknowledged STREAM frames. --- diff --git a/src/h3.c b/src/h3.c index 926632ee9b..d75c7d04c5 100644 --- a/src/h3.c +++ b/src/h3.c @@ -122,6 +122,8 @@ struct h3c { uint64_t qpack_blocked_streams; uint64_t max_field_section_size; + uint64_t id_goaway; /* stream ID used for a GOAWAY frame */ + struct buffer_wait buf_wait; /* wait list for buffer allocations */ /* Stats counters */ struct h3_counters *prx_counters; @@ -417,6 +419,19 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf, return -1; } + /* RFC 9114 5.2. Connection Shutdown + * + * The GOAWAY frame contains an identifier that + * indicates to the receiver the range of requests or pushes that were + * or might be processed in this connection. The server sends a client- + * initiated bidirectional stream ID; the client sends a push ID. + * Requests or pushes with the indicated identifier or greater are + * rejected (Section 4.1.1) by the sender of the GOAWAY. This + * identifier MAY be zero if no requests or pushes were processed. + */ + if (qcs->id >= h3c->id_goaway) + h3c->id_goaway = qcs->id + 4; + /* buffer is transferred to the stream connector and set to NULL * except on stream creation error. */ @@ -1092,6 +1107,37 @@ static int h3_finalize(void *ctx) return 1; } +/* Generate a GOAWAY frame for connection on the control stream. + * + * Returns 0 on success else non-zero. + */ +static int h3_send_goaway(struct h3c *h3c) +{ + struct qcs *qcs = h3c->ctrl_strm; + struct buffer pos, *res; + unsigned char data[3 * QUIC_VARINT_MAX_SIZE]; + size_t frm_len = quic_int_getsize(h3c->id_goaway); + + if (!qcs) + return 1; + + pos = b_make((char *)data, sizeof(data), 0, 0); + + b_quic_enc_int(&pos, H3_FT_GOAWAY); + b_quic_enc_int(&pos, frm_len); + b_quic_enc_int(&pos, h3c->id_goaway); + + res = mux_get_buf(qcs); + if (!res || b_room(res) < b_data(&pos)) { + /* Do not try forcefully to emit GOAWAY if no space left. */ + return 1; + } + + b_force_xfer(res, &pos, b_data(&pos)); + + return 0; +} + /* Initialize the HTTP/3 context for mux. * Return 1 if succeeded, 0 if not. */ @@ -1108,6 +1154,7 @@ static int h3_init(struct qcc *qcc) h3c->ctrl_strm = NULL; h3c->err = H3_NO_ERROR; h3c->flags = 0; + h3c->id_goaway = 0; qcc->ctx = h3c; h3c->prx_counters = @@ -1125,6 +1172,15 @@ static void h3_release(void *ctx) { struct h3c *h3c = ctx; + /* RFC 9114 5.2. Connection Shutdown + * + * Even when a connection is not idle, either endpoint can decide to + * stop using the connection and initiate a graceful connection close. + * Endpoints initiate the graceful shutdown of an HTTP/3 connection by + * sending a GOAWAY frame. + */ + h3_send_goaway(h3c); + /* RFC 9114 5.2. Connection Shutdown * * An endpoint that completes a