From: Christopher Faulet Date: Wed, 23 Sep 2026 08:41:00 +0000 (+0200) Subject: REG-TESTS: http-messaging: Add a script to test interim responses for H1/H2 X-Git-Tag: v3.5-dev8~131 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=27219b2c0eccbae9f815bd62aa2e47d61708c08b;p=haproxy.git REG-TESTS: http-messaging: Add a script to test interim responses for H1/H2 http_1xx_responses.vtc script was added to tests handling of 1xx interim responses in H1 and H2. --- diff --git a/reg-tests/http-messaging/http_1xx_responses.vtc b/reg-tests/http-messaging/http_1xx_responses.vtc new file mode 100644 index 000000000..f7cefd160 --- /dev/null +++ b/reg-tests/http-messaging/http_1xx_responses.vtc @@ -0,0 +1,396 @@ +# This reg-test checks the handling of 1xx interim responses (mainly 100 +# Continue and 103 Early Hints). These responses must be forwarded to the +# client and must not end the HTTP transaction. The final response must still +# be properly parsed and forwarded, with its payload, if any. +# +# Interim responses are tested from H1 and H2 servers, to H1 and H2 clients, +# when they are emitted by the server or by HAProxy itself (via +# "http-request early-hint" or when handling "Expect: 100-continue" header on +# its own). + +varnishtest "Test 1xx interim responses (100 Continue and 103 Early Hints)" +feature ignore_unknown_macro + +# H1 server sending 103 responses before a final response with a +# content-length payload, then with a chunked payload, then with no payload +server s1 { + rxreq + expect req.url == "/clen" + txresp -status 103 -nolen \ + -hdr "Link: ; rel=preload; as=style" + txresp -status 103 -nolen \ + -hdr "Link: ; rel=preload; as=script" + txresp -status 200 \ + -body "response 1" + + rxreq + expect req.url == "/chunked" + txresp -status 103 -nolen \ + -hdr "Link: ; rel=preload; as=style" + txresp -status 200 -nolen \ + -hdr "Transfer-Encoding: chunked" \ + -body "a\r\nresponse 2\r\n0\r\n\r\n" + + rxreq + expect req.url == "/no-body" + txresp -status 103 -nolen \ + -hdr "Link: ; rel=preload; as=style" + txresp -status 204 -nolen + + rxreq + expect req.url == "/close" + txresp -status 103 -nolen \ + -hdr "Link: ; rel=preload; as=style" + txresp -status 200 -nolen \ + -hdr "Connection: close" \ + -body "response 4" +} -repeat 2 -start + +# H1 server handling "Expect: 100-continue" on its own +server s2 { + rxreqhdrs + expect req.method == "POST" + expect req.http.expect == "100-continue" + expect req.bodylen == "" + txresp -status 100 -nolen + rxreqbody + expect req.bodylen == 10 + expect req.body == "request 1 " + txresp -status 200 \ + -body "response 1" +} -repeat 2 -start + +# H1 server never seeing the "Expect" header, because it was handled by +# HAProxy. The whole request must be received. +server s3 { + rxreq + expect req.method == "POST" + expect req.http.expect == + expect req.bodylen == 10 + expect req.body == "request 2 " + txresp -status 200 \ + -body "response 2" +} -start + +# H1 server replying to a request for which HAProxy emitted early hints +server s4 { + rxreq + expect req.url == "/early-hint" + txresp -status 200 \ + -body "response 3" +} -repeat 2 -start + +# H2 server sending 103 responses before the final response +server s5 { + rxpri + stream 0 { + txsettings + rxsettings + txsettings -ack + } -run + + stream 1 { + rxreq + expect req.http.:path == "/clen" + txresp -status 103 -nostrend \ + -hdr "link" "; rel=preload; as=style" + txresp -status 103 -nostrend \ + -hdr "link" "; rel=preload; as=script" + txresp -status 200 \ + -body "response 1" + } -run + + stream 3 { + rxreq + expect req.http.:path == "/no-body" + txresp -status 103 -nostrend \ + -hdr "link" "; rel=preload; as=style" + txresp -status 204 + } -run +} -start + +# H2 server handling "Expect: 100-continue" on its own +server s6 { + rxpri + stream 0 { + txsettings + rxsettings + txsettings -ack + } -run + + stream 1 { + rxhdrs + expect req.method == "POST" + expect req.http.expect == "100-continue" + txresp -status 100 -nostrend + rxdata -all + expect req.body == "request 1 " + txresp -status 200 \ + -body "response 1" + } -run +} -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}" + + # 103 Early Hints from an H1 server + listen fe1 + bind "fd@${fe1}" + bind "fd@${fe1h2}" proto h2 + server s1 ${s1_addr}:${s1_port} + + # 100 Continue from an H1 server + listen fe2 + bind "fd@${fe2}" + bind "fd@${fe2h2}" proto h2 + server s2 ${s2_addr}:${s2_port} + + # 100 Continue emitted by HAProxy itself + listen fe3 + bind "fd@${fe3}" + http-request wait-for-body time 1s + server s3 ${s3_addr}:${s3_port} + + # 103 Early Hints emitted by HAProxy itself + listen fe4 + bind "fd@${fe4}" + bind "fd@${fe4h2}" proto h2 + http-request early-hint Link "; rel=preload; as=style" + http-request early-hint Link "; rel=preload; as=script" + server s4 ${s4_addr}:${s4_port} + + # 103 Early Hints from an H2 server + listen fe5 + bind "fd@${fe5}" + server s5 ${s5_addr}:${s5_port} proto h2 + + # 100 Continue from an H2 server + listen fe6 + bind "fd@${fe6}" + server s6 ${s6_addr}:${s6_port} proto h2 +} -start + +# H1 client / H1 server : 103 Early Hints +client c1 -connect ${h1_fe1_sock} { + txreq -url "/clen" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + expect resp.http.content-length == + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=script" + rxresp + expect resp.status == 200 + expect resp.http.link == + expect resp.body == "response 1" + + txreq -url "/chunked" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 200 + expect resp.http.transfer-encoding == "chunked" + expect resp.body == "response 2" + + txreq -url "/no-body" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 204 + expect resp.bodylen == 0 + + txreq -url "/close" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 200 + expect resp.body == "response 4" +} -run + +# H2 client / H1 server : 103 Early Hints +client c2 -connect ${h1_fe1h2_sock} { + txpri + stream 0 { + txsettings + rxsettings + txsettings -ack + rxsettings + expect settings.ack == true + } -run + + stream 1 { + txreq -url "/clen" + rxhdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxhdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=script" + rxresp + expect resp.status == 200 + expect resp.http.link == + expect resp.body == "response 1" + } -run + + stream 3 { + txreq -url "/chunked" + rxhdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 200 + expect resp.body == "response 2" + } -run + + stream 5 { + txreq -url "/no-body" + rxhdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 204 + expect resp.bodylen == 0 + } -run + + stream 7 { + txreq -url "/close" + rxhdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 200 + expect resp.body == "response 4" + } -run +} -run + +# H1 client / H1 server : 100 Continue from the server +client c3 -connect ${h1_fe2_sock} { + txreq -method POST -url "/" \ + -hdr "Expect: 100-continue" \ + -body "request 1 " + rxresphdrs + expect resp.status == 100 + expect resp.http.content-length == + rxresp + expect resp.status == 200 + expect resp.body == "response 1" +} -run + +# H2 client / H1 server : 100 Continue from the server +client c4 -connect ${h1_fe2h2_sock} { + txpri + stream 0 { + txsettings + rxsettings + txsettings -ack + rxsettings + expect settings.ack == true + } -run + + stream 1 { + txreq -method POST -scheme "http" -url "/" -nostrend \ + -hdr "content-length" "10" \ + -hdr "expect" "100-continue" + rxhdrs + expect resp.status == 100 + txdata -data "request 1 " + rxresp + expect resp.status == 200 + expect resp.body == "response 1" + } -run +} -run + +# H1 client / H1 server : 100 Continue from HAProxy +client c5 -connect ${h1_fe3_sock} { + txreq -method POST -url "/" \ + -hdr "Expect: 100-continue" \ + -body "request 2 " + rxresphdrs + expect resp.status == 100 + rxresp + expect resp.status == 200 + expect resp.body == "response 2" +} -run + +# H1 client / H1 server : 103 Early Hints from HAProxy +client c6 -connect ${h1_fe4_sock} { + txreq -url "/early-hint" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 200 + expect resp.http.link == + expect resp.body == "response 3" +} -run + +# H2 client / H1 server : 103 Early Hints from HAProxy +client c7 -connect ${h1_fe4h2_sock} { + txpri + stream 0 { + txsettings + rxsettings + txsettings -ack + rxsettings + expect settings.ack == true + } -run + + stream 1 { + txreq -url "/early-hint" + rxhdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 200 + expect resp.http.link == + expect resp.body == "response 3" + } -run +} -run + +# H1 client / H2 server : 103 Early Hints +client c8 -connect ${h1_fe5_sock} { + txreq -url "/clen" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=script" + rxresp + expect resp.status == 200 + expect resp.http.link == + expect resp.body == "response 1" + + txreq -url "/no-body" + rxresphdrs + expect resp.status == 103 + expect resp.http.link == "; rel=preload; as=style" + rxresp + expect resp.status == 204 + expect resp.bodylen == 0 +} -run + +# H1 client / H2 server : 100 Continue from the server +client c9 -connect ${h1_fe6_sock} { + txreq -method POST -url "/" \ + -hdr "Expect: 100-continue" \ + -body "request 1 " + rxresphdrs + expect resp.status == 100 + rxresp + expect resp.status == 200 + expect resp.body == "response 1" +} -run