]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: hlua/filters: Make Channel.may_recv relative to the filter's data
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 23 Oct 2026 07:23:28 +0000 (09:23 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 23 Oct 2026 17:43:33 +0000 (19:43 +0200)
Channel.may_recv() tested the whole channel to know if more data may be
received, ignoring the data visible by the calling filter. But when several
Lua filters are chained on a channel and an earlier one holds back some data
(for instance to rewrite the body), the channel input may be closed while
the current filter still waits for data held upstream. In that case, the
function must return true, because the held data can still be exposed to
the filter, even if nothing will be received on the connection anymore.

Now, like HTTPMessage.eom(), HTTPMessage.may_recv() and
HTTPMessage.closed(), the result is relative to the data visible by the
current filter: the function always returns true if an earlier filter
holds back data.

This patch should be backported to all supported versions.

doc/lua-api/index.rst
src/hlua.c

index f7f6dd4e64a6aa6c85fc35257e74c44be9444d58..2a53814a1a702be50342415a93b05dc5ee37e4f3 100644 (file)
@@ -2295,7 +2295,11 @@ Channel class
 
 .. js:function:: Channel.may_recv(channel)
 
-  This function returns true if the channel may still receive data.
+  This function returns true if the channel may still receive data. When
+  called by a filter, the result is relative to the data visible by this
+  filter: if an earlier filter holds back some data, the function always
+  returns true, even if the input is closed, because the held data may still
+  be received by the current filter.
 
   :param class_channel channel: The manipulated Channel.
   :returns: a boolean
@@ -4737,7 +4741,11 @@ HTTPMessage class
 
 .. js:function:: HTTPMessage.set_eom(http_msg)
 
-  This function set the end of message for the HTTP message **http_msg**.
+  This function set the end of message for the HTTP message **http_msg**. It
+  can only be called when all data of the message are visible by the current
+  filter. Otherwise, an error is raised, because an earlier filter holding
+  data back may already have reached the end of the message, or may expose
+  data after it.
 
   :param class_httpmessage http_msg: The manipulated HTTP message.
 
@@ -4804,6 +4812,10 @@ HTTPMessage class
 .. js:function:: HTTPMessage.unset_eom(http_msg)
 
   This function remove the end of message for the HTTP message **http_msg**.
+  It can only be called when all data of the message are visible by the
+  current filter. Otherwise, an error is raised, because an earlier filter
+  holding data back may expose data carrying the end of message, which could
+  not be removed.
 
   :param class_httpmessage http_msg: The manipulated HTTP message.
 
index 9a51f1b27c4581055d3e343b7d2213adb7043754..ee38cf46126b9e1fbe9e192304aa920463a2b0c3 100644 (file)
@@ -3868,6 +3868,19 @@ static struct filter *hlua_channel_filter(lua_State *L, int ud, struct channel *
        return filter;
 }
 
+/* Helper function to know if all data in a channel are visible by the
+ * current filter. Returns 1 if no filter are attached to the channel or if
+ * all input data are visible by the filter. Otherwise 0 is returned.
+ */
+static int hlua_channel_fully_visible(lua_State *L, struct channel *chn)
+{
+       size_t offset = co_data(chn);
+       size_t len = ci_data(chn);
+
+       hlua_channel_filter(L, 1, chn, &offset, &len);
+       return (offset + len == c_data(chn));
+}
+
 /* Copies <len> bytes of data present in the channel's buffer, starting at the
 * offset <offset>, and put it in a LUA string variable. It is the caller
 * responsibility to ensure <len> and <offset> are valid. It always return the
@@ -4807,10 +4820,14 @@ __LJMP static int hlua_channel_is_full(lua_State *L)
 __LJMP static int hlua_channel_may_recv(lua_State *L)
 {
        struct channel *chn;
+       int may_recv;
 
        MAY_LJMP(check_args(L, 1, "may_recv"));
        chn = MAY_LJMP(hlua_checkchannel(L, 1));
-       lua_pushboolean(L, (!channel_input_closed(chn) && channel_may_recv(chn)));
+       may_recv = !hlua_channel_fully_visible(L, chn) || (!channel_input_closed(chn) &&
+                                                         channel_may_recv(chn));
+
+       lua_pushboolean(L, may_recv);
        return 1;
 }