]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: hlua/filters: make HTTPMessage.eom/may_recv relative to the filter's...
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 20 Oct 2026 12:46:51 +0000 (12:46 +0000)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 23 Oct 2026 17:42:37 +0000 (19:42 +0200)
HTTPMessage.eom() and HTTPMessage.may_recv() rely on the whole HTX message
to know if more data are expected or not, by testing if the end of message
was reached. However when several Lua filters are chained on a channel and
an earlier one holds back some data (for instance to rewrite the body), the
next ones may perform a test on data it should not see. This could lead to
unexpected behavior, mainly by disclosing too early the end of the message
to next layer.

Now an help function is used to know if all data are visible or
not. HTTPMessage.eom() is always false is all data are not visible by the
current filter and HTTPMessage.may_recv() always true in that case.

This should be backported to all supported versions but it will have to be
adapted because the HTX API was changed in 3.5.

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

index 20364e79a8aabd3fb8b73884447e0950e51402aa..e2350ff1f3c37edfb60ef73c532d528dab784d4d 100644 (file)
@@ -4485,10 +4485,13 @@ HTTPMessage class
 .. js:function:: HTTPMessage.eom(http_msg)
 
   This function returns true if the end of message is reached for the HTTP
-  message **http_msg**.
+  message **http_msg**. 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 false, even if the end of the message was received,
+  because the held data may still carry it.
 
   :param class_httpmessage http_msg: The manipulated HTTP message.
-  :returns: an integer containing the amount of available bytes.
+  :returns: a boolean
 
 .. js:function:: HTTPMessage.del_header(http_msg, name)
 
@@ -4590,7 +4593,10 @@ HTTPMessage class
 .. js:function:: HTTPMessage.may_recv(http_msg)
 
   This function returns true if the HTTP message **http_msg** may still receive
-  data.
+  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, because the held data may still be exposed to the current
+  filter, even if nothing will be received anymore.
 
   :param class_httpmessage http_msg: The manipulated HTTP message.
   :returns: a boolean
index f2f12b23cf774a3935629bd6e589a59ae29b32b4..6e2ab7ccb3f0d4dddbe6731b540617b619c672e9 100644 (file)
@@ -6915,6 +6915,17 @@ static struct filter *hlua_http_msg_filter(lua_State *L, int ud, struct http_msg
        return filter;
 }
 
+/* Helper function to know if all data in an HTX message are visible.
+ * Returns 1 if no filter are attached to the HTTP message or if all HTX data are
+ * visible by the filter Otherwise 0 is returned. */
+static int hlua_http_msg_fully_visible(lua_State *L, struct http_msg *msg, struct htx *htx)
+{
+       size_t offset, len;
+
+       hlua_http_msg_filter(L, 1, msg, &offset, &len);
+       return (offset + len == htx->data);
+}
+
 /* Returns true if the channel attached to the HTTP message is the response
  * channel.
  */
@@ -7263,11 +7274,16 @@ __LJMP static int hlua_http_msg_may_recv(lua_State *L)
 {
        struct http_msg *msg;
        struct htx *htx;
+       int may_recv;
 
        MAY_LJMP(check_args(L, 1, "may_recv"));
        msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
        htx = htxbuf(&msg->chn->buf);
-       lua_pushboolean(L, (htx_expect_more(htx) && !channel_input_closed(msg->chn) && channel_may_recv(msg->chn)));
+       may_recv = !hlua_http_msg_fully_visible(L, msg, htx) || (htx_expect_more(htx) &&
+                                                                !channel_input_closed(msg->chn) &&
+                                                                channel_may_recv(msg->chn));
+
+       lua_pushboolean(L, may_recv);
        return 1;
 }
 
@@ -7276,11 +7292,13 @@ __LJMP static int hlua_http_msg_is_eom(lua_State *L)
 {
        struct http_msg *msg;
        struct htx *htx;
+       int eom;
 
-       MAY_LJMP(check_args(L, 1, "may_recv"));
+       MAY_LJMP(check_args(L, 1, "eom"));
        msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
        htx = htxbuf(&msg->chn->buf);
-       lua_pushboolean(L, !htx_expect_more(htx));
+       eom = hlua_http_msg_fully_visible(L, msg, htx) && htx_msg_ended(htx);
+       lua_pushboolean(L, eom);
        return 1;
 }