MAJOR: start to change buffer API

This is intentionally the minimal and safest set of changes, some cleanups
area still required. These changes are quite tricky and cannot be
independantly tested, so it's important to keep this patch as bisectable
as possible.

buf_empty and buf_wanted were changed and are now exactly similar since
there's no <p> member in the structure anymore. Given that no test is
ever made in the code to check that buf == &buf_wanted, it may be possible
that we don't need to have two anymore, unless some buf_empty tests have
precedence. This will have to be investigated.

A significant part of this commit affects the HTTP compression code,
which used to deeply manipulate the input and output buffers without
any reasonable solution for a better abstraction. For this reason, if
any regression is met and designates this patch as the culprit, it is
important to run tests which specifically involve compression or which
definitely don't use it in order to spot the issue.

Cc: Olivier Houchard <ohouchard@haproxy.com>
This commit is contained in:
Willy Tarreau 2018-06-29 18:42:02 +02:00
parent 81521ed850
commit d54a8ceb97
5 changed files with 122 additions and 141 deletions

View File

@ -32,10 +32,10 @@
/* Structure defining a buffer's head */ /* Structure defining a buffer's head */
struct buffer { struct buffer {
char *p; /* buffer's start pointer, separates in and out data */ size_t head; /* start offset of remaining data relative to data */
size_t len; /* length of data after head */
size_t size; /* buffer size in bytes */ size_t size; /* buffer size in bytes */
size_t i; /* number of input bytes pending for analysis in the buffer */ size_t output; /* TEMPORARY: part of <len> which is to be forwarded */
size_t o; /* number of out bytes the sender can consume from this buffer */
char data[0]; /* <size> bytes of stored data */ char data[0]; /* <size> bytes of stored data */
}; };
@ -73,7 +73,7 @@ static inline char *b_wrap(const struct buffer *b)
/* b_data() : returns the number of bytes present in the buffer. */ /* b_data() : returns the number of bytes present in the buffer. */
static inline size_t b_data(const struct buffer *b) static inline size_t b_data(const struct buffer *b)
{ {
return b->i + b->o; return b->len;
} }
/* b_room() : returns the amount of room left in the buffer */ /* b_room() : returns the amount of room left in the buffer */
@ -95,12 +95,12 @@ static inline size_t b_full(const struct buffer *b)
*/ */
static inline size_t __b_stop_ofs(const struct buffer *b) static inline size_t __b_stop_ofs(const struct buffer *b)
{ {
return b->p - b->data + b->i; return b->head + b->len;
} }
static inline const char *__b_stop(const struct buffer *b) static inline const char *__b_stop(const struct buffer *b)
{ {
return b->p + b->i; return b_orig(b) + __b_stop_ofs(b);
} }
static inline size_t b_stop_ofs(const struct buffer *b) static inline size_t b_stop_ofs(const struct buffer *b)
@ -114,7 +114,7 @@ static inline size_t b_stop_ofs(const struct buffer *b)
static inline const char *b_stop(const struct buffer *b) static inline const char *b_stop(const struct buffer *b)
{ {
return b->data + b_stop_ofs(b); return b_orig(b) + b_stop_ofs(b);
} }
@ -125,32 +125,27 @@ static inline const char *b_stop(const struct buffer *b)
*/ */
static inline size_t __b_peek_ofs(const struct buffer *b, size_t ofs) static inline size_t __b_peek_ofs(const struct buffer *b, size_t ofs)
{ {
return b->p - b->data + ofs - b->o; return b->head + ofs;
} }
static inline char *__b_peek(const struct buffer *b, size_t ofs) static inline char *__b_peek(const struct buffer *b, size_t ofs)
{ {
return b->p - b->o + ofs; return b_orig(b) + __b_peek_ofs(b, ofs);
} }
static inline size_t b_peek_ofs(const struct buffer *b, size_t ofs) static inline size_t b_peek_ofs(const struct buffer *b, size_t ofs)
{ {
size_t ret = __b_peek_ofs(b, ofs); size_t ret = __b_peek_ofs(b, ofs);
if (ret >= b->size) { if (ret >= b->size)
/* wraps either up or down */ ret -= b->size;
if ((ssize_t)ret < 0)
ret += b->size;
else
ret -= b->size;
}
return ret; return ret;
} }
static inline char *b_peek(const struct buffer *b, size_t ofs) static inline char *b_peek(const struct buffer *b, size_t ofs)
{ {
return (char *)b->data + b_peek_ofs(b, ofs); return b_orig(b) + b_peek_ofs(b, ofs);
} }
@ -160,22 +155,22 @@ static inline char *b_peek(const struct buffer *b, size_t ofs)
*/ */
static inline size_t __b_head_ofs(const struct buffer *b) static inline size_t __b_head_ofs(const struct buffer *b)
{ {
return __b_peek_ofs(b, 0); return b->head;
} }
static inline char *__b_head(const struct buffer *b) static inline char *__b_head(const struct buffer *b)
{ {
return __b_peek(b, 0); return b_orig(b) + __b_head_ofs(b);
} }
static inline size_t b_head_ofs(const struct buffer *b) static inline size_t b_head_ofs(const struct buffer *b)
{ {
return b_peek_ofs(b, 0); return __b_head_ofs(b);
} }
static inline char *b_head(const struct buffer *b) static inline char *b_head(const struct buffer *b)
{ {
return b_peek(b, 0); return __b_head(b);
} }
@ -248,20 +243,11 @@ static inline int b_almost_full(const struct buffer *b)
} }
/* b_space_wraps() : returns non-zero only if the buffer's free space wraps : /* b_space_wraps() : returns non-zero only if the buffer's free space wraps :
* [ |oooo| ] => yes * [ |xxxx| ] => yes
* [ |iiii| ] => yes * [xxxx| ] => no
* [ |oooo|iiii| ] => yes * [ |xxxx] => no
* [oooo| ] => no * [xxxx| |xxxx] => no
* [ |oooo] => no * [xxxxxxxxxx|xxxxxxxxxxx] => no
* [iiii| ] => no
* [ |iiii] => no
* [oooo|iiii| ] => no
* [ |oooo|iiii] => no
* [iiii| |oooo] => no
* [oo|iiii| |oo] => no
* [iiii| |oo|ii] => no
* [oooooooooo|iiiiiiiiiii] => no
* [iiiiiiiiiiiii|oooooooo] => no
* *
* So the only case where the buffer does not wrap is when there's data either * So the only case where the buffer does not wrap is when there's data either
* at the beginning or at the end of the buffer. Thus we have this : * at the beginning or at the end of the buffer. Thus we have this :
@ -379,32 +365,29 @@ static inline size_t b_getblk_nc(const struct buffer *buf, const char **blk1, si
/* b_reset() : resets a buffer. The size is not touched. */ /* b_reset() : resets a buffer. The size is not touched. */
static inline void b_reset(struct buffer *b) static inline void b_reset(struct buffer *b)
{ {
b->o = 0; b->head = 0;
b->i = 0; b->len = 0;
b->p = b_orig(b); b->output = 0;
} }
/* b_sub() : decreases the buffer length by <count> */ /* b_sub() : decreases the buffer length by <count> */
static inline void b_sub(struct buffer *b, size_t count) static inline void b_sub(struct buffer *b, size_t count)
{ {
b->i -= count; b->len -= count;
} }
/* b_add() : increase the buffer length by <count> */ /* b_add() : increase the buffer length by <count> */
static inline void b_add(struct buffer *b, size_t count) static inline void b_add(struct buffer *b, size_t count)
{ {
b->i += count; b->len += count;
} }
/* b_set_data() : sets the buffer's length */ /* b_set_data() : sets the buffer's length */
static inline void b_set_data(struct buffer *b, size_t len) static inline void b_set_data(struct buffer *b, size_t len)
{ {
if (len >= b->o) if (len < b->output)
b->i = len - b->o; b->output = len;
else { b->len = len;
b->o = len;
b->i = 0;
}
} }
/* b_del() : skips <del> bytes in a buffer <b>. Covers both the output and the /* b_del() : skips <del> bytes in a buffer <b>. Covers both the output and the
@ -413,22 +396,21 @@ static inline void b_set_data(struct buffer *b, size_t len)
*/ */
static inline void b_del(struct buffer *b, size_t del) static inline void b_del(struct buffer *b, size_t del)
{ {
if (del <= b->o) { if (del >= b->output)
b->o -= del; b->output = 0;
del = 0; else
} b->output -= del;
if (del) { b->len -= del;
b->p = b_peek(b, del); b->head += del;
b->i -= del; if (b->head >= b->size)
del = 0; b->head -= b->size;
}
} }
/* b_realign_if_empty() : realigns a buffer if it's empty */ /* b_realign_if_empty() : realigns a buffer if it's empty */
static inline void b_realign_if_empty(struct buffer *b) static inline void b_realign_if_empty(struct buffer *b)
{ {
if (!b_data(b)) if (!b_data(b))
b->p = b->data; b->head = 0;
} }
/* b_slow_realign() : this function realigns a possibly wrapping buffer so that /* b_slow_realign() : this function realigns a possibly wrapping buffer so that
@ -470,7 +452,7 @@ static inline void b_slow_realign(struct buffer *b, char *swap, size_t output)
memcpy(b_orig(b), swap, b_data(b) - output); memcpy(b_orig(b), swap, b_data(b) - output);
memcpy(b_wrap(b) - output, swap + b_size(b) - output, output); memcpy(b_wrap(b) - output, swap + b_size(b) - output, output);
b->p = b->data; b->head = b_size(b) - output;
} }
#endif /* _COMMON_BUF_H */ #endif /* _COMMON_BUF_H */

