BUG/MEDIUM: compression/htx: Fix the adding of the last data block

The function htx_add_data_before() is buggy and cannot work. It first add a data
block and then move it before another one, passed in argument. The problem
happens when a defragmentation is done to add the new block. In this case, the
reference is no longer valid, because the blocks are rearranged. So, instead of
moving the new block before the reference, it is moved at the head of the HTX
message.

So this function has been removed. It was only used by the compression filter to
add a last data block before a TLR, EOT or EOM block. Now, the new function
htx_add_last_data() is used. It adds a last data block, after all others and
before any TLR, EOT or EOM block. Then, the next bock is get. It is the first
non-data block after data in the HTX message. The compression loop continues
with it.

This patch must be backported to 1.9.
This commit is contained in:
Christopher Faulet 2019-06-11 10:38:38 +02:00
parent bda8397fba
commit 86bc8df955
3 changed files with 27 additions and 17 deletions

View File

@ -189,7 +189,7 @@ struct htx_blk *htx_add_all_trailers(struct htx *htx, const struct http_hdr *hdr
struct htx_blk *htx_add_endof(struct htx *htx, enum htx_blk_type type);
struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data);
size_t htx_add_data(struct htx *htx, const struct ist data);
struct htx_blk *htx_add_data_before(struct htx *htx, const struct htx_blk *ref, const struct ist data);
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data);
int htx_reqline_to_h1(const struct htx_sl *sl, struct buffer *chk);
int htx_stline_to_h1(const struct htx_sl *sl, struct buffer *chk);

View File

@ -256,7 +256,10 @@ comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
if (htx_compression_buffer_end(st, &trash, 1) < 0)
goto error;
if (b_data(&trash)) {
blk = htx_add_data_before(htx, blk, ist2(b_head(&trash), b_data(&trash)));
struct htx_blk *last = htx_add_last_data(htx, ist2(b_head(&trash), b_data(&trash)));
if (!last)
goto error;
blk = htx_get_next_blk(htx, last);
if (!blk)
goto error;
to_forward += b_data(&trash);

View File

@ -868,22 +868,29 @@ size_t htx_add_data(struct htx *htx, const struct ist data)
return len;
}
struct htx_blk *htx_add_data_before(struct htx *htx, const struct htx_blk *ref,
const struct ist data)
{
struct htx_blk *blk;
int32_t prev;
/* FIXME: check data.len (< 256MB) */
blk = htx_add_blk(htx, HTX_BLK_DATA, data.len);
/* Adds an HTX block of type DATA in <htx> just after all other DATA
* blocks. Because it relies on htx_add_data_atonce(), It may be happened to a
* DATA block if possible. But, if the function succeeds, it will be the last
* DATA block in all cases. If an error occurred, NULL is returned. Otherwise,
* on success, the updated block (or the new one) is returned.
*/
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data)
{
struct htx_blk *blk, *pblk;
blk = htx_add_data_atonce(htx, data);
if (!blk)
return NULL;
blk->info += data.len;
memcpy(htx_get_blk_ptr(htx, blk), data.ptr, data.len);
for (pblk = htx_get_prev_blk(htx, blk); pblk; pblk = htx_get_prev_blk(htx, pblk)) {
int32_t cur, prev;
for (prev = htx_get_prev(htx, htx->tail); prev != -1; prev = htx_get_prev(htx, prev)) {
struct htx_blk *pblk = htx_get_blk(htx, prev);
if (htx_get_blk_type(pblk) <= HTX_BLK_DATA)
break;
cur = htx_get_blk_pos(htx, blk);
prev = htx_get_blk_pos(htx, pblk);
/* Swap .addr and .info fields */
blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
@ -891,11 +898,11 @@ struct htx_blk *htx_add_data_before(struct htx *htx, const struct htx_blk *ref,
if (blk->addr == pblk->addr)
blk->addr += htx_get_blksz(pblk);
htx->front = prev;
if (pblk == ref)
break;
blk = pblk;
if (htx->front == cur)
htx->front = prev;
else if (htx->front == prev)
htx->front = cur;
}
if (htx_get_blk_pos(htx, blk) != htx->front)