summaryrefslogtreecommitdiffstats
path: root/reg-tests/http-rules
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--reg-tests/http-rules/1k.txt16
-rw-r--r--reg-tests/http-rules/acl_cli_spaces.vtc77
-rw-r--r--reg-tests/http-rules/agents.acl1
-rw-r--r--reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.map1
-rw-r--r--reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.vtc231
-rw-r--r--reg-tests/http-rules/default_rules.vtc159
-rw-r--r--reg-tests/http-rules/del_header.vtc93
-rw-r--r--reg-tests/http-rules/except-forwardfor-originalto.vtc143
-rw-r--r--reg-tests/http-rules/forwarded-header-7239.vtc171
-rw-r--r--reg-tests/http-rules/h1or2_to_h1c.vtc233
-rw-r--r--reg-tests/http-rules/http_after_response.vtc192
-rw-r--r--reg-tests/http-rules/http_return.vtc99
-rw-r--r--reg-tests/http-rules/ifnone-forwardfor.vtc98
-rw-r--r--reg-tests/http-rules/lf-file.txt1
-rw-r--r--reg-tests/http-rules/map_ordering.map4
-rw-r--r--reg-tests/http-rules/map_ordering.vtc32
-rw-r--r--reg-tests/http-rules/map_redirect-be.map4
-rw-r--r--reg-tests/http-rules/map_redirect.map5
-rw-r--r--reg-tests/http-rules/map_redirect.vtc200
-rw-r--r--reg-tests/http-rules/map_regm_with_backref.map1
-rw-r--r--reg-tests/http-rules/map_regm_with_backref.vtc73
-rw-r--r--reg-tests/http-rules/normalize_uri.vtc549
-rw-r--r--reg-tests/http-rules/path_and_pathq.vtc64
-rw-r--r--reg-tests/http-rules/restrict_req_hdr_names.vtc185
-rw-r--r--reg-tests/http-rules/strict_rw_mode.vtc164
25 files changed, 2796 insertions, 0 deletions
diff --git a/reg-tests/http-rules/1k.txt b/reg-tests/http-rules/1k.txt
new file mode 100644
index 0000000..bbdd7e7
--- /dev/null
+++ b/reg-tests/http-rules/1k.txt
@@ -0,0 +1,16 @@
+s313l7hzIJ5FIXCmpr+zAyFK80lNfjOIRNZSXJJn/GQVNQsBqDl3AFcb7JQt1ler
+KgBNE1LAiU02vTj4+hlW2qi4Xg1T3lshEOSSxJQN/ITQG/1KVmDiTtsjjSv8iWUj
+T403xvLKQd0FB2h00N9pwd9ApbGPYF8uG1kjnNQmzJOqQ2Pz7jUkNpF+sAAQHaRD
+ocjEucTsb676w8l9EqWNE+DK5IqoO2AK47bHbr4u38ZOwXjQWGw9MiUJZmVQEqdC
+QZlmpFuSKQiig1SZFZlmKVidf1genz6q+4BT80IFU2UE+pWiay/HcZttwM++eG7w
+n/Va7yd3D+ryK2j4rw0sOYM7Cu7AwleZeGEaCZINZmnVAWtg2OVFOTxx6jz8wNuY
+VJPb3VFD72WnnBhtbik5mEqjzVJy530sQBlGlcxi3Tivq69ZnAk55RBN0LO+jWf4
+DI4189LTIfY5WroA8AQeCCQYnzyXo5O/vDmic+uwKALlQ6TXzSuCNpHO8fL1UwHH
+7KBqxHi+/yYJ0431V/LAvRBpVFPYJ8iED7Md67GRVQWy8o+tgC1PmycJtS5ADQGO
+Jys46KjhL9cnaS3aP1YcuuGuSUOVMA7BjqPcz7r+hqYTCZ3akaY4w7AGRCZyRf8e
+finlAkgFpzKSFwaa2M6H3vUE14WzHC0hJ/bO2epjlcOeoMcgBVn5uUMYMVroAK0+
+vI9jg1RDV17oHberVmWj8VAXolDNS0pW2rt+JbqHsAVDDk/Ex3NJWFSYByHFyglQ
+cjYMwrzIsWC09ykW6WhUN6IsopLlDk7O2jcKaHxZ6WaFiVxIGFkepfNhf4wYZ6O9
+tjwMuOqTQtSjdP3MbbVEM6VpAkJW/Si1jmSW02ObMcdEpxJHTB9fC30waMM7e+T4
+FT/AlwB49un/3yYU2iUndW+wenoED9UkdbZ7uvjyu+UgQ3bMaQhX9Z9eHxhfi6Gy
+aRM2IJVeEpk5w0ifAQxrL4Wp+dFbzfGN1yPkI2UAo6WbWi63D \ No newline at end of file
diff --git a/reg-tests/http-rules/acl_cli_spaces.vtc b/reg-tests/http-rules/acl_cli_spaces.vtc
new file mode 100644
index 0000000..a554977
--- /dev/null
+++ b/reg-tests/http-rules/acl_cli_spaces.vtc
@@ -0,0 +1,77 @@
+varnishtest "haproxy ACL, CLI and mCLI spaces"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.method == "GET"
+ txresp
+} -repeat 2 -start
+
+haproxy h1 -W -S -conf {
+ defaults
+ mode http
+ log global
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+
+ http-request deny if { req.hdr(user-agent) -i -m str -f ${testdir}/agents.acl }
+
+ default_backend be1
+
+ backend be1
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -hdr "User-Agent: Mon User Agent"
+ rxresp
+ expect resp.status == 200
+} -run
+
+haproxy h1 -cli {
+ send "add acl ${testdir}/agents.acl Mon\\ User\\ Agent\\;"
+ expect ~ .*
+
+ send "show acl ${testdir}/agents.acl"
+ expect ~ ".*Mon User Agent.*"
+}
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -hdr "User-Agent: Mon User Agent;"
+ rxresp
+ expect resp.status == 403
+} -run
+
+
+haproxy h1 -cli {
+ send "del acl ${testdir}/agents.acl Mon\\ User\\ Agent\\;"
+ expect ~ .*
+
+ send "show acl ${testdir}/agents.acl"
+ expect ~ .*
+}
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -hdr "User-Agent: Mon User Agent;"
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+# Try it with the master CLI
+haproxy h1 -mcli {
+ send "@1 add acl ${testdir}/agents.acl Mon\\ User\\ Agent\\;;@1 show acl ${testdir}/agents.acl"
+ expect ~ ".*Mon User Agent;.*"
+}
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -hdr "User-Agent: Mon User Agent;"
+ rxresp
+ expect resp.status == 403
+} -run
diff --git a/reg-tests/http-rules/agents.acl b/reg-tests/http-rules/agents.acl
new file mode 100644
index 0000000..345e6ae
--- /dev/null
+++ b/reg-tests/http-rules/agents.acl
@@ -0,0 +1 @@
+Test
diff --git a/reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.map b/reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.map
new file mode 100644
index 0000000..9a3e8e6
--- /dev/null
+++ b/reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.map
@@ -0,0 +1 @@
+^(.+)_(.+)$ \2_\1
diff --git a/reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.vtc b/reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.vtc
new file mode 100644
index 0000000..55bcb5f
--- /dev/null
+++ b/reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.vtc
@@ -0,0 +1,231 @@
+varnishtest "Minimal tests for 1.9 converters: ipmask,concat,strcmp,field,word"
+feature ignore_unknown_macro
+
+# ipmask,hdr_ip tests server
+server s1 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.srciphdr == "192.168.1.101"
+ expect req.http.srcmask1 == "192.168.1.0"
+ expect req.http.srcmask2 == "192.168.0.0"
+ expect req.http.srcmask3 == "192.0.0.0"
+
+ expect req.http.test1mask128 ~ "2001:db8:[0:]*:1"
+ expect req.http.test2mask64 ~ "2001:db8:[0:]+"
+ expect req.http.test2mask128 ~ "2001:db8:[0:]*:bad:c0f:ffff"
+ expect req.http.test2mask120 ~ "2001:db8:[0:]*:bad:c0f:ff00"
+ expect req.http.test2maskff00 ~ "2001:db8:[0:]*:bad:c0f:ff00"
+ expect req.http.test2maskfee0 ~ "2001:db8:[0:]*:bad:c0f:fee0"
+
+ expect req.http.test3mask64 ~ "2001:db8:c001:c01a:[0:]+"
+ expect req.http.test3mask64v2 ~ "2001:db8:c001:c01a:[0:]+"
+ expect req.http.test3mask64v3 ~ "2001:db8:c001:c01a:[0:]+"
+ expect req.http.test3maskff ~ "2001:db8:c001:c01a:[0:]*:ffff:10:[0:]+"
+ expect req.http.test3maskv2 ~ "2001:db8:c001:c01a:c001:c001:[0:]+"
+
+ expect req.http.test4mask32 == "192.168.1.101"
+
+ expect req.http.test5mask24 == "192.168.1.0"
+ expect req.http.test6mask24 == "192.168.1.0"
+ expect req.http.test6mask25 == "192.168.1.128"
+
+ expect req.http.ipv4plain == "192.168.2.1"
+ expect req.http.ipv4port == "192.168.2.1"
+ expect req.http.ipv6plain == "2001:db8:c001:c01a:ffff:ffff:20:ffff"
+ expect req.http.ipv6brackets == "2001:db8:c001:c01a:ffff:ffff:20:ffff"
+
+ txresp
+} -start
+
+# concat,strcmp,field,word tests server
+server s2 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.fieldconcat == "f1_f2_f3__f5"
+ expect req.http.fieldconcat2 == "f1_f2_f3__f5"
+ expect req.http.fieldconcat3 == "f1_f2_f3__f5"
+ expect req.http.fieldstrcmp == "0"
+
+ # field tests
+ expect req.http.fieldtest1 == "f5"
+ expect req.http.fieldtest2 == "f2_f3__f5"
+ expect req.http.fieldtest3 == "f2_f3"
+ expect req.http.fieldtest4 == "f2_f3_"
+ expect req.http.fieldtest5 == "f1_f2_f3"
+ expect req.http.okfieldtest == "ok"
+ expect req.http.qsfieldtest == "IT_IS"
+ expect req.http.qsfieldconcat == "IT_IS_ok"
+ expect req.http.fieldtest1strcmp == "0"
+
+ # word tests
+ expect req.http.wordtest1 == "f5"
+ expect req.http.wordtest2 == "f2_f3__f5"
+ expect req.http.wordtest3 == "f3__f5"
+ expect req.http.wordtest4 == "f1_f2_f3"
+ expect req.http.wordtest5 == "f1_f2"
+ expect req.http.okwordtest == "OK"
+ expect req.http.qswordtest == "Yes_It"
+ expect req.http.qswordregmtest == "It_Yes"
+ expect req.http.wordtest1strcmp == "0"
+ txresp
+} -start
+
+
+# ipmask,hdr_ip tests with accept-proxy bind
+haproxy h1 -conf {
+ defaults
+ mode http
+ log global
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ # accept-proxy so test client can send src ip
+ bind "fd@${fe1}" accept-proxy
+
+ # ipmask tests w/src
+ http-request set-header Srciphdr %[src]
+ http-request set-header Srcmask1 %[src,ipmask(24)] # 192.168.1.0
+ http-request set-header Srcmask2 %[src,ipmask(16)] # 192.168.0.0
+ http-request set-header Srcmask3 %[src,ipmask(8)] # 192.0.0.0
+
+ # ipmask tests from headers
+ http-request set-header Test1mask128 %[req.hdr_ip(Addr1),ipmask(24,128)]
+
+ http-request set-header Test2mask64 %[req.hdr_ip(Addr2),ipmask(24,64)]
+ http-request set-header Test2mask128 %[req.hdr_ip(Addr2),ipmask(24,128)]
+ http-request set-header Test2mask120 %[req.hdr_ip(Addr2),ipmask(24,120)]
+ http-request set-header Test2maskff00 %[req.hdr_ip(Addr2),ipmask(24,ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00)]
+ http-request set-header Test2maskfee0 %[req.hdr_ip(Addr2),ipmask(24,ffff:ffff:ffff:ffff:ffff:ffff:ffff:fee0)]
+
+ http-request set-header Test3mask64 %[req.hdr_ip(Addr3),ipmask(24,64)]
+ http-request set-header Test3mask64v2 %[req.hdr_ip(Addr3),ipmask(24,ffff:ffff:ffff:ffff:0:0:0:0)]
+ http-request set-header Test3mask64v3 %[req.hdr_ip(Addr3),ipmask(24,ffff:ffff:ffff:ffff::)]
+ http-request set-header Test3maskff %[req.hdr_ip(Addr3),ipmask(24,ffff:ffff:ffff:ffff:0:ffff:ffff:0)]
+ http-request set-header Test3maskv2 %[req.hdr_ip(Addr3),ipmask(24,ffff:ffff:ffff:ffff:c001:c001:0000:0000)]
+
+ # ipv4 mask applied to ipv4 mapped address
+ http-request set-header Test4mask32 %[req.hdr_ip(Addr4),ipmask(32,64)]
+
+ http-request set-header Test5mask24 %[req.hdr_ip(Addr5),ipmask(24)]
+
+ http-request set-header Test6mask24 %[req.hdr_ip(Addr6),ipmask(24)]
+ http-request set-header Test6mask25 %[req.hdr_ip(Addr6),ipmask(25)]
+
+ # track addr/mask in stick table
+ http-request track-sc0 src,ipmask(24) table be1
+ http-request track-sc1 hdr_ip(Addr4),ipmask(32) table be1
+ http-request track-sc2 hdr_ip(Addr3),ipmask(24,64) table be1
+
+ # hdr_ip tests
+ http-request set-header IPv4plain %[req.hdr_ip(AddrIPv4plain)]
+ http-request set-header IPv4port %[req.hdr_ip(AddrIPv4port)]
+ http-request set-header IPv6plain %[req.hdr_ip(AddrIPv6plain)]
+ http-request set-header IPv6brackets %[req.hdr_ip(AddrIPv6brackets)]
+
+ default_backend be1
+
+ backend be1
+ stick-table type ipv6 size 20 expire 360s store gpc0,conn_cnt
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+# concat,strcmp,word,field haproxy
+haproxy h2 -conf {
+ defaults
+ mode http
+ log global
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe2
+ bind "fd@${fe2}"
+
+ # concat f1_f2 + _ + f3__f5 tests
+ http-request set-var(sess.field1) hdr(Field1)
+ http-request set-var(sess.field2) hdr(Field2)
+ http-request set-var(sess.fieldhdr) hdr(Fieldhdr)
+ http-request set-var(sess.fieldconcat) hdr(Field1),concat(_,sess.field2,)
+ http-request set-header Fieldconcat2 %[var(sess.field1),concat(_,sess.field2,)]
+ http-request set-header Fieldconcat3 %[hdr(Field1),concat(_,sess.field2,)]
+ http-request set-header Fieldconcat %[var(sess.fieldconcat)]
+ http-request set-header Fieldstrcmp %[hdr(Fieldhdr),strcmp(sess.fieldconcat)]
+ http-request deny unless { hdr(Fieldhdr),strcmp(sess.fieldconcat) eq 0 }
+
+ # field tests
+ http-request set-header Fieldtest1 %[hdr(Fieldhdr),field(5,_)] #f5
+ http-request set-var(sess.fieldtest1var) hdr(Fieldtest1)
+ http-request set-var(sess.okfield) path,lower,field(4,/,1) #ok
+ http-request set-header Okfieldtest %[var(sess.okfield)] #ok
+ http-request set-var(sess.qsfield) url_param(qs),upper,field(2,_,2) #IT_IS
+ http-request set-header Qsfieldtest %[var(sess.qsfield)] #IT_IS
+ http-request set-header Qsfieldconcat %[var(sess.qsfield),concat(_,sess.okfield,)] #IT_IS_ok
+ http-request set-header Fieldtest2 %[var(sess.fieldhdr),field(2,_,0)] #f2_f3__f5
+ http-request set-header Fieldtest3 %[var(sess.fieldconcat),field(2,_,2)] #f2_f3
+ http-request set-header Fieldtest4 %[hdr(Fieldconcat2),field(-2,_,3)] #f2_f3_
+ http-request set-header Fieldtest5 %[hdr(Fieldconcat3),field(-3,_,0)] #f1_f2_f3
+ http-request set-header Fieldtest1strcmp %[str(f5),strcmp(sess.fieldtest1var)]
+ http-request deny unless { str(f5),strcmp(sess.fieldtest1var) eq 0 }
+ http-request deny unless { str(ok),strcmp(sess.okfield) eq 0 }
+ http-request deny unless { str(IT_IS),strcmp(sess.qsfield) eq 0 }
+
+ # word tests
+ http-request set-header Wordtest1 %[hdr(Fieldhdr),word(4,_)] #f5
+ http-request set-var(sess.wordtest1var) hdr(Wordtest1)
+ http-request set-var(sess.okword) path,upper,word(3,/,1) #OK
+ http-request set-header Okwordtest %[var(sess.okword)] #OK
+ http-request set-var(sess.qsword) url_param(qs),word(1,_,2) #Yes_It
+ http-request set-header Qswordtest %[var(sess.qsword)] #Yes_It
+ http-request set-header Qswordregmtest %[var(sess.qsword),map_regm(${testdir}/converters_ipmask_concat_strcmp_field_word.map)] #It_Yes
+ http-request set-header Wordtest2 %[var(sess.fieldhdr),word(2,_,0)] #f2_f3__f5
+ http-request set-header Wordtest3 %[var(sess.fieldconcat),word(3,_,2)] #f3__f5
+ http-request set-header Wordtest4 %[hdr(Fieldconcat2),word(-2,_,3)] #f1_f2_f3
+ http-request set-header Wordtest5 %[hdr(Fieldconcat3),word(-3,_,0)] #f1_f2
+ http-request set-header Wordtest1strcmp %[str(f5),strcmp(sess.wordtest1var)]
+ http-request deny unless { str(f5),strcmp(sess.wordtest1var) eq 0 }
+ http-request deny unless { str(OK),strcmp(sess.okword) eq 0 }
+ http-request deny unless { str(Yes_It),strcmp(sess.qsword) eq 0 }
+
+ default_backend be2
+
+ backend be2
+ server s2 ${s2_addr}:${s2_port}
+} -start
+
+# ipmask,hdr_ip tests
+client c1 -connect ${h1_fe1_sock} -proxy2 "192.168.1.101:1234 127.0.0.1:2345" {
+ txreq -hdr "Addr1: 2001:db8::1" \
+ -hdr "Addr2: 2001:db8::bad:c0f:ffff" \
+ -hdr "Addr3: 2001:db8:c001:c01a:ffff:ffff:10:ffff" \
+ -hdr "Addr4: ::FFFF:192.168.1.101" \
+ -hdr "Addr5: 192.168.1.2" \
+ -hdr "Addr6: 192.168.1.255" \
+ -hdr "AddrIPv4plain: 192.168.2.1" \
+ -hdr "AddrIPv4port: 192.168.2.1:6789" \
+ -hdr "AddrIPv6plain: 2001:db8:c001:c01a:ffff:ffff:20:ffff" \
+ -hdr "AddrIPv6brackets: [2001:db8:c001:c01a:ffff:ffff:20:ffff]"
+ rxresp
+ expect resp.status == 200
+} -run
+
+# cli show be1 stick table
+haproxy h1 -cli {
+ send "show table be1"
+ expect ~ "^# table: be1, type: ipv6, size:20, used:3\\n0x[a-f0-9]+: key=::ffff:192\\.168\\.1\\.0 use=0 exp=[[:digit:]]+ shard=0 gpc0=0 conn_cnt=1\\n0x[a-f0-9]+: key=::ffff:192\\.168\\.1\\.101 use=0 exp=[[:digit:]]+ shard=0 gpc0=0 conn_cnt=1\\n0x[a-f0-9]+: key=2001:db8:c001:c01a:[0:]+ use=0 exp=[[:digit:]]+ shard=0 gpc0=0 conn_cnt=1\\n"
+}
+
+# concat,strcmp,word,field tests
+client c2 -connect ${h2_fe2_sock} {
+ txreq -req GET \
+ -url /is/this/Ok/or/not?qs=Yes_It_Is \
+ -hdr "Fieldhdr: f1_f2_f3__f5" \
+ -hdr "Field1: f1_f2" \
+ -hdr "Field2: f3__f5"
+ rxresp
+ expect resp.status == 200
+} -run
+
diff --git a/reg-tests/http-rules/default_rules.vtc b/reg-tests/http-rules/default_rules.vtc
new file mode 100644
index 0000000..cc726ab
--- /dev/null
+++ b/reg-tests/http-rules/default_rules.vtc
@@ -0,0 +1,159 @@
+varnishtest "Test declaration of HTTP rules in default sections"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.http.x-frontend == "fe"
+ expect req.http.x-backend == "be"
+ expect req.http.x-test1-frt == "def_front"
+ expect req.http.x-test1-bck == "def_back"
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ defaults common
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ defaults def_front from common
+ http-request set-header x-frontend "%[fe_name]"
+ http-request set-var(txn.test1) "str(def_front)"
+ http-response set-header x-frontend "%[fe_name]"
+ http-response set-var(txn.test2) "str(def_front)"
+ http-after-response set-var(txn.test3) "str(def_front)"
+
+ defaults def_back from common
+ http-request set-header x-backend "%[be_name]"
+ http-request set-var(txn.test1) "str(def_back)"
+ http-response set-header x-backend "%[be_name]"
+ http-response set-var(txn.test2) "str(def_back)"
+ http-after-response set-var(txn.test3) "str(def_back)"
+
+ frontend fe from def_front
+ bind "fd@${feh1}"
+
+ http-request set-header x-test1-frt "%[var(txn.test1)]"
+ http-response set-header x-test2-frt "%[var(txn.test2)]"
+ http-after-response set-header x-test3-frt "%[var(txn.test3)]"
+
+ default_backend be
+
+ backend be from def_back
+ http-request set-header x-test1-bck "%[var(txn.test1)]"
+ http-response set-header x-test2-bck "%[var(txn.test2)]"
+ http-after-response set-header x-test3-bck "%[var(txn.test3)]"
+
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+
+haproxy h2 -conf {
+ defaults common
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ defaults def_front from common
+ http-request allow
+ http-response allow
+ http-after-response allow
+
+ defaults def_back from common
+ http-request allow
+ http-response allow
+ http-after-response allow
+
+ frontend fe from def_front
+ bind "fd@${feh2}"
+
+ http-request deny status 403
+ http-response deny status 502
+ http-after-response set-status 502
+
+ default_backend be
+
+ backend be from def_back
+ http-request deny status 403
+ http-response deny status 502
+ http-after-response set-status 502
+
+ server s2 ${s2_addr}:${s2_port}
+
+} -start
+
+haproxy h3 -conf {
+ defaults base-http
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ http-request capture hdr(Host) len 64 # idx 0
+ http-request capture hdr(X-req-1) len 32 # idx 1
+
+ frontend fe1 from base-http
+ bind "fd@${fe1h3}"
+ declare capture request len 32 # idx 2
+
+ http-request capture hdr(X-req-2) id 2
+ http-request return status 200 hdr "X-Capture-1" "%[capture.req.hdr(0)]" hdr "X-Capture-2" "%[capture.req.hdr(1)]" hdr "X-Capture-3" "%[capture.req.hdr(2)]"
+
+ frontend fe2 from base-http
+ bind "fd@${fe2h3}"
+ http-request capture hdr(X-req-2) id 1
+ http-request return status 200 hdr "X-Capture-1" "%[capture.req.hdr(0)]" hdr "X-Capture-2" "%[capture.req.hdr(1)]"
+
+} -start
+
+client c1 -connect ${h1_feh1_sock} {
+ txreq -req GET -url /
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-frontend == "fe"
+ expect resp.http.x-backend == "be"
+ expect resp.http.x-test2-bck == "def_back"
+ expect resp.http.x-test2-frt == "def_front"
+ expect resp.http.x-test3-bck == "def_back"
+ expect resp.http.x-test3-frt == "def_front"
+} -run
+
+client c2 -connect ${h2_feh2_sock} {
+ txreq -req GET -url /
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c3 -connect ${h3_fe1h3_sock} {
+ txreq -req GET -url / \
+ -hdr "host: v-test" \
+ -hdr "x-req-1: val1" \
+ -hdr "x-req-2: val2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-capture-1 == "v-test"
+ expect resp.http.x-capture-2 == "val1"
+ expect resp.http.x-capture-3 == "val2"
+} -run
+
+client c4 -connect ${h3_fe2h3_sock} {
+ txreq -req GET -url / \
+ -hdr "host: v-test" \
+ -hdr "x-req-1: val1" \
+ -hdr "x-req-2: val2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-capture-1 == "v-test"
+ expect resp.http.x-capture-2 == "val2"
+ expect resp.http.x-capture-3 == "<undef>"
+} -run
diff --git a/reg-tests/http-rules/del_header.vtc b/reg-tests/http-rules/del_header.vtc
new file mode 100644
index 0000000..0f74a60
--- /dev/null
+++ b/reg-tests/http-rules/del_header.vtc
@@ -0,0 +1,93 @@
+varnishtest "del-header tests"
+
+# This config tests various http-request/response del-header operations
+# with or without specified header name matching method.
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == /
+ expect req.http.x-always == always
+ expect req.http.x-str1 == <undef>
+ expect req.http.x-str2 == <undef>
+ expect req.http.x-beg1 == <undef>
+ expect req.http.x-beg2 == <undef>
+ expect req.http.x-end1 == <undef>
+ expect req.http.x-end2 == end2
+ expect req.http.x-sub1 == <undef>
+ expect req.http.x-sub2 == <undef>
+ expect req.http.x-reg1 == <undef>
+ expect req.http.x-reg2 == <undef>
+ txresp -hdr "x-always: always" \
+ -hdr "x-str1: str1" \
+ -hdr "x-str2: str2" \
+ -hdr "x-beg1: beg1" \
+ -hdr "x-beg2: beg2" \
+ -hdr "x-end1: end1" \
+ -hdr "x-end2: end2" \
+ -hdr "x-sub1: sub1" \
+ -hdr "x-sub2: sub2" \
+ -hdr "x-reg1: reg1" \
+ -hdr "x-reg2: reg2"
+
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe
+ bind "fd@${fe}"
+
+ http-request del-header x-str1
+ http-request del-header x-str2 -m str
+ http-request del-header x-beg -m beg
+ http-request del-header end1 -m end
+ http-request del-header sub -m sub
+ http-request del-header ^x.reg.$ -m reg
+
+ http-response del-header x-str1
+ http-response del-header x-str2 -m str
+ http-response del-header x-beg -m beg
+ http-response del-header end1 -m end
+ http-response del-header sub -m sub
+ http-response del-header ^x.reg.$ -m reg
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -req GET -url / \
+ -hdr "x-always: always" \
+ -hdr "x-str1: str1" \
+ -hdr "x-str2: str2" \
+ -hdr "x-beg1: beg1" \
+ -hdr "x-beg2: beg2" \
+ -hdr "x-end1: end1" \
+ -hdr "x-end2: end2" \
+ -hdr "x-sub1: sub1" \
+ -hdr "x-sub2: sub2" \
+ -hdr "x-reg1: reg1" \
+ -hdr "x-reg2: reg2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-always == always
+ expect resp.http.x-str1 == <undef>
+ expect resp.http.x-str2 == <undef>
+ expect resp.http.x-beg1 == <undef>
+ expect resp.http.x-beg2 == <undef>
+ expect resp.http.x-end1 == <undef>
+ expect resp.http.x-end2 == end2
+ expect resp.http.x-sub1 == <undef>
+ expect resp.http.x-sub2 == <undef>
+ expect resp.http.x-reg1 == <undef>
+ expect resp.http.x-reg2 == <undef>
+} -run
diff --git a/reg-tests/http-rules/except-forwardfor-originalto.vtc b/reg-tests/http-rules/except-forwardfor-originalto.vtc
new file mode 100644
index 0000000..a859160
--- /dev/null
+++ b/reg-tests/http-rules/except-forwardfor-originalto.vtc
@@ -0,0 +1,143 @@
+varnishtest "Test IPv4/IPv6 except param for the forwardfor and originalto options"
+#REQUIRE_VERSION=2.4
+
+# This config tests the except parameter for the HTTP forwardfor and originalto
+# options.
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ # WT: limit false-positives causing "HTTP header incomplete" due to
+ # idle server connections being randomly used and randomly expiring
+ # under us.
+ tune.idle-pool.shared off
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ http-request set-src hdr(x-src)
+ http-request set-dst hdr(x-dst)
+ use_backend be1 if { path /req1 }
+ use_backend be2 if { path /req2 }
+ use_backend be3 if { path /req3 }
+ use_backend be4 if { path /req4 }
+ use_backend be5 if { path /req5 }
+
+ frontend fe2
+ bind "fd@${fe2}"
+ http-request return status 200 hdr x-ff "%[req.hdr(x-forwarded-for)]" hdr x-ot "%[req.hdr(x-original-to)]"
+
+ backend be1
+ option forwardfor except 127.0.0.1
+ option originalto except 127.0.0.1
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be2
+ option forwardfor except 10.0.0.1/25
+ option originalto except 10.0.0.1/25
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be3
+ option forwardfor except ::1
+ option originalto except ::1
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be4
+ option forwardfor except 2001:db8::1:0:0:1
+ option originalto except 2001:db8::1:0:0:1
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be5
+ option forwardfor except 2001:db8:1f89::/48
+ option originalto except 2001:db8:1f89::/48
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /req1 \
+ -hdr "x-src: 127.0.0.1" \
+ -hdr "x-dst: 127.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == <undef>
+ expect resp.http.x-ot == <undef>
+
+ txreq -req GET -url /req1 \
+ -hdr "x-src: 127.0.0.2" \
+ -hdr "x-dst: 127.0.0.2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == "127.0.0.2"
+ expect resp.http.x-ot == "127.0.0.2"
+
+
+ txreq -req GET -url /req2 \
+ -hdr "x-src: 10.0.0.1" \
+ -hdr "x-dst: 10.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == <undef>
+ expect resp.http.x-ot == <undef>
+
+ txreq -req GET -url /req2 \
+ -hdr "x-src: 10.0.0.128" \
+ -hdr "x-dst: 10.0.0.128"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == "10.0.0.128"
+ expect resp.http.x-ot == "10.0.0.128"
+
+ txreq -req GET -url /req3 \
+ -hdr "x-src: ::1" \
+ -hdr "x-dst: ::1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == <undef>
+ expect resp.http.x-ot == <undef>
+
+ txreq -req GET -url /req3 \
+ -hdr "x-src: ::2" \
+ -hdr "x-dst: ::2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == "::2"
+ expect resp.http.x-ot == "::2"
+
+ txreq -req GET -url /req4 \
+ -hdr "x-src: 2001:db8::1:0:0:1" \
+ -hdr "x-dst: 2001:db8::1:0:0:1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == <undef>
+ expect resp.http.x-ot == <undef>
+
+ txreq -req GET -url /req4 \
+ -hdr "x-src: 2001:db8::1:0:0:2" \
+ -hdr "x-dst: 2001:db8::1:0:0:2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == "2001:db8::1:0:0:2"
+ expect resp.http.x-ot == "2001:db8::1:0:0:2"
+
+ txreq -req GET -url /req5 \
+ -hdr "x-src: 2001:db8:1f89::1" \
+ -hdr "x-dst: 2001:db8:1f89::1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == <undef>
+ expect resp.http.x-ot == <undef>
+
+ txreq -req GET -url /req5 \
+ -hdr "x-src: 2001:db8:1f90::1" \
+ -hdr "x-dst: 2001:db8:1f90::1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == "2001:db8:1f90::1"
+ expect resp.http.x-ot == "2001:db8:1f90::1"
+} -run
diff --git a/reg-tests/http-rules/forwarded-header-7239.vtc b/reg-tests/http-rules/forwarded-header-7239.vtc
new file mode 100644
index 0000000..a894113
--- /dev/null
+++ b/reg-tests/http-rules/forwarded-header-7239.vtc
@@ -0,0 +1,171 @@
+varnishtest "Test RFC 7239 forwarded header support (forwarded option and related converters)"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.8-dev0)'"
+
+# This config tests the HTTP forwarded option and RFC7239 related converters.
+
+feature ignore_unknown_macro
+
+#test: converters, parsing and header injection logic
+haproxy h1 -conf {
+ global
+ # WT: limit false-positives causing "HTTP header incomplete" due to
+ # idle server connections being randomly used and randomly expiring
+ # under us.
+ tune.idle-pool.shared off
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ http-request set-src hdr(x-src)
+ http-request set-dst hdr(x-dst)
+ http-request set-header host %[str(vtest)]
+ use_backend be1 if { path /req1 }
+ use_backend be2 if { path /req2 }
+ use_backend be3 if { path /req3 }
+ use_backend be4 if { path /req4 }
+
+ frontend fe2
+ bind "fd@${fe2}"
+ http-request return status 200 hdr forwarded "%[req.hdr(forwarded)]"
+
+ backend be1
+ option forwarded
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be2
+ option forwarded for-expr src for_port-expr str(id) by by_port-expr int(10)
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be3
+ acl valid req.hdr(forwarded),rfc7239_is_valid
+ http-request return status 200 if valid
+ http-request return status 400
+
+ backend be4
+ http-request set-var(req.fnode) req.hdr(forwarded),rfc7239_field(for)
+ http-request return status 200 hdr nodename "%[var(req.fnode),rfc7239_n2nn]" hdr nodeport "%[var(req.fnode),rfc7239_n2np]"
+
+} -start
+
+#test: "default" and "no option forwarded"
+haproxy h2 -conf {
+ global
+ # WT: limit false-positives causing "HTTP header incomplete" due to
+ # idle server connections being randomly used and randomly expiring
+ # under us.
+ tune.idle-pool.shared off
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ option forwarded
+
+ frontend fe1
+ bind "fd@${fe1h2}"
+ use_backend default if { path /default }
+ use_backend override if { path /override }
+ use_backend disabled if { path /disabled }
+
+ backend default
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend override
+ option forwarded host-expr str(override)
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend disabled
+ no option forwarded
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /req1 \
+ -hdr "x-src: 127.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.forwarded == "proto=http;for=127.0.0.1"
+
+ txreq -req GET -url /req2 \
+ -hdr "x-src: 127.0.0.2" \
+ -hdr "x-dst: 127.0.0.3"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.forwarded == "by=\"127.0.0.3:10\";for=\"127.0.0.2:_id\""
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: for=\"unknown:132\";host=\"[::1]:65535\";by=\"_obfs:_port\";proto=https"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: for=\"127.0.0.1\";host=v.test"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: fore=\"unknown:132\""
+ rxresp
+ expect resp.status == 400
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: proto=http;proto=http"
+ rxresp
+ expect resp.status == 400
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: \""
+ rxresp
+ expect resp.status == 400
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: by=[::1]"
+ rxresp
+ expect resp.status == 400
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: by=\"[::1]\""
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: by=\"[::1]:\""
+ rxresp
+ expect resp.status == 400
+
+ txreq -req GET -url /req3 \
+ -hdr "forwarded: by=\"[::1]:3\""
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req4 \
+ -hdr "forwarded: proto=http;for=\"[::1]:_id\""
+ rxresp
+ expect resp.status == 200
+ expect resp.http.nodename == "::1"
+ expect resp.http.nodeport == "_id"
+} -run
+
+client c2 -connect ${h2_fe1h2_sock} {
+ txreq -req GET -url /default
+ rxresp
+ expect resp.status == 200
+ expect resp.http.forwarded != <undef>
+
+ txreq -req GET -url /override
+ rxresp
+ expect resp.status == 200
+ expect resp.http.forwarded == "host=\"override\""
+
+ txreq -req GET -url /disabled
+ rxresp
+ expect resp.status == 200
+ expect resp.http.forwarded == <undef>
+} -run
diff --git a/reg-tests/http-rules/h1or2_to_h1c.vtc b/reg-tests/http-rules/h1or2_to_h1c.vtc
new file mode 100644
index 0000000..3dd907e
--- /dev/null
+++ b/reg-tests/http-rules/h1or2_to_h1c.vtc
@@ -0,0 +1,233 @@
+varnishtest "Composite HTTP manipulation test (H1 and H2 clear to H1 clear)"
+
+# 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:" \
+ -body "This is a body"
+
+ expect req.method == "GET"
+ expect req.http.fe-sl1-crc == 1874847043
+ expect req.http.fe-sl2-crc == 1142278307
+ expect req.http.fe-hdr-crc == 1719311923
+ expect req.http.be-sl1-crc == 3455320059
+ expect req.http.be-sl2-crc == 2509326257
+ expect req.http.be-hdr-crc == 3634102538
+} -repeat 2 -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe
+ bind "fd@${feh1}"
+ bind "fd@${feh2}" proto h2
+
+ #### 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-var(req.cl) req.fhdr(content-length)
+
+ http-request set-header sl1 "sl1: "
+
+ http-request set-method "%[str(GET)]"
+ http-request set-uri %[str(),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 sl1 "%[req.fhdr(sl1)] cl=<%[var(req.cl)]>"
+ 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 sl2 "%[req.fhdr(sl2)] cl=<%[req.fhdr(content-length)]>"
+ 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-var(req.cl) req.fhdr(content-length)
+
+ http-request set-header sl1 "sl1: "
+
+ http-request set-method "%[str(GET)]"
+ http-request set-uri %[str(),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 sl1 "%[req.fhdr(sl1)] cl=<%[var(req.cl)]>"
+ 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,,i)]>"
+ http-request set-header sl2 "%[req.fhdr(sl2)] cl=<%[req.fhdr(content-length)]>"
+ 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]"
+ http-response allow
+ http-response deny # must not be evaluated
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1h1 -connect ${h1_feh1_sock} {
+ txreq \
+ -req GET \
+ -url /path/to/file.extension?qs_arg=qs_value \
+ -hdr "content-length: 000, 00" \
+ -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
+
+client c1h2 -connect ${h1_feh2_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+ stream 1 {
+ # warning: -req, -scheme, -url MUST be placed first otherwise
+ # the H2 protocol is invalid since they are pseudo-headers
+ txreq \
+ -req GET \
+ -scheme "https" \
+ -url /path/to/file.extension?qs_arg=qs_value \
+ -hdr "content-length" "000, 00" \
+ -hdr "hdr1" "val1" \
+ -hdr "hdr2" " val2a" \
+ -hdr "hdr2" " val2b" \
+ -hdr "hdr3" " val3a, val3b" \
+ -hdr "hdr4" ""
+
+ rxhdrs
+ 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.http.content-length == 14
+
+ rxdata -all
+ expect resp.body == "This is a body"
+ } -run
+} -run
diff --git a/reg-tests/http-rules/http_after_response.vtc b/reg-tests/http-rules/http_after_response.vtc
new file mode 100644
index 0000000..7e8cc1d
--- /dev/null
+++ b/reg-tests/http-rules/http_after_response.vtc
@@ -0,0 +1,192 @@
+varnishtest "Test HTTP response manipulation under the http-after-response rulesets"
+#REQUIRE_VERSION=2.2
+
+# This config tests various http-after-response rules for HTTP responses from a
+# server and the stats applet, but also for internal responses
+# (deny/redirect/auth/return).
+
+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:" \
+ -body "This is a body"
+} -repeat 2 -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe
+ bind "fd@${feh1}"
+
+ http-request deny if { path /deny }
+ http-request redirect location / if { path /redir }
+ http-request auth if { path /auth }
+
+ http-after-response allow if { status eq 403 }
+ http-after-response allow if { status eq 302 }
+ http-after-response allow if { status eq 401 }
+
+ http-after-response set-header be-sl1 "%[res.fhdr(sl1)]"
+ http-after-response set-header be-sl2 "%[res.fhdr(sl2)]"
+ http-after-response set-header be-hdr "%[res.fhdr(hdr)]"
+
+ http-after-response set-header be-sl1-crc "%[res.fhdr(sl1-crc)]"
+ http-after-response set-header be-sl2-crc "%[res.fhdr(sl2-crc)]"
+ http-after-response set-header be-hdr-crc "%[res.fhdr(hdr-crc)]"
+
+ http-after-response set-var(res.status) status
+ http-after-response set-header sl1 "sl1: "
+
+ http-after-response set-status 200
+
+ http-after-response set-header sl2 "sl2: "
+
+ http-after-response set-header sl1 "%[res.fhdr(sl1)] status=<%[var(res.status)]>;"
+ http-after-response set-header sl2 "%[res.fhdr(sl2)] status=<%[status]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr1=<%[res.hdr(hdr1)]>; fhdr1=<%[res.fhdr(hdr1)]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr2=<%[res.hdr(hdr2)]>; fhdr2=<%[res.fhdr(hdr2)]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr3=<%[res.hdr(hdr3)]>; fhdr3=<%[res.fhdr(hdr3)]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr4=<%[res.hdr(hdr4)]>; fhdr4=<%[res.fhdr(hdr4)]>;"
+
+ http-after-response set-header fe-sl1-crc "%[res.fhdr(sl1),crc32]"
+ http-after-response set-header fe-sl2-crc "%[res.fhdr(sl2),crc32]"
+ http-after-response set-header fe-hdr-crc "%[res.fhdr(hdr),crc32]"
+
+ default_backend be
+
+ backend be
+ stats enable
+ stats uri /stats
+
+ http-request return status 234 content-type "text/plain" string "This is a body" if { path /return }
+
+ http-response deny if { capture.req.uri /deny-srv }
+
+ http-after-response allow if { status eq 502 }
+
+ http-after-response set-status 234 if { capture.req.uri /stats }
+ http-after-response add-header hdr1 val1 unless { capture.req.uri / }
+ http-after-response add-header hdr2 val2a unless { capture.req.uri / }
+ http-after-response add-header hdr2 val2b unless { capture.req.uri / }
+ http-after-response add-header hdr3 "val3a, val3b" unless { capture.req.uri / }
+ http-after-response add-header hdr4 "%[str()]" unless { capture.req.uri / }
+ http-after-response del-header content-type
+
+ http-after-response set-var(res.status) status
+ http-after-response set-header sl1 "sl1: "
+
+ http-after-response set-status 200
+
+ http-after-response set-header sl2 "sl2: "
+
+ http-after-response set-header sl1 "%[res.fhdr(sl1)] status=<%[var(res.status)]>;"
+ http-after-response set-header sl2 "%[res.fhdr(sl2)] status=<%[status]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr1=<%[res.hdr(hdr1)]>; fhdr1=<%[res.fhdr(hdr1)]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr2=<%[res.hdr(hdr2)]>; fhdr2=<%[res.fhdr(hdr2)]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr3=<%[res.hdr(hdr3)]>; fhdr3=<%[res.fhdr(hdr3)]>;"
+ http-after-response set-header hdr "%[res.fhdr(hdr)] hdr4=<%[res.hdr(hdr4)]>; fhdr4=<%[res.fhdr(hdr4)]>;"
+
+ http-after-response set-header sl1-crc "%[res.fhdr(sl1),crc32]"
+ http-after-response set-header sl2-crc "%[res.fhdr(sl2),crc32]"
+ http-after-response set-header hdr-crc "%[res.fhdr(hdr),crc32]"
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_feh1_sock} {
+ txreq -req GET -url /
+ 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.http.content-type == <undef>
+ expect resp.bodylen == 14
+ expect resp.body == "This is a body"
+
+ txreq -req GET -url /return
+ 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.http.content-type == <undef>
+ expect resp.bodylen == 14
+ expect resp.body == "This is a body"
+
+ txreq -req GET -url /stats
+ 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.http.content-type == <undef>
+} -run
+
+client c2 -connect ${h1_feh1_sock} {
+ txreq -req GET -url /deny
+ rxresp
+ expect resp.status == 403
+ expect resp.http.be-sl1 == <undef>
+ expect resp.http.be-sl2 == <undef>
+ expect resp.http.be-hdr == <undef>
+ expect resp.http.sl1 == <undef>
+ expect resp.http.sl2 == <undef>
+ expect resp.http.hdr == <undef>
+} -run
+
+client c3 -connect ${h1_feh1_sock} {
+ txreq -req GET -url /redir
+ rxresp
+ expect resp.status == 302
+ expect resp.http.be-sl1 == <undef>
+ expect resp.http.be-sl2 == <undef>
+ expect resp.http.be-hdr == <undef>
+ expect resp.http.sl1 == <undef>
+ expect resp.http.sl2 == <undef>
+ expect resp.http.hdr == <undef>
+} -run
+
+client c4 -connect ${h1_feh1_sock} {
+ txreq -req GET -url /auth
+ rxresp
+ expect resp.status == 401
+ expect resp.http.be-sl1 == <undef>
+ expect resp.http.be-sl2 == <undef>
+ expect resp.http.be-hdr == <undef>
+ expect resp.http.sl1 == <undef>
+ expect resp.http.sl2 == <undef>
+ expect resp.http.hdr == <undef>
+} -run
+
+client c5 -connect ${h1_feh1_sock} {
+ txreq -req GET -url /deny-srv
+ rxresp
+ expect resp.status == 200
+ expect resp.http.be-sl1 == ""
+ expect resp.http.be-sl2 == ""
+ expect resp.http.be-hdr == ""
+ expect resp.http.fe-sl1-crc == 3104968915
+ expect resp.http.fe-sl2-crc == 561949791
+ expect resp.http.fe-hdr-crc == 623352154
+} -run
diff --git a/reg-tests/http-rules/http_return.vtc b/reg-tests/http-rules/http_return.vtc
new file mode 100644
index 0000000..ae96775
--- /dev/null
+++ b/reg-tests/http-rules/http_return.vtc
@@ -0,0 +1,99 @@
+varnishtest "Test the HTTP return action"
+#REQUIRE_VERSION=2.2
+
+# This config tests the HTTP return action.
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ # WT: limit false-positives causing "HTTP header incomplete" due to
+ # idle server connections being randomly used and randomly expiring
+ # under us.
+ tune.idle-pool.shared off
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ http-request return if { path /def-1 }
+ http-request return hdr "x-custom-hdr" "%[url]" if { path /def-2 }
+ http-request return status 403 if { path /def-3 }
+ http-request return content-type "text/plain" if { path /def-4 }
+
+ http-request return content-type "text/plain" string "hello" hdr "x-custom-hdr" "%[url]" if { path /string }
+ http-request return content-type "text/plain" lf-string "path is %[url]" hdr "x-custom-hdr" "%[url]" if { path /lf-string }
+ http-request return content-type "text/plain" file /dev/null hdr "x-custom-hdr" "%[url]" if { path /empty-file }
+ http-request return content-type "text/plain" file ${testdir}/1k.txt hdr "x-custom-hdr" "%[url]" if { path /file }
+ http-request return content-type "text/plain" lf-file ${testdir}/lf-file.txt hdr "x-custom-hdr" "%[url]" if { path /lf-file }
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /def-1
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 0
+ expect resp.http.content-type == <undef>
+ expect resp.http.x-custom-hdr == <undef>
+
+ txreq -req GET -url /def-2
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 0
+ expect resp.http.content-type == <undef>
+ expect resp.http.x-custom-hdr == "/def-2"
+
+ txreq -req GET -url /def-3
+ rxresp
+ expect resp.status == 403
+ expect resp.http.content-length == 0
+ expect resp.http.content-type == <undef>
+
+ txreq -req GET -url /def-4
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 0
+ expect resp.http.content-type == <undef>
+
+ txreq -req GET -url /string
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 5
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.x-custom-hdr == "/string"
+ expect resp.body == "hello"
+
+ txreq -req GET -url /lf-string
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 18
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.x-custom-hdr == "/lf-string"
+ expect resp.body == "path is /lf-string"
+
+ txreq -req GET -url /empty-file
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 0
+ expect resp.http.content-type == <undef>
+ expect resp.http.x-custom-hdr == "/empty-file"
+
+ txreq -req GET -url /file
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 1024
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.x-custom-hdr == "/file"
+
+ txreq -req GET -url /lf-file
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == 17
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.x-custom-hdr == "/lf-file"
+ expect resp.body == "path is /lf-file\n"
+} -run
diff --git a/reg-tests/http-rules/ifnone-forwardfor.vtc b/reg-tests/http-rules/ifnone-forwardfor.vtc
new file mode 100644
index 0000000..a743b10
--- /dev/null
+++ b/reg-tests/http-rules/ifnone-forwardfor.vtc
@@ -0,0 +1,98 @@
+varnishtest "Test if-none param for the forwardfor option"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.6-dev0)'"
+
+# This config tests the if-none parameter for the HTTP forwardfor option.
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ # WT: limit false-positives causing "HTTP header incomplete" due to
+ # idle server connections being randomly used and randomly expiring
+ # under us.
+ tune.idle-pool.shared off
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend none
+ bind "fd@${none}"
+ http-request set-src hdr(x-src)
+ option forwardfor if-none
+ use_backend be1 if { path /req1 }
+ use_backend be2 if { path /req2 }
+ use_backend be3 if { path /req3 }
+
+ frontend normal
+ bind "fd@${normal}"
+ http-request set-src hdr(x-src)
+ option forwardfor
+ use_backend be1 if { path /req1 }
+ use_backend be2 if { path /req2 }
+
+
+ frontend fe2
+ bind "fd@${fe2}"
+ http-request return status 200 hdr x-ff "%[req.fhdr_cnt(x-forwarded-for)]"
+
+ backend be1
+ option forwardfor
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be2
+ option forwardfor if-none
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+
+ backend be3
+ server s1 ${h1_fe2_addr}:${h1_fe2_port}
+} -start
+
+client c1 -connect ${h1_none_sock} {
+ txreq -req GET -url /req1 \
+ -hdr "x-src: 10.0.0.128" \
+ -hdr "x-forwarded-for: 127.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == 2
+
+ txreq -req GET -url /req2 \
+ -hdr "x-src: 10.0.0.128" \
+ -hdr "x-forwarded-for: 127.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == 1
+
+ txreq -req GET -url /req2 \
+ -hdr "x-src: 10.0.0.128"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == 1
+
+ txreq -req GET -url /req3 \
+ -hdr "x-src: 10.0.0.128" \
+ -hdr "x-forwarded-for: 127.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == 1
+
+} -run
+
+client c1 -connect ${h1_normal_sock} {
+ txreq -req GET -url /req1 \
+ -hdr "x-src: 10.0.0.128" \
+ -hdr "x-forwarded-for: 127.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == 2
+
+ txreq -req GET -url /req2 \
+ -hdr "x-src: 10.0.0.128" \
+ -hdr "x-forwarded-for: 127.0.0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ff == 2
+
+} -run
diff --git a/reg-tests/http-rules/lf-file.txt b/reg-tests/http-rules/lf-file.txt
new file mode 100644
index 0000000..7fda1d4
--- /dev/null
+++ b/reg-tests/http-rules/lf-file.txt
@@ -0,0 +1 @@
+path is %[url]
diff --git a/reg-tests/http-rules/map_ordering.map b/reg-tests/http-rules/map_ordering.map
new file mode 100644
index 0000000..dcd9529
--- /dev/null
+++ b/reg-tests/http-rules/map_ordering.map
@@ -0,0 +1,4 @@
+# These entries are used for list-based match ordering tests
+first.domain.tld first
+domain.tld domain
+second.domain.tld second
diff --git a/reg-tests/http-rules/map_ordering.vtc b/reg-tests/http-rules/map_ordering.vtc
new file mode 100644
index 0000000..40da465
--- /dev/null
+++ b/reg-tests/http-rules/map_ordering.vtc
@@ -0,0 +1,32 @@
+varnishtest "Test list-based matching types ordering"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+
+ # check list ordering using map_dom (list-based match)
+ http-request return hdr dom %[req.hdr(Host),lower,map_dom(${testdir}/map_ordering.map)] if { url_beg /dom }
+} -start
+
+# Check map ordering
+client c1 -connect ${h1_fe1_sock} {
+ # first.domain.tld is above domain.tld so it should match first
+ txreq -url "/dom" -hdr "Host: first.domain.tld"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.dom == "first"
+
+ # second.domain.tld is below domain.tld so domain.tld should match first
+ txreq -url "/dom" -hdr "Host: second.domain.tld"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.dom == "domain"
+} -run
diff --git a/reg-tests/http-rules/map_redirect-be.map b/reg-tests/http-rules/map_redirect-be.map
new file mode 100644
index 0000000..c8822fc
--- /dev/null
+++ b/reg-tests/http-rules/map_redirect-be.map
@@ -0,0 +1,4 @@
+# These entries are used for use_backend rules
+test1.example.com test1_be
+test1.example.invalid test1_be
+test2.example.com test2_be
diff --git a/reg-tests/http-rules/map_redirect.map b/reg-tests/http-rules/map_redirect.map
new file mode 100644
index 0000000..c4743f6
--- /dev/null
+++ b/reg-tests/http-rules/map_redirect.map
@@ -0,0 +1,5 @@
+# These entries are used for http-request redirect rules
+example.org https://www.example.org
+subdomain.example.org https://www.subdomain.example.org
+
+/path/to/old/file /path/to/new/file
diff --git a/reg-tests/http-rules/map_redirect.vtc b/reg-tests/http-rules/map_redirect.vtc
new file mode 100644
index 0000000..f55e0d8
--- /dev/null
+++ b/reg-tests/http-rules/map_redirect.vtc
@@ -0,0 +1,200 @@
+varnishtest "haproxy host header: map / redirect tests"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev5) && (feature(PCRE) || feature(PCRE2))'"
+feature ignore_unknown_macro
+
+
+server s1 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.host == "test1.example.com"
+ txresp -body "test1 ok"
+} -start
+
+server s2 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.host == "test2.example.com"
+ txresp -body "test2 ok"
+} -start
+
+server s3 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.host == "test3.example.com"
+ txresp -body "test3 ok"
+} -start
+
+server s4 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.host == "test1.example.invalid"
+ txresp -body "test1 after del map ok"
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ log global
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+
+ # automatically redirect matching paths from maps but skip rule on no-match
+ http-request redirect code 301 location %[path,map_str(${testdir}/map_redirect.map)] ignore-empty
+
+ # redirect Host: example.org / subdomain.example.org
+ http-request redirect prefix %[req.hdr(Host),lower,regsub(:\d+$,,),map_str(${testdir}/map_redirect.map)] code 301 if { hdr(Host),lower,regsub(:\d+$,,),map_str(${testdir}/map_redirect.map) -m found }
+
+ # set var and redirect in be1
+ http-request set-var(txn.testvar) req.hdr(Testvar),lower,regsub(:\d+$,,),map_str(${testdir}/map_redirect.map) if { hdr(Testvar),lower,regsub(:\d+$,,),map_str(${testdir}/map_redirect.map) -m found }
+
+ # use map to select backend (no default map value)
+ use_backend %[req.hdr(Host),lower,map_dom(${testdir}/map_redirect-be.map)] if { hdr_dom(Host) -i test1.example.com || hdr_dom(Host) -i test2.example.com }
+
+ # use map to select backend with default value(test3_be)
+ use_backend %[req.hdr(Host),lower,map_dom(${testdir}/map_redirect-be.map,test3_be)] if { hdr_dom(Host) -m end -i example.com }
+
+ # use map(after del map test1.example.com) default value(test4_be)
+ use_backend %[req.hdr(Host),lower,map_dom(${testdir}/map_redirect-be.map,test4_be)] if { hdr_dom(Host) -m end -i example.invalid }
+
+ default_backend be1
+
+ backend be1
+ http-request redirect prefix %[var(txn.testvar)] code 301 if { var(txn.testvar) -m found }
+ http-request deny
+
+ backend test1_be
+ server s1 ${s1_addr}:${s1_port}
+
+ backend test2_be
+ server s2 ${s2_addr}:${s2_port}
+
+ backend test3_be
+ server s3 ${s3_addr}:${s3_port}
+
+ backend test4_be
+ server s4 ${s4_addr}:${s4_port}
+} -start
+
+# Check map redirects
+client c1 -connect ${h1_fe1_sock} {
+ txreq -hdr "Host: example.org:8443"
+ rxresp
+ expect resp.status == 301
+ expect resp.http.location ~ "https://www.example.org"
+
+ txreq -url /path/to/old/file
+ rxresp
+ expect resp.status == 301
+ expect resp.http.location ~ "/path/to/new/file"
+
+ # Closes connection
+} -run
+
+client c2 -connect ${h1_fe1_sock} {
+ txreq -hdr "Host: subdomain.example.org"
+ rxresp
+ expect resp.status == 301
+ expect resp.http.location ~ "https://www.subdomain.example.org"
+ # Closes connection
+} -run
+
+client c3 -connect ${h1_fe1_sock} {
+ # redirect on Testvar header
+ txreq -hdr "Testvar: subdomain.example.org"
+ rxresp
+ expect resp.status == 301
+ expect resp.http.location ~ "https://www.subdomain.example.org"
+ # Closes connection
+} -run
+
+client c4 -connect ${h1_fe1_sock} {
+ txreq -hdr "Host: www.subdomain.example.org"
+ rxresp
+ expect resp.status == 403
+ # Closes connection
+} -run
+
+client c5 -connect ${h1_fe1_sock} {
+ txreq -hdr "Testvar: www.subdomain.example.org"
+ rxresp
+ expect resp.status == 403
+ # Closes connection
+} -run
+
+client c6 -connect ${h1_fe1_sock} {
+ txreq -hdr "Host: :8443example.org"
+ rxresp
+ expect resp.status == 403
+ # Closes connection
+} -run
+
+# Check map backend selection
+client c7 -connect ${h1_fe1_sock} {
+ txreq -hdr "Host: test1.example.com"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "test1 ok"
+
+ txreq -hdr "Host: test2.example.com"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "test2 ok"
+
+ txreq -hdr "Host: test3.example.com"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "test3 ok"
+} -run
+
+# cli show maps
+haproxy h1 -cli {
+ send "show map ${testdir}/map_redirect.map"
+ expect ~ "^0x[a-f0-9]+ example\\.org https://www\\.example\\.org\\n0x[a-f0-9]+ subdomain\\.example\\.org https://www\\.subdomain\\.example\\.org\\n0x[a-f0-9]+ /path/to/old/file /path/to/new/file\n$"
+
+ send "show map ${testdir}/map_redirect-be.map"
+ expect ~ "^0x[a-f0-9]+ test1\\.example\\.com test1_be\\n0x[a-f0-9]+ test1\\.example\\.invalid test1_be\\n0x[a-f0-9]+ test2\\.example\\.com test2_be\\n$"
+}
+
+haproxy h1 -cli {
+ # clear map ${testdir}/map_redirect.map
+ send "clear map ${testdir}/map_redirect.map"
+ expect ~ "^\\n"
+
+ send "show map ${testdir}/map_redirect.map"
+ expect ~ "^\\n"
+
+ # add map ${testdir}/map_redirect.map
+ send "add map ${testdir}/map_redirect.map site1_key site1_value"
+ expect ~ "^\\n"
+
+ # add 2 more entries as payload
+ send "add map ${testdir}/map_redirect.map <<\nsite2_key site2_value\nsite3_key site3_value\n"
+ expect ~ "^\\n"
+
+ send "show map ${testdir}/map_redirect.map"
+ expect ~ "^0x[a-f0-9]+ site1_key site1_value\\n0x[a-f0-9]+ site2_key site2_value\\n0x[a-f0-9]+ site3_key site3_value\\n$"
+
+ # del map ${testdir}/map_redirect-be.map test1.example.{com,invalid}
+ send "del map ${testdir}/map_redirect-be.map test1.example.com"
+ expect ~ "^\\n"
+
+ send "del map ${testdir}/map_redirect-be.map test1.example.invalid"
+ expect ~ "^\\n"
+
+ send "show map ${testdir}/map_redirect-be.map"
+ expect ~ "^0x[a-f0-9]+ test2\\.example\\.com test2_be\\n$"
+}
+
+# Check map backend after del map
+client c6 -connect ${h1_fe1_sock} {
+ # test1.example.invalid should go to test4_be after del map
+ txreq -hdr "Host: test1.example.invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "test1 after del map ok"
+} -run
diff --git a/reg-tests/http-rules/map_regm_with_backref.map b/reg-tests/http-rules/map_regm_with_backref.map
new file mode 100644
index 0000000..08ffcfb
--- /dev/null
+++ b/reg-tests/http-rules/map_regm_with_backref.map
@@ -0,0 +1 @@
+^(.*)\.(.*)$ \1_AND_\2
diff --git a/reg-tests/http-rules/map_regm_with_backref.vtc b/reg-tests/http-rules/map_regm_with_backref.vtc
new file mode 100644
index 0000000..c3b21fb
--- /dev/null
+++ b/reg-tests/http-rules/map_regm_with_backref.vtc
@@ -0,0 +1,73 @@
+#commit 271022150d7961b9aa39dbfd88e0c6a4bc48c3ee
+# BUG/MINOR: map: fix map_regm with backref
+#
+# Due to a cascade of get_trash_chunk calls the sample is
+# corrupted when we want to read it.
+#
+# The fix consist to use a temporary chunk to copy the sample
+# value and use it.
+
+varnishtest "map_regm get_trash_chunk test"
+feature ignore_unknown_macro
+
+#REGTEST_TYPE=bug
+
+syslog S1 -level notice {
+ recv info
+ # not expecting ${h1_pid} with master-worker
+ expect ~ "[^:\\[ ]\\[[[:digit:]]+\\]: .* fe1 be1/s1 [[:digit:]]+/[[:digit:]]+/[[:digit:]]+/[[:digit:]]+/[[:digit:]]+ 200 [[:digit:]]+ - - ---- .* \"GET / HTTP/(1|2)(\\.1)?\""
+} -start
+
+server s1 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.x-mapped-from-header == example_AND_org
+ expect req.http.x-mapped-from-var == example_AND_org
+ txresp
+
+ rxreq
+ expect req.method == "GET"
+ expect req.http.x-mapped-from-header == www.example_AND_org
+ expect req.http.x-mapped-from-var == www.example_AND_org
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ log ${S1_addr}:${S1_port} local0 debug err
+
+ defaults
+ mode http
+ log global
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ # Remove port from Host header
+ http-request replace-value Host '(.*):.*' '\1'
+ # Store host header in variable
+ http-request set-var(txn.host) req.hdr(Host)
+ # This works correctly
+ http-request set-header X-Mapped-From-Header %[req.hdr(Host),map_regm(${testdir}/map_regm_with_backref.map,"unknown")]
+ # This breaks before commit 271022150d7961b9aa39dbfd88e0c6a4bc48c3ee
+ http-request set-header X-Mapped-From-Var %[var(txn.host),map_regm(${testdir}/map_regm_with_backref.map,"unknown")]
+
+ default_backend be1
+
+ backend be1
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -hdr "Host: example.org:8443"
+ rxresp
+ expect resp.status == 200
+
+ txreq -hdr "Host: www.example.org"
+ rxresp
+ expect resp.status == 200
+} -run
+
diff --git a/reg-tests/http-rules/normalize_uri.vtc b/reg-tests/http-rules/normalize_uri.vtc
new file mode 100644
index 0000000..ad7b44a
--- /dev/null
+++ b/reg-tests/http-rules/normalize_uri.vtc
@@ -0,0 +1,549 @@
+varnishtest "normalize-uri tests"
+#REQUIRE_VERSION=2.4
+
+# This reg-test tests the http-request normalize-uri action.
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "connection: close"
+} -repeat 70 -start
+
+haproxy h1 -conf {
+ global
+ # WT: limit false-positives causing "HTTP header incomplete" due to
+ # idle server connections being randomly used and randomly expiring
+ # under us.
+ tune.idle-pool.shared off
+ expose-experimental-directives
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe_path_merge_slashes
+ bind "fd@${fe_path_merge_slashes}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri path-merge-slashes
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_path_strip_dotdot
+ bind "fd@${fe_path_strip_dotdot}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri path-strip-dotdot
+ http-request set-var(txn.after) url
+
+ http-request set-uri %[var(txn.before)]
+ http-request normalize-uri path-strip-dotdot full
+ http-request set-var(txn.after_full) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+ http-response add-header after-full %[var(txn.after_full)]
+
+ default_backend be
+
+ frontend fe_sort_query_by_name
+ bind "fd@${fe_sort_query_by_name}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri query-sort-by-name
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_percent_to_uppercase
+ bind "fd@${fe_percent_to_uppercase}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri percent-to-uppercase
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_percent_to_uppercase_strict
+ bind "fd@${fe_percent_to_uppercase_strict}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri percent-to-uppercase strict
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_dot
+ bind "fd@${fe_dot}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri path-strip-dot
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_percent_decode_unreserved
+ bind "fd@${fe_percent_decode_unreserved}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri percent-decode-unreserved
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_percent_decode_unreserved_strict
+ bind "fd@${fe_percent_decode_unreserved_strict}"
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri percent-decode-unreserved strict
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_fragment_strip
+ bind "fd@${fe_fragment_strip}"
+ option accept-invalid-http-request
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri fragment-strip
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_fragment_encode
+ bind "fd@${fe_fragment_encode}"
+ option accept-invalid-http-request
+
+ http-request set-var(txn.before) url
+ http-request normalize-uri fragment-encode
+ http-request set-var(txn.after) url
+
+ http-response add-header before %[var(txn.before)]
+ http-response add-header after %[var(txn.after)]
+
+ default_backend be
+
+ frontend fe_fragment_block
+ bind "fd@${fe_fragment_block}"
+ http-request normalize-uri fragment-strip
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_fe_path_merge_slashes_sock} {
+ txreq -url "/foo/bar"
+ rxresp
+ expect resp.http.before == "/foo/bar"
+ expect resp.http.after == "/foo/bar"
+
+ txreq -url "/foo//bar"
+ rxresp
+ expect resp.http.before == "/foo//bar"
+ expect resp.http.after == "/foo/bar"
+
+ txreq -url "/foo///bar"
+ rxresp
+ expect resp.http.before == "/foo///bar"
+ expect resp.http.after == "/foo/bar"
+
+ txreq -url "///foo///bar"
+ rxresp
+ expect resp.http.before == "///foo///bar"
+ expect resp.http.after == "/foo/bar"
+
+ txreq -url "///foo/bar"
+ rxresp
+ expect resp.http.before == "///foo/bar"
+ expect resp.http.after == "/foo/bar"
+
+ txreq -url "///foo///bar///"
+ rxresp
+ expect resp.http.before == "///foo///bar///"
+ expect resp.http.after == "/foo/bar/"
+
+ txreq -url "///"
+ rxresp
+ expect resp.http.before == "///"
+ expect resp.http.after == "/"
+
+ txreq -url "/foo?bar=///"
+ rxresp
+ expect resp.http.before == "/foo?bar=///"
+ expect resp.http.after == "/foo?bar=///"
+
+ txreq -url "//foo?bar=///"
+ rxresp
+ expect resp.http.before == "//foo?bar=///"
+ expect resp.http.after == "/foo?bar=///"
+
+ txreq -req OPTIONS -url "*"
+ rxresp
+ expect resp.http.before == "*"
+ expect resp.http.after == "*"
+} -run
+
+client c2 -connect ${h1_fe_path_strip_dotdot_sock} {
+ txreq -url "/foo/bar"
+ rxresp
+ expect resp.http.before == "/foo/bar"
+ expect resp.http.after == "/foo/bar"
+ expect resp.http.after-full == "/foo/bar"
+
+ txreq -url "/foo/.."
+ rxresp
+ expect resp.http.before == "/foo/.."
+ expect resp.http.after == "/"
+ expect resp.http.after-full == "/"
+
+ txreq -url "/foo/../"
+ rxresp
+ expect resp.http.before == "/foo/../"
+ expect resp.http.after == "/"
+ expect resp.http.after-full == "/"
+
+ txreq -url "/foo/bar/../"
+ rxresp
+ expect resp.http.before == "/foo/bar/../"
+ expect resp.http.after == "/foo/"
+ expect resp.http.after-full == "/foo/"
+
+ txreq -url "/foo/../bar"
+ rxresp
+ expect resp.http.before == "/foo/../bar"
+ expect resp.http.after == "/bar"
+ expect resp.http.after-full == "/bar"
+
+ txreq -url "/foo/../bar/"
+ rxresp
+ expect resp.http.before == "/foo/../bar/"
+ expect resp.http.after == "/bar/"
+ expect resp.http.after-full == "/bar/"
+
+ txreq -url "/foo/../../bar/"
+ rxresp
+ expect resp.http.before == "/foo/../../bar/"
+ expect resp.http.after == "/../bar/"
+ expect resp.http.after-full == "/bar/"
+
+ txreq -url "/foo//../../bar/"
+ rxresp
+ expect resp.http.before == "/foo//../../bar/"
+ expect resp.http.after == "/bar/"
+ expect resp.http.after-full == "/bar/"
+
+ txreq -url "/foo/?bar=/foo/../"
+ rxresp
+ expect resp.http.before == "/foo/?bar=/foo/../"
+ expect resp.http.after == "/foo/?bar=/foo/../"
+ expect resp.http.after-full == "/foo/?bar=/foo/../"
+
+ txreq -url "/foo/../?bar=/foo/../"
+ rxresp
+ expect resp.http.before == "/foo/../?bar=/foo/../"
+ expect resp.http.after == "/?bar=/foo/../"
+ expect resp.http.after-full == "/?bar=/foo/../"
+
+ txreq -req OPTIONS -url "*"
+ rxresp
+ expect resp.http.before == "*"
+ expect resp.http.after == "*"
+ expect resp.http.after-full == "*"
+} -run
+
+client c3 -connect ${h1_fe_sort_query_by_name_sock} {
+ txreq -url "/?a=a"
+ rxresp
+ expect resp.http.before == "/?a=a"
+ expect resp.http.after == "/?a=a"
+
+ txreq -url "/?a=a&z=z"
+ rxresp
+ expect resp.http.before == "/?a=a&z=z"
+ expect resp.http.after == "/?a=a&z=z"
+
+ txreq -url "/?z=z&a=a"
+ rxresp
+ expect resp.http.before == "/?z=z&a=a"
+ expect resp.http.after == "/?a=a&z=z"
+
+ txreq -url "/?a=z&z=a"
+ rxresp
+ expect resp.http.before == "/?a=z&z=a"
+ expect resp.http.after == "/?a=z&z=a"
+
+ txreq -url "/?z=a&a=z"
+ rxresp
+ expect resp.http.before == "/?z=a&a=z"
+ expect resp.http.after == "/?a=z&z=a"
+
+ txreq -url "/?c&b&a&z&x&y"
+ rxresp
+ expect resp.http.before == "/?c&b&a&z&x&y"
+ expect resp.http.after == "/?a&b&c&x&y&z"
+
+ txreq -url "/?a=&aa=&aaa=&aaaa="
+ rxresp
+ expect resp.http.before == "/?a=&aa=&aaa=&aaaa="
+ expect resp.http.after == "/?a=&aa=&aaa=&aaaa="
+
+ txreq -url "/?aaaa=&a=&aa=&aaa="
+ rxresp
+ expect resp.http.before == "/?aaaa=&a=&aa=&aaa="
+ expect resp.http.after == "/?a=&aa=&aaa=&aaaa="
+
+ txreq -url "/?a=5&a=3&a=1&a=2&a=4"
+ rxresp
+ expect resp.http.before == "/?a=5&a=3&a=1&a=2&a=4"
+ expect resp.http.after == "/?a=5&a=3&a=1&a=2&a=4"
+
+ txreq -url "/?a=5&b=3&a=1&a=2&b=4"
+ rxresp
+ expect resp.http.before == "/?a=5&b=3&a=1&a=2&b=4"
+ expect resp.http.after == "/?a=5&a=1&a=2&b=3&b=4"
+
+ txreq -url "/"
+ rxresp
+ expect resp.http.before == "/"
+ expect resp.http.after == "/"
+
+ txreq -url "/?"
+ rxresp
+ expect resp.http.before == "/?"
+ expect resp.http.after == "/?"
+
+ txreq -req OPTIONS -url "*"
+ rxresp
+ expect resp.http.before == "*"
+ expect resp.http.after == "*"
+} -run
+
+client c4 -connect ${h1_fe_percent_to_uppercase_sock} {
+ txreq -url "/a?a=a"
+ rxresp
+ expect resp.http.before == "/a?a=a"
+ expect resp.http.after == "/a?a=a"
+
+ txreq -url "/%aa?a=%aa"
+ rxresp
+ expect resp.http.before == "/%aa?a=%aa"
+ expect resp.http.after == "/%AA?a=%AA"
+
+ txreq -url "/%zz?a=%zz"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.before == "/%zz?a=%zz"
+ expect resp.http.after == "/%zz?a=%zz"
+
+ txreq -req OPTIONS -url "*"
+ rxresp
+ expect resp.http.before == "*"
+ expect resp.http.after == "*"
+} -run
+
+client c5 -connect ${h1_fe_percent_to_uppercase_strict_sock} {
+ txreq -url "/a?a=a"
+ rxresp
+ expect resp.http.before == "/a?a=a"
+ expect resp.http.after == "/a?a=a"
+
+ txreq -url "/%aa?a=%aa"
+ rxresp
+ expect resp.http.before == "/%aa?a=%aa"
+ expect resp.http.after == "/%AA?a=%AA"
+
+ txreq -url "/%zz?a=%zz"
+ rxresp
+ expect resp.status == 400
+} -run
+
+client c6 -connect ${h1_fe_dot_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.http.before == "/"
+ expect resp.http.after == "/"
+
+ txreq -url "/a/b"
+ rxresp
+ expect resp.http.before == "/a/b"
+ expect resp.http.after == "/a/b"
+
+ txreq -url "/."
+ rxresp
+ expect resp.http.before == "/."
+ expect resp.http.after == "/"
+
+ txreq -url "/./"
+ rxresp
+ expect resp.http.before == "/./"
+ expect resp.http.after == "/"
+
+ txreq -url "/a/."
+ rxresp
+ expect resp.http.before == "/a/."
+ expect resp.http.after == "/a/"
+
+ txreq -url "/a."
+ rxresp
+ expect resp.http.before == "/a."
+ expect resp.http.after == "/a."
+
+ txreq -url "/.a"
+ rxresp
+ expect resp.http.before == "/.a"
+ expect resp.http.after == "/.a"
+
+ txreq -url "/a/."
+ rxresp
+ expect resp.http.before == "/a/."
+ expect resp.http.after == "/a/"
+
+ txreq -url "/a/./"
+ rxresp
+ expect resp.http.before == "/a/./"
+ expect resp.http.after == "/a/"
+
+ txreq -url "/a/./a"
+ rxresp
+ expect resp.http.before == "/a/./a"
+ expect resp.http.after == "/a/a"
+
+ txreq -url "/a/../"
+ rxresp
+ expect resp.http.before == "/a/../"
+ expect resp.http.after == "/a/../"
+
+ txreq -url "/a/../a"
+ rxresp
+ expect resp.http.before == "/a/../a"
+ expect resp.http.after == "/a/../a"
+
+ txreq -url "/?a=/./"
+ rxresp
+ expect resp.http.before == "/?a=/./"
+ expect resp.http.after == "/?a=/./"
+} -run
+
+client c7 -connect ${h1_fe_percent_decode_unreserved_sock} {
+ txreq -url "/a?a=a"
+ rxresp
+ expect resp.http.before == "/a?a=a"
+ expect resp.http.after == "/a?a=a"
+
+ txreq -url "/%61?%61=%61"
+ rxresp
+ expect resp.http.before == "/%61?%61=%61"
+ expect resp.http.after == "/a?a=a"
+
+ txreq -url "/%3F?foo=bar"
+ rxresp
+ expect resp.http.before == "/%3F?foo=bar"
+ expect resp.http.after == "/%3F?foo=bar"
+
+ txreq -url "/%%36%36"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.before == "/%%36%36"
+ expect resp.http.after == "/%66"
+
+ txreq -req OPTIONS -url "*"
+ rxresp
+ expect resp.http.before == "*"
+ expect resp.http.after == "*"
+} -run
+
+client c8 -connect ${h1_fe_percent_decode_unreserved_strict_sock} {
+ txreq -url "/a?a=a"
+ rxresp
+ expect resp.http.before == "/a?a=a"
+ expect resp.http.after == "/a?a=a"
+
+ txreq -url "/%61?%61=%61"
+ rxresp
+ expect resp.http.before == "/%61?%61=%61"
+ expect resp.http.after == "/a?a=a"
+
+ txreq -url "/%3F?foo=bar"
+ rxresp
+ expect resp.http.before == "/%3F?foo=bar"
+ expect resp.http.after == "/%3F?foo=bar"
+
+ txreq -url "/%%36%36"
+ rxresp
+ expect resp.status == 400
+} -run
+
+client c9 -connect ${h1_fe_fragment_strip_sock} {
+ txreq -url "/#foo"
+ rxresp
+ expect resp.http.before == "/#foo"
+ expect resp.http.after == "/"
+
+ txreq -url "/%23foo"
+ rxresp
+ expect resp.http.before == "/%23foo"
+ expect resp.http.after == "/%23foo"
+
+ txreq -req OPTIONS -url "*"
+ rxresp
+ expect resp.http.before == "*"
+ expect resp.http.after == "*"
+} -run
+
+client c10 -connect ${h1_fe_fragment_encode_sock} {
+ txreq -url "/#foo"
+ rxresp
+ expect resp.http.before == "/#foo"
+ expect resp.http.after == "/%23foo"
+
+ txreq -url "/#foo/#foo"
+ rxresp
+ expect resp.http.before == "/#foo/#foo"
+ expect resp.http.after == "/%23foo/%23foo"
+
+ txreq -url "/%23foo"
+ rxresp
+ expect resp.http.before == "/%23foo"
+ expect resp.http.after == "/%23foo"
+
+ txreq -req OPTIONS -url "*"
+ rxresp
+ expect resp.http.before == "*"
+ expect resp.http.after == "*"
+} -run
+
+client c11 -connect ${h1_fe_fragment_block_sock} {
+ txreq -url "/#foo"
+ rxresp
+ expect resp.status == 400
+} -run
diff --git a/reg-tests/http-rules/path_and_pathq.vtc b/reg-tests/http-rules/path_and_pathq.vtc
new file mode 100644
index 0000000..31e85be
--- /dev/null
+++ b/reg-tests/http-rules/path_and_pathq.vtc
@@ -0,0 +1,64 @@
+varnishtest "path vs pathq tests"
+#REQUIRE_VERSION=2.2
+
+# This config tests various http request rules (set/replace) manipulating the
+# path, with or without the query-string. It also test path and pathq sample
+# fetches.
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == /regtest/foo/fe/req1/bar?param1=val1&param2=val2
+ expect req.http.x-path == /req1
+ expect req.http.x-pathq == /req1?param1=val1&param2=val2
+ expect req.http.x-query == param1=val1&param2=val2
+ expect req.http.x-url == /req1?param1=val1&param2=val2
+ txresp
+
+ rxreq
+ expect req.url == http://127.0.0.1/regtest/foo/fe/req2/bar?param1=val1&param2=val2
+ expect req.http.x-path == /req2
+ expect req.http.x-pathq == /req2?param1=val1&param2=val2
+ expect req.http.x-query == param1=val1&param2=val2
+ expect req.http.x-url == http://127.0.0.1/req2?param1=val1&param2=val2
+ txresp
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe
+ bind "fd@${fe}"
+
+ http-request add-header x-path %[path]
+ http-request add-header x-pathq %[pathq]
+ http-request add-header x-query %[query]
+ http-request add-header x-url %[url]
+
+ http-request set-path /fe%[path]
+ http-request replace-path (.*) /foo\1
+ http-request replace-path (.*) \1/bar
+ http-request set-pathq %[path]?app=regtest&%[query]
+ http-request replace-pathq /([^?]*)\?app=([^&]*)&?(.*) /\2/\1?\3
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -req GET -url /req1?param1=val1&param2=val2
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url http://127.0.0.1/req2?param1=val1&param2=val2
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/http-rules/restrict_req_hdr_names.vtc b/reg-tests/http-rules/restrict_req_hdr_names.vtc
new file mode 100644
index 0000000..4b26e33
--- /dev/null
+++ b/reg-tests/http-rules/restrict_req_hdr_names.vtc
@@ -0,0 +1,185 @@
+varnishtest "http-restrict-req-hdr-names option tests"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.0-dev0)'"
+
+# This config tests "http-restrict-req-hdr-names" option
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.http.x-my_hdr == on
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ expect req.http.x-my_hdr == <undef>
+ txresp
+} -start
+
+server s3 {
+ rxreq
+ expect req.http.x-my_hdr == on
+ txresp
+} -start
+
+server s4 {
+ rxreq
+ expect req.http.x-my_hdr == <undef>
+ txresp
+} -start
+
+server s5 {
+ rxreq
+ expect req.http.x-my_hdr == on
+ txresp
+} -start
+
+server s6 {
+ rxreq
+ expect req.http.x_my_hdr_with_lots_of_underscores == <undef>
+ txresp
+} -start
+
+server s7 {
+ rxreq
+ expect req.http.x_my_hdr-1 == <undef>
+ expect req.http.x-my-hdr-2 == on
+ txresp
+} -start
+
+server s8 {
+ rxreq
+ expect req.http.x-my_hdr-1 == <undef>
+ expect req.http.x-my_hdr-2 == <undef>
+ txresp
+} -start
+
+server s9 {
+ rxreq
+ expect req.http.x-my-hdr-with-trailing-underscore_ == <undef>
+ txresp
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ use_backend be-http1 if { path /req1 }
+ use_backend be-http2 if { path /req2 }
+ use_backend be-http3 if { path /req3 }
+ use_backend be-fcgi1 if { path /req4 }
+ use_backend be-fcgi2 if { path /req5 }
+ use_backend be-fcgi3 if { path /req6 }
+ use_backend be-http4 if { path /req7 }
+ use_backend be-http5 if { path /req8 }
+ use_backend be-http6 if { path /req9 }
+ use_backend be-http7 if { path /req10 }
+
+ backend be-http1
+ server s1 ${s1_addr}:${s1_port}
+
+ backend be-http2
+ option http-restrict-req-hdr-names delete
+ server s2 ${s2_addr}:${s2_port}
+
+ backend be-http3
+ option http-restrict-req-hdr-names reject
+
+ backend be-fcgi1
+ option http-restrict-req-hdr-names preserve
+ server s3 ${s3_addr}:${s3_port}
+
+ backend be-fcgi2
+ option http-restrict-req-hdr-names delete
+ server s4 ${s4_addr}:${s4_port}
+
+ backend be-fcgi3
+ option http-restrict-req-hdr-names reject
+
+ backend be-http4
+ option http-restrict-req-hdr-names delete
+ server s6 ${s6_addr}:${s6_port}
+
+ backend be-http5
+ option http-restrict-req-hdr-names delete
+ server s7 ${s7_addr}:${s7_port}
+
+ backend be-http6
+ option http-restrict-req-hdr-names delete
+ server s8 ${s8_addr}:${s8_port}
+
+ backend be-http7
+ option http-restrict-req-hdr-names delete
+ server s9 ${s9_addr}:${s9_port}
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ option http-restrict-req-hdr-names preserve
+
+ frontend fe2
+ bind "fd@${fe2}"
+ default_backend be-fcgi4
+
+ backend be-fcgi4
+ server s5 ${s5_addr}:${s5_port}
+
+ fcgi-app my-fcgi-app
+ docroot ${testdir}
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /req1 -hdr "X-my_hdr: on"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req2 -hdr "X-my_hdr: on"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req3 -hdr "X-my_hdr: on"
+ rxresp
+ expect resp.status == 403
+
+ txreq -req GET -url /req4 -hdr "X-my_hdr: on"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req5 -hdr "X-my_hdr: on"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req6 -hdr "X-my_hdr: on"
+ rxresp
+ expect resp.status == 403
+
+ txreq -req GET -url /req7 -hdr "X_my_hdr_with_lots_of_underscores: on"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req8 -hdr "X_my_hdr-1: on" -hdr "X-my-hdr-2: on"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req9 -hdr "X-my_hdr-1: on" -hdr "X-my_hdr-2: on"
+ rxresp
+ expect resp.status == 200
+
+ txreq -req GET -url /req10 -hdr "X-my-hdr-with-trailing-underscore_: on"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -connect ${h1_fe2_sock} {
+ txreq -req GET -url /req1 -hdr "X-my_hdr: on"
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/http-rules/strict_rw_mode.vtc b/reg-tests/http-rules/strict_rw_mode.vtc
new file mode 100644
index 0000000..14e6901
--- /dev/null
+++ b/reg-tests/http-rules/strict_rw_mode.vtc
@@ -0,0 +1,164 @@
+varnishtest "Test the strict rewriting mode"
+#REQUIRE_VERSION=2.2
+
+# This config tests the strict-mode of HTTP rules.
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp \
+ -status 200
+ expect req.method == "GET"
+ expect req.url == "/req1"
+ expect req.http.x-hdr1 == "123456789012345678901234567890123456789012345678901234567890"
+
+ accept
+ rxreq
+ txresp \
+ -status 200
+ expect req.method == "GET"
+ expect req.url == "/req3"
+ expect req.http.x-hdr1 == "123456789012345678901234567890123456789012345678901234567890"
+ expect req.http.x-hdr3 == <undef>
+} -start
+
+server s2 {
+ rxreq
+ txresp \
+ -status 200 \
+ -hdr "x-req: /req1" \
+ -bodylen 2000
+ expect req.method == "GET"
+ expect req.url == "/req1"
+
+ accept
+ rxreq
+ txresp \
+ -status 200 \
+ -hdr "x-req: /req2" \
+ -bodylen 2000
+ expect req.method == "GET"
+ expect req.url == "/req2"
+
+ accept
+ rxreq
+ txresp \
+ -status 200 \
+ -hdr "x-req: /req3" \
+ -bodylen 2000
+ expect req.method == "GET"
+ expect req.url == "/req3"
+
+ accept
+ rxreq
+ txresp \
+ -status 200 \
+ -hdr "x-req: /req4" \
+ -bodylen 2000
+ expect req.method == "GET"
+ expect req.url == "/req4"
+
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.bufsize 2048
+ tune.maxrewrite 128
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ option http-buffer-request
+
+ frontend fe1
+ bind "fd@${fe1}"
+ http-request set-header x-hdr1 123456789012345678901234567890123456789012345678901234567890
+ http-request set-header x-hdr2 123456789012345678901234567890123456789012345678901234567890 if { path /req2 }
+ http-request strict-mode off if { path /req3 }
+ http-request set-header x-hdr3 123456789012345678901234567890123456789012345678901234567890 if { path /req3 }
+ default_backend be1
+
+ backend be1
+ http-request set-header x-hdr3 123456789012345678901234567890123456789012345678901234567890 if { path /req4 }
+ server s1 ${s1_addr}:${s1_port}
+
+ frontend fe2
+ bind "fd@${fe2}"
+ http-response set-header x-hdr4 123456789012345678901234567890123456789012345678901234567890 if { res.hdr(x-req) /req4 }
+ default_backend be2
+ backend be2
+ http-response set-header x-hdr1 123456789012345678901234567890123456789012345678901234567890
+ http-response set-header x-hdr2 123456789012345678901234567890123456789012345678901234567890 if { res.hdr(x-req) /req2 }
+ http-response strict-mode off if { res.hdr(x-req) /req3 }
+ http-response set-header x-hdr3 123456789012345678901234567890123456789012345678901234567890 if { res.hdr(-req) /req3 }
+ server s2 ${s2_addr}:${s2_port}
+
+} -start
+
+client c1r1 -connect ${h1_fe1_sock} {
+ txreq \
+ -req GET \
+ -url /req1 \
+ -bodylen 2000
+ rxresp
+ expect resp.status == 200
+} -run
+client c1r2 -connect ${h1_fe1_sock} {
+ txreq \
+ -req GET \
+ -url /req2 \
+ -bodylen 2000
+ rxresp
+ expect resp.status == 500
+} -run
+client c1r3 -connect ${h1_fe1_sock} {
+ txreq \
+ -req GET \
+ -url /req3 \
+ -bodylen 2000
+ rxresp
+ expect resp.status == 200
+} -run
+client c1r4 -connect ${h1_fe1_sock} {
+ txreq \
+ -req GET \
+ -url /req4 \
+ -bodylen 2000
+ rxresp
+ expect resp.status == 500
+} -run
+
+client c2r1 -connect ${h1_fe2_sock} {
+ txreq \
+ -req GET \
+ -url /req1
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-hdr1 == "123456789012345678901234567890123456789012345678901234567890"
+} -run
+client c2r2 -connect ${h1_fe2_sock} {
+ txreq \
+ -req GET \
+ -url /req2
+ rxresp
+ expect resp.status == 500
+} -run
+client c2r3 -connect ${h1_fe2_sock} {
+ txreq \
+ -req GET \
+ -url /req3
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-hdr1 == "123456789012345678901234567890123456789012345678901234567890"
+ expect resp.http.x-hdr3 == <undef>
+} -run
+client c2r4 -connect ${h1_fe2_sock} {
+ txreq \
+ -req GET \
+ -url /req4
+ rxresp
+ expect resp.status == 500
+} -run