MEDIUM: filters/lua: Be prepared to filter TCP payloads

For filters written in lua, the tcp payloads will be filtered using methods
exposed by the Channel class. So the corrsponding C binding functions must
be prepared to process payload in a filter context and not only in an action
context.

The main change is the offset where to start to process data in the channel
buffer, and the length of these data. For an action, all input data are
considered. But for a filter, it depends on what the filter is allow to
forward when the tcp_payload callback function is called. It depends on
previous calls but also on other filters.

In addition, when the payload is modified by a lua filter, its context must
be updated. Note also that channel functions cannot yield when called from a
filter context.

For now, it is not possible to define callbacks to filter data and the
documentation has not been updated.
diff --git a/src/hlua.c b/src/hlua.c
index 7f0aa9b..f3c0221 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -3009,7 +3009,7 @@
  * filter is attached, NULL is returned and <offet> and <len> are not
  * initialized.
  */
-static __maybe_unused struct filter *hlua_channel_filter(lua_State *L, int ud, struct channel *chn, size_t *offset, size_t *len)
+static struct filter *hlua_channel_filter(lua_State *L, int ud, struct channel *chn, size_t *offset, size_t *len)
 {
 	struct filter *filter = NULL;
 
@@ -3090,29 +3090,38 @@
  * offset (0 by default) and a length (all remaining input data starting for the
  * offset by default). If there is not enough input data and more data can be
  * received, this function yields.
+ *
+ * From an action, All input data are considered. For a filter, the offset and
+ * the length of input data to consider are retrieved from the filter context.
  */
 __LJMP static int hlua_channel_get_data_yield(lua_State *L, int status, lua_KContext ctx)
 {
 	struct channel *chn;
-	int offset = 0, len = 0;
+	struct filter *filter;
+	size_t input, output;
+	int offset, len;
 
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
 
-	if (!ci_data(chn) && channel_input_closed(chn)) {
-		lua_pushnil(L);
-		return 1;
-	}
+	output = co_data(chn);
+	input = ci_data(chn);
 
+	filter = hlua_channel_filter(L, 1, chn, &output, &input);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
+	offset = output;
 	if (lua_gettop(L) > 1) {
 		offset = MAY_LJMP(luaL_checkinteger(L, 2));
 		if (offset < 0)
-			offset = MAX(0, ci_data(chn) + offset);
-		if (offset > ci_data(chn)) {
+			offset = MAX(0, input + offset);
+		offset += output;
+		if (offset < output || offset > input + output) {
 			lua_pushfstring(L, "offset out of range.");
 			WILL_LJMP(lua_error(L));
 		}
 	}
-	len = ci_data(chn) - offset;
+	len = output + input - offset;
 	if (lua_gettop(L) == 3) {
 		len = MAY_LJMP(luaL_checkinteger(L, 3));
 		if (!len)
@@ -3125,16 +3134,16 @@
 		}
 	}
 
-	if (offset + len > ci_data(chn)) {
+	if (offset + len > output + input) {
 		if (!HLUA_CANT_YIELD(hlua_gethlua(L)) && !channel_input_closed(chn) && channel_may_recv(chn)) {
 			/* Yield waiting for more data, as requested */
 			MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_get_data_yield, TICK_ETERNITY, 0));
 		}
-		len = ci_data(chn) - offset;
+		len = output + input - offset;
 	}
 
   dup:
-	_hlua_channel_dup(chn, L, co_data(chn) + offset, len);
+	_hlua_channel_dup(chn, L, offset, len);
 	return 1;
 }
 
@@ -3145,29 +3154,38 @@
  * yields. If a length is explicitly specified, no more data are
  * copied. Otherwise, if no LF is found and more data can be received, this
  * function yields.
+ *
+ * From an action, All input data are considered. For a filter, the offset and
+ * the length of input data to consider are retrieved from the filter context.
  */
 __LJMP static int hlua_channel_get_line_yield(lua_State *L, int status, lua_KContext ctx)
 {
 	struct channel *chn;
-	int l, offset = 0, len = 0;
+	struct filter *filter;
+	size_t l, input, output;
+	int offset, len;
 
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
+	output = co_data(chn);
+	input = ci_data(chn);
 
-	if (!ci_data(chn) && channel_input_closed(chn)) {
-		lua_pushnil(L);
-		return 1;
-	}
+	filter = hlua_channel_filter(L, 1, chn, &output, &input);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
 
+	offset = output;
 	if (lua_gettop(L) > 1) {
 		offset = MAY_LJMP(luaL_checkinteger(L, 2));
 		if (offset < 0)
-			offset = MAX(0, ci_data(chn) + offset);
-		if (offset > ci_data(chn)) {
+			offset = MAX(0, input + offset);
+		offset += output;
+		if (offset < output || offset > input + output) {
 			lua_pushfstring(L, "offset out of range.");
 			WILL_LJMP(lua_error(L));
 		}
 	}
-	len = ci_data(chn) - offset;
+
+	len = output + input - offset;
 	if (lua_gettop(L) == 3) {
 		len = MAY_LJMP(luaL_checkinteger(L, 3));
 		if (!len)
@@ -3181,24 +3199,24 @@
 	}
 
 	for (l = 0; l < len; l++) {
-		if (l + offset >= ci_data(chn))
+		if (l + offset >= output + input)
 			break;
-		if (*(b_peek(&chn->buf, co_data(chn) + offset + l)) == '\n') {
+		if (*(b_peek(&chn->buf, offset + l)) == '\n') {
 			len = l+1;
 			goto dup;
 		}
 	}
 
-	if (offset + len > ci_data(chn)) {
+	if (offset + len > output + input) {
 		if (!HLUA_CANT_YIELD(hlua_gethlua(L)) && !channel_input_closed(chn) && channel_may_recv(chn)) {
 			/* Yield waiting for more data */
 			MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_get_line_yield, TICK_ETERNITY, 0));
 		}
-		len = ci_data(chn) - offset;
+		len = output + input - offset;
 	}
 
   dup:
-	_hlua_channel_dup(chn, L, co_data(chn) + offset, len);
+	_hlua_channel_dup(chn, L, offset, len);
 	return 1;
 }
 
@@ -3206,11 +3224,15 @@
  *
  * Duplicate all input data foud in the channel's buffer. The data are not
  * removed from the buffer. This function relies on _hlua_channel_dup().
+ *
+ * From an action, All input data are considered. For a filter, the offset and
+ * the length of input data to consider are retrieved from the filter context.
  */
 __LJMP static int hlua_channel_dup(lua_State *L)
 {
 	struct channel *chn;
-	int offset = 0, len = 0;
+	struct filter *filter;
+	size_t offset, len;
 
 	MAY_LJMP(check_args(L, 1, "dup"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
@@ -3218,9 +3240,14 @@
 		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
 		WILL_LJMP(lua_error(L));
 	}
+
 	offset = co_data(chn);
 	len = ci_data(chn);
 
+	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
 	if (!ci_data(chn) && channel_input_closed(chn)) {
 		lua_pushnil(L);
 		return 1;
@@ -3235,11 +3262,15 @@
  * Get all input data foud in the channel's buffer. The data are removed from
  * the buffer after the copy. This function relies on _hlua_channel_dup() and
  * _hlua_channel_delete().
+ *
+ * From an action, All input data are considered. For a filter, the offset and
+ * the length of input data to consider are retrieved from the filter context.
  */
 __LJMP static int hlua_channel_get(lua_State *L)
 {
 	struct channel *chn;
-	int offset = 0, len = 0;
+	struct filter *filter;
+	size_t offset, len;
 	int ret;
 
 	MAY_LJMP(check_args(L, 1, "get"));
@@ -3248,9 +3279,14 @@
 		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
 		WILL_LJMP(lua_error(L));
 	}
+
 	offset = co_data(chn);
 	len = ci_data(chn);
 
+	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
 	if (!ci_data(chn) && channel_input_closed(chn)) {
 		lua_pushnil(L);
 		return 1;
@@ -3265,18 +3301,26 @@
  * and the last data does not contains a final '\n', the data are returned
  * without the final '\n'. When no more data are available, it returns nil
  * value.
+ *
+ * From an action, All input data are considered. For a filter, the offset and
+ * the length of input data to consider are retrieved from the filter context.
  */
 __LJMP static int hlua_channel_getline_yield(lua_State *L, int status, lua_KContext ctx)
 {
 	struct channel *chn;
-	size_t l;
-	int offset = 0, len = 0;
+	struct filter *filter;
+	size_t l, offset, len;
 	int ret;
 
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
+
 	offset = co_data(chn);
 	len = ci_data(chn);
 
+	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
 	if (!ci_data(chn) && channel_input_closed(chn)) {
 		lua_pushnil(L);
 		return 1;
@@ -3295,7 +3339,7 @@
 	}
 
   dup:
-	ret = _hlua_channel_dup(chn, L, co_data(chn) + offset, len);
+	ret = _hlua_channel_dup(chn, L, offset, len);
 	_hlua_channel_delete(chn, offset, ret);
 	return 1;
 }
@@ -3359,23 +3403,39 @@
  * written string, or -1 if the channel is closed or if the buffer size is too
  * little for the data. 0 may be returned if nothing is copied. This function
  * does not yield.
+ *
+ * For a filter, the context is updated on success.
  */
 __LJMP static int hlua_channel_append(lua_State *L)
 {
 	struct channel *chn;
+	struct filter *filter;
 	const char *str;
-	size_t len;
+	size_t sz, offset, len;
 	int ret;
 
 	MAY_LJMP(check_args(L, 2, "append"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
-	str = MAY_LJMP(luaL_checklstring(L, 2, &len));
+	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
 	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_insert(chn, L, ist2(str, len), co_data(chn) + ci_data(chn));
+	offset = co_data(chn);
+	len = ci_data(chn);
+
+	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
+	ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
+	if (ret > 0 && filter) {
+		struct hlua_flt_ctx *flt_ctx = filter->ctx;
+
+		flt_update_offsets(filter, chn, ret);
+		flt_ctx->cur_len[CHN_IDX(chn)] += ret;
+	}
 	lua_pushinteger(L, ret);
 	return 1;
 }
@@ -3384,12 +3444,15 @@
  * written string, or -1 if the channel is closed or if the buffer size is too
  * little for the data. 0 may be returned if nothing is copied. This function
  * does not yield.
+ *
+ * For a filter, the context is updated on success.
  */
 __LJMP static int hlua_channel_prepend(lua_State *L)
 {
 	struct channel *chn;
+	struct filter *filter;
 	const char *str;
-	size_t sz;
+	size_t sz, offset, len;
 	int ret;
 
 	MAY_LJMP(check_args(L, 2, "prepend"));
@@ -3400,7 +3463,21 @@
 		WILL_LJMP(lua_error(L));
 	}
 
+	offset = co_data(chn);
+	len = ci_data(chn);
+
+	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
-	ret = _hlua_channel_insert(chn, L, ist2(str, sz), co_data(chn));
+	ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
+	if (ret > 0 && filter) {
+		struct hlua_flt_ctx *flt_ctx = filter->ctx;
+
+		flt_update_offsets(filter, chn, ret);
+		flt_ctx->cur_len[CHN_IDX(chn)] += ret;
+	}
+
 	lua_pushinteger(L, ret);
 	return 1;
 }
@@ -3409,35 +3486,54 @@
  * content. By default the string is appended at the end of input data. It
  * returns the length of the written string, or -1 if the channel is closed or
  * if the buffer size is too little for the data.
+ *
+ * For a filter, the context is updated on success.
  */
 __LJMP static int hlua_channel_insert_data(lua_State *L)
 {
 	struct channel *chn;
+	struct filter *filter;
 	const char *str;
-	size_t sz;
+	size_t sz, input, output;
 	int ret, offset;
 
 	if (lua_gettop(L) < 2 || lua_gettop(L) > 3)
 		WILL_LJMP(luaL_error(L, "'insert' expects at least 1 argument and at most 2 arguments"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
 	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
-	offset = ci_data(chn);
-	if (lua_gettop(L) > 2)
+
+	output = co_data(chn);
+	input = ci_data(chn);
+
+	filter = hlua_channel_filter(L, 1, chn, &output, &input);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
+	offset = input + output;
+	if (lua_gettop(L) > 2) {
 		offset = MAY_LJMP(luaL_checkinteger(L, 3));
+		if (offset < 0)
+			offset = MAX(0, input + offset);
+		offset += output;
+
+		if (offset < output || offset > output + input) {
+			lua_pushfstring(L, "offset out of range.");
+			WILL_LJMP(lua_error(L));
+		}
+	}
 	if (IS_HTX_STRM(chn_strm(chn))) {
 		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
 		WILL_LJMP(lua_error(L));
 	}
 
-	if (offset < 0)
-		offset = MAX(0, ci_data(chn) + offset);
+	ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
+	if (ret > 0 && filter) {
+		struct hlua_flt_ctx *flt_ctx = filter->ctx;
 
-	if (offset > ci_data(chn)) {
-		lua_pushfstring(L, "offset out of range.");
-		WILL_LJMP(lua_error(L));
+		flt_update_offsets(filter, chn, ret);
+		flt_ctx->cur_len[CHN_IDX(chn)] += ret;
 	}
 
-	ret = _hlua_channel_insert(chn, L, ist2(str, sz), co_data(chn) + offset);
 	lua_pushinteger(L, ret);
 	return 1;
 }
@@ -3445,80 +3541,142 @@
  * content. By default all remaining data are removed (offset = 0 and len =
  * -1). It returns the length of the written string, or -1 if the channel is
  * closed or if the buffer size is too little for the data.
+ *
+ * For a filter, the context is updated on success.
  */
 __LJMP static int hlua_channel_set_data(lua_State *L)
 {
 	struct channel *chn;
+	struct filter *filter;
 	const char *str;
-	size_t sz;
-	int ret, offset = 0, len = -1;
+	size_t sz, input, output;
+	int ret, offset, len;
 
 	if (lua_gettop(L) < 2 || lua_gettop(L) > 4)
 		WILL_LJMP(luaL_error(L, "'set' expects at least 1 argument and at most 3 arguments"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
 	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
-	if (lua_gettop(L) > 2)
-		offset = MAY_LJMP(luaL_checkinteger(L, 3));
-	if (lua_gettop(L) == 4)
-		len = MAY_LJMP(luaL_checkinteger(L, 4));
+
 	if (IS_HTX_STRM(chn_strm(chn))) {
 		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
 		WILL_LJMP(lua_error(L));
 	}
 
-	if (offset < 0)
-		offset = MAX(0, ci_data(chn) + offset);
-	if (len < 0)
-		len = ci_data(chn) - offset;
+	output = co_data(chn);
+	input = ci_data(chn);
 
-	if (offset + len > ci_data(chn)) {
-		lua_pushfstring(L, "offset or length out of range.");
+	filter = hlua_channel_filter(L, 1, chn, &output, &input);
+	if (filter && !hlua_filter_from_payload(filter))
 		WILL_LJMP(lua_error(L));
+
+	offset = output;
+	if (lua_gettop(L) > 2) {
+		offset = MAY_LJMP(luaL_checkinteger(L, 3));
+		if (offset < 0)
+			offset = MAX(0, input + offset);
+		offset += output;
+		if (offset < output || offset > input + output) {
+			lua_pushfstring(L, "offset out of range.");
+			WILL_LJMP(lua_error(L));
+		}
 	}
 
+	len = output + input - offset;
+	if (lua_gettop(L) == 4) {
+		len = MAY_LJMP(luaL_checkinteger(L, 4));
+		if (!len)
+			goto set;
+		if (len == -1)
+			len = output + input - offset;
+		if (len < 0 || offset + len > output + input) {
+			lua_pushfstring(L, "length out of range.");
+			WILL_LJMP(lua_error(L));
+		}
+	}
+
+  set:
 	/* Be sure we can copied the string once input data will be removed. */
 	if (sz > c_room(chn) + len)
 		lua_pushinteger(L, -1);
 	else {
-		_hlua_channel_delete(chn, co_data(chn) + offset, len);
-		ret = _hlua_channel_insert(chn, L, ist2(str, sz), co_data(chn) + offset);
+		_hlua_channel_delete(chn, offset, len);
+		ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
+		if (filter) {
+			struct hlua_flt_ctx *flt_ctx = filter->ctx;
+
+			len -= (ret > 0 ? ret : 0);
+			flt_update_offsets(filter, chn, -len);
+			flt_ctx->cur_len[CHN_IDX(chn)] -= len;
+		}
+
 		lua_pushinteger(L, ret);
 	}
 	return 1;
 }
 
 /* Removes a given amount of input data at the given offset. By default all
- * intput data are removed (offset = 0 and len = -1). It returns the amount of
+ * input data are removed (offset = 0 and len = -1). It returns the amount of
  * the removed data.
+ *
+ * For a filter, the context is updated on success.
  */
 __LJMP static int hlua_channel_del_data(lua_State *L)
 {
 	struct channel *chn;
-	int offset = 0, len = -1;
+	struct filter *filter;
+	size_t input, output;
+	int offset, len;
 
 	if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
 		WILL_LJMP(luaL_error(L, "'remove' expects at most 2 arguments"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
-	if (lua_gettop(L) > 1)
-		offset = MAY_LJMP(luaL_checkinteger(L, 2));
-	if (lua_gettop(L) == 3)
-		len = MAY_LJMP(luaL_checkinteger(L, 3));
+
 	if (IS_HTX_STRM(chn_strm(chn))) {
 		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
 		WILL_LJMP(lua_error(L));
 	}
 
-	if (offset < 0)
-		offset = MAX(0, ci_data(chn) + offset);
-	if (len < 0)
-		len = ci_data(chn) - offset;
+	output = co_data(chn);
+	input = ci_data(chn);
 
-	if (offset + len > ci_data(chn)) {
-		lua_pushfstring(L, "offset or length out of range.");
+	filter = hlua_channel_filter(L, 1, chn, &output, &input);
+	if (filter && !hlua_filter_from_payload(filter))
 		WILL_LJMP(lua_error(L));
+
+	offset = output;
+	if (lua_gettop(L) > 2) {
+		offset = MAY_LJMP(luaL_checkinteger(L, 3));
+		if (offset < 0)
+			offset = MAX(0, input + offset);
+		offset += output;
+		if (offset < output || offset > input + output) {
+			lua_pushfstring(L, "offset out of range.");
+			WILL_LJMP(lua_error(L));
+		}
 	}
 
-	_hlua_channel_delete(chn, co_data(chn) + offset, len);
+	len = output + input - offset;
+	if (lua_gettop(L) == 4) {
+		len = MAY_LJMP(luaL_checkinteger(L, 4));
+		if (!len)
+			goto end;
+		if (len == -1)
+			len = output + input - offset;
+		if (len < 0 || offset + len > output + input) {
+			lua_pushfstring(L, "length out of range.");
+			WILL_LJMP(lua_error(L));
+		}
+	}
+
+	_hlua_channel_delete(chn, offset, len);
+	if (filter) {
+		struct hlua_flt_ctx *flt_ctx = filter->ctx;
+
+		flt_update_offsets(filter, chn, -len);
+		flt_ctx->cur_len[CHN_IDX(chn)] -= len;
+	}
+
+  end:
 	lua_pushinteger(L, len);
 	return 1;
 }
@@ -3531,8 +3689,9 @@
 __LJMP static int hlua_channel_send_yield(lua_State *L, int status, lua_KContext ctx)
 {
 	struct channel *chn;
+	struct filter *filter;
 	const char *str;
-	size_t sz, len;
+	size_t offset, len, sz;
 	int l, ret;
 	struct hlua *hlua;
 
@@ -3547,38 +3706,51 @@
 	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
 	l = MAY_LJMP(luaL_checkinteger(L, 3));
 
+	offset = co_data(chn);
+	len = ci_data(chn);
+
+	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
+
 	if (unlikely(channel_output_closed(chn))) {
 		lua_pushinteger(L, -1);
 		return 1;
 	}
 
-	/* Check if the buffer is available because HAProxy doesn't allocate
-	 * the request buffer if its not required.
-	 */
-	if (chn->buf.size == 0) {
-		if (HLUA_CANT_YIELD(hlua_gethlua(L)))
-			return 1;
-		si_rx_buff_blk(chn_prod(chn));
-		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_send_yield, TICK_ETERNITY, 0));
-	}
-
 	len = c_room(chn);
-	if (len > sz - l)
+	if (len > sz -l) {
+		if (filter) {
+			lua_pushinteger(L, -1);
+			return 1;
+		}
 		len = sz - l;
+	}
 
-	ret = _hlua_channel_insert(chn, L, ist2(str+l, len), co_data(chn));
+	ret = _hlua_channel_insert(chn, L, ist2(str, len), offset);
 	if (ret == -1) {
 		lua_pop(L, 1);
 		lua_pushinteger(L, -1);
 		return 1;
 	}
 	if (ret) {
-		c_adv(chn, ret);
+		if (filter) {
+			struct hlua_flt_ctx *flt_ctx = filter->ctx;
+
+
+			flt_update_offsets(filter, chn, ret);
+			FLT_OFF(filter, chn) += ret;
+			flt_ctx->cur_off[CHN_IDX(chn)] += ret;
+		}
+		else
+			c_adv(chn, ret);
 
 		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. */
@@ -3602,6 +3774,8 @@
 /* Just a wrapper of "_hlua_channel_send". This wrapper permits
  * yield the LUA process, and resume it without checking the
  * input arguments.
+ *
+ * This function cannot be called from a filter.
  */
 __LJMP static int hlua_channel_send(lua_State *L)
 {
@@ -3627,7 +3801,8 @@
 __LJMP static int hlua_channel_forward_yield(lua_State *L, int status, lua_KContext ctx)
 {
 	struct channel *chn;
-	size_t len;
+	struct filter *filter;
+	size_t offset, len, fwd;
 	int l, max;
 	struct hlua *hlua;
 
@@ -3639,21 +3814,36 @@
 	}
 
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
-	len = MAY_LJMP(luaL_checkinteger(L, 2));
+	fwd = 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);
+	offset = co_data(chn);
+	len = ci_data(chn);
 
-	channel_forward(chn, max);
+	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
+	if (filter && !hlua_filter_from_payload(filter))
+		WILL_LJMP(lua_error(L));
+
+	max = fwd - l;
+	if (max > len)
+		max = len;
+
+	if (filter) {
+		struct hlua_flt_ctx *flt_ctx = filter->ctx;
+
+		FLT_OFF(filter, chn) += max;
+		flt_ctx->cur_off[CHN_IDX(chn)] += max;
+		flt_ctx->cur_len[CHN_IDX(chn)] -= max;
+	}
+	else
+		channel_forward(chn, max);
 
 	l += max;
 	lua_pop(L, 1);
 	lua_pushinteger(L, l);
 
 	/* Check if it miss bytes to forward. */
-	if (l < len) {
+	if (l < fwd) {
 		/* The the input channel or the output channel are closed, we
 		 * must return the amount of data forwarded.
 		 */
@@ -3678,6 +3868,8 @@
 
 /* Just check the input and prepare the stack for the previous
  * function "hlua_channel_forward_yield"
+ *
+ * This function cannot be called from a filter.
  */
 __LJMP static int hlua_channel_forward(lua_State *L)
 {
@@ -3699,15 +3891,22 @@
 __LJMP static int hlua_channel_get_in_len(lua_State *L)
 {
 	struct channel *chn;
+	struct filter *filter;
+	size_t output, input;
 
 	MAY_LJMP(check_args(L, 1, "input"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
-	if (IS_HTX_STRM(chn_strm(chn))) {
+
+	output = co_data(chn);
+	input = ci_data(chn);
+	filter = hlua_channel_filter(L, 1, chn, &output, &input);
+	if (filter || !IS_HTX_STRM(chn_strm(chn)))
+		lua_pushinteger(L, input);
+	else {
 		struct htx *htx = htxbuf(&chn->buf);
+
 		lua_pushinteger(L, htx->data - co_data(chn));
 	}
-	else
-		lua_pushinteger(L, ci_data(chn));
 	return 1;
 }
 
@@ -3754,10 +3953,16 @@
 __LJMP static int hlua_channel_get_out_len(lua_State *L)
 {
 	struct channel *chn;
+	size_t output, input;
 
 	MAY_LJMP(check_args(L, 1, "output"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
-	lua_pushinteger(L, co_data(chn));
+
+	output = co_data(chn);
+	input = ci_data(chn);
+	hlua_channel_filter(L, 1, chn, &output, &input);
+
+	lua_pushinteger(L, output);
 	return 1;
 }