diff --git a/doc/lua-api/index.rst b/doc/lua-api/index.rst index d98c77eeb..06468eb29 100644 --- a/doc/lua-api/index.rst +++ b/doc/lua-api/index.rst @@ -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: diff --git a/src/hlua.c b/src/hlua.c index 1e95ac9ac..5c2d3ccd6 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -2931,23 +2931,15 @@ static int hlua_channel_new(lua_State *L, struct channel *channel) /* Copies bytes of data present in the channel's buffer, starting at the * offset , and put it in a LUA string variable. It is the caller -* responsibility to ensure and 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 and are valid. It always return the +* length of the built string. 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 , and put it in a LUA string variable. It copies at most - * bytes. It is the caller responsibility to ensure and 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), 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 to the channel's buffer at the 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);