REGTEST: add a basic test for HTTP rules manipulating headers

There is always a risk of breaking HTTP processing when performing certain
code changes. This test modifies a request's start line, uses variables,
adds and modifies headers, interleaves them with the start-line changes,
and makes use of different header formats involving duplicated names,
duplicated values, empty fields and spaces around values. These operations
are performed both in the frontend and in the backend, for both the request
and the response. A CRC is computed on the concatenation of all the values,
and the concatenations are sent as individual header fields as well to help
debugging when the test fails.

The test reliably works since 1.6, implying that the HTTP processing did
not change. It currently fails on HTX.
diff --git a/reg-tests/http-rules/h00000.vtc b/reg-tests/http-rules/h00000.vtc
new file mode 100644
index 0000000..252408f
--- /dev/null
+++ b/reg-tests/http-rules/h00000.vtc
@@ -0,0 +1,187 @@
+varnishtest "Composite HTTP manipulation test"
+#REQUIRE_VERSION=1.6
+
+# This config tests several http-request features and their interactions.
+# It extracts some samples, places them into variables, modifies some header
+# fields, appends multiple identical header fields, overwrites the start line
+# using several methods, then dumps the initial list of variables and the final
+# one, then applies CRC32 to these values as signatures that are easy to test.
+# Then it does it again in the backend after saving the current headers into
+# the same names prefixed by "fe-". Then it does the same on the response path.
+# If some modifications are performed, the crc values need to be adjusted based
+# on the failed logs.
+#
+# Run it with HAPROXY_PROGRAM=$PWD/haproxy varnishtest -l -k -t 1 "$1"
+
+feature ignore_unknown_macro
+
+server s1 {
+	rxreq
+	txresp \
+	  -status 234 \
+	  -hdr "hdr1: val1" \
+	  -hdr "hdr2:  val2a" \
+	  -hdr "hdr2:    val2b" \
+	  -hdr "hdr3:      val3a, val3b" \
+	  -hdr "hdr4:" \
+	  -bodylen 14 \
+	  -body "This is a body"
+
+	expect req.method == "GET"
+	expect req.http.fe-sl1-crc == 992395575
+	expect req.http.fe-sl2-crc == 1270056220
+	expect req.http.fe-hdr-crc == 1719311923
+	expect req.http.be-sl1-crc == 2604236007
+	expect req.http.be-sl2-crc == 4181358964
+	expect req.http.be-hdr-crc == 3634102538
+} -start
+
+haproxy h1 -conf {
+    defaults
+	mode http
+	timeout connect 1s
+	timeout client  1s
+	timeout server  1s
+
+    frontend fe
+	bind "fd@${fe}"
+
+	#### requests
+	http-request set-var(req.method)     method
+	http-request set-var(req.uri)        url
+	http-request set-var(req.path)       path
+	http-request set-var(req.query)      query
+	http-request set-var(req.param)      url_param(qs_arg)
+
+	http-request set-header     sl1      "sl1: "
+
+	http-request set-method              "%[str(GET)]"
+	http-request set-uri                 concat(/bu/,req.uri,/eu)
+	http-request set-path                "/bp/%[var(req.path)]/ep"
+	http-request set-query               "bq&%[var(req.query)]&eq"
+
+	http-request set-header     sl2      "sl2: "
+
+	http-request set-header     sl1      "%[req.fhdr(sl1)] method=<%[var(req.method)]>; uri=<%[var(req.uri)]>; path=<%[var(req.path)]>;"
+	http-request set-header     sl1      "%[req.fhdr(sl1)] query=<%[var(req.query)]>; param=<%[var(req.param)]>"
+	http-request set-header     sl2      "%[req.fhdr(sl2)] method=<%[method]>; uri=<%[url]>; path=<%[path]>; "
+	http-request set-header     sl2      "%[req.fhdr(sl2)] query=<%[query]>; param=<%[url_param(qs_arg)]>"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr1=<%[req.hdr(hdr1)]>; fhdr1=<%[req.fhdr(hdr1)]>;"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr2=<%[req.hdr(hdr2)]>; fhdr2=<%[req.fhdr(hdr2)]>;"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr3=<%[req.hdr(hdr3)]>; fhdr3=<%[req.fhdr(hdr3)]>;"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr4=<%[req.hdr(hdr4)]>; fhdr4=<%[req.fhdr(hdr4)]>;"
+
+	http-request set-header     sl1-crc  "%[req.fhdr(sl1),crc32]"
+	http-request set-header     sl2-crc  "%[req.fhdr(sl2),crc32]"
+	http-request set-header     hdr-crc  "%[req.fhdr(hdr),crc32]"
+
+	#### responses
+	http-response set-header    be-sl1   "%[res.fhdr(sl1)]"
+	http-response set-header    be-sl2   "%[res.fhdr(sl2)]"
+	http-response set-header    be-hdr   "%[res.fhdr(hdr)]"
+
+	http-response set-header  be-sl1-crc "%[res.fhdr(sl1-crc)]"
+	http-response set-header  be-sl2-crc "%[res.fhdr(sl2-crc)]"
+	http-response set-header  be-hdr-crc "%[res.fhdr(hdr-crc)]"
+
+	http-response set-var(res.status)    status
+	http-response set-header    sl1      "sl1: "
+
+	http-response set-status    200
+
+	http-response set-header    sl2      "sl2: "
+
+	http-response set-header    sl1      "%[res.fhdr(sl1)] status=<%[var(res.status)]>;"
+	http-response set-header    sl2      "%[res.fhdr(sl2)] status=<%[status]>;"
+	http-response set-header    hdr      "%[res.fhdr(hdr)] hdr1=<%[res.hdr(hdr1)]>; fhdr1=<%[res.fhdr(hdr1)]>;"
+	http-response set-header    hdr      "%[res.fhdr(hdr)] hdr2=<%[res.hdr(hdr2)]>; fhdr2=<%[res.fhdr(hdr2)]>;"
+	http-response set-header    hdr      "%[res.fhdr(hdr)] hdr3=<%[res.hdr(hdr3)]>; fhdr3=<%[res.fhdr(hdr3)]>;"
+	http-response set-header    hdr      "%[res.fhdr(hdr)] hdr4=<%[res.hdr(hdr4)]>; fhdr4=<%[res.fhdr(hdr4)]>;"
+
+	http-response set-header  fe-sl1-crc "%[res.fhdr(sl1),crc32]"
+	http-response set-header  fe-sl2-crc "%[res.fhdr(sl2),crc32]"
+	http-response set-header  fe-hdr-crc "%[res.fhdr(hdr),crc32]"
+
+	default_backend be
+
+    backend be
+	#### requests
+	http-request set-header     fe-sl1   "%[req.fhdr(sl1)]"
+	http-request set-header     fe-sl2   "%[req.fhdr(sl2)]"
+	http-request set-header     fe-hdr   "%[req.fhdr(hdr)]"
+
+	http-request set-header   fe-sl1-crc "%[req.fhdr(sl1-crc)]"
+	http-request set-header   fe-sl2-crc "%[req.fhdr(sl2-crc)]"
+	http-request set-header   fe-hdr-crc "%[req.fhdr(hdr-crc)]"
+
+	http-request set-var(req.method)     method
+	http-request set-var(req.uri)        url
+	http-request set-var(req.path)       path
+	http-request set-var(req.query)      query
+	http-request set-var(req.param)      url_param(qs_arg)
+
+	http-request set-header     sl1      "sl1: "
+
+	http-request set-method              "%[str(GET)]"
+	http-request set-uri                 concat(/bu/,req.uri,/eu)
+	http-request set-path                "/bp/%[var(req.path)]/ep"
+	http-request set-query               "bq&%[var(req.query)]&eq"
+
+	http-request set-header     sl2      "sl2: "
+
+	http-request set-header     sl1      "%[req.fhdr(sl1)] method=<%[var(req.method)]>; uri=<%[var(req.uri)]>; path=<%[var(req.path)]>;"
+	http-request set-header     sl1      "%[req.fhdr(sl1)] query=<%[var(req.query)]>; param=<%[var(req.param)]>"
+	http-request set-header     sl2      "%[req.fhdr(sl2)] method=<%[method]>; uri=<%[url]>; path=<%[path]>; "
+	http-request set-header     sl2      "%[req.fhdr(sl2)] query=<%[query]>; param=<%[url_param(qs_arg)]>"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr1=<%[req.hdr(hdr1)]>; fhdr1=<%[req.fhdr(hdr1)]>;"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr2=<%[req.hdr(hdr2)]>; fhdr2=<%[req.fhdr(hdr2)]>;"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr3=<%[req.hdr(hdr3)]>; fhdr3=<%[req.fhdr(hdr3)]>;"
+	http-request set-header     hdr      "%[req.fhdr(hdr)] hdr4=<%[req.hdr(hdr4)]>; fhdr4=<%[req.fhdr(hdr4)]>;"
+
+	http-request set-header   be-sl1-crc "%[req.fhdr(sl1),crc32]"
+	http-request set-header   be-sl2-crc "%[req.fhdr(sl2),crc32]"
+	http-request set-header   be-hdr-crc "%[req.fhdr(hdr),crc32]"
+
+	#### responses
+	http-response set-var(res.status)    status
+	http-response set-header     sl1     "sl1: "
+
+	http-response set-status     200
+
+	http-response set-header     sl2     "sl2: "
+
+	http-response set-header     sl1     "%[res.fhdr(sl1)] status=<%[var(res.status)]>;"
+	http-response set-header     sl2     "%[res.fhdr(sl2)] status=<%[status]>;"
+	http-response set-header     hdr     "%[res.fhdr(hdr)] hdr1=<%[res.hdr(hdr1)]>; fhdr1=<%[res.fhdr(hdr1)]>;"
+	http-response set-header     hdr     "%[res.fhdr(hdr)] hdr2=<%[res.hdr(hdr2)]>; fhdr2=<%[res.fhdr(hdr2)]>;"
+	http-response set-header     hdr     "%[res.fhdr(hdr)] hdr3=<%[res.hdr(hdr3)]>; fhdr3=<%[res.fhdr(hdr3)]>;"
+	http-response set-header     hdr     "%[res.fhdr(hdr)] hdr4=<%[res.hdr(hdr4)]>; fhdr4=<%[res.fhdr(hdr4)]>;"
+
+	http-response set-header     sl1-crc "%[res.fhdr(sl1),crc32]"
+	http-response set-header     sl2-crc "%[res.fhdr(sl2),crc32]"
+	http-response set-header     hdr-crc "%[res.fhdr(hdr),crc32]"
+
+	server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+	txreq \
+	  -req GET \
+	  -url /path/to/file.extension?qs_arg=qs_value \
+	  -hdr "hdr1: val1" \
+	  -hdr "hdr2:  val2a" \
+	  -hdr "hdr2:    val2b" \
+	  -hdr "hdr3:      val3a, val3b" \
+	  -hdr "hdr4:"
+	rxresp
+
+	expect resp.status == 200
+	expect resp.http.be-sl1-crc == 487202719
+	expect resp.http.be-sl2-crc == 561949791
+	expect resp.http.be-hdr-crc == 1719311923
+	expect resp.http.fe-sl1-crc == 146151597
+	expect resp.http.fe-sl2-crc == 561949791
+	expect resp.http.fe-hdr-crc == 3634102538
+	expect resp.bodylen == 14
+	expect resp.body == "This is a body"
+} -run