MEDIUM: hlua: Add function to change the body length of an HTTP Message

There was no function for a lua filter to change the body length of an HTTP
Message. But it is mandatory to be able to alter the message payload. It is
not possible update to directly update the message headers because the
internal state of the message must also be updated accordingly.

It is the purpose of HTTPMessage.set_body_len() function. The new body
length myst be passed as argument. If it is an integer, the right
"Content-Length" header is set. If the "chunked" string is used, it forces
the message to be chunked-encoded and in that case the "Transfer-Encoding"
header.

This patch should fix the issue #2837. It could be backported as far as 2.6.
This commit is contained in:
Christopher Faulet 2025-05-16 14:10:31 +02:00
parent f2d7aa8406
commit 94055a5e73
2 changed files with 109 additions and 0 deletions

View File

@ -4607,6 +4607,27 @@ HTTPMessage class
data by default.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: HTTPMessage.set_body_len(http_msg, length)
This function changes the expected payload length of the HTTP message
**http_msg**. **length** can be an integer value. In that case, a
"Content-Length" header is added with the given value. It is also possible to
pass the **"chunked"** string instead of an integer value to force the HTTP
message to be chunk-encoded. In that case, a "Transfer-Encoding" header is
added with the "chunked" value. In both cases, all existing "Content-Length"
and "Transfer-Encoding" headers are removed.
This fnuction should be used in the filter context to be able to alter the
payload of the HTTP message. The internal state fo the HTTP message is updated
accordingly. :js:func:`HTTPMessage.add_header()` or
:js:func:`HTTPMessage.set_header()` functions must to be used in that case.
:param class_httpmessage http_msg: The manipulated HTTP message.
:param type length: The new payload length to set. It can be an integer or
the string "chunked".
:returns: true if the payload length was successfully updated, false
otherwise.
.. js:function:: HTTPMessage.set_eom(http_msg)
This function set the end of message for the HTTP message **http_msg**.

View File

@ -7183,6 +7183,93 @@ __LJMP static int hlua_http_msg_set_status(lua_State *L)
return 1;
}
/* Change the body length. Accepts a positive integer or the string "chunked". */
__LJMP static int hlua_http_msg_set_body_len(lua_State *L)
{
struct http_msg *msg;
struct htx *htx;
struct htx_sl *sl;
int type;
MAY_LJMP(check_args(L, 2, "set_body_len"));
msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
if (msg->msg_state > HTTP_MSG_BODY)
WILL_LJMP(luaL_error(L, "The 'set_body_len' function cannot be called during the message forwarding"));
htx = htxbuf(&msg->chn->buf);
sl = ASSUME_NONNULL(http_get_stline(htx));
type = lua_type(L, 2);
if (type == LUA_TSTRING) {
const char *str = lua_tostring(L, 2);
struct http_hdr_ctx ctx;
if (strcmp(str, "chunked") != 0)
goto error;
/* Already chunked, nothing to do */
if (msg->flags & HTTP_MSGF_TE_CHNK)
goto success;
/* add "Transfer-Encoding: chunked" header */
if (!http_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
goto failure;
msg->flags |= (HTTP_MSGF_VER_11|HTTP_MSGF_XFER_LEN|HTTP_MSGF_TE_CHNK);
sl->flags |= (HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_XFER_ENC|HTX_SL_F_CHNK);
/* remove any Content-Length header */
if (msg->flags & HTTP_MSGF_CNT_LEN) {
ctx.blk = NULL;
while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
http_remove_header(htx, &ctx);
msg->flags &= ~HTTP_MSGF_CNT_LEN;
sl->flags &= ~HTX_SL_F_CLEN;
}
}
else if (type == LUA_TNUMBER) {
const char *clen;
ssize_t len;
struct http_hdr_ctx ctx;
len = lua_tointeger(L, 2);
if (len < 0)
goto error;
clen = ultoa(len);
/* remove any Content-Length header */
if (msg->flags & HTTP_MSGF_CNT_LEN) {
ctx.blk = NULL;
while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
http_remove_header(htx, &ctx);
}
/* Now add Content-Length header */
if (!http_add_header(htx, ist("Content-Length"), ist(clen)))
goto failure;
msg->flags |= (HTTP_MSGF_VER_11|HTTP_MSGF_XFER_LEN|HTTP_MSGF_CNT_LEN);
sl->flags |= (HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
/* remove any Transfer-Encoding header */
if (msg->flags & HTTP_MSGF_TE_CHNK) {
ctx.blk = NULL;
while (http_find_header(htx, ist("Transfer-Encoding"), &ctx, 1))
http_remove_header(htx, &ctx);
msg->flags &= ~HTTP_MSGF_TE_CHNK;
sl->flags &= ~(HTX_SL_F_XFER_ENC|HTX_SL_F_CHNK);
}
}
else {
error:
WILL_LJMP(luaL_error(L, "The 'set_body_len' function expects a positive integer or the string 'chunked' as argument."));
}
success:
lua_pushboolean(L, 1);
return 1;
failure:
lua_pushboolean(L, 0);
return 1;
}
/* Returns true if the HTTP message is full. */
__LJMP static int hlua_http_msg_is_full(lua_State *L)
{
@ -14321,6 +14408,7 @@ lua_State *hlua_init_state(int thread_num)
hlua_class_function(L, "set_query", hlua_http_msg_set_query);
hlua_class_function(L, "set_uri", hlua_http_msg_set_uri);
hlua_class_function(L, "set_status", hlua_http_msg_set_status);
hlua_class_function(L, "set_body_len",hlua_http_msg_set_body_len);
hlua_class_function(L, "is_full", hlua_http_msg_is_full);
hlua_class_function(L, "may_recv", hlua_http_msg_may_recv);
hlua_class_function(L, "eom", hlua_http_msg_is_eom);