View File

@ -104,16 +104,14 @@ static inline int buffer_almost_full(const struct buffer *buf)
return b_almost_full(buf); return b_almost_full(buf);
} }
/* Cut the first <n> pending bytes in a contiguous buffer. It is illegal to /* Cut the first <n> pending bytes in a contiguous buffer. The caller must
* call this function with remaining data waiting to be sent (o > 0). The * ensure that <n> is smaller than the actual buffer's length. This is mainly
* caller must ensure that <n> is smaller than the actual buffer's length. * used to remove empty lines at the beginning of a request or a response.
* This is mainly used to remove empty lines at the beginning of a request
* or a response.
*/ */
static inline void bi_fast_delete(struct buffer *buf, int n) static inline void bi_fast_delete(struct buffer *buf, int n)
{ {
buf->i -= n; buf->len -= n;
buf->p += n; buf->head += n;
} }
/* This function writes the string <str> at position <pos> which must be in /* This function writes the string <str> at position <pos> which must be in
@ -128,16 +126,16 @@ static inline int buffer_replace(struct buffer *b, char *pos, char *end, const c
return buffer_replace2(b, pos, end, str, strlen(str)); return buffer_replace2(b, pos, end, str, strlen(str));
} }
/* Tries to write char <c> into output data at buffer <b>. Supports wrapping. /* Tries to append char <c> at the end of buffer <b>. Supports wrapping. Data
* Data are truncated if buffer is full. * are truncated if buffer is full.
*/ */
static inline void bo_putchr(struct buffer *b, char c) static inline void bo_putchr(struct buffer *b, char c)
{ {
if (b_data(b) == b->size) if (b_data(b) == b->size)
return; return;
*b_tail(b) = c; *b_tail(b) = c;
b->p = b_peek(b, b->o + 1); b->len++;
b->o++; b->output++;
} }
/* Tries to append block <blk> at the end of buffer <b>. Supports wrapping. /* Tries to append block <blk> at the end of buffer <b>. Supports wrapping.
@ -158,13 +156,12 @@ static inline unsigned int bo_putblk(struct buffer *b, const char *blk, unsigned
half = len; half = len;
memcpy(b_tail(b), blk, half); memcpy(b_tail(b), blk, half);
b->p = b_peek(b, b->o + half); b->len += half;
b->o += half;
if (len > half) { if (len > half) {
memcpy(b_tail(b), blk + half, len - half); memcpy(b_tail(b), blk + half, len - half);
b->p = b_peek(b, b->o + len - half); b->len += len - half;
b->o += len - half;
} }
b->output += len;
return len; return len;
} }
@ -194,7 +191,7 @@ static inline void bi_putchr(struct buffer *b, char c)
if (b_data(b) == b->size) if (b_data(b) == b->size)
return; return;
*b_tail(b) = c; *b_tail(b) = c;
b->i++; b->len++;
} }
/* Tries to append block <blk> at the end of buffer <b>. Supports wrapping. /* Tries to append block <blk> at the end of buffer <b>. Supports wrapping.
@ -215,10 +212,10 @@ static inline unsigned int bi_putblk(struct buffer *b, const char *blk, unsigned
half = len; half = len;
memcpy(b_tail(b), blk, half); memcpy(b_tail(b), blk, half);
b->i += half; b->len += half;
if (len > half) { if (len > half) {
memcpy(b_tail(b), blk + half, len - half); memcpy(b_tail(b), blk + half, len - half);
b->i += len - half; b->len += len - half;
} }
return len; return len;
} }
@ -443,7 +440,7 @@ static inline int bi_istput(struct buffer *b, const struct ist ist)
return r.len < b->size ? 0 : -1; return r.len < b->size ? 0 : -1;
p = b_tail(b); p = b_tail(b);
b->i += r.len; b->len += r.len;
while (r.len--) { while (r.len--) {
*p++ = *r.ptr++; *p++ = *r.ptr++;
if (unlikely(p == end)) if (unlikely(p == end))
@ -472,8 +469,8 @@ static inline int bo_istput(struct buffer *b, const struct ist ist)
return r.len < b->size ? 0 : -1; return r.len < b->size ? 0 : -1;
p = b_tail(b); p = b_tail(b);
b->p = b_peek(b, b->o + r.len); b->len += r.len;
b->o += r.len; b->output += r.len;
while (r.len--) { while (r.len--) {
*p++ = *r.ptr++; *p++ = *r.ptr++;
if (unlikely(p == end)) if (unlikely(p == end))

View File

@ -128,7 +128,7 @@ static inline size_t c_full(const struct channel *c)
/* co_data() : returns the amount of output data in the channel's buffer */ /* co_data() : returns the amount of output data in the channel's buffer */
static inline size_t co_data(const struct channel *c) static inline size_t co_data(const struct channel *c)
{ {
return c->buf->o; return c->buf->output;
} }
/* ci_data() : returns the amount of input data in the channel's buffer */ /* ci_data() : returns the amount of input data in the channel's buffer */
@ -170,11 +170,7 @@ static inline char *c_ptr(const struct channel *c, ssize_t ofs)
*/ */
static inline void c_adv(struct channel *c, size_t adv) static inline void c_adv(struct channel *c, size_t adv)
{ {
struct buffer *b = c->buf; c->buf->output += adv;
b->p = c_ptr(c, adv);
b->i -= adv;
b->o += adv;
} }
/* c_rew() : rewinds the channel's buffer by <adv> bytes, which means that the /* c_rew() : rewinds the channel's buffer by <adv> bytes, which means that the
@ -184,11 +180,7 @@ static inline void c_adv(struct channel *c, size_t adv)
*/ */
static inline void c_rew(struct channel *c, size_t adv) static inline void c_rew(struct channel *c, size_t adv)
{ {
struct buffer *b = c->buf; c->buf->output -= adv;
b->p = c_ptr(c, (int)-adv);
b->i += adv;
b->o -= adv;
} }
/* c_realign_if_empty() : realign the channel's buffer if it's empty */ /* c_realign_if_empty() : realign the channel's buffer if it's empty */
@ -200,7 +192,8 @@ static inline void c_realign_if_empty(struct channel *chn)
/* Sets the amount of output for the channel */ /* Sets the amount of output for the channel */
static inline void co_set_data(struct channel *c, size_t output) static inline void co_set_data(struct channel *c, size_t output)
{ {
c->buf->o = output; c->buf->len += output - c->buf->output;
c->buf->output = output;
} }
@ -750,7 +743,7 @@ static inline void channel_truncate(struct channel *chn)
if (!ci_data(chn)) if (!ci_data(chn))
return; return;
chn->buf->i = 0; chn->buf->len = co_data(chn);
} }
/* This function realigns a possibly wrapping channel buffer so that the input /* This function realigns a possibly wrapping channel buffer so that the input

View File

@ -28,8 +28,8 @@ struct pool_head *pool_head_buffer;
* what channel wants a buffer. They can reliably be exchanged, the split * what channel wants a buffer. They can reliably be exchanged, the split
* between the two is only an optimization. * between the two is only an optimization.
*/ */
struct buffer buf_empty = { .p = buf_empty.data }; struct buffer buf_empty = { };
struct buffer buf_wanted = { .p = buf_wanted.data }; struct buffer buf_wanted = { };
/* list of objects waiting for at least one buffer */ /* list of objects waiting for at least one buffer */
struct list buffer_wq = LIST_HEAD_INIT(buffer_wq); struct list buffer_wq = LIST_HEAD_INIT(buffer_wq);

