From 068ce2d5d2c94574734411aaca3c31748409e36e Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue, 30 Apr 2024 15:20:24 +0200
Subject: [PATCH] MINOR: stconn: Add samples to retrieve about stream aborts

It is now possible to retrieve some info about the abort received for a
server or a client stream, if any.

  * fs.aborted and bs.aborted can be used to know if an abort was received
    on frontend or backend side. A boolean is returned.

  * fs.rst_code and bs.rst_code return the code of the received RESET_STREAM
    frame for a H2 stream or the code of the received STOP_SENDING frame for
    a QUIC stream. In both cases, the error code attached to the frame is
    returned. The sample fetch fails if no such frame was received or if the
    stream is not an H2/QUIC stream.
---
 doc/configuration.txt | 24 +++++++++++++++++++
 src/stconn.c          | 55 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 220b551f0..ed9b67f34 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -23604,9 +23604,13 @@ Summary of sample fetch methods in this section and their respective types:
   keyword                                             output type
 ----------------------------------------------------+-------------
 bs.id                                                 integer
+bs.aborted                                            boolean
+bs.rst_code                                           integer
 distcc_body(<token>[,<occ>])                          binary
 distcc_param(<token>[,<occ>])                         integer
 fs.id                                                 integer
+fs.aborted                                            boolean
+fs.rst_code                                           integer
 payload(<offset>,<length>)                            binary
 payload_lv(<offset1>,<length>[,<offset2>])            binary
 req.len                                               integer
@@ -23642,6 +23646,16 @@ bs.id : integer
   Returns the multiplexer's stream ID on the server side. It is the
   multiplexer's responsibility to return the appropriate information.
 
+bs.aborted: boolean
+  Returns true is an abort was received from the server for the current
+  stream. Otherwise false is returned.
+
+bs.rst_code: integer
+  Returns the reset code received from the server for the current stream. The
+  code of the H2 RST_STEAM frame or the QUIC STOP_SENDING frame received from
+  the server is returned. The sample fetch fails if no abort was received or if
+  the server stream is not an H2/QUIC stream.
+
 distcc_body(<token>[,<occ>]) : binary
   Parses a distcc message and returns the body associated to occurrence #<occ>
   of the token <token>. Occurrences start at 1, and when unspecified, any may
@@ -23673,6 +23687,16 @@ fs.id : integer
   multiplexer's responsibility to return the appropriate information. For
   instance, on a raw TCP, 0 is always returned because there is no stream.
 
+fs.aborted: boolean
+  Returns true is an abort was received from the client for the current
+  stream. Otherwise false is returned.
+
+fs.rst_code: integer
+  Returns the reset code received from the client for the current stream. The
+  code of the H2 RST_STEAM frame or the QUIC STOP_SENDING frame received from the
+  client is returned. The sample fetch fails if no abort was received or
+  if the client stream is not an H2/QUIC stream.
+
 payload(<offset>,<length>) : binary (deprecated)
   This is an alias for "req.payload" when used in the context of a request (e.g.
   "stick on", "stick match"), and for "res.payload" when used in the context of
diff --git a/src/stconn.c b/src/stconn.c
index 53888efb4..a00f8e114 100644
--- a/src/stconn.c
+++ b/src/stconn.c
@@ -2401,6 +2401,57 @@ smp_fetch_sid(const struct arg *args, struct sample *smp, const char *kw, void *
 	return 1;
 }
 
+/* return 1 if the frontend or backend mux stream has received an abort and 0 otherwise.
+ */
+static int
+smp_fetch_strm_aborted(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+	struct stconn *sc;
+	unsigned int aborted = 0;
+
+	if (!smp->strm)
+		return 0;
+
+	sc = (kw[0] == 'f' ? smp->strm->scf : smp->strm->scb);
+	if (sc->sedesc->abort_info.info)
+		aborted = 1;
+
+	smp->flags = SMP_F_VOL_TXN;
+	smp->data.type = SMP_T_BOOL;
+	smp->data.u.sint = aborted;
+
+	return 1;
+}
+
+/* return the H2/QUIC RESET code of the frontend or backend mux stream. Any value
+ * means an a RST_STREAM was received on H2 and a STOP_SENDING on QUIC. Otherwise the sample fetch fails.
+ */
+static int
+smp_fetch_strm_rst_code(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+	struct stconn *sc;
+	unsigned int source;
+	unsigned long long code = 0;
+
+	if (!smp->strm)
+		return 0;
+
+	sc = (kw[0] == 'f' ? smp->strm->scf : smp->strm->scb);
+	source = ((sc->sedesc->abort_info.info & SE_ABRT_SRC_MASK) >> SE_ABRT_SRC_SHIFT);
+	if (source != SE_ABRT_SRC_MUX_H2 && source != SE_ABRT_SRC_MUX_QUIC) {
+		if (!source)
+			smp->flags |= SMP_F_MAY_CHANGE;
+		return 0;
+	}
+	code = sc->sedesc->abort_info.code;
+
+	smp->flags = SMP_F_VOL_TXN;
+	smp->data.type = SMP_T_SINT;
+	smp->data.u.sint = code;
+
+	return 1;
+}
+
 /* Note: must not be declared <const> as its list will be overwritten.
  * Note: fetches that may return multiple types should be declared using the
  * appropriate pseudo-type. If not available it must be declared as the lowest
@@ -2408,7 +2459,11 @@ smp_fetch_sid(const struct arg *args, struct sample *smp, const char *kw, void *
  */
 static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
 	{ "bs.id", smp_fetch_sid, 0, NULL, SMP_T_SINT, SMP_USE_L6REQ },
+	{ "bs.aborted", smp_fetch_strm_aborted, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV },
+	{ "bs.rst_code", smp_fetch_strm_rst_code, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV },
 	{ "fs.id", smp_fetch_sid, 0, NULL, SMP_T_STR, SMP_USE_L6RES },
+	{ "fs.aborted", smp_fetch_strm_aborted, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
+	{ "fs.rst_code", smp_fetch_strm_rst_code, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
 	{ /* END */ },
 }};