MEDIUM: lua: Improve/revisit the lua api to manipulate channels

First of all, following functions are now considered deprecated:

  * Channel:dup()
  * Channel:get()
  * Channel:getline()
  * Channel:get_in_len()
  * Cahnnel:get_out_len()

It is just informative, there is no warning and functions may still be
used. Howver it is recommended to use new functions. New functions are more
flexible and use a better naming pattern. In addition, the same names will
be used in the http_msg class to manipulate http messages from lua filters.

The new API is:

  * Channel:data()
  * Channel:line()
  * Channel:append()
  * Channel:prepend()
  * Channel:insert()
  * Channel:remove()
  * Channel:set()
  * Channel:input()
  * Channel:output()
  * Channel:send()
  * Channel:forward()
  * Channel:is_resp()
  * Channel:is_full()
  * Channel:may_recv()

The lua documentation was updated accordingly.
This commit is contained in:
Christopher Faulet 2021-08-06 16:02:36 +02:00
parent 9a6ffda795
commit 6a79fc16bd
2 changed files with 629 additions and 171 deletions

View File

@ -1278,106 +1278,304 @@ Channel class
The following diagram shows where the channel class function are applied.
**Warning**: It is not possible to read from the response in request action,
and it is not possible to read for the request channel in response action.
.. image:: _static/channel.png
**Warning**: It is forbidden to alter the Channels buffer from HTTP contexts.
So only :js:func:`Channel.get_in_length`, :js:func:`Channel.get_out_length`
and :js:func:`Channel.is_full` can be called from an HTTP conetext.
.. warning::
It is not possible to read from the response in request action, and it is
not possible to read for the request channel in response action.
.. image:: _static/channel.png
.. warning::
It is forbidden to alter the Channels buffer from HTTP contexts. So only
:js:func:`Channel.input`, :js:func:`Channel.output`,
:js:func:`Channel.may_recv`, :js:func:`Channel.is_full` and
:js:func:`Channel.is_resp` can be called from an HTTP conetext.
.. js:function:: Channel.dup(channel)
This function returns a string that contain the entire buffer. The data is
not remove from the buffer and can be reprocessed later.
If the buffer can't receive more data, a 'nil' value is returned.
:param class_channel channel: The manipulated Channel.
:returns: a string containing all the available data or nil.
.. js:function:: Channel.get(channel)
This function returns a string that contain the entire buffer. The data is
consumed from the buffer.
If the buffer can't receive more data, a 'nil' value is returned.
:param class_channel channel: The manipulated Channel.
:returns: a string containing all the available data or nil.
.. js:function:: Channel.getline(channel)
This function returns a string that contain the first line of the buffer. The
data is consumed. If the data returned doesn't contains a final '\n' its
assumed than its the last available data in the buffer.
If the buffer can't receive more data, a 'nil' value is returned.
:param class_channel channel: The manipulated Channel.
:returns: a string containing the available line or nil.
.. js:function:: Channel.set(channel, string)
This function replace the content of the buffer by the string. The function
returns the copied length, otherwise, it returns -1.
The data set with this function are not send. They wait for the end of
HAProxy processing, so the buffer can be full.
:param class_channel channel: The manipulated Channel.
:param string string: The data which will sent.
:returns: an integer containing the amount of bytes copied or -1.
.. note::
Channel object may only be manipulated in the context of a registered
action, sample-fetch or converter. However, only actions are allowed to
yield. When it is said one of the following functions may yield, it is only
true in the context of an action.
.. js:function:: Channel.append(channel, string)
This function append the string argument to the content of the buffer. The
function returns the copied length, otherwise, it returns -1.
This function copies the string **string** at the end of incoming data of the
channel buffer. The function returns the copied length on success or -1 if
data cannot be copied.
The data set with this function are not send. They wait for the end of
HAProxy processing, so the buffer can be full.
Same that :js:func:`Channel.insert(channel, string, channel:input())`.
:param class_channel channel: The manipulated Channel.
:param string string: The data which will sent.
:param string string: The data to copied into incoming data.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: Channel.data(channel [, offset [, length]])
This function returns **length** bytes of incoming data from the channel
buffer, starting at the offset **offset**. The data are not removed from the
buffer.
By default, if no length is provided, all incoming data found, starting at the
given offset, are returned. If **length** is set to -1, the function tries to
retrieve a maximum of data and yields if necessary. It also waits for more
data if the requested length exceeds the available amount of incoming data. Do
not providing an offset is the same that setting it to 0. A positive offset is
relative to the begining of incoming data of the channel buffer while negative
offset is relative to their end.
If there is no incoming data and the channel can't receive more data, a 'nil'
value is returned.
:param class_channel channel: The manipulated Channel.
:param integer offset: *optional* The offset in incoming data to start to get
data. 0 by default. May be negative to be relative to
the end of incoming data.
:param integer length: *optional* The expected length of data to retrieve. All
incoming data by default. May be set to -1 to get a
maximum of data.
:returns: a string containing the data found or nil.
.. js:function:: Channel.forward(channel, length)
This function forwards **length** bytes of data from the channel buffer. If
the requested length exceeds the available amount of incoming data, the
function yields, waiting for more data to forward. It returns the amount of
data forwarded.
:param class_channel channel: The manipulated Channel.
:param integer int: The amount of data to forward.
.. js:function:: Channel.input(channel)
This function returns the length of incoming data of the channel buffer.
:param class_channel channel: The manipulated Channel.
:returns: an integer containing the amount of available bytes.
.. js:function:: Channel.insert(channel, string [, offset])
This function copies the string **string** at the offset **offset** in
incoming data of the channel buffer. The function returns the copied length on
success or -1 if data cannot be copied.
By default, if no offset is provided, the string is copied in front of
incoming data. A positive offset is relative to the begining of incoming data
of the channel buffer while negative offset is relative to their end.
:param class_channel channel: The manipulated Channel.
:param string string: The data to copied into incoming data.
:param integer offset: *optional* The offset in incomding data where to copied
data. 0 by default. May be negative to be relative to
the end of incoming data.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: Channel.is_full(channel)
This function returns true if the channel buffer is full.
:returns: a boolean
.. js:function:: Channel.is_resp(channel)
This function returns true if the channel is the response one.
:returns: a boolean
.. js:function:: Channel.line(channel [, offset [, length]])
This function parses **length** bytes of incoming data of the channel buffer,
starting at offset **offset**, and returns the first line found, including the
'\n'. The data are not removed from the buffer. If no line is found, all data
are returned.
By default, if no length is provided, all incoming data, starting at the given
offset, are evaluated. If **length** is set to -1, the function tries to
retrieve a maximum of data and yields if necessary. It also waits for more
data if the requested length exceeds the available amount of incoming data. Do
not providing an offset is the same that setting it to 0. A positive offset is
relative to the begining of incoming data of the channel buffer while negative
offset is relative to their end.
If there is no incoming data and the channel can't receive more data, a 'nil'
value is returned.
:param class_channel channel: The manipulated Channel.
:param integer offset: *optional* The offset in incomding data to start to
parse data. 0 by default. May be negative to be
relative to the end of incoming data.
:param integer length: *optional* The length of data to parse. All incoming
data by default. May be set to -1 to get a maximum of
data.
:returns: a string containing the line found or nil.
.. js:function:: Channel.may_recv(channel)
This function returns true if the channel may still receive data.
:returns: a boolean
.. js:function:: Channel.output(channel)
This function returns the length of outgoing data of the channel buffer.
:param class_channel channel: The manipulated Channel.
:returns: an integer containing the amount of available bytes.
.. js:function:: Channel.prepend(channel, string)
This function copies the string **string** in front of incoming data of the
channel buffer. The function returns the copied length on success or -1 if
data cannot be copied.
Same that :js:func:`Channel.insert(channel, string, 0)`.
:param class_channel channel: The manipulated Channel.
:param string string: The data to copied into incoming data.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: Channel.remove(channel [, offset [, length]])
This function removes **length** bytes of incoming data of the channel buffer,
starting at offset **offset**. This function returns number of bytes removed
on success.
By default, if no length is provided, all incoming data, starting at the given
offset, are removed. Do not providing an offset is the same that setting it
to 0. A positive offset is relative to the begining of incoming data of the
channel buffer while negative offset is relative to their end.
:param class_channel channel: The manipulated Channel.
:param integer offset: *optional* The offset in incomding data where to start
to remove data. 0 by default. May be negative to
be relative to the end of incoming data.
:param integer length: *optional* The length of data to remove. All incoming
data by default.
:returns: an integer containing the amount of bytes removed.
.. js:function:: Channel.send(channel, string)
This function required immediate send of the data. Unless if the connection
is close, the buffer is regularly flushed and all the string can be sent.
This function required immediate send of the string **string**. It means the
string is copied at the begining of incoming data of the channel buffer and
immediately forwarded. Unless if the connection is close, this function yields
to copied and forward all the string.
:param class_channel channel: The manipulated Channel.
:param string string: The data which will sent.
:param string string: The data to send.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: Channel.get_in_length(channel)
.. js:function:: Channel.set(channel, string [, offset [, length]])
This function replace **length** bytes of incoming data of the channel buffer,
starting at offset **offset**, by the string **string**. The function returns
the copied length on success or -1 if data cannot be copied.
By default, if no length is provided, all incoming data, starting at the given
offset, are replaced. Do not providing an offset is the same that setting it
to 0. A positive offset is relative to the begining of incoming data of the
channel buffer while negative offset is relative to their end.
:param class_channel channel: The manipulated Channel.
:param string string: The data to copied into incoming data.
:param integer offset: *optional* The offset in incomding data where to start
the data replacement. 0 by default. May be negative to
be relative to the end of incoming data.
:param integer length: *optional* The length of data to replace. All incoming
data by default.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: Channel.dup(channel)
**DEPRECATED**
This function returns all incoming data found in the channel buffer. The data
are not remove from the buffer and can be reprocessed later.
If there is no incoming data and the channel can't receive more data, a 'nil'
value is returned.
:param class_channel channel: The manipulated Channel.
:returns: a string containing all data found or nil.
.. warning::
This function is deprecated. :js:func:`Channel.data()` must be used
instead.
.. js:function:: Channel.get(channel)
**DEPRECATED**
This function returns all incoming data found in the channel buffer and remove
them from the buffer.
If there is no incoming data and the channel can't receive more data, a 'nil'
value is returned.
:param class_channel channel: The manipulated Channel.
:returns: a string containing all the data found or nil.
.. warning::
This function is deprecated. :js:func:`Channel.data()` must be used to
retrieve data followed by a call to :js:func:`Channel:remove()` to remove
data.
.. code-block:: lua
local data = chn:data()
chn:remove(0, data:len())
..
.. js:function:: Channel.getline(channel)
**DEPRECATED**
This function returns the first line found in incoming data of the channel
buffer, including the '\n'. The returned data are removed from the buffer. If
no line is found, this function yields to wait for more data, except if the
channel can't receive more data. In this case all data are returned.
If there is no incoming data and the channel can't receive more data, a 'nil'
value is returned.
:param class_channel channel: The manipulated Channel.
:returns: a string containing the line found or nil.
.. warning::
This function is depdrecated. :js:func:`Channel.line()` must be used to
retrieve a line followed by a call to :js:func:`Channel:remove()` to remove
data.
.. code-block:: lua
local line = chn:line(0, -1)
chn:remove(0, line:len())
..
.. js:function:: Channel.get_in_len(channel)
**DEPDRECATED**
This function returns the length of the input part of the buffer.
:param class_channel channel: The manipulated Channel.
:returns: an integer containing the amount of available bytes.
.. js:function:: Channel.get_out_length(channel)
.. warning::
This function is deprecated. :js:func:`Channel.input()` must be used
instead.
.. js:function:: Channel.get_out_len(channel)
**DEPDRECATED**
This function returns the length of the output part of the buffer.
:param class_channel channel: The manipulated Channel.
:returns: an integer containing the amount of available bytes.
.. js:function:: Channel.forward(channel, int)
This function transfer bytes from the input part of the buffer to the output
part.
:param class_channel channel: The manipulated Channel.
:param integer int: The amount of data which will be forwarded.
.. js:function:: Channel.is_full(channel)
This function returns true if the buffer channel is full.
:returns: a boolean
.. warning::
This function is deprecated. :js:func:`Channel.output()` must be used
instead.
.. _http_class:

