MEDIUM: mux-h2: Forward h2 client cancellations to h2 servers

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.
This commit is contained in:
Christopher Faulet 2024-04-30 14:29:57 +02:00
parent dea79f3fe1
commit 20b156ee15

View 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);
}