View File

@ -38,6 +38,7 @@ static struct pool_head *pool_head_comp_state = NULL;
static THREAD_LOCAL struct buffer *tmpbuf = &buf_empty; static THREAD_LOCAL struct buffer *tmpbuf = &buf_empty;
static THREAD_LOCAL struct buffer *zbuf = &buf_empty; static THREAD_LOCAL struct buffer *zbuf = &buf_empty;
static THREAD_LOCAL unsigned int buf_output;
struct comp_state { struct comp_state {
struct comp_ctx *comp_ctx; /* compression context */ struct comp_ctx *comp_ctx; /* compression context */
@ -56,13 +57,14 @@ static int select_compression_response_header(struct comp_state *st,
struct stream *s, struct stream *s,
struct http_msg *msg); struct http_msg *msg);
static int http_compression_buffer_init(struct channel *inc, struct buffer *out); static int http_compression_buffer_init(struct channel *inc, struct buffer *out, unsigned int *out_len);
static int http_compression_buffer_add_data(struct comp_state *st, static int http_compression_buffer_add_data(struct comp_state *st,
struct buffer *in, struct buffer *in,
int in_out,
struct buffer *out, int sz); struct buffer *out, int sz);
static int http_compression_buffer_end(struct comp_state *st, struct stream *s, static int http_compression_buffer_end(struct comp_state *st, struct stream *s,
struct channel *chn, struct buffer **out, struct channel *chn, struct buffer **out,
int end); unsigned int *out_len, int end);
/***********************************************************************/ /***********************************************************************/
static int static int
@ -191,7 +193,7 @@ comp_http_data(struct stream *s, struct filter *filter, struct http_msg *msg)
b_reset(tmpbuf); b_reset(tmpbuf);
c_adv(chn, fwd); c_adv(chn, fwd);
ret = http_compression_buffer_init(chn, zbuf); ret = http_compression_buffer_init(chn, zbuf, &buf_output);
c_rew(chn, fwd); c_rew(chn, fwd);
if (ret < 0) { if (ret < 0) {
msg->chn->flags |= CF_WAKE_WRITE; msg->chn->flags |= CF_WAKE_WRITE;
@ -216,7 +218,7 @@ comp_http_data(struct stream *s, struct filter *filter, struct http_msg *msg)
} }
else { else {
c_adv(chn, *nxt); c_adv(chn, *nxt);
ret = http_compression_buffer_add_data(st, chn->buf, zbuf, len); ret = http_compression_buffer_add_data(st, chn->buf, co_data(chn), zbuf, len);
c_rew(chn, *nxt); c_rew(chn, *nxt);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -242,7 +244,7 @@ comp_http_chunk_trailers(struct stream *s, struct filter *filter,
b_reset(tmpbuf); b_reset(tmpbuf);
c_adv(chn, fwd); c_adv(chn, fwd);
http_compression_buffer_init(chn, zbuf); http_compression_buffer_init(chn, zbuf, &buf_output);
c_rew(chn, fwd); c_rew(chn, fwd);
st->initialized = 1; st->initialized = 1;
} }
@ -296,17 +298,18 @@ comp_http_forward_data(struct stream *s, struct filter *filter,
} }
if (msg->flags & HTTP_MSGF_TE_CHNK) { if (msg->flags & HTTP_MSGF_TE_CHNK) {
ret = http_compression_buffer_add_data(st, tmpbuf, zbuf, tmpbuf->i); ret = http_compression_buffer_add_data(st, tmpbuf, 0,
if (ret != tmpbuf->i) { zbuf, b_data(tmpbuf));
if (ret != b_data(tmpbuf)) {
ha_warning("HTTP compression failed: Must consume %u bytes but only %d bytes consumed\n", ha_warning("HTTP compression failed: Must consume %u bytes but only %d bytes consumed\n",
(unsigned int)tmpbuf->i, ret); (unsigned int)b_data(tmpbuf), ret);
return -1; return -1;
} }
} }
st->consumed = len - st->hdrs_len - st->tlrs_len; st->consumed = len - st->hdrs_len - st->tlrs_len;
c_adv(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len); c_adv(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len);
ret = http_compression_buffer_end(st, s, msg->chn, &zbuf, msg->msg_state >= HTTP_MSG_TRAILERS); ret = http_compression_buffer_end(st, s, msg->chn, &zbuf, &buf_output, msg->msg_state >= HTTP_MSG_TRAILERS);
c_rew(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len); c_rew(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -601,7 +604,7 @@ http_emit_chunk_size(char *end, unsigned int chksz)
* Init HTTP compression * Init HTTP compression
*/ */
static int static int
http_compression_buffer_init(struct channel *inc, struct buffer *out) http_compression_buffer_init(struct channel *inc, struct buffer *out, unsigned int *out_len)
{ {
/* output stream requires at least 10 bytes for the gzip header, plus /* output stream requires at least 10 bytes for the gzip header, plus
* at least 8 bytes for the gzip trailer (crc+len), plus a possible * at least 8 bytes for the gzip trailer (crc+len), plus a possible
@ -616,9 +619,8 @@ http_compression_buffer_init(struct channel *inc, struct buffer *out)
* cancel the operation later, it's cheap. * cancel the operation later, it's cheap.
*/ */
b_reset(out); b_reset(out);
out->o = co_data(inc); *out_len = co_data(inc);
out->p += out->o; out->head += *out_len + 10;
out->i = 10;
return 0; return 0;
} }
@ -627,7 +629,7 @@ http_compression_buffer_init(struct channel *inc, struct buffer *out)
*/ */
static int static int
http_compression_buffer_add_data(struct comp_state *st, struct buffer *in, http_compression_buffer_add_data(struct comp_state *st, struct buffer *in,
struct buffer *out, int sz) int in_out, struct buffer *out, int sz)
{ {
int consumed_data = 0; int consumed_data = 0;
int data_process_len; int data_process_len;
@ -643,15 +645,15 @@ http_compression_buffer_add_data(struct comp_state *st, struct buffer *in,
data_process_len = MIN(b_room(out), sz); data_process_len = MIN(b_room(out), sz);
block1 = data_process_len; block1 = data_process_len;
if (block1 > b_contig_data(in, in->o)) if (block1 > b_contig_data(in, in_out))
block1 = b_contig_data(in, in->o); block1 = b_contig_data(in, in_out);
block2 = data_process_len - block1; block2 = data_process_len - block1;
/* compressors return < 0 upon error or the amount of bytes read */ /* compressors return < 0 upon error or the amount of bytes read */
consumed_data = st->comp_algo->add_data(st->comp_ctx, b_peek(in, in->o), block1, out); consumed_data = st->comp_algo->add_data(st->comp_ctx, b_head(in) + in_out, block1, out);
if (consumed_data != block1 || !block2) if (consumed_data != block1 || !block2)
goto end; goto end;
consumed_data = st->comp_algo->add_data(st->comp_ctx, in->data, block2, out); consumed_data = st->comp_algo->add_data(st->comp_ctx, b_peek(in, 0), block2, out);
if (consumed_data < 0) if (consumed_data < 0)
goto end; goto end;
consumed_data += block1; consumed_data += block1;
@ -667,11 +669,12 @@ http_compression_buffer_add_data(struct comp_state *st, struct buffer *in,
static int static int
http_compression_buffer_end(struct comp_state *st, struct stream *s, http_compression_buffer_end(struct comp_state *st, struct stream *s,
struct channel *chn, struct buffer **out, struct channel *chn, struct buffer **out,
int end) unsigned int *buf_out, int end)
{ {
struct buffer *ib = chn->buf, *ob = *out; struct buffer *ob = *out;
char *tail; char *tail;
int to_forward, left; int to_forward, left;
unsigned int tmp_out;
#if defined(USE_SLZ) || defined(USE_ZLIB) #if defined(USE_SLZ) || defined(USE_ZLIB)
int ret; int ret;
@ -701,35 +704,38 @@ http_compression_buffer_end(struct comp_state *st, struct stream *s,
* +---------+---+------------+-----------+ * +---------+---+------------+-----------+
* data p size * data p size
* *
* <out> is the room reserved to copy ib->o. It starts at ob->data and * <out> is the room reserved to copy the channel output. It starts at
* has not yet been filled. <c> is the room reserved to write the chunk * ob->data and has not yet been filled. <c> is the room reserved to
* size (10 bytes). <comp_in> is the compressed equivalent of the data * write the chunk size (10 bytes). <comp_in> is the compressed
* part of ib->i. <empty> is the amount of empty bytes at the end of * equivalent of the data part of ib->len. <empty> is the amount of
* the buffer, into which we may have to copy the remaining bytes from * empty bytes at the end of the buffer, into which we may have to
* ib->i after the data (chunk size, trailers, ...). * copy the remaining bytes from ib->len after the data
* (chunk size, trailers, ...).
*/ */
/* Write real size at the begining of the chunk, no need of wrapping. /* Write real size at the begining of the chunk, no need of wrapping.
* We write the chunk using a dynamic length and adjust ob->p and ob->i * We write the chunk using a dynamic length and adjust ob->p and ob->i
* accordingly afterwards. That will move <out> away from <data>. * accordingly afterwards. That will move <out> away from <data>.
*/ */
left = 10 - http_emit_chunk_size(ob->p + 10, ob->i - 10); left = http_emit_chunk_size(b_head(ob), b_data(ob));
ob->p += left; b_add(ob, left);
ob->i -= left; ob->head -= *buf_out + (left);
/* Copy previous data from chn into ob */
if (co_data(chn) > 0) {
left = b_contig_data(chn->buf, 0);
if (left > *buf_out)
left = *buf_out;
/* Copy previous data from ib->o into ob->o */ memcpy(b_head(ob), co_head(chn), left);
if (ib->o > 0) { b_add(ob, left);
left = b_contig_data(ib, 0); if (co_data(chn) - left) {/* second part of the buffer */
if (left > ib->o) memcpy(b_head(ob) + left, b_orig(chn->buf), co_data(chn) - left);
left = ib->o; b_add(ob, co_data(chn) - left);
}
memcpy(ob->p - ob->o, b_head(ib), left);
if (ib->o - left) /* second part of the buffer */
memcpy(ob->p - ob->o + left, ib->data, ib->o - left);
} }
/* chunked encoding requires CRLF after data */ /* chunked encoding requires CRLF after data */
tail = ob->p + ob->i; tail = b_tail(ob);
*tail++ = '\r'; *tail++ = '\r';
*tail++ = '\n'; *tail++ = '\n';
@ -751,8 +757,8 @@ http_compression_buffer_end(struct comp_state *st, struct stream *s,
} }
} }
ob->i = tail - ob->p; b_add(ob, tail - b_tail(ob));
to_forward = ob->i; to_forward = b_data(ob) - *buf_out;
/* update input rate */ /* update input rate */
if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) { if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
@ -766,19 +772,22 @@ http_compression_buffer_end(struct comp_state *st, struct stream *s,
/* copy the remaining data in the tmp buffer. */ /* copy the remaining data in the tmp buffer. */
c_adv(chn, st->consumed); c_adv(chn, st->consumed);
if (ib->i > 0) { if (b_data(chn->buf) - co_data(chn) > 0) {
left = ci_contig_data(chn); left = ci_contig_data(chn);
memcpy(ob->p + ob->i, ci_head(chn), left); memcpy(b_tail(ob), ci_head(chn), left);
b_add(ob, left); b_add(ob, left);
if (ib->i - left) { if (b_data(chn->buf) - (co_data(chn) + left)) {
memcpy(ob->p + ob->i, ib->data, ib->i - left); memcpy(b_tail(ob), b_orig(chn->buf), b_data(chn->buf) - left);
b_add(ob, ib->i - left); b_add(ob, b_data(chn->buf) - left);
} }
} }
/* swap the buffers */ /* swap the buffers */
*out = chn->buf;
chn->buf = ob; chn->buf = ob;
*out = ib; tmp_out = chn->buf->output;
chn->buf->output = *buf_out;
*buf_out = tmp_out;
if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) { if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {