]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: h3: implement graceful shutdown with GOAWAY
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 28 Mar 2022 12:53:45 +0000 (14:53 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 15 Jul 2022 13:56:13 +0000 (15:56 +0200)
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.

src/h3.c

index 926632ee9bdbdfc51c87eec17ce08d73e4142c7c..d75c7d04c58403fafa34d7a1fee1b1f2b4dcfe44 100644 (file)
--- 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 <h3c> 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 <qcc> 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