Hi,

as discussed in the cloudauth RFC thread, here are two patches about
the http_reset filter callback on L7 retries.

The filters documentation says that http_reset is called when the
request is sent again for a L7 retry, but it was only called for the
1xx informational responses, since the L7 retries were added in
a254a37ad ("MEDIUM: streams: Add the ability to retry a request on L7
failure.").

  0001 BUG/MINOR: http-ana: call the http_reset filter callback on L7
       retries

       do_l7_retry() calls it for the response only, once
       sc_reset_endp() succeeded, and before the L7 buffer swap, while
       the failed response is still in the buffer. The request is sent
       again as is, so it is not reset.

  0002 DOC: filters: channel_start_analyze is called again on L7
       retries

       The documentation said that the start and end analyzers are
       called once per stream, which is not true for the response
       after a L7 retry.

I checked it with the trace filter, a 503 retried to a second server:
one http_reset call on the response with the patch, none without it.
The filters, http-messaging, proxy, compression and cache regtests
pass, including the Lua filter ones (lua-retry.vtc). I did not add a
regtest, since the trace filter only writes to stderr.

The first patch could be backported to all stable versions.

Regards,
Aleks
From 42ac69c5e7215071c2ac77a95443441c0bb42684 Mon Sep 17 00:00:00 2001
From: Aleksandar Lazic <[email protected]>
Date: Wed, 21 Oct 2026 22:25:34 +0200
Subject: [PATCH 2/2] DOC: filters: channel_start_analyze is called again on L7
 retries

The filters documentation says that the flt_start_analyze and
flt_end_analyze analyzers are called only once per stream. On a L7
retry, the response analyzers are set again when the connection is
established, so channel_start_analyze is called a second time on the
response channel, without a channel_end_analyze call in between. It is
preceded by an http_reset call on the response.

Signed-off-by: Aleksandar Lazic <[email protected]>
---
 doc/internals/api/filters.txt | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/doc/internals/api/filters.txt b/doc/internals/api/filters.txt
index 87898b45d..a4aed16f0 100644
--- a/doc/internals/api/filters.txt
+++ b/doc/internals/api/filters.txt
@@ -1201,7 +1201,10 @@ has been added :
     processing. From the filter point of view, it calls the
     'flt_ops.channel_end_analyze' callback.
 
-These analyzers are called only once per streams.
+These analyzers are called only once per stream, except on a L7 retry: the
+response analyzers are set again when the connection is established, so
+'flt_start_analyze' is called a second time on the response channel, after an
+'http_reset' callback and without any 'flt_end_analyze' call in between.
 
 'flt_ops.channel_start_analyze' and 'flt_ops.channel_end_analyze' callbacks can
 interrupt the stream processing, as 'flt_ops.channel_analyze'. Here is an
-- 
2.56.0

From 810f1d25aa6d749308735cf2705f87d33eaa4f81 Mon Sep 17 00:00:00 2001
From: Aleksandar Lazic <[email protected]>
Date: Wed, 21 Oct 2026 22:25:18 +0200
Subject: [PATCH 1/2] BUG/MINOR: http-ana: call the http_reset filter callback
 on L7 retries

The filters documentation says that the http_reset callback is called
when the request is sent again to the server for a L7 retry, but it was
only called for the 1xx informational responses. It was documented with
the L7 retries in commit a254a37ad ("MEDIUM: streams: Add the ability to
retry a request on L7 failure."), but never called on this path.

do_l7_retry() now calls it for the response, once sc_reset_endp()
succeeded so that the retry is certain, and before the L7 buffer swap,
while the failed response is still in the buffer and txn->status is
set, like for the 1xx responses. The request is sent again as is from
the L7 buffer and its callbacks are not called again, so it is not
reset. The documentation now tells it.

This patch could be backported to all stable versions.

Signed-off-by: Aleksandar Lazic <[email protected]>
---
 doc/internals/api/filters.txt | 3 +++
 src/filters.c                 | 3 ++-
 src/http_ana.c                | 6 ++++++
 3 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/doc/internals/api/filters.txt b/doc/internals/api/filters.txt
index 5a83505f5..87898b45d 100644
--- a/doc/internals/api/filters.txt
+++ b/doc/internals/api/filters.txt
@@ -1578,6 +1578,9 @@ Then, to finish, there are 2 informational callbacks :
     By checking s->txn.http->status, it is possible to know why this callback is
     called. If it's a 1xx, we're called because of an informational
     message. Otherwise, it is a L7 retry.
+    On a L7 retry, it is only called for the response, once the retry is
+    certain, while the failed response, if any, is still in the channel buffer.
+    The request is sent again as is, so its callbacks are not called again.
 
   * 'flt_ops.http_reply' : This callback is called when, at any time, HAProxy
     decides to stop the processing on a HTTP message and to send an internal
diff --git a/src/filters.c b/src/filters.c
index fc521b811..698ddfafd 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -1331,7 +1331,8 @@ flt_http_end(struct stream *s, struct http_msg *msg)
 
 /*
  * Calls 'http_reset' callback for all filters attached to a stream. This
- * happens when a 100-continue response is received.
+ * happens when a 1xx informational response is received, and on the response
+ * when the request is sent again for a L7 retry.
  */
 void
 flt_http_reset(struct stream *s, struct http_msg *msg)
diff --git a/src/http_ana.c b/src/http_ana.c
index 397680595..f17a40fc5 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -1225,6 +1225,12 @@ static __inline int do_l7_retry(struct stream *s, struct stconn *sc)
 		return -1;
 	}
 
+	/* The retry is now certain: notify the filters that the response is
+	 * reset, while the failed one is still in the buffer. The request is
+	 * sent again as is from the L7 buffer, so it is not reset.
+	 */
+	FLT_STRM_CB(s, flt_http_reset(s, &s->txn.http->rsp));
+
 	b_free(&req->buf);
 
 	/* Swap the L7 buffer with the channel buffer */
-- 
2.56.0

Reply via email to