From: Willy Tarreau Date: Wed, 8 Sep 2010 15:04:31 +0000 (+0200) Subject: [MEDIUM] buffers: rework the functions to exchange between SI and buffers X-Git-Tag: v1.5-dev8~469 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=74b08c9ab792140a0423dc0e78ac15bb4ace812a;p=thirdparty%2Fhaproxy.git [MEDIUM] buffers: rework the functions to exchange between SI and buffers There was no consistency between all the functions used to exchange data between a buffer and a stream interface. Also, the functions used to send data to a buffer did not consider the possibility that the buffer was shutdown for read. Now the functions are called buffer_{put,get}_{char,block,chunk,string}. The old buffer_feed* functions have been left available for existing code but marked deprecated. --- diff --git a/include/proto/buffers.h b/include/proto/buffers.h index dca3c889a0..8ae2a7fe0a 100644 --- a/include/proto/buffers.h +++ b/include/proto/buffers.h @@ -39,6 +39,18 @@ extern struct pool_head *pool2_buffer; /* perform minimal intializations, report 0 in case of error, 1 if OK. */ int init_buffer(); +/* SI-to-buffer functions : buffer_{get,put}_{char,block,string,chunk} */ +int buffer_write(struct buffer *buf, const char *msg, int len); +int buffer_put_block(struct buffer *buf, const char *str, int len); +int buffer_put_char(struct buffer *buf, char c); +int buffer_get_line(struct buffer *buf, char *str, int len); +int buffer_get_block(struct buffer *buf, char *blk, int len, int offset); +int buffer_replace(struct buffer *b, char *pos, char *end, const char *str); +int buffer_replace2(struct buffer *b, char *pos, char *end, const char *str, int len); +int buffer_insert_line2(struct buffer *b, char *pos, const char *str, int len); +void buffer_dump(FILE *o, struct buffer *b, int from, int to); +void buffer_bounce_realign(struct buffer *buf); + /* Initialize all fields in the buffer. The BF_OUT_EMPTY flags is set. */ static inline void buffer_init(struct buffer *buf) { @@ -65,6 +77,18 @@ static inline int buffer_max_len(struct buffer *buf) return buf->size - global.tune.maxrewrite + buf->to_forward + buf->send_max; } +/* Returns true if the buffer's input is already closed */ +static inline int buffer_input_closed(struct buffer *buf) +{ + return ((buf->flags & BF_SHUTR) != 0); +} + +/* Returns true if the buffer's output is already closed */ +static inline int buffer_output_closed(struct buffer *buf) +{ + return ((buf->flags & BF_SHUTW) != 0); +} + /* Check buffer timeouts, and set the corresponding flags. The * likely/unlikely have been optimized for fastest normal path. * The read/write timeouts are not set if there was activity on the buffer. @@ -291,7 +315,7 @@ static inline int buffer_realign(struct buffer *buf) /* * Return the maximum amount of bytes that can be written into the buffer in - * one call to buffer_feed*(). + * one call to buffer_put_*(). */ static inline int buffer_free_space(struct buffer *buf) { @@ -406,66 +430,6 @@ static inline void buffer_skip(struct buffer *buf, int len) buf->flags |= BF_WRITE_PARTIAL; } -/* - * Return one char from the buffer. If the buffer is empty and closed, return -1. - * If the buffer is just empty, return -2. The buffer's pointer is not advanced, - * it's up to the caller to call buffer_skip(buf, 1) when it has consumed the char. - * Also note that this function respects the send_max limit. - */ -static inline int buffer_si_peekchar(struct buffer *buf) -{ - if (buf->send_max) - return *buf->w; - - if (buf->flags & (BF_SHUTW|BF_SHUTW_NOW)) - return -1; - else - return -2; -} - -/* Try to write character into buffer after length controls. This - * work like buffer_feed2(buf, &c, 1). - * Returns non-zero in case of success, 0 if the buffer was full. - * The send limit is automatically adjusted with the amount of data written. - */ -static inline int buffer_si_putchar(struct buffer *buf, char c) -{ - if (buf->flags & BF_FULL) - return 0; - - *buf->r = c; - - buf->l++; - if (buf->l >= buffer_max_len(buf)) - buf->flags |= BF_FULL; - - buf->r++; - if (buf->r - buf->data == buf->size) - buf->r -= buf->size; - - if (buf->to_forward >= 1) { - if (buf->to_forward != BUF_INFINITE_FORWARD) - buf->to_forward--; - buf->send_max++; - buf->flags &= ~BF_OUT_EMPTY; - } - - buf->total++; - return 1; -} - -int buffer_write(struct buffer *buf, const char *msg, int len); -int buffer_feed2(struct buffer *buf, const char *str, int len); -int buffer_si_putchar(struct buffer *buf, char c); -int buffer_si_peekline(struct buffer *buf, char *str, int len); -int buffer_replace(struct buffer *b, char *pos, char *end, const char *str); -int buffer_replace2(struct buffer *b, char *pos, char *end, const char *str, int len); -int buffer_insert_line2(struct buffer *b, char *pos, const char *str, int len); -void buffer_dump(FILE *o, struct buffer *b, int from, int to); -void buffer_bounce_realign(struct buffer *buf); - - - /* writes the chunk to buffer . Returns -1 in case of success, * -2 if it is larger than the buffer size, or the number of bytes available * otherwise. If the chunk has been written, its size is automatically reset @@ -482,35 +446,91 @@ static inline int buffer_write_chunk(struct buffer *buf, struct chunk *chunk) return ret; } -/* Try to write chunk into buffer after length controls. This is - * the equivalent of buffer_write_chunk() except that to_forward and send_max - * are updated and that max_len is respected. Returns -1 in case of success, - * -2 if it is larger than the buffer size, or the number of bytes available - * otherwise. If the chunk has been written, its size is automatically reset - * to zero. The send limit is automatically adjusted with the amount of data - * written. +/* Tries to copy chunk into buffer after length controls. + * The send_max and to_forward pointers are updated. If the buffer's input is + * closed, -2 is returned. If the block is too large for this buffer, -3 is + * returned. If there is not enough room left in the buffer, -1 is returned. + * Otherwise the number of bytes copied is returned (0 being a valid number). + * Buffer flags FULL, EMPTY and READ_PARTIAL are updated if some data can be + * transferred. The chunk's length is updated with the number of bytes sent. */ -static inline int buffer_feed_chunk(struct buffer *buf, struct chunk *chunk) +static inline int buffer_put_chunk(struct buffer *buf, struct chunk *chunk) { int ret; - ret = buffer_feed2(buf, chunk->str, chunk->len); - if (ret == -1) - chunk->len = 0; + ret = buffer_put_block(buf, chunk->str, chunk->len); + if (ret > 0) + chunk->len -= ret; return ret; } -/* Try to write string into buffer after length controls. This is - * the equivalent of buffer_feed2() except that string length is measured by - * the function. Returns -1 in case of success, -2 if it is larger than the - * buffer size, or the number of bytes available otherwise. The send limit is - * automatically adjusted with the amount of data written. +/* Tries to copy string at once into buffer after length controls. + * The send_max and to_forward pointers are updated. If the buffer's input is + * closed, -2 is returned. If the block is too large for this buffer, -3 is + * returned. If there is not enough room left in the buffer, -1 is returned. + * Otherwise the number of bytes copied is returned (0 being a valid number). + * Buffer flags FULL, EMPTY and READ_PARTIAL are updated if some data can be + * transferred. + */ +static inline int buffer_put_string(struct buffer *buf, const char *str) +{ + return buffer_put_block(buf, str, strlen(str)); +} + +/* + * Return one char from the buffer. If the buffer is empty and closed, return -2. + * If the buffer is just empty, return -1. The buffer's pointer is not advanced, + * it's up to the caller to call buffer_skip(buf, 1) when it has consumed the char. + * Also note that this function respects the send_max limit. + */ +static inline int buffer_get_char(struct buffer *buf) +{ + /* closed or empty + imminent close = -2; empty = -1 */ + if (unlikely(buf->flags & (BF_OUT_EMPTY|BF_SHUTW))) { + if (buf->flags & (BF_SHUTW|BF_SHUTW_NOW)) + return -2; + return -1; + } + return *buf->w; +} + + +/* DEPRECATED, just provided for compatibility, use buffer_put_chunk() instead !!! + * returns >= 0 if the buffer is too small to hold the message, -1 if the + * transfer was OK, -2 in case of failure. + */ +static inline int buffer_feed_chunk(struct buffer *buf, struct chunk *msg) +{ + int ret = buffer_put_chunk(buf, msg); + if (ret >= 0) /* transfer OK */ + return -1; + if (ret == -1) /* missing room */ + return 1; + /* failure */ + return -2; +} + +/* DEPRECATED, just provided for compatibility, use buffer_put_string() instead !!! + * returns >= 0 if the buffer is too small to hold the message, -1 if the + * transfer was OK, -2 in case of failure. */ static inline int buffer_feed(struct buffer *buf, const char *str) { - return buffer_feed2(buf, str, strlen(str)); + int ret = buffer_put_string(buf, str); + if (ret >= 0) /* transfer OK */ + return -1; + if (ret == -1) /* missing room */ + return 1; + /* failure */ + return -2; } +/* + * + * Functions below are used to manage chunks + * + */ + static inline void chunk_init(struct chunk *chk, char *str, size_t size) { chk->str = str; chk->len = 0; diff --git a/src/buffers.c b/src/buffers.c index c128bb9a35..1572a37cc5 100644 --- a/src/buffers.c +++ b/src/buffers.c @@ -72,37 +72,78 @@ int buffer_write(struct buffer *buf, const char *msg, int len) return -1; } -/* Try to write string into buffer after length controls. This - * is the equivalent of buffer_write() except that to_forward and send_max - * are updated and that max_len is respected. Returns -1 in case of success, - * -2 if it is larger than the buffer size, or the number of bytes available - * otherwise. The send limit is automatically adjusted with the amount of data - * written. +/* Tries to copy character into buffer after length controls. The + * send_max and to_forward pointers are updated. If the buffer's input is + * closed, -2 is returned. If there is not enough room left in the buffer, -1 + * is returned. Otherwise the number of bytes copied is returned (1). Buffer + * flags FULL, EMPTY and READ_PARTIAL are updated if some data can be + * transferred. */ -int buffer_feed2(struct buffer *buf, const char *str, int len) +int buffer_put_char(struct buffer *buf, char c) { - int max; + if (unlikely(buffer_input_closed(buf))) + return -2; - if (len == 0) + if (buf->flags & BF_FULL) return -1; + *buf->r = c; + + buf->l++; + if (buf->l >= buffer_max_len(buf)) + buf->flags |= BF_FULL; + buf->flags |= BF_READ_PARTIAL; + + buf->r++; + if (buf->r - buf->data == buf->size) + buf->r -= buf->size; + + if (buf->to_forward >= 1) { + if (buf->to_forward != BUF_INFINITE_FORWARD) + buf->to_forward--; + buf->send_max++; + buf->flags &= ~BF_OUT_EMPTY; + } + + buf->total++; + return 1; +} + +/* Tries to copy block at once into buffer after length controls. + * The send_max and to_forward pointers are updated. If the buffer's input is + * closed, -2 is returned. If the block is too large for this buffer, -3 is + * returned. If there is not enough room left in the buffer, -1 is returned. + * Otherwise the number of bytes copied is returned (0 being a valid number). + * Buffer flags FULL, EMPTY and READ_PARTIAL are updated if some data can be + * transferred. + */ +int buffer_put_block(struct buffer *buf, const char *blk, int len) +{ + int max; + + if (unlikely(buffer_input_closed(buf))) + return -2; + max = buffer_max_len(buf); - if (len > max - buf->l) { + if (unlikely(len > max - buf->l)) { /* we can't write this chunk right now because the buffer is * almost full or because the block is too large. Return the * available space or -2 if impossible. */ if (len > max) - return -2; + return -3; - return max - buf->l; + return -1; } + if (unlikely(len == 0)) + return 0; + /* OK so the data fits in the buffer in one or two blocks */ max = buffer_contig_space_with_len(buf, max); - memcpy(buf->r, str, MIN(len, max)); + memcpy(buf->r, blk, MIN(len, max)); if (len > max) - memcpy(buf->data, str + max, len - max); + memcpy(buf->data, blk + max, len - max); buf->l += len; buf->r += len; @@ -127,27 +168,29 @@ int buffer_feed2(struct buffer *buf, const char *str, int len) /* notify that some data was read from the SI into the buffer */ buf->flags |= BF_READ_PARTIAL; - return -1; + return len; } -/* Get one text line out of a buffer from a stream interface. +/* Gets one text line out of a buffer from a stream interface. * Return values : * >0 : number of bytes read. Includes the \n if present before len or end. - * =0 : no '\n' before end found. is undefined. - * <0 : no more bytes readable + shutdown set. + * =0 : no '\n' before end found. is left undefined. + * <0 : no more bytes readable because output is shut. * The buffer status is not changed. The caller must call buffer_skip() to * update it. The '\n' is waited for as long as neither the buffer nor the * output are full. If either of them is full, the string may be returned * as is, without the '\n'. */ -int buffer_si_peekline(struct buffer *buf, char *str, int len) +int buffer_get_line(struct buffer *buf, char *str, int len) { int ret, max; char *p; ret = 0; max = len; - if (!buf->send_max) { + + /* closed or empty + imminent close = -1; empty = 0 */ + if (unlikely(buf->flags & (BF_OUT_EMPTY|BF_SHUTW))) { if (buf->flags & (BF_SHUTW|BF_SHUTW_NOW)) ret = -1; goto out; @@ -180,6 +223,43 @@ int buffer_si_peekline(struct buffer *buf, char *str, int len) return ret; } +/* Gets one full block of data at once from a buffer, optionally from a + * specific offset. Return values : + * >0 : number of bytes read, equal to requested size. + * =0 : not enough data available. is left undefined. + * <0 : no more bytes readable because output is shut. + * The buffer status is not changed. The caller must call buffer_skip() to + * update it. + */ +int buffer_get_block(struct buffer *buf, char *blk, int len, int offset) +{ + int firstblock; + + if (buf->flags & BF_SHUTW) + return -1; + + if (len + offset > buf->send_max) { + if (buf->flags & (BF_SHUTW|BF_SHUTW_NOW)) + return -1; + return 0; + } + + firstblock = buf->data + buf->size - buf->w; + if (firstblock > offset) { + if (firstblock >= len + offset) { + memcpy(blk, buf->w + offset, len); + return len; + } + + memcpy(blk, buf->w + offset, firstblock - offset); + memcpy(blk + firstblock - offset, buf->data, len - firstblock + offset); + return len; + } + + memcpy(blk, buf->data + offset - firstblock, len); + return len; +} + /* * this function writes the string at position which must be in buffer , * and moves just after the end of . diff --git a/src/dumpstats.c b/src/dumpstats.c index aa5b02f0e0..59607f95b7 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -858,7 +858,7 @@ void stats_io_handler(struct stream_interface *si) if (buffer_almost_full(si->ib)) break; - reql = buffer_si_peekline(si->ob, trash, sizeof(trash)); + reql = buffer_get_line(si->ob, trash, sizeof(trash)); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) break;