]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-h2: Forward h2 client cancellations to h2 servers
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 30 Apr 2024 12:29:57 +0000 (14:29 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 6 May 2024 20:00:00 +0000 (22:00 +0200)
When a H2 client sends a RST_STREAM(CANCEL) frame to abort a request, the
abort reason is now used on server side, in the H2 mux, to set the
RST_STREAM code. The main use case is to forward client cancellations to
gRPC applications.

This patch should fix the issue #172.

src/mux_h2.c

index 287979bf92ce26aaadb46e6f5eb78384418a136c..f567ea5228b7ef4ceb98f3667ebb6f005d1dbff7 100644 (file)
@@ -1264,6 +1264,20 @@ static inline int h2s_mws(const struct h2s *h2s)
        return h2s->sws + h2s->h2c->miw;
 }
 
+/* Returns 1 if the H2 error of the opposite side is forwardable to the peer.
+ * Otherwise 0 is returned.
+ * For now, only CANCEL from the client is forwardable to the server.
+ */
+static inline int h2s_is_forwardable_abort(struct h2s *h2s, struct se_abort_info *reason)
+{
+       enum h2_err err = H2_ERR_NO_ERROR;
+
+       if (reason && ((reason->info & SE_ABRT_SRC_MASK) >> SE_ABRT_SRC_SHIFT) == SE_ABRT_SRC_MUX_H2)
+               err = reason->code;
+
+       return ((h2s->h2c->flags & H2_CF_IS_BACK) && (err == H2_ERR_CANCEL));
+}
+
 /* marks an error on the connection. Before settings are sent, we must not send
  * a GOAWAY frame, and the error state will prevent h2c_send_goaway_error()
  * from verifying this so we set H2_CF_GOAWAY_FAILED to make sure it will not
@@ -4915,6 +4929,10 @@ static void h2_do_shutr(struct h2s *h2s, struct se_abort_info *reason)
                h2c_error(h2c, H2_ERR_ENHANCE_YOUR_CALM);
                h2s_error(h2s, H2_ERR_ENHANCE_YOUR_CALM);
        }
+       else if (h2s_is_forwardable_abort(h2s, reason)) {
+               TRACE_STATE("shutr using opposite endp code", H2_EV_STRM_SHUT, h2c->conn, h2s);
+               h2s_error(h2s, reason->code);
+       }
        else if (!(h2s->flags & H2_SF_HEADERS_SENT)) {
                /* Nothing was never sent for this stream, so reset with
                 * REFUSED_STREAM error to let the client retry the
@@ -4960,8 +4978,9 @@ add_to_list:
        return;
 }
 
+
 /* Performs a synchronous or asynchronous shutw(). */
-static void h2_do_shutw(struct h2s *h2s)
+static void h2_do_shutw(struct h2s *h2s, struct se_abort_info *reason)
 {
        struct h2c *h2c = h2s->h2c;
 
@@ -4971,6 +4990,7 @@ static void h2_do_shutw(struct h2s *h2s)
        TRACE_ENTER(H2_EV_STRM_SHUT, h2c->conn, h2s);
 
        if (h2s->st != H2_SS_ERROR &&
+           !h2s_is_forwardable_abort(h2s, reason) &&
            (h2s->flags & (H2_SF_HEADERS_SENT | H2_SF_MORE_HTX_DATA)) == H2_SF_HEADERS_SENT) {
                /* we can cleanly close using an empty data frame only after headers
                 * and if no more data is expected to be sent.
@@ -4995,6 +5015,10 @@ static void h2_do_shutw(struct h2s *h2s)
                        h2c_error(h2c, H2_ERR_ENHANCE_YOUR_CALM);
                        h2s_error(h2s, H2_ERR_ENHANCE_YOUR_CALM);
                }
+               else if (h2s_is_forwardable_abort(h2s, reason)) {
+                       TRACE_STATE("shutw using opposite endp code", H2_EV_STRM_SHUT, h2c->conn, h2s);
+                       h2s_error(h2s, reason->code);
+               }
                else if (h2s->flags & H2_SF_MORE_HTX_DATA) {
                        /* some unsent data were pending (e.g. abort during an upload),
                         * let's send a CANCEL.
@@ -5061,10 +5085,10 @@ struct task *h2_deferred_shut(struct task *t, void *ctx, unsigned int state)
        }
 
        if (h2s->flags & H2_SF_WANT_SHUTW)
-               h2_do_shutw(h2s);
+               h2_do_shutw(h2s, NULL);
 
        if (h2s->flags & H2_SF_WANT_SHUTR)
-               h2_do_shutr(h2s);
+               h2_do_shutr(h2s, NULL);
 
        if (!(h2s->flags & (H2_SF_WANT_SHUTR|H2_SF_WANT_SHUTW))) {
                /* We're done trying to send, remove ourself from the send_list */
@@ -5088,10 +5112,12 @@ static void h2_shut(struct stconn *sc, enum se_shut_mode mode, struct se_abort_i
        struct h2s *h2s = __sc_mux_strm(sc);
 
        TRACE_ENTER(H2_EV_STRM_SHUT, h2s->h2c->conn, h2s);
-       if (mode & (SE_SHW_SILENT|SE_SHW_NORMAL))
-               h2_do_shutw(h2s);
+       if (mode & (SE_SHW_SILENT|SE_SHW_NORMAL)) {
+               /* Pass the reason for silent shutw only (abort) */
+               h2_do_shutw(h2s, (mode & SE_SHW_SILENT) ? reason : NULL);
+       }
        if (mode & SE_SHR_RESET)
-               h2_do_shutr(h2s);
+               h2_do_shutr(h2s, reason);
        TRACE_LEAVE(H2_EV_STRM_SHUT, h2s->h2c->conn, h2s);
 }