]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: adjust request reject when MUX is already freed
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 7 Feb 2023 13:24:54 +0000 (14:24 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 20 Feb 2023 09:52:05 +0000 (10:52 +0100)
When the MUX is freed, the quic-conn layer may stay active until all
streams acknowledgment are processed. In this interval, if a new stream
is opened by the client, the quic-conn is thus now responsible to handle
it. This is done by the emission of a STOP_SENDING.

This process is closely related to HTTP/3 protocol despite being handled
by the quic-conn layer. This highlights a flaw in our QUIC architecture
which should be adjusted. To reflect this situation, the function
qc_stop_sending_frm_enqueue() is renamed qc_h3_request_reject(). Also,
internal H3 treatment such as uni-directional bypass has been moved
inside the function.

This commit is only a refactor. However, bug fix on next patches will
rely on it so it should be backported up to 2.6.

src/quic_conn.c

index 4d22a49b410b83ad3256f2f8d5c592a5679ec056..4b1d56ccec0a659768133dfa9679994ece8a5beb 100644 (file)
@@ -2697,24 +2697,33 @@ static void qc_cc_err_count_inc(struct quic_conn *qc, struct quic_frame *frm)
        TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);
 }
 
-/* Enqueue a STOP_SENDING frame to send into 1RTT packet number space
- * frame list to send.
- * Return 1 if succeeded, 0 if not.
+/* Cancel a request on connection <qc> for stream id <id>. This is useful when
+ * the client opens a new stream but the MUX has already been released. A
+ * STOP_SENDING frame is prepared for emission.
+ *
+ * TODO this function is closely related to H3. Its place should be in H3 layer
+ * instead of quic-conn but this requires an architecture adjustment.
+ *
+ * Returns 1 on sucess else 0.
  */
-static int qc_stop_sending_frm_enqueue(struct quic_conn *qc, uint64_t id)
+static int qc_h3_request_reject(struct quic_conn *qc, uint64_t id)
 {
        int ret = 0;
        struct quic_frame *frm;
        struct quic_enc_level *qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
-       uint64_t app_error_code;
+       const uint64_t app_error_code = H3_REQUEST_REJECTED;
 
        TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
 
-       /* TODO: the mux may be released, we cannot have more
-        * information about the application error code to send
-        * at this time.
+       /* Do not emit rejection for unknown unidirectional stream as it is
+        * forbidden to close some of them (H3 control stream and QPACK
+        * encoder/decoder streams).
         */
-       app_error_code = H3_REQUEST_REJECTED;
+       if (quic_stream_is_uni(id)) {
+               ret = 1;
+               goto out;
+       }
+
        frm = qc_frm_alloc(QUIC_FT_STOP_SENDING);
        if (!frm) {
                TRACE_ERROR("failed to allocate quic_frame", QUIC_EV_CONN_PRSHPKT, qc);
@@ -2935,19 +2944,10 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
                                }
                                else {
                                        TRACE_DEVEL("No mux for new stream", QUIC_EV_CONN_PRSHPKT, qc);
-                                       if (qc->app_ops == &h3_ops && quic_stream_is_uni(stream->id)) {
-                                               /* Do not send STOP_SENDING frames for h3 unidirectional streams.
-                                                * TODO: this test should be removed when the connection closure
-                                                * will be more clean.
-                                                * At quic_conn level there is no mean to know that an application
-                                                * want to forbid stream closure requests to receivers. This is the
-                                                * case for the Control and QPACK h3 unidirectional streams.
-                                                */
-                                               goto leave;
+                                       if (qc->app_ops == &h3_ops) {
+                                               if (!qc_h3_request_reject(qc, stream->id))
+                                                       TRACE_ERROR("could not enqueue STOP_SENDING frame", QUIC_EV_CONN_PRSHPKT, qc);
                                        }
-
-                                       if (!qc_stop_sending_frm_enqueue(qc, stream->id))
-                                               TRACE_ERROR("could not enqueue STOP_SENDING frame", QUIC_EV_CONN_PRSHPKT, qc);
                                        /* This packet will not be acknowledged */
                                        goto leave;
                                }