* decompression/http.vtc: response and request decompression in HTTP.
* decompression/tcp.vtc: raw TCP streams decompression.
* decompression/http_recompression.vtc: decompression then recompression.
--- /dev/null
+varnishtest "HTTP decompression, requests and responses"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(SLZ)'"
+
+feature ignore_unknown_macro
+
+# response decompression in "auto" mode
+server s1 {
+ # 1. no Content-Encoding at all: nothing must be touched, the framing
+ # must be left as-is
+ rxreq
+ expect req.url == "/plain"
+ # supported algorithms are advertised to the server
+ expect req.http.accept-encoding == "gzip"
+ txresp -body "not compressed"
+
+ # 2. gzip body: decompressed, and switched to the chunked encoding
+ rxreq
+ expect req.url == "/gzip"
+ txresp -gzipbody "decompressed by haproxy"
+
+ # 3. unsupported coding: "auto" is best-effort, the body is forwarded
+ # as-is
+ rxreq
+ expect req.url == "/unsupported"
+ txresp -hdr "Content-Encoding: br" -body "still compressed"
+
+ # 4. 304 has no body: everything, including Content-Encoding, must be
+ # preserved as it describes the stored representation
+ rxreq
+ expect req.url == "/304"
+ txresp -status 304 -nolen -hdr "Content-Encoding: gzip" -hdr "ETag: \"strong\""
+
+ # 5. 204 has no body either
+ rxreq
+ expect req.url == "/204"
+ txresp -status 204 -nolen -hdr "Content-Encoding: gzip"
+
+ # 6. a response to HEAD has no body, the announced Content-Length must
+ # not be replaced by a chunked encoding
+ rxreq
+ expect req.url == "/head"
+ expect req.method == "HEAD"
+ txresp -nolen -hdr "Content-Encoding: gzip" -hdr "Content-Length: 100"
+
+ # 7. in "auto" mode the client's own Accept-Encoding is preserved
+ rxreq
+ expect req.url == "/keep-ae"
+ expect req.http.accept-encoding == "br"
+ txresp -gzipbody "decompressed by haproxy"
+
+ # 8. a large body, decompressed over several passes
+ rxreq
+ expect req.url == "/big"
+ txresp -gziplen 50000
+} -start
+
+# response decompression in "always" mode, with two algorithms
+server s2 {
+ rxreq
+ expect req.url == "/gzip"
+ # "always" mode drops the client's Accept-Encoding and only advertises
+ # what we can decompress
+ expect req.http.accept-encoding != "br"
+ txresp -gzipbody "gzip decompressed"
+
+ rxreq
+ expect req.url == "/deflate"
+ txresp -nolen -hdr "Content-Encoding: deflate" -hdr "Content-Length: 21"
+ # zlib("deflated body")
+ sendhex "78DA4B494DCB492C494D5148CA4FA9040023510508"
+
+ accept
+
+ # an unsupported coding is a hard error in "always" mode
+ rxreq
+ expect req.url == "/unsupported"
+ txresp -hdr "Content-Encoding: br" -body "still compressed"
+} -start
+
+# response decompression of a raw deflate body
+server s3 {
+ rxreq
+ expect req.url == "/raw-deflate"
+ txresp -nolen -hdr "Content-Encoding: deflate" -hdr "Content-Length: 19"
+ # rawdeflate("raw deflated body")
+ sendhex "2B4A2C5748494DCB492C494D5148CA4FA90400"
+} -start
+
+# request decompression in "always" mode
+server s4 {
+ # 1. gzip request body: decompressed and rechunked
+ rxreq
+ expect req.url == "/gzip"
+ expect req.http.content-encoding == "<undef>"
+ expect req.http.content-length == "<undef>"
+ expect req.http.transfer-encoding == "chunked"
+ expect req.body == "decompressed request"
+ txresp -body "ok"
+
+ # 2. a request that is not compressed at all is not an error, there is
+ # simply nothing to decompress
+ rxreq
+ expect req.url == "/plain"
+ expect req.http.transfer-encoding == "<undef>"
+ expect req.body == "plain request"
+ txresp -body "ok"
+
+ # 3. a large gzip request body
+ rxreq
+ expect req.url == "/big"
+ expect req.bodylen == 50000
+ txresp -body "ok"
+} -start
+
+haproxy h1 -conf {
+ global
+ .if feature(THREAD)
+ thread-groups 1
+ .endif
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen res-auto
+ mode http
+ bind "fd@${res_auto}"
+ decompression algo-res gzip
+ decompression mode-res auto
+ filter decomp-res
+ server s1 ${s1_addr}:${s1_port}
+
+ listen res-always
+ mode http
+ bind "fd@${res_always}"
+ decompression algo-res gzip deflate
+ decompression mode-res always
+ filter decomp-res
+ server s2 ${s2_addr}:${s2_port}
+
+ listen res-raw-deflate
+ mode http
+ bind "fd@${res_raw_deflate}"
+ # "raw-deflate" is advertised as "deflate" in Accept-Encoding, like
+ # for the compression
+ decompression algo-res raw-deflate
+ decompression mode-res always
+ filter decomp-res
+ server s3 ${s3_addr}:${s3_port}
+
+ listen req-always
+ mode http
+ bind "fd@${req_always}"
+ decompression algo-req gzip
+ decompression mode-req always
+ filter decomp-req
+ server s4 ${s4_addr}:${s4_port}
+} -start
+
+client c1 -connect ${h1_res_auto_sock} {
+ # 1. uncompressed response, untouched
+ txreq -url "/plain"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.content-length == "14"
+ expect resp.body == "not compressed"
+
+ # 2. gzip response, decompressed. The body size is unknown when the
+ # headers are processed, so the message is switched to chunked and
+ # the Content-Length is dropped.
+ txreq -url "/gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.content-length == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.body == "decompressed by haproxy"
+
+ # 3. unsupported coding, forwarded as-is in "auto" mode
+ txreq -url "/unsupported"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "br"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.body == "still compressed"
+
+ # 4. 304, left completely untouched
+ txreq -url "/304"
+ rxresp
+ expect resp.status == 304
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.etag == "\"strong\""
+
+ # 5. 204, left completely untouched
+ txreq -url "/204"
+ rxresp
+ expect resp.status == 204
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "<undef>"
+
+ # 6. response to HEAD, left completely untouched
+ txreq -req "HEAD" -url "/head"
+ rxresp -no_obj
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.content-length == "100"
+ expect resp.http.transfer-encoding == "<undef>"
+
+ # 7. the client's Accept-Encoding is preserved in "auto" mode
+ txreq -url "/keep-ae" -hdr "Accept-Encoding: br"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "decompressed by haproxy"
+
+ # 8. large body
+ txreq -url "/big"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.bodylen == 50000
+} -run
+
+client c2 -connect ${h1_res_always_sock} {
+ # 1. gzip
+ txreq -url "/gzip" -hdr "Accept-Encoding: br"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.body == "gzip decompressed"
+
+ # 2. zlib deflate
+ txreq -url "/deflate"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.content-length == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.body == "deflated body"
+} -run
+
+# an unsupported coding cannot be decompressed while "always" was requested:
+# the stream is aborted. Note the headers were already received at this point,
+# so for now nothing at all is reported to the client, the connection is just
+# closed.
+client c3 -connect ${h1_res_always_sock} {
+ txreq -url "/unsupported"
+ expect_close
+} -run
+
+client c4 -connect ${h1_res_raw_deflate_sock} {
+ txreq -url "/raw-deflate"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.body == "raw deflated body"
+} -run
+
+client c5 -connect ${h1_req_always_sock} {
+ # 1. gzip request body
+ txreq -url "/gzip" -gzipbody "decompressed request"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "ok"
+
+ # 2. uncompressed request body
+ txreq -url "/plain" -body "plain request"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "ok"
+
+ # 3. large gzip request body
+ txreq -url "/big" -gziplen 50000
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "ok"
+} -run
+
+# an unsupported coding on the request while "always" was requested: the
+# stream is aborted before the request is sent to the server
+client c6 -connect ${h1_req_always_sock} {
+ txreq -url "/unsupported" -hdr "Content-Encoding: br" -body "compressed"
+ rxresp
+ expect resp.status == 400
+} -run
--- /dev/null
+varnishtest "HTTP decompression chained with the compression"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(SLZ)'"
+
+feature ignore_unknown_macro
+
+# The proxies below decompress the messages they receive and compress them
+# again before forwarding them. This is the typical setup where haproxy has to
+# inspect or transform a compressed payload, or where the two endpoints don't
+# agree on the same coding.
+
+# same coding on both sides
+server s1 {
+ # 1. the request was gzipped by the client, decompressed by the proxy,
+ # then gzipped again. The server must see a brand new gzip stream
+ # over a chunked message.
+ rxreq
+ expect req.url == "/both-ways"
+ expect req.http.content-encoding == "gzip"
+ expect req.http.content-length == "<undef>"
+ expect req.http.transfer-encoding == "chunked"
+ gunzip
+ expect req.body == "request from the client"
+
+ # the response decompression advertises what we support, so the server
+ # is free to compress its response
+ expect req.http.accept-encoding == "gzip"
+ txresp -hdr "Content-Type: text/plain" -gzipbody "response from the server"
+
+ # 2. nothing to decompress in either direction: the decompression must
+ # not get in the way of the compression
+ rxreq
+ expect req.url == "/nothing-to-decompress"
+ expect req.http.content-encoding == "gzip"
+ gunzip
+ expect req.body == "uncompressed request"
+ txresp -hdr "Content-Type: text/plain" -body "uncompressed response"
+
+ # 3. the client accepts no coding at all. The response decompression
+ # still advertises gzip to the server on its own behalf, but that
+ # must not be mistaken for the client's own "Accept-Encoding": the
+ # response has to be delivered decompressed.
+ rxreq
+ expect req.url == "/no-accept-encoding"
+ expect req.http.accept-encoding == "gzip"
+ txresp -hdr "Content-Type: text/plain" -gzipbody "third response"
+} -start
+
+# the client only knows gzip, the server only knows deflate
+server s2 {
+ rxreq
+ expect req.url == "/transcode"
+ # the request was gzipped by the client and is deflated for the server
+ expect req.http.content-encoding == "deflate"
+ expect req.http.content-length == "<undef>"
+ expect req.http.transfer-encoding == "chunked"
+ # only what we are able to decompress on the response path is
+ # advertised, the client's own "gzip" must not leak here
+ expect req.http.accept-encoding == "deflate"
+
+ txresp -nolen \
+ -hdr "Content-Type: text/plain" \
+ -hdr "Content-Encoding: deflate" \
+ -hdr "Content-Length: 30"
+ # zlib("deflated by the server")
+ sendhex "78DA4B494DCB492C494D5148AA5428C94855284E2D2A4B2D02005EB4084D"
+} -start
+
+# same as s2, but with the compression offloading enabled
+server s3 {
+ rxreq
+ expect req.url == "/offload"
+ expect req.http.content-encoding == "deflate"
+ expect req.http.transfer-encoding == "chunked"
+ # "compression offload" removed the client's own codings, so only the
+ # one advertised by the response decompression is left. Note the
+ # decompression is in "auto" mode here, it does not remove anything by
+ # itself.
+ expect req.http.accept-encoding == "deflate"
+
+ txresp -nolen \
+ -hdr "Content-Type: text/plain" \
+ -hdr "Content-Encoding: deflate" \
+ -hdr "ETag: \"strong\"" \
+ -hdr "Content-Length: 30"
+ # zlib("offloaded and deflated")
+ sendhex "78DACB4F4BCBC94F4C494D5148CC4B5148494DCB492C494D0100603B0851"
+} -start
+
+haproxy h1 -conf {
+ global
+ .if feature(THREAD)
+ thread-groups 1
+ .endif
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen recomp
+ mode http
+ bind "fd@${recomp}"
+
+ decompression algo-req gzip
+ decompression mode-req always
+ decompression algo-res gzip
+ decompression mode-res always
+
+ compression direction both
+ compression algo-req gzip
+ compression type-req text/plain
+ compression algo-res gzip
+ compression type-res text/plain
+
+ server s1 ${s1_addr}:${s1_port}
+
+ listen recomp-transcode
+ mode http
+ bind "fd@${recomp_transcode}"
+
+ decompression algo-req gzip
+ decompression mode-req always
+ decompression algo-res deflate
+ decompression mode-res always
+
+ compression direction both
+ compression algo-req deflate
+ compression type-req text/plain
+ compression algo-res gzip
+ compression type-res text/plain
+
+ server s2 ${s2_addr}:${s2_port}
+
+ listen recomp-offload
+ mode http
+ bind "fd@${recomp_offload}"
+
+ decompression algo-req gzip
+ decompression mode-req always
+ decompression algo-res deflate
+ decompression mode-res auto
+
+ compression direction both
+ compression algo-req deflate
+ compression type-req text/plain
+ compression algo-res gzip
+ compression type-res text/plain
+ # hide the client's codings from the server. The codings advertised by
+ # the response decompression must survive it, otherwise the server
+ # would stop compressing and there would be nothing left to decompress.
+ compression offload
+
+ server s3 ${s3_addr}:${s3_port}
+} -start
+
+client c1 -connect ${h1_recomp_sock} {
+ # 1. compressed in both directions
+ txreq -url "/both-ways" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Content-Type: text/plain" \
+ -gzipbody "request from the client"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "chunked"
+ gunzip
+ expect resp.body == "response from the server"
+
+ # 2. uncompressed in both directions: only the compression must kick in
+ txreq -url "/nothing-to-decompress" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Content-Type: text/plain" \
+ -body "uncompressed request"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "chunked"
+ gunzip
+ expect resp.body == "uncompressed response"
+
+ # 3. no "Accept-Encoding" from the client: the response is only
+ # decompressed, it must not be compressed again
+ txreq -url "/no-accept-encoding" \
+ -hdr "Content-Type: text/plain" \
+ -gzipbody "third request"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.body == "third response"
+} -run
+
+# gzip on the client side, deflate on the server side
+client c2 -connect ${h1_recomp_transcode_sock} {
+ txreq -url "/transcode" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Content-Type: text/plain" \
+ -gzipbody "gzipped by the client"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.content-length == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ gunzip
+ expect resp.body == "deflated by the server"
+} -run
+
+# same as c2, but the compression offloading hides the client's codings from
+# the server
+client c3 -connect ${h1_recomp_offload_sock} {
+ txreq -url "/offload" \
+ -hdr "Accept-Encoding: gzip, br" \
+ -hdr "Content-Type: text/plain" \
+ -gzipbody "gzipped by the client"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.http.vary == "Accept-Encoding"
+ # both filters alter the representation but the ETag must only be
+ # weakened once
+ expect resp.http.etag == "W/\"strong\""
+ gunzip
+ expect resp.body == "offloaded and deflated"
+} -run
--- /dev/null
+varnishtest "Decompression of raw TCP streams"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(SLZ)'"
+
+feature ignore_unknown_macro
+
+# HAProxy is in TCP mode here, so it does not parse anything: the payloads
+# below are opaque byte streams for it. The clients and the servers only
+# agree on HTTP because it gives us a convenient and readable way to assert
+# on the *decompressed* bytes.
+#
+# Compressed payloads are injected as hex with "sendhex". They were produced
+# with the following helper, where <wbits> is 31 for gzip, 15 for zlib and -15
+# for raw deflate. The 4 gzip mtime bytes are zeroed so that the payloads stay
+# stable:
+#
+# python3 -c 'import sys, zlib; \
+# d = sys.stdin.buffer.read(); \
+# c = zlib.compressobj(9, zlib.DEFLATED, <wbits>); \
+# o = c.compress(d) + c.flush(); \
+# o = (o[:4] + bytes(4) + o[8:]) if o[:2] == b"\x1f\x8b" else o; \
+# print(o.hex().upper())'
+
+# gzip in both directions
+server s1 {
+ rxreq
+ expect req.method == "GET"
+ expect req.url == "/tcp-gzip"
+ expect req.http.host == "decomp"
+
+ # gzip("HTTP/1.1 200 OK\r\nContent-Length: 17\r\n\r\ntcp gzip response")
+ sendhex "1F8B0800000000000203F3080909D037D4335430323050F0F7E6E572CECF2B49"
+ sendhex "CD2BD1F549CD4B2FC9B0523034E7E5E2E52A492E5048AFCA2C50284A2D2EC8CF"
+ sendhex "2B4E0500771922E238000000"
+} -start
+
+# uslz-auto on the request only, exercised with a zlib stream then with a
+# raw deflate stream
+server s2 {
+ rxreq
+ expect req.url == "/tcp-auto-zlib"
+ txresp -body "zlib request seen"
+
+ accept
+
+ rxreq
+ expect req.url == "/tcp-auto-raw"
+ txresp -body "raw deflate request seen"
+} -start
+
+# raw deflate on the response only
+server s3 {
+ rxreq
+ expect req.url == "/tcp-raw-deflate"
+
+ # rawdeflate("HTTP/1.1 200 OK\r\nContent-Length: 25\r\n\r\ntcp raw-deflate response")
+ sendhex "F3080909D037D4335430323050F0F7E6E572CECF2B49CD2BD1F549CD4B2FC9B0"
+ sendhex "523032E1E5E2E52A492E50284A2CD74D494DCB492C4955284A2D2EC8CF2B4E05"
+ sendhex "00"
+} -start
+
+# decompression configured but disabled by the "none" mode
+server s4 {
+ rxreq
+ expect req.url == "/tcp-none"
+ expect req.body == "not compressed"
+ txresp -body "not compressed either"
+} -start
+
+# large payloads, to force the decompressed output to be drained over
+# several passes
+server s5 {
+ rxreq
+ expect req.url == "/tcp-big"
+ expect req.bodylen == 50000
+
+ # gzip("HTTP/1.1 200 OK\r\nContent-Length: 50000\r\n\r\n" + 50000 * "y")
+ sendhex "1F8B0800000000000203EDC1B10984300000C03E901DB2806F146C6CBF1114B4"
+ sendhex "7005D12A36697EFB770EB9BB69DFB7B6FB74A9CF39AD730CDFBBD4A3D46639CA"
+ sendhex "59AF310DF911430C3F0000000000000000000000000000000000000000000000"
+ sendhex "000000000000000000000000000000000000000000000000005EE50F669DE619"
+ sendhex "7AC30000"
+} -start
+
+# "always" mode on a stream that is not compressed at all: the stream must be
+# aborted, so the server must never see any data
+server s6 {
+ expect_close
+} -start
+
+haproxy h1 -conf {
+ global
+ .if feature(THREAD)
+ thread-groups 1
+ .endif
+
+ defaults
+ mode tcp
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen tcp-gzip
+ mode tcp
+ bind "fd@${tcp_gzip}"
+ decompression algo-req gzip
+ decompression mode-req always
+ decompression algo-res gzip
+ decompression mode-res always
+ filter decomp-req
+ filter decomp-res
+ server s1 ${s1_addr}:${s1_port}
+
+ listen tcp-auto
+ mode tcp
+ bind "fd@${tcp_auto}"
+ # "uslz-auto" detects the zlib/gzip wrapper and falls back to raw
+ # deflate otherwise. It is only allowed on TCP proxies.
+ decompression algo-req uslz-auto
+ decompression mode-req always
+ filter decomp-req
+ server s2 ${s2_addr}:${s2_port}
+
+ listen tcp-raw-deflate
+ mode tcp
+ bind "fd@${tcp_raw_deflate}"
+ decompression algo-res raw-deflate
+ decompression mode-res always
+ filter decomp-res
+ server s3 ${s3_addr}:${s3_port}
+
+ listen tcp-none
+ mode tcp
+ bind "fd@${tcp_none}"
+ # algorithms are configured but the "none" mode disables the
+ # decompression: both directions must pass through untouched
+ decompression algo-req gzip
+ decompression mode-req none
+ decompression algo-res gzip
+ decompression mode-res none
+ filter decomp-req
+ filter decomp-res
+ server s4 ${s4_addr}:${s4_port}
+
+ listen tcp-big
+ mode tcp
+ bind "fd@${tcp_big}"
+ decompression algo-req gzip
+ decompression mode-req always
+ decompression algo-res gzip
+ decompression mode-res always
+ filter decomp-req
+ filter decomp-res
+ server s5 ${s5_addr}:${s5_port}
+
+ listen tcp-fail
+ mode tcp
+ bind "fd@${tcp_fail}"
+ decompression algo-req gzip
+ decompression mode-req always
+ filter decomp-req
+ server s6 ${s6_addr}:${s6_port}
+} -start
+
+# 1. gzip request and gzip response on the same connection. Both streams are
+# independent, each one is decompressed on its own way.
+client c1 -connect ${h1_tcp_gzip_sock} {
+ # gzip("GET /tcp-gzip HTTP/1.1\r\nHost: decomp\r\n\r\n")
+ sendhex "1F8B080000000000020373770D51D02F492ED04DAFCA2C50F0080909D037D433"
+ sendhex "E4E5F2C82F2EB15248494DCECF2DE0E5E2E50200B410B0A128000000"
+
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "tcp gzip response"
+} -run
+
+# 2. zlib stream, detected by "uslz-auto"
+client c2 -connect ${h1_tcp_auto_sock} {
+ # zlib("GET /tcp-auto-zlib HTTP/1.1\r\nHost: decomp\r\n\r\n")
+ sendhex "78DA73770D51D02F492ED04D2C2DC9D7ADCAC94C52F0080909D037D433E4E5F2"
+ sendhex "C82F2EB15248494DCECF2DE0E5E2E502003FB40D0F"
+
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "zlib request seen"
+} -run
+
+# 3. raw deflate stream, "uslz-auto" falls back to it since there is no
+# recognizable wrapper
+client c3 -connect ${h1_tcp_auto_sock} {
+ # rawdeflate("GET /tcp-auto-raw HTTP/1.1\r\nHost: decomp\r\n\r\n")
+ sendhex "73770D51D02F492ED04D2C2DC9D72D4A2C57F0080909D037D433E4E5F2C82F2E"
+ sendhex "B15248494DCECF2DE0E5E2E50200"
+
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "raw deflate request seen"
+} -run
+
+# 4. raw deflate on the response path only, the request is sent as-is
+client c4 -connect ${h1_tcp_raw_deflate_sock} {
+ txreq -url "/tcp-raw-deflate" -hdr "Host: decomp" -nohost
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "tcp raw-deflate response"
+} -run
+
+# 5. "none" mode, nothing must be altered in either direction
+client c5 -connect ${h1_tcp_none_sock} {
+ txreq -url "/tcp-none" -body "not compressed"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "not compressed either"
+} -run
+
+# 6. large gzip payloads in both directions
+client c6 -connect ${h1_tcp_big_sock} {
+ # gzip("GET /tcp-big HTTP/1.1\r\nHost: decomp\r\nContent-Length: 50000\r\n\r\n" + 50000 * "x")
+ sendhex "1F8B0800000000000203EDC1310E82300000C0BD49FFD00F2032B8B01A238383"
+ sendhex "435F201274B025B183CFD77798BB3B9F72EADBBC75B7E79AA69CAFFDB01B6298"
+ sendhex "EABB8DE9BECCF5B5C570ACA52DA57597A5ACED31A6C3FE2786183E0000000000"
+ sendhex "0000000000000000000000000000000000000000000000000000000000000000"
+ sendhex "0000000000000000000000FC952FA3C1D4B28EC30000"
+
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 50000
+} -run
+
+# 7. uncompressed stream while "always" is requested: the stream is rejected
+client c7 -connect ${h1_tcp_fail_sock} {
+ txreq -url "/tcp-fail"
+ expect_close
+} -run