]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: httpclient/lua: handle the streaming into the lua applet
authorWilliam Lallemand <wlallemand@haproxy.org>
Thu, 28 Oct 2021 13:41:38 +0000 (15:41 +0200)
committerWilliam Lallemand <wlallemand@haproxy.org>
Thu, 28 Oct 2021 14:24:14 +0000 (16:24 +0200)
With this feature the lua implementation of the httpclient is now able
to stream a payload larger than an haproxy buffer.

The hlua_httpclient_send() function is now split into:

hlua_httpclient_send() which initiate the httpclient and parse the lua
parameters

hlua_httpclient_snd_yield() which will send the request and be called
again to stream the request if the body is larger than an haproxy buffer

hlua_httpclient_rcv_yield() which will receive the response and store it
in the lua buffer.

include/haproxy/hlua-t.h
src/hlua.c

index e4853d34795d334ebaa16900a2c1b69b2a38ec30..6e2ac8b6a7f23876663714a0d13f5c03bb8d519a 100644 (file)
@@ -195,6 +195,7 @@ struct hlua_concat {
 /* This struct is used to store the httpclient */
 struct hlua_httpclient {
        struct httpclient *hc; /* ptr to the httpclient instance */
+       size_t sent; /* payload sent */
        luaL_Buffer b; /* buffer used to prepare strings. */
 };
 
index 0bb902434deafdada1226d5d74963f8f276a6ca1..435455bfa7765ec67e3e8471efd31cbef6fcdac1 100644 (file)
@@ -6972,7 +6972,7 @@ __LJMP static int hlua_httpclient_new(lua_State *L)
  *
  */
 
-static void hlua_httpclient_res_cb(struct httpclient *hc)
+static void hlua_httpclient_cb(struct httpclient *hc)
 {
        struct hlua *hlua = hc->caller;
 
@@ -7036,54 +7036,6 @@ __LJMP static int hlua_httpclient_get_headers(lua_State *L, struct hlua_httpclie
        return 1;
 }
 
-/*
- * For each yield, checks if there is some data in the httpclient and push them
- * in the lua buffer, once the httpclient finished its job, push the result on
- * the stack
- */
-__LJMP static int hlua_httpclient_send_yield(lua_State *L, int status, lua_KContext ctx)
-{
-       struct buffer *tr;
-       int res;
-       struct hlua *hlua = hlua_gethlua(L);
-       struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
-
-
-       tr = get_trash_chunk();
-
-       res = httpclient_res_xfer(hlua_hc->hc, tr);
-       luaL_addlstring(&hlua_hc->b, b_orig(tr), res);
-
-       if (!httpclient_data(hlua_hc->hc) && httpclient_ended(hlua_hc->hc)) {
-
-               luaL_pushresult(&hlua_hc->b);
-               lua_settable(L, -3);
-
-               lua_pushstring(L, "status");
-               lua_pushinteger(L, hlua_hc->hc->res.status);
-               lua_settable(L, -3);
-
-
-               lua_pushstring(L, "reason");
-               lua_pushlstring(L, hlua_hc->hc->res.reason.ptr, hlua_hc->hc->res.reason.len);
-               lua_settable(L, -3);
-
-               lua_pushstring(L, "headers");
-               hlua_httpclient_get_headers(L, hlua_hc);
-               lua_settable(L, -3);
-
-               return 1;
-       }
-
-       if (httpclient_data(hlua_hc->hc))
-               task_wakeup(hlua->task, TASK_WOKEN_MSG);
-
-
-       MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_send_yield, TICK_ETERNITY, 0));
-       return 0;
-}
-
-
 /*
  * Allocate and return an array of http_hdr ist extracted from the <headers> lua table
  *
@@ -7153,6 +7105,108 @@ skip_headers:
 }
 
 
+/*
+ * For each yield, checks if there is some data in the httpclient and push them
+ * in the lua buffer, once the httpclient finished its job, push the result on
+ * the stack
+ */
+__LJMP static int hlua_httpclient_rcv_yield(lua_State *L, int status, lua_KContext ctx)
+{
+       struct buffer *tr;
+       int res;
+       struct hlua *hlua = hlua_gethlua(L);
+       struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
+
+
+       tr = get_trash_chunk();
+
+       res = httpclient_res_xfer(hlua_hc->hc, tr);
+       luaL_addlstring(&hlua_hc->b, b_orig(tr), res);
+
+       if (!httpclient_data(hlua_hc->hc) && httpclient_ended(hlua_hc->hc)) {
+
+               luaL_pushresult(&hlua_hc->b);
+               lua_settable(L, -3);
+
+               lua_pushstring(L, "status");
+               lua_pushinteger(L, hlua_hc->hc->res.status);
+               lua_settable(L, -3);
+
+
+               lua_pushstring(L, "reason");
+               lua_pushlstring(L, hlua_hc->hc->res.reason.ptr, hlua_hc->hc->res.reason.len);
+               lua_settable(L, -3);
+
+               lua_pushstring(L, "headers");
+               hlua_httpclient_get_headers(L, hlua_hc);
+               lua_settable(L, -3);
+
+               return 1;
+       }
+
+       if (httpclient_data(hlua_hc->hc))
+               task_wakeup(hlua->task, TASK_WOKEN_MSG);
+
+       MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
+
+       return 0;
+}
+
+/*
+ * Call this when trying to stream a body during a request
+ */
+__LJMP static int hlua_httpclient_snd_yield(lua_State *L, int status, lua_KContext ctx)
+{
+       struct hlua *hlua;
+       struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
+       const char *body_str = NULL;
+       int ret;
+       int end = 0;
+       size_t buf_len;
+       size_t to_send = 0;
+
+       hlua = hlua_gethlua(L);
+
+       if (!hlua || !hlua->task)
+               WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
+                                    "'frontend', 'backend' or 'task'"));
+
+       ret = lua_getfield(L, -1, "body");
+       if (ret != LUA_TSTRING)
+               goto rcv;
+
+       body_str = lua_tolstring(L, -1, &buf_len);
+       lua_pop(L, 1);
+
+       to_send = MIN(buf_len - hlua_hc->sent, 1024);
+
+       if ((hlua_hc->sent + to_send) >= buf_len)
+               end = 1;
+
+       /* the end flag is always set since we are using the whole remaining size */
+       hlua_hc->sent += httpclient_req_xfer(hlua_hc->hc, ist2(body_str + hlua_hc->sent, to_send), end);
+
+       if (buf_len > hlua_hc->sent) {
+               /* still need to process the buffer */
+               MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
+       } else {
+               goto rcv;
+               /* we sent the whole request buffer we can recv */
+       }
+       return 0;
+
+rcv:
+
+       /* we return a "res" object */
+       lua_newtable(L);
+
+       luaL_buffinit(L, &hlua_hc->b);
+       lua_pushstring(L, "body");
+
+       MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
+
+       return 1;
+}
 
 /*
  * Send an HTTP request and wait for a response
@@ -7166,6 +7220,7 @@ __LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
        struct hlua *hlua;
        const char *url_str = NULL;
        const char *body_str = NULL;
+       size_t buf_len;
        int ret;
 
        hlua = hlua_gethlua(L);
@@ -7191,8 +7246,9 @@ __LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
 
        ret = lua_getfield(L, -1, "body");
        if (ret == LUA_TSTRING) {
-               body_str = lua_tostring(L, -1);
+               body_str = lua_tolstring(L, -1, &buf_len);
        }
+
        lua_pop(L, 1);
 
        if (!url_str) {
@@ -7206,13 +7262,16 @@ __LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
        hlua_hc->hc->req.meth = meth;
 
        /* update the httpclient callbacks */
-       hlua_hc->hc->ops.res_stline = hlua_httpclient_res_cb;
-       hlua_hc->hc->ops.res_headers = hlua_httpclient_res_cb;
-       hlua_hc->hc->ops.res_payload = hlua_httpclient_res_cb;
-       hlua_hc->hc->ops.res_end = hlua_httpclient_res_cb;
+       hlua_hc->hc->ops.res_stline = hlua_httpclient_cb;
+       hlua_hc->hc->ops.res_headers = hlua_httpclient_cb;
+       hlua_hc->hc->ops.res_payload = hlua_httpclient_cb;
 
+       /* a body is available, it will use the request callback */
+       if (body_str) {
+               hlua_hc->hc->ops.req_payload = hlua_httpclient_cb;
+       }
 
-       ret = httpclient_req_gen(hlua_hc->hc, hlua_hc->hc->req.url, meth, hdrs, ist(body_str));
+       ret = httpclient_req_gen(hlua_hc->hc, hlua_hc->hc->req.url, meth, hdrs, IST_NULL);
 
        /* free the temporary headers array */
        hdrs_i = hdrs;
@@ -7229,16 +7288,10 @@ __LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
                return 0;
        }
 
-
        httpclient_start(hlua_hc->hc);
 
-       /* we return a "res" object */
-       lua_newtable(L);
-
-       luaL_buffinit(L, &hlua_hc->b);
-       lua_pushstring(L, "body");
+       MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
 
-       MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_send_yield, TICK_ETERNITY, 0));
        return 0;
 }