MEDIUM: lua: Process buffer data using an offset and a length
The main change is that following functions will now process channel's data
using an offset and a length:
* hlua_channel_dup_yield()
* hlua_channel_get_yield()
* hlua_channel_getline_yield()
* hlua_channel_append_yield()
* hlua_channel_set()
* hlua_channel_send_yield()
* hlua_channel_forward_yield()
So for now, the offset is always the input data position and the length is
the input data length. But with the support for filters, from a filter
context, these values will be relative to the filter.
To make all processing clearer, the function _hlua_channel_dup() has been
updated and _hlua_channel_dupline(), _hlua_channel_insert() and
_hlua_channel_delete() have been added.
This patch is mandatory to allow the support of the filters written in lua.
diff --git a/src/hlua.c b/src/hlua.c
index e7bb223..1e95ac9 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -2929,56 +2929,108 @@
return 1;
}
-/* Duplicate all the data present in the input channel and put it
- * in a string LUA variables. Returns -1 and push a nil value in
- * the stack if the channel is closed and all the data are consumed,
- * returns 0 if no data are available, otherwise it returns the length
- * of the built string.
- */
-static inline int _hlua_channel_dup(struct channel *chn, lua_State *L)
+/* 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. If some data are copied
+* (len != 0), it returns the length of the built string. If no data are copied
+* (len == 0), it returns -1 and push a nil value in the stack if the channel's
+* input is closed. Otherwise it returns 0.
+*/
+static inline int _hlua_channel_dup(struct channel *chn, lua_State *L, size_t offset, size_t len)
{
- char *blk1;
- char *blk2;
- size_t len1;
- size_t len2;
- int ret;
+ size_t block1, block2;
luaL_Buffer b;
- ret = ci_getblk_nc(chn, &blk1, &len1, &blk2, &len2);
- if (unlikely(ret <= 0)) {
- if (ret < 0 || HLUA_CANT_YIELD(hlua_gethlua(L))) {
+ if (unlikely(len == 0)) {
+ if (channel_input_closed(chn) || HLUA_CANT_YIELD(hlua_gethlua(L))) {
lua_pushnil(L);
- return -1;
+ return -1;
}
- return 0;
}
+ block1 = len;
+ if (block1 > b_contig_data(&chn->buf, b_peek_ofs(&chn->buf, offset)))
+ block1 = b_contig_data(&chn->buf, b_peek_ofs(&chn->buf, offset));
+ block2 = len - block1;
+
luaL_buffinit(L, &b);
- luaL_addlstring(&b, blk1, len1);
- if (unlikely(ret == 2))
- luaL_addlstring(&b, blk2, len2);
+ luaL_addlstring(&b, b_peek(&chn->buf, offset), block1);
+ if (block2)
+ luaL_addlstring(&b, b_orig(&chn->buf), block2);
luaL_pushresult(&b);
+ return len;
+}
+
+/* Copies the first line (including the LF) in the channel's buffer, starting at
+ * the offset <offset>, and put it in a LUA string variable. It copies at most
+ * <len> bytes. It is the caller responsibility to ensure <len> and <offset> are
+ * valid. If LF is found, the line is copied. If no LF is found and the no more
+ * data can be received (channel's input is closed or full), <len> bytes are
+ * copied. In both cases, the length of the built string is returned. Otherwise
+ * nothing is copied, waiting for more data and 0 is returned.
+ */
+static inline int _hlua_channel_dupline(struct channel *chn, lua_State *L, size_t offset, size_t len)
+{
+ size_t l;
+
+ for (l = 0; l < len; l++) {
+ if (*(b_peek(&chn->buf, offset+l)) == '\n')
+ return _hlua_channel_dup(chn, L, offset, l+1);
+ }
+
+ /* No LF found, and the channel may still receive new data, so wait */
+ if (!HLUA_CANT_YIELD(chn_strm(chn)->hlua) && !channel_input_closed(chn) && channel_may_recv(chn))
+ return 0;
+
+ return _hlua_channel_dup(chn, L, offset, l);
+}
+
+/* Inserts the string <str> to the channel's buffer at the offset <offset>. This
+ * function returns -1 if data cannot be copied. Otherwise, it returns the
+ * number of bytes copied.
+ */
+static int _hlua_channel_insert(struct channel *chn, lua_State *L, struct ist str, size_t offset)
+{
+ int ret = 0;
- if (unlikely(ret == 2))
- return len1 + len2;
- return len1;
+ /* Nothing to do, just return */
+ if (unlikely(istlen(str) == 0))
+ goto end;
+
+ if (istlen(str) > c_room(chn) || channel_input_closed(chn)) {
+ ret = -1;
+ goto end;
+ }
+ ret = b_insert_blk(&chn->buf, offset, istptr(str), istlen(str));
+
+ end:
+ return ret;
}
+/* Removes <len> bytes of data at the absolute position <offset>.
+ */
+static void _hlua_channel_delete(struct channel *chn, size_t offset, size_t len)
+{
+ size_t end = offset + len;
+
+ if (b_peek(&chn->buf, end) != b_tail(&chn->buf))
+ b_move(&chn->buf, b_peek_ofs(&chn->buf, end),
+ b_data(&chn->buf) - end, -len);
+ b_sub(&chn->buf, len);
+}
+
/* "_hlua_channel_dup" wrapper. If no data are available, it returns
* a yield. This function keep the data in the buffer.
*/
__LJMP static int hlua_channel_dup_yield(lua_State *L, int status, lua_KContext ctx)
{
struct channel *chn;
+ int offset = 0, len = 0;
chn = MAY_LJMP(hlua_checkchannel(L, 1));
-
- if (IS_HTX_STRM(chn_strm(chn))) {
- lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
- WILL_LJMP(lua_error(L));
- }
-
- if (_hlua_channel_dup(chn, L) == 0)
+ offset = co_data(chn);
+ len = ci_data(chn);
+ if (_hlua_channel_dup(chn, L, offset, len) == 0)
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_dup_yield, TICK_ETERNITY, 0));
return 1;
}
@@ -2986,44 +3038,53 @@
/* Check arguments for the function "hlua_channel_dup_yield". */
__LJMP static int hlua_channel_dup(lua_State *L)
{
+ struct channel *chn;
+
MAY_LJMP(check_args(L, 1, "dup"));
- MAY_LJMP(hlua_checkchannel(L, 1));
+ chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ if (IS_HTX_STRM(chn_strm(chn))) {
+ lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
+ WILL_LJMP(lua_error(L));
+ }
return MAY_LJMP(hlua_channel_dup_yield(L, 0, 0));
}
-/* "_hlua_channel_dup" wrapper. If no data are available, it returns
- * a yield. This function consumes the data in the buffer. It returns
- * a string containing the data or a nil pointer if no data are available
- * and the channel is closed.
+/* "_hlua_channel_dup" + "_hlua_channel_erase" wrapper. If no data are
+ * available, it returns a yield. This function consumes the data in the
+ * buffer. It returns a string containing the data or a nil pointer if no data
+ * are available and the channel is closed.
*/
__LJMP static int hlua_channel_get_yield(lua_State *L, int status, lua_KContext ctx)
{
struct channel *chn;
+ int offset = 0, len = 0;
int ret;
chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ offset = co_data(chn);
+ len = ci_data(chn);
- if (IS_HTX_STRM(chn_strm(chn))) {
- lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
- WILL_LJMP(lua_error(L));
- }
-
- ret = _hlua_channel_dup(chn, L);
+ ret = _hlua_channel_dup(chn, L, offset, len);
if (unlikely(ret == 0))
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_get_yield, TICK_ETERNITY, 0));
-
if (unlikely(ret == -1))
return 1;
- b_sub(&chn->buf, ret);
+ _hlua_channel_delete(chn, offset, ret);
return 1;
}
/* Check arguments for the function "hlua_channel_get_yield". */
__LJMP static int hlua_channel_get(lua_State *L)
{
+ struct channel *chn;
+
MAY_LJMP(check_args(L, 1, "get"));
- MAY_LJMP(hlua_checkchannel(L, 1));
+ chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ if (IS_HTX_STRM(chn_strm(chn))) {
+ lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
+ WILL_LJMP(lua_error(L));
+ }
return MAY_LJMP(hlua_channel_get_yield(L, 0, 0));
}
@@ -3034,53 +3095,35 @@
*/
__LJMP static int hlua_channel_getline_yield(lua_State *L, int status, lua_KContext ctx)
{
- char *blk1;
- char *blk2;
- size_t len1;
- size_t len2;
- size_t len;
struct channel *chn;
+ int offset = 0, len = 0;
int ret;
- luaL_Buffer b;
chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ offset = co_data(chn);
+ len = ci_data(chn);
- if (IS_HTX_STRM(chn_strm(chn))) {
- lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
- WILL_LJMP(lua_error(L));
- }
-
- ret = ci_getline_nc(chn, &blk1, &len1, &blk2, &len2);
- if (ret == 0) {
- if (HLUA_CANT_YIELD(hlua_gethlua(L))) {
- _hlua_channel_dup(chn, L);
- return 1;
- }
+ ret = _hlua_channel_dupline(chn, L, offset, len);
+ if (ret == 0)
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_getline_yield, TICK_ETERNITY, 0));
- }
-
- if (ret == -1) {
- lua_pushnil(L);
+ if (unlikely(ret == -1))
return 1;
- }
- luaL_buffinit(L, &b);
- luaL_addlstring(&b, blk1, len1);
- len = len1;
- if (unlikely(ret == 2)) {
- luaL_addlstring(&b, blk2, len2);
- len += len2;
- }
- luaL_pushresult(&b);
- b_rep_blk(&chn->buf, ci_head(chn), ci_head(chn) + len, NULL, 0);
+ _hlua_channel_delete(chn, offset, ret);
return 1;
}
/* Check arguments for the function "hlua_channel_getline_yield". */
__LJMP static int hlua_channel_getline(lua_State *L)
{
+ struct channel *chn;
+
MAY_LJMP(check_args(L, 1, "getline"));
- MAY_LJMP(hlua_checkchannel(L, 1));
+ chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ if (IS_HTX_STRM(chn_strm(chn))) {
+ lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
+ WILL_LJMP(lua_error(L));
+ }
return MAY_LJMP(hlua_channel_getline_yield(L, 0, 0));
}
@@ -3094,6 +3137,7 @@
struct channel *chn;
const char *str;
size_t len;
+ int ret;
MAY_LJMP(check_args(L, 2, "append"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
@@ -3104,10 +3148,8 @@
WILL_LJMP(lua_error(L));
}
- if (len > c_room(chn) || ci_putblk(chn, str, len) < 0)
- lua_pushinteger(L, -1);
- else
- lua_pushinteger(L, len);
+ ret = _hlua_channel_insert(chn, L, ist2(str, len), co_data(chn) + ci_data(chn));
+ lua_pushinteger(L, ret);
return 1;
}
@@ -3121,6 +3163,7 @@
struct channel *chn;
const char *str;
size_t len;
+ int ret;
MAY_LJMP(check_args(L, 2, "set"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
@@ -3135,11 +3178,9 @@
if (len > c_room(chn) + ci_data(chn) || channel_input_closed(chn))
lua_pushinteger(L, -1);
else {
- b_set_data(&chn->buf, co_data(chn));
- if (ci_putblk(chn, str, len) < 0)
- lua_pushinteger(L, -1);
- else
- lua_pushinteger(L, len);
+ _hlua_channel_delete(chn, co_data(chn), ci_data(chn));
+ ret = _hlua_channel_insert(chn, L, ist2(str, len), co_data(chn));
+ lua_pushinteger(L, ret);
}
return 1;
}
@@ -3151,11 +3192,10 @@
*/
__LJMP static int hlua_channel_send_yield(lua_State *L, int status, lua_KContext ctx)
{
- struct channel *chn = MAY_LJMP(hlua_checkchannel(L, 1));
- size_t len;
- const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
- int l = MAY_LJMP(luaL_checkinteger(L, 3));
- int max;
+ struct channel *chn;
+ const char *str;
+ size_t sz, len;
+ int l, ret;
struct hlua *hlua;
/* Get hlua struct, or NULL if we execute from main lua state */
@@ -3165,10 +3205,9 @@
return 1;
}
- if (IS_HTX_STRM(chn_strm(chn))) {
- lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
- WILL_LJMP(lua_error(L));
- }
+ chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
+ l = MAY_LJMP(luaL_checkinteger(L, 3));
if (unlikely(channel_output_closed(chn))) {
lua_pushinteger(L, -1);
@@ -3185,49 +3224,27 @@
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_send_yield, TICK_ETERNITY, 0));
}
- /* The written data will be immediately sent, so we can check
- * the available space without taking in account the reserve.
- * The reserve is guaranteed for the processing of incoming
- * data, because the buffer will be flushed.
- */
- max = b_room(&chn->buf);
+ len = c_room(chn);
+ if (len > sz - l)
+ len = sz - l;
- /* If there is no space available, and the output buffer is empty.
- * in this case, we cannot add more data, so we cannot yield,
- * we return the amount of copied data.
- */
- if (max == 0 && co_data(chn) == 0)
+ ret = _hlua_channel_insert(chn, L, ist2(str+l, len), co_data(chn));
+ if (ret == -1) {
+ lua_pop(L, 1);
+ lua_pushinteger(L, -1);
return 1;
-
- /* Adjust the real required length. */
- if (max > len - l)
- max = len - l;
-
- /* The buffer available size may be not contiguous. This test
- * detects a non contiguous buffer and realign it.
- */
- if (ci_space_for_replace(chn) < max)
- channel_slow_realign(chn, trash.area);
-
- /* Copy input data in the buffer. */
- max = b_rep_blk(&chn->buf, ci_head(chn), ci_head(chn), str + l, max);
-
- /* buffer replace considers that the input part is filled.
- * so, I must forward these new data in the output part.
- */
- c_adv(chn, max);
-
- l += max;
- lua_pop(L, 1);
- lua_pushinteger(L, l);
+ }
+ if (ret) {
+ c_adv(chn, ret);
- if (l < len) {
- /* If there is no space available, and the output buffer is empty.
- * in this case, we cannot add more data, so we cannot yield,
- * we return the amount of copied data.
- */
- max = b_room(&chn->buf);
- if ((max == 0 && co_data(chn) == 0) || HLUA_CANT_YIELD(hlua_gethlua(L)))
+ l += ret;
+ lua_pop(L, 1);
+ lua_pushinteger(L, l);
+ }
+ if (l < sz) {
+ /* Yield only if the channel's output is not empty.
+ * Otherwise it means we cannot add more data. */
+ if (co_data(chn) == 0 || HLUA_CANT_YIELD(hlua_gethlua(L)))
return 1;
/* If we are waiting for space in the response buffer, we
@@ -3250,9 +3267,15 @@
*/
__LJMP static int hlua_channel_send(lua_State *L)
{
+ struct channel *chn;
+
MAY_LJMP(check_args(L, 2, "send"));
+ chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ if (IS_HTX_STRM(chn_strm(chn))) {
+ lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
+ WILL_LJMP(lua_error(L));
+ }
lua_pushinteger(L, 0);
-
return MAY_LJMP(hlua_channel_send_yield(L, 0, 0));
}
@@ -3266,32 +3289,28 @@
__LJMP static int hlua_channel_forward_yield(lua_State *L, int status, lua_KContext ctx)
{
struct channel *chn;
- int len;
- int l;
- int max;
+ size_t len;
+ int l, max;
struct hlua *hlua;
/* Get hlua struct, or NULL if we execute from main lua state */
hlua = hlua_gethlua(L);
- if (!hlua)
+ if (!hlua) {
+ lua_pushnil(L);
return 1;
-
- chn = MAY_LJMP(hlua_checkchannel(L, 1));
-
- if (IS_HTX_STRM(chn_strm(chn))) {
- lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
- WILL_LJMP(lua_error(L));
}
+ chn = MAY_LJMP(hlua_checkchannel(L, 1));
len = MAY_LJMP(luaL_checkinteger(L, 2));
l = MAY_LJMP(luaL_checkinteger(L, -1));
max = len - l;
if (max > ci_data(chn))
max = ci_data(chn);
+
channel_forward(chn, max);
- l += max;
+ l += max;
lua_pop(L, 1);
lua_pushinteger(L, l);
@@ -3324,10 +3343,14 @@
*/
__LJMP static int hlua_channel_forward(lua_State *L)
{
- MAY_LJMP(check_args(L, 2, "forward"));
- MAY_LJMP(hlua_checkchannel(L, 1));
- MAY_LJMP(luaL_checkinteger(L, 2));
+ struct channel *chn;
+ MAY_LJMP(check_args(L, 2, "forward"));
+ chn = MAY_LJMP(hlua_checkchannel(L, 1));
+ if (IS_HTX_STRM(chn_strm(chn))) {
+ lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
+ WILL_LJMP(lua_error(L));
+ }
lua_pushinteger(L, 0);
return MAY_LJMP(hlua_channel_forward_yield(L, 0, 0));
}