View File

@ -2931,23 +2931,15 @@ static int hlua_channel_new(lua_State *L, struct channel *channel)
/* 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.
* responsibility to ensure <len> and <offset> are valid. It always return the
* length of the built string. <len> may be 0, in this case, an empty string is
* created and 0 is returned.
*/
static inline int _hlua_channel_dup(struct channel *chn, lua_State *L, size_t offset, size_t len)
{
size_t block1, block2;
luaL_Buffer b;
if (unlikely(len == 0)) {
if (channel_input_closed(chn) || HLUA_CANT_YIELD(hlua_gethlua(L))) {
lua_pushnil(L);
return -1;
}
}
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));
@ -2961,30 +2953,6 @@ static inline int _hlua_channel_dup(struct channel *chn, lua_State *L, size_t of
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.
@ -2997,7 +2965,7 @@ static int _hlua_channel_insert(struct channel *chn, lua_State *L, struct ist st
if (unlikely(istlen(str) == 0))
goto end;
if (istlen(str) > c_room(chn) || channel_input_closed(chn)) {
if (istlen(str) > c_room(chn)) {
ret = -1;
goto end;
}
@ -3019,26 +2987,131 @@ static void _hlua_channel_delete(struct channel *chn, size_t offset, size_t 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.
/* Copies input data in the channel's buffer. It is possible to set a specific
* 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.
*/
__LJMP static int hlua_channel_dup_yield(lua_State *L, int status, lua_KContext ctx)
__LJMP static int hlua_channel_get_data_yield(lua_State *L, int status, lua_KContext ctx)
{
struct channel *chn;
int offset = 0, len = 0;
chn = MAY_LJMP(hlua_checkchannel(L, 1));
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));
if (!ci_data(chn) && channel_input_closed(chn)) {
lua_pushnil(L);
return 1;
}
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)) {
lua_pushfstring(L, "offset out of range.");
WILL_LJMP(lua_error(L));
}
}
len = ci_data(chn) - offset;
if (lua_gettop(L) == 3) {
len = MAY_LJMP(luaL_checkinteger(L, 3));
if (!len)
goto dup;
if (len == -1)
len = global.tune.bufsize;
if (len < 0) {
lua_pushfstring(L, "length out of range.");
WILL_LJMP(lua_error(L));
}
}
if (offset + len > ci_data(chn)) {
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;
}
dup:
_hlua_channel_dup(chn, L, co_data(chn) + offset, len);
return 1;
}
/* Check arguments for the function "hlua_channel_dup_yield". */
/* Copies the first line (including the trailing LF) of input data in the
* channel's buffer. It is possible to set a specific 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, the function
* 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.
*/
__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;
chn = MAY_LJMP(hlua_checkchannel(L, 1));
if (!ci_data(chn) && channel_input_closed(chn)) {
lua_pushnil(L);
return 1;
}
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)) {
lua_pushfstring(L, "offset out of range.");
WILL_LJMP(lua_error(L));
}
}
len = ci_data(chn) - offset;
if (lua_gettop(L) == 3) {
len = MAY_LJMP(luaL_checkinteger(L, 3));
if (!len)
goto dup;
if (len == -1)
len = global.tune.bufsize;
if (len < 0) {
lua_pushfstring(L, "length out of range.");
WILL_LJMP(lua_error(L));
}
}
for (l = 0; l < len; l++) {
if (l + offset >= ci_data(chn))
break;
if (*(b_peek(&chn->buf, co_data(chn) + offset + l)) == '\n') {
len = l+1;
goto dup;
}
}
if (offset + len > ci_data(chn)) {
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;
}
dup:
_hlua_channel_dup(chn, L, co_data(chn) + offset, len);
return 1;
}
/* [ DEPRECATED ]
*
* 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().
*/
__LJMP static int hlua_channel_dup(lua_State *L)
{
struct channel *chn;
int offset = 0, len = 0;
MAY_LJMP(check_args(L, 1, "dup"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
@ -3046,46 +3119,47 @@ __LJMP static int hlua_channel_dup(lua_State *L)
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));
offset = co_data(chn);
len = ci_data(chn);
if (!ci_data(chn) && channel_input_closed(chn)) {
lua_pushnil(L);
return 1;
}
_hlua_channel_dup(chn, L, offset, len);
return 1;
}
/* "_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.
/* [ DEPRECATED ]
*
* 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().
*/
__LJMP static int hlua_channel_get_yield(lua_State *L, int status, lua_KContext ctx)
__LJMP static int hlua_channel_get(lua_State *L)
{
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);
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;
_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"));
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));
offset = co_data(chn);
len = ci_data(chn);
if (!ci_data(chn) && channel_input_closed(chn)) {
lua_pushnil(L);
return 1;
}
ret = _hlua_channel_dup(chn, L, offset, len);
_hlua_channel_delete(chn, offset, ret);
return 1;
}
/* This functions consumes and returns one line. If the channel is closed,
@ -3096,6 +3170,7 @@ __LJMP static int hlua_channel_get(lua_State *L)
__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;
int ret;
@ -3103,17 +3178,33 @@ __LJMP static int hlua_channel_getline_yield(lua_State *L, int status, lua_KCont
offset = co_data(chn);
len = ci_data(chn);
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 (unlikely(ret == -1))
if (!ci_data(chn) && channel_input_closed(chn)) {
lua_pushnil(L);
return 1;
}
for (l = 0; l < len; l++) {
if (*(b_peek(&chn->buf, offset+l)) == '\n') {
len = l+1;
goto dup;
}
}
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_getline_yield, TICK_ETERNITY, 0));
}
dup:
ret = _hlua_channel_dup(chn, L, co_data(chn) + offset, len);
_hlua_channel_delete(chn, offset, ret);
return 1;
}
/* Check arguments for the function "hlua_channel_getline_yield". */
/* [ DEPRECATED ]
*
* Check arguments for the function "hlua_channel_getline_yield".
*/
__LJMP static int hlua_channel_getline(lua_State *L)
{
struct channel *chn;
@ -3127,10 +3218,48 @@ __LJMP static int hlua_channel_getline(lua_State *L)
return MAY_LJMP(hlua_channel_getline_yield(L, 0, 0));
}
/* This function takes a string as argument, and append it at the input side of
* channel. If data cannot be copied, because there is not enough space to do so
* or because the channel is closed, it returns -1. Otherwise, it returns the
* amount of data written (the string length). This function does not yield.
/* Retrieves a given amount of input data at the given offset. By default all
* available input data are returned. The offset may be negactive to start from
* the end of input data. The length may be -1 to set it to the maximum buffer
* size.
*/
__LJMP static int hlua_channel_get_data(lua_State *L)
{
struct channel *chn;
if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
WILL_LJMP(luaL_error(L, "'data' expects at most 2 arguments"));
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_data_yield(L, 0, 0));
}
/* Retrieves a given amount of input data at the given offset. By default all
* available input data are returned. The offset may be negactive to start from
* the end of input data. The length may be -1 to set it to the maximum buffer
* size.
*/
__LJMP static int hlua_channel_get_line(lua_State *L)
{
struct channel *chn;
if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
WILL_LJMP(luaL_error(L, "'line' expects at most 2 arguments"));
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_line_yield(L, 0, 0));
}
/* Appends a string into the input side of channel. 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. 0 may be returned if nothing is copied. This function
* does not yield.
*/
__LJMP static int hlua_channel_append(lua_State *L)
{
@ -3142,7 +3271,6 @@ __LJMP static int hlua_channel_append(lua_State *L)
MAY_LJMP(check_args(L, 2, "append"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
str = MAY_LJMP(luaL_checklstring(L, 2, &len));
if (IS_HTX_STRM(chn_strm(chn))) {
lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
WILL_LJMP(lua_error(L));
@ -3153,38 +3281,149 @@ __LJMP static int hlua_channel_append(lua_State *L)
return 1;
}
/* The function replaces input data from the channel by the string passed as
* argument. Before clearing the buffer, we take care the copy will be
* possible. It returns the amount of data written (the string length) on
* success or -1 if the copy is not performed. This function does not yield.
/* Prepends a string into the input side of channel. 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. 0 may be returned if nothing is copied. This function
* does not yield.
*/
__LJMP static int hlua_channel_set(lua_State *L)
__LJMP static int hlua_channel_prepend(lua_State *L)
{
struct channel *chn;
const char *str;
size_t len;
size_t sz;
int ret;
MAY_LJMP(check_args(L, 2, "set"));
MAY_LJMP(check_args(L, 2, "prepend"));
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, sz), co_data(chn));
lua_pushinteger(L, ret);
return 1;
}
/* Inserts a given amount of input data at the given offset by a string
* 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.
*/
__LJMP static int hlua_channel_insert_data(lua_State *L)
{
struct channel *chn;
const char *str;
size_t sz;
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)
offset = 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 (offset > ci_data(chn)) {
lua_pushfstring(L, "offset out of range.");
WILL_LJMP(lua_error(L));
}
ret = _hlua_channel_insert(chn, L, ist2(str, sz), co_data(chn) + offset);
lua_pushinteger(L, ret);
return 1;
}
/* Replaces a given amount of input data at the given offset by a string
* 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.
*/
__LJMP static int hlua_channel_set_data(lua_State *L)
{
struct channel *chn;
const char *str;
size_t sz;
int ret, offset = 0, len = -1;
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;
if (offset + len > ci_data(chn)) {
lua_pushfstring(L, "offset or length out of range.");
WILL_LJMP(lua_error(L));
}
/* Be sure we can copied the string once input data will be removed. */
if (len > c_room(chn) + ci_data(chn) || channel_input_closed(chn))
if (sz > c_room(chn) + len)
lua_pushinteger(L, -1);
else {
_hlua_channel_delete(chn, co_data(chn), ci_data(chn));
ret = _hlua_channel_insert(chn, L, ist2(str, len), co_data(chn));
_hlua_channel_delete(chn, co_data(chn) + offset, len);
ret = _hlua_channel_insert(chn, L, ist2(str, sz), co_data(chn) + offset);
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
* the removed data.
*/
__LJMP static int hlua_channel_del_data(lua_State *L)
{
struct channel *chn;
int offset = 0, len = -1;
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;
if (offset + len > ci_data(chn)) {
lua_pushfstring(L, "offset or length out of range.");
WILL_LJMP(lua_error(L));
}
_hlua_channel_delete(chn, co_data(chn) + offset, len);
lua_pushinteger(L, len);
return 1;
}
/* Append data in the output side of the buffer. This data is immediately
* sent. The function returns the amount of data written. If the buffer
* cannot contain the data, the function yields. The function returns -1
@ -3362,7 +3601,7 @@ __LJMP static int hlua_channel_get_in_len(lua_State *L)
{
struct channel *chn;
MAY_LJMP(check_args(L, 1, "get_in_len"));
MAY_LJMP(check_args(L, 1, "input"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
if (IS_HTX_STRM(chn_strm(chn))) {
struct htx *htx = htxbuf(&chn->buf);
@ -3387,6 +3626,17 @@ __LJMP static int hlua_channel_is_full(lua_State *L)
return 1;
}
/* Returns true if the channel may still receive data. */
__LJMP static int hlua_channel_may_recv(lua_State *L)
{
struct channel *chn;
MAY_LJMP(check_args(L, 1, "may_recv"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
lua_pushboolean(L, (!channel_input_closed(chn) && channel_may_recv(chn)));
return 1;
}
/* Returns true if the channel is the response channel. */
__LJMP static int hlua_channel_is_resp(lua_State *L)
{
@ -3406,7 +3656,7 @@ __LJMP static int hlua_channel_get_out_len(lua_State *L)
{
struct channel *chn;
MAY_LJMP(check_args(L, 1, "get_out_len"));
MAY_LJMP(check_args(L, 1, "output"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
lua_pushinteger(L, co_data(chn));
return 1;
@ -8912,17 +9162,27 @@ lua_State *hlua_init_state(int thread_num)
lua_newtable(L);
/* Register . */
hlua_class_function(L, "data", hlua_channel_get_data);
hlua_class_function(L, "line", hlua_channel_get_line);
hlua_class_function(L, "set", hlua_channel_set_data);
hlua_class_function(L, "remove", hlua_channel_del_data);
hlua_class_function(L, "append", hlua_channel_append);
hlua_class_function(L, "prepend", hlua_channel_prepend);
hlua_class_function(L, "insert", hlua_channel_insert_data);
hlua_class_function(L, "send", hlua_channel_send);
hlua_class_function(L, "forward", hlua_channel_forward);
hlua_class_function(L, "input", hlua_channel_get_in_len);
hlua_class_function(L, "output", hlua_channel_get_out_len);
hlua_class_function(L, "may_recv", hlua_channel_may_recv);
hlua_class_function(L, "is_full", hlua_channel_is_full);
hlua_class_function(L, "is_resp", hlua_channel_is_resp);
/* Deprecated API */
hlua_class_function(L, "get", hlua_channel_get);
hlua_class_function(L, "dup", hlua_channel_dup);
hlua_class_function(L, "getline", hlua_channel_getline);
hlua_class_function(L, "set", hlua_channel_set);
hlua_class_function(L, "append", hlua_channel_append);
hlua_class_function(L, "send", hlua_channel_send);
hlua_class_function(L, "forward", hlua_channel_forward);
hlua_class_function(L, "get_in_len", hlua_channel_get_in_len);
hlua_class_function(L, "get_out_len", hlua_channel_get_out_len);
hlua_class_function(L, "is_full", hlua_channel_is_full);
hlua_class_function(L, "is_resp", hlua_channel_is_resp);
lua_rawset(L, -3);