--- /dev/null
+varnishtest "Compression filter: incompressible contents in large buffers"
+
+# The compressed output replaces the input in the HTX buffer, and is built in
+# a trash chunk. With large buffers, a DATA block may be larger than the
+# regular trash, so the chunk must be as large as the buffer, otherwise the
+# deflater's output exceeds it on incompressible data (input + 5 bytes per
+# stored block + the gzip header). Here the response is waited for into a
+# large buffer, so that its body forms a single DATA block of up to 200 kB,
+# well beyond the 16 kB of the regular buffers. The client decompresses it,
+# which checks the gzip trailer's CRC and length, and compares the length to
+# the one announced by the service.
+feature cmd "$HAPROXY_PROGRAM -cc '(feature(ZLIB) || feature(SLZ)) && feature(LUA)'"
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ tune.bufsize 16384
+ tune.bufsize.large 262144
+ tune.lua.bool-sample-conversion normal
+ lua-load ${testdir}/output_margin.lua
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe-gzip
+ bind "fd@${fe_gzip}"
+ compression algo gzip
+ compression type text/plain
+ default_backend be
+
+ backend be
+ http-response wait-for-body time 5s use-large-buffer
+ server gen ${h1_gen_addr}:${h1_gen_port}
+
+ listen gen
+ bind "fd@${gen}"
+ http-request use-service lua.gen
+} -start
+
+client c1 -connect ${h1_fe_gzip_sock} {
+ txreq -url "/?p=random&c=whole&n=20000" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ gunzip
+ expect resp.bodylen == 20000
+
+ txreq -url "/?p=random&c=whole&n=200000" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ gunzip
+ expect resp.bodylen == 200000
+
+ txreq -url "/?p=mixed&c=whole&n=200000" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ gunzip
+ expect resp.bodylen == 200000
+} -run
struct htx *htx = htxbuf(&msg->chn->buf);
struct htx_ret htxret = htx_find_offset(htx, offset);
struct htx_blk *blk, *next;
+ struct buffer *out;
int ret, consumed = 0, to_forward = 0, last = 0, flush;
blk = htxret.blk;
if (!(st->flags & COMP_STATE_PROCESSING))
goto consume;
- if (htx_compression_buffer_init(htx, &trash) < 0) {
+ /* The output replaces the input in the HTX, so the chunk must
+ * be as large as the buffer, which may be a large one.
+ */
+ out = get_best_trash_chunk(&msg->chn->buf, b_size(&msg->chn->buf));
+ if (!out)
+ goto error;
+
+ if (htx_compression_buffer_init(htx, out) < 0) {
msg->chn->flags |= CF_WAKE_WRITE;
goto end;
}
last = 0;
v.len = len;
}
- if (v.len > b_size(&trash)) {
+ if (v.len > b_size(out)) {
last = 0;
- v.len = b_size(&trash);
+ v.len = b_size(out);
}
/* The output only needs to be flushed when no other
*/
flush = last || v.len == len || !next || htx_get_blk_type(next) != HTX_BLK_DATA;
- ret = htx_compression_buffer_add_data(st, v.ptr, v.len, &trash, dir);
- if (ret < 0 || (flush && htx_compression_buffer_end(st, &trash, last, dir) < 0))
+ ret = htx_compression_buffer_add_data(st, v.ptr, v.len, out, dir);
+ if (ret < 0 || (flush && htx_compression_buffer_end(st, out, last, dir) < 0))
goto error;
BUG_ON(v.len != ret);
- if (ret == sz && !b_data(&trash))
+ if (ret == sz && !b_data(out))
next = htx_remove_blk(htx, blk);
else {
- blk = htx_replace_blk_value(htx, blk, v, ist2(b_head(&trash), b_data(&trash)));
+ blk = htx_replace_blk_value(htx, blk, v, ist2(b_head(out), b_data(out)));
if (!blk)
goto error;
next = htx_get_next_blk(htx, blk);
len -= ret;
consumed += ret;
- to_forward += b_data(&trash);
+ to_forward += b_data(out);
if (last) {
st->flags &= ~COMP_STATE_PROCESSING;
case HTX_BLK_TLR:
case HTX_BLK_EOT:
- if (htx_compression_buffer_end(st, &trash, 1, dir) < 0)
+ if (htx_compression_buffer_end(st, out, 1, dir) < 0)
goto error;
- if (b_data(&trash)) {
- struct htx_blk *last = htx_add_last_data(htx, ist2(b_head(&trash), b_data(&trash)));
+ if (b_data(out)) {
+ struct htx_blk *last = htx_add_last_data(htx, ist2(b_head(out), b_data(out)));
if (!last)
goto error;
blk = htx_get_next_blk(htx, last);
if (!blk)
goto error;
next = htx_get_next_blk(htx, blk);
- to_forward += b_data(&trash);
+ to_forward += b_data(out);
}
st->flags &= ~COMP_STATE_PROCESSING;
__fallthrough;