summaryrefslogtreecommitdiffstats
path: root/reg-tests
diff options
context:
space:
mode:
Diffstat (limited to 'reg-tests')
-rw-r--r--reg-tests/README71
-rw-r--r--reg-tests/balance/balance-rr.vtc73
-rw-r--r--reg-tests/balance/balance-uri-path-only.vtc97
-rw-r--r--reg-tests/balance/balance-uri.vtc74
-rw-r--r--reg-tests/cache/basic.vtc55
-rw-r--r--reg-tests/cache/caching_rules.vtc256
-rw-r--r--reg-tests/cache/expires.vtc127
-rw-r--r--reg-tests/cache/if-modified-since.vtc154
-rw-r--r--reg-tests/cache/if-none-match.vtc93
-rw-r--r--reg-tests/cache/post_on_entry.vtc65
-rw-r--r--reg-tests/cache/sample_fetches.vtc139
-rw-r--r--reg-tests/cache/vary.vtc410
-rw-r--r--reg-tests/cache/vary_accept_encoding.vtc333
-rw-r--r--reg-tests/checks/1be_40srv_odd_health_checks.vtc117
-rw-r--r--reg-tests/checks/40be_2srv_odd_health_checks.vtc645
-rw-r--r--reg-tests/checks/4be_1srv_health_checks.vtc201
-rw-r--r--reg-tests/checks/4be_1srv_smtpchk_httpchk_layer47errors.vtc99
-rw-r--r--reg-tests/checks/agent-check.vtc42
l---------reg-tests/checks/common.pem1
-rw-r--r--reg-tests/checks/http-check-expect.vtc64
-rw-r--r--reg-tests/checks/http-check-send.vtc168
-rw-r--r--reg-tests/checks/http-check.vtc157
-rw-r--r--reg-tests/checks/http-monitor-uri.vtc56
-rw-r--r--reg-tests/checks/ldap-check.vtc96
-rw-r--r--reg-tests/checks/mysql-check.vtc123
-rw-r--r--reg-tests/checks/pgsql-check.vtc93
-rw-r--r--reg-tests/checks/redis-check.vtc61
-rw-r--r--reg-tests/checks/smtp-check.vtc110
-rw-r--r--reg-tests/checks/spop-check.vtc94
-rw-r--r--reg-tests/checks/ssl-hello-check.vtc76
-rw-r--r--reg-tests/checks/tcp-check-ssl.vtc118
-rw-r--r--reg-tests/checks/tcp-check_min-recv.vtc68
-rw-r--r--reg-tests/checks/tcp-check_multiple_ports.vtc48
-rw-r--r--reg-tests/checks/tcp-checks-socks4.vtc61
-rw-r--r--reg-tests/checks/tls_health_checks.vtc118
-rw-r--r--reg-tests/compression/basic.vtc378
l---------reg-tests/compression/common.pem1
-rw-r--r--reg-tests/compression/etags_conversion.vtc231
-rw-r--r--reg-tests/compression/lua_validation.lua19
-rw-r--r--reg-tests/compression/lua_validation.vtc59
-rw-r--r--reg-tests/compression/vary.vtc192
-rw-r--r--reg-tests/connection/cli_src_dst.vtc290
l---------reg-tests/connection/common.pem1
-rw-r--r--reg-tests/connection/dispatch.vtc42
-rw-r--r--reg-tests/connection/http_reuse_aggressive.vtc45
-rw-r--r--reg-tests/connection/http_reuse_always.vtc43
-rw-r--r--reg-tests/connection/http_reuse_be_transparent.vtc81
-rw-r--r--reg-tests/connection/http_reuse_conn_hash.vtc163
-rw-r--r--reg-tests/connection/http_reuse_dispatch.vtc79
-rw-r--r--reg-tests/connection/http_reuse_never.vtc79
-rw-r--r--reg-tests/connection/http_reuse_safe.vtc78
-rw-r--r--reg-tests/connection/proxy_protocol_random_fail.vtc60
-rw-r--r--reg-tests/connection/proxy_protocol_send_unique_id.vtc42
-rw-r--r--reg-tests/connection/proxy_protocol_send_unique_id_alpn.vtc33
-rw-r--r--reg-tests/connection/proxy_protocol_tlv_validation.vtc142
-rw-r--r--reg-tests/connection/tcp_to_http_upgrade.vtc162
-rw-r--r--reg-tests/contrib/prometheus.vtc113
-rw-r--r--reg-tests/converter/add_item.vtc50
-rw-r--r--reg-tests/converter/be2dec.vtc56
-rw-r--r--reg-tests/converter/be2hex.vtc60
-rw-r--r--reg-tests/converter/digest.vtc57
-rw-r--r--reg-tests/converter/field.vtc39
-rw-r--r--reg-tests/converter/fix.vtc235
-rw-r--r--reg-tests/converter/hmac.vtc55
-rw-r--r--reg-tests/converter/iif.vtc46
-rw-r--r--reg-tests/converter/json.vtc40
-rw-r--r--reg-tests/converter/json_query.vtc101
-rw-r--r--reg-tests/converter/mqtt.vtc238
-rw-r--r--reg-tests/converter/secure_memcmp.vtc143
-rw-r--r--reg-tests/converter/sha2.vtc57
-rw-r--r--reg-tests/converter/url_dec.vtc37
-rw-r--r--reg-tests/converter/url_enc.vtc43
-rw-r--r--reg-tests/filters/random-forwarding.vtc139
-rw-r--r--reg-tests/http-capture/multiple_headers.vtc91
-rw-r--r--reg-tests/http-cookies/cookie_insert_indirect.vtc54
-rw-r--r--reg-tests/http-cookies/h2_cookie_concat.vtc42
-rw-r--r--reg-tests/http-errorfiles/errorfiles.vtc51
-rw-r--r--reg-tests/http-errorfiles/errors/400-1.http9
-rw-r--r--reg-tests/http-errorfiles/errors/400-2.http9
-rw-r--r--reg-tests/http-errorfiles/errors/400-3.http9
-rw-r--r--reg-tests/http-errorfiles/errors/400.http9
-rw-r--r--reg-tests/http-errorfiles/errors/403-1.http9
-rw-r--r--reg-tests/http-errorfiles/errors/403-2.http9
-rw-r--r--reg-tests/http-errorfiles/errors/403.http9
-rw-r--r--reg-tests/http-errorfiles/errors/404-1.http9
-rw-r--r--reg-tests/http-errorfiles/errors/404-2.http9
-rw-r--r--reg-tests/http-errorfiles/errors/404-3.http9
-rw-r--r--reg-tests/http-errorfiles/errors/404.http9
-rw-r--r--reg-tests/http-errorfiles/errors/500-1.http9
-rw-r--r--reg-tests/http-errorfiles/errors/500.http9
-rw-r--r--reg-tests/http-errorfiles/errors/lf-403.txt1
-rw-r--r--reg-tests/http-errorfiles/http-error.vtc75
-rw-r--r--reg-tests/http-errorfiles/http_deny_errors.vtc77
-rw-r--r--reg-tests/http-errorfiles/http_errors.vtc134
-rw-r--r--reg-tests/http-errorfiles/http_return.vtc46
l---------reg-tests/http-messaging/common.pem1
-rw-r--r--reg-tests/http-messaging/h1_host_normalization.vtc603
-rw-r--r--reg-tests/http-messaging/h1_to_h1.vtc275
-rw-r--r--reg-tests/http-messaging/h2_desync_attacks.vtc167
-rw-r--r--reg-tests/http-messaging/h2_to_h1.vtc265
-rw-r--r--reg-tests/http-messaging/http_abortonclose.vtc142
-rw-r--r--reg-tests/http-messaging/http_bodyless_response.vtc127
-rw-r--r--reg-tests/http-messaging/http_msg_full_on_eom.vtc62
-rw-r--r--reg-tests/http-messaging/http_request_buffer.vtc135
-rw-r--r--reg-tests/http-messaging/http_splicing.vtc75
-rw-r--r--reg-tests/http-messaging/http_transfer_encoding.vtc202
-rw-r--r--reg-tests/http-messaging/http_wait_for_body.vtc171
-rw-r--r--reg-tests/http-messaging/protocol_upgrade.vtc228
-rw-r--r--reg-tests/http-messaging/scheme_based_normalize.vtc125
-rw-r--r--reg-tests/http-messaging/srv_ws.vtc180
-rw-r--r--reg-tests/http-messaging/websocket.vtc205
-rw-r--r--reg-tests/http-rules/1k.txt16
-rw-r--r--reg-tests/http-rules/acl_cli_spaces.vtc79
-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.vtc218
-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/h1_to_h1c.vtc186
-rw-r--r--reg-tests/http-rules/h1or2_to_h1c.vtc226
-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/lf-file.txt1
-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.vtc536
-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
-rw-r--r--reg-tests/http-set-timeout/set_timeout.vtc82
-rwxr-xr-xreg-tests/jwt/build_token.py22
-rw-r--r--reg-tests/jwt/es256-public.pem4
-rw-r--r--reg-tests/jwt/es384-public.pem5
-rw-r--r--reg-tests/jwt/es512-public.pem6
-rw-r--r--reg-tests/jwt/jws_verify.vtc379
-rw-r--r--reg-tests/jwt/rsa-public.pem14
-rw-r--r--reg-tests/log/last_rule.vtc165
-rw-r--r--reg-tests/log/load_balancing.vtc161
-rw-r--r--reg-tests/log/log_forward.vtc57
-rw-r--r--reg-tests/log/log_uri.vtc61
-rw-r--r--reg-tests/log/wrong_ip_port_logging.vtc62
-rw-r--r--reg-tests/lua/bad_http_clt_req_duration.lua8
-rw-r--r--reg-tests/lua/bad_http_clt_req_duration.vtc76
-rw-r--r--reg-tests/lua/close_wait_lf.lua1
-rw-r--r--reg-tests/lua/close_wait_lf.vtc53
l---------reg-tests/lua/common.pem1
-rw-r--r--reg-tests/lua/h_txn_get_priv.lua15
-rw-r--r--reg-tests/lua/h_txn_get_priv.vtc33
-rw-r--r--reg-tests/lua/httpclient_action.lua8
-rw-r--r--reg-tests/lua/httpclient_action.vtc39
-rw-r--r--reg-tests/lua/lua_httpclient.lua49
-rw-r--r--reg-tests/lua/lua_httpclient.vtc68
-rw-r--r--reg-tests/lua/lua_socket.lua44
-rw-r--r--reg-tests/lua/lua_socket.vtc33
-rw-r--r--reg-tests/lua/set_var.lua25
-rw-r--r--reg-tests/lua/set_var.vtc83
-rw-r--r--reg-tests/lua/txn_get_priv-print_r.lua96
-rw-r--r--reg-tests/lua/txn_get_priv-thread.vtc69
-rw-r--r--reg-tests/lua/txn_get_priv.lua180
-rw-r--r--reg-tests/lua/txn_get_priv.vtc35
-rw-r--r--reg-tests/lua/wrong_types_usage.lua3
-rw-r--r--reg-tests/lua/wrong_types_usage.vtc77
-rw-r--r--reg-tests/mailers/healthcheckmail.lua70
-rw-r--r--reg-tests/mailers/healthcheckmail.vtc59
-rw-r--r--reg-tests/mcli/mcli_show_info.vtc29
-rw-r--r--reg-tests/mcli/mcli_start_progs.vtc37
-rw-r--r--reg-tests/peers/basic_sync.vtc121
-rw-r--r--reg-tests/peers/basic_sync_wo_stkt_backend.vtc116
l---------reg-tests/peers/common.pem1
-rw-r--r--reg-tests/peers/tls_basic_sync.vtc158
-rw-r--r--reg-tests/peers/tls_basic_sync_wo_stkt_backend.vtc152
-rw-r--r--reg-tests/sample_fetches/cond_set_var.vtc362
-rw-r--r--reg-tests/sample_fetches/cook.vtc37
-rw-r--r--reg-tests/sample_fetches/hashes.vtc101
-rw-r--r--reg-tests/sample_fetches/so_name.vtc22
-rw-r--r--reg-tests/sample_fetches/srv_name.vtc46
-rw-r--r--reg-tests/sample_fetches/ubase64.vtc57
-rw-r--r--reg-tests/sample_fetches/vars.vtc84
-rw-r--r--reg-tests/seamless-reload/abns_socket.vtc53
-rw-r--r--reg-tests/server/cli_add_check_server.vtc161
-rw-r--r--reg-tests/server/cli_add_server.vtc87
-rw-r--r--reg-tests/server/cli_add_ssl_server.vtc110
-rw-r--r--reg-tests/server/cli_add_track_server.vtc242
-rw-r--r--reg-tests/server/cli_delete_dynamic_server.vtc94
-rw-r--r--reg-tests/server/cli_delete_server.vtc60
-rw-r--r--reg-tests/server/cli_delete_server_lua.vtc43
-rw-r--r--reg-tests/server/cli_set_fdqn.vtc32
-rw-r--r--reg-tests/server/cli_set_ssl.vtc60
l---------reg-tests/server/common.pem1
-rw-r--r--reg-tests/server/get_srv_stats.lua11
-rw-r--r--reg-tests/spoe/wrong_init.vtc22
-rw-r--r--reg-tests/ssl/README2
-rw-r--r--reg-tests/ssl/add_ssl_crt-list.vtc114
-rw-r--r--reg-tests/ssl/ca-auth.crt33
-rw-r--r--reg-tests/ssl/cert1-example.com.pem.ecdsa17
-rw-r--r--reg-tests/ssl/cert1-example.com.pem.rsa80
-rw-r--r--reg-tests/ssl/cert2-example.com.pem.ecdsa17
-rw-r--r--reg-tests/ssl/cert2-example.com.pem.rsa80
-rw-r--r--reg-tests/ssl/client.ecdsa.pem28
-rw-r--r--reg-tests/ssl/client1.pem187
-rw-r--r--reg-tests/ssl/client2_expired.pem81
-rw-r--r--reg-tests/ssl/client3_revoked.pem81
-rw-r--r--reg-tests/ssl/common.4096.dh13
-rw-r--r--reg-tests/ssl/common.crt90
-rw-r--r--reg-tests/ssl/common.key28
-rw-r--r--reg-tests/ssl/common.pem117
-rw-r--r--reg-tests/ssl/crl-auth.pem18
-rw-r--r--reg-tests/ssl/del_ssl_crt-list.vtc102
-rw-r--r--reg-tests/ssl/dynamic_server_ssl.vtc113
-rw-r--r--reg-tests/ssl/ecdsa.crt12
-rw-r--r--reg-tests/ssl/ecdsa.key6
-rw-r--r--reg-tests/ssl/ecdsa.pem17
-rw-r--r--reg-tests/ssl/filters.crt-list2
-rw-r--r--reg-tests/ssl/generate_certificates/gen_cert_ca.pem23
-rw-r--r--reg-tests/ssl/generate_certificates/gen_cert_server.pem18
-rw-r--r--reg-tests/ssl/interCA1_crl.pem27
-rw-r--r--reg-tests/ssl/interCA1_crl_empty.pem27
-rw-r--r--reg-tests/ssl/interCA2_crl.pem27
-rw-r--r--reg-tests/ssl/interCA2_crl_empty.pem27
-rw-r--r--reg-tests/ssl/localhost.crt-list5
-rw-r--r--reg-tests/ssl/log_forward_ssl.vtc60
-rw-r--r--reg-tests/ssl/new_del_ssl_cafile.vtc137
-rw-r--r--reg-tests/ssl/new_del_ssl_crlfile.vtc139
-rw-r--r--reg-tests/ssl/rootCA_crl.pem16
-rw-r--r--reg-tests/ssl/set_cafile_client.pem95
-rw-r--r--reg-tests/ssl/set_cafile_interCA1.crt24
-rw-r--r--reg-tests/ssl/set_cafile_interCA2.crt24
-rw-r--r--reg-tests/ssl/set_cafile_rootCA.crt30
-rw-r--r--reg-tests/ssl/set_cafile_server.pem95
-rw-r--r--reg-tests/ssl/set_default_cert.crt-list2
-rw-r--r--reg-tests/ssl/set_default_cert.pem52
-rw-r--r--reg-tests/ssl/set_ssl_cafile.vtc165
-rw-r--r--reg-tests/ssl/set_ssl_cert.vtc206
-rw-r--r--reg-tests/ssl/set_ssl_cert_bundle.vtc111
-rw-r--r--reg-tests/ssl/set_ssl_cert_noext.vtc90
-rw-r--r--reg-tests/ssl/set_ssl_crlfile.vtc146
-rw-r--r--reg-tests/ssl/set_ssl_server_cert.vtc129
-rw-r--r--reg-tests/ssl/show_ocsp_server.pem119
-rw-r--r--reg-tests/ssl/show_ocsp_server.pem.issuer30
-rw-r--r--reg-tests/ssl/show_ocsp_server.pem.ocspbin0 -> 2281 bytes
-rw-r--r--reg-tests/ssl/show_ocsp_server.pem.ocsp.revokedbin0 -> 2298 bytes
-rw-r--r--reg-tests/ssl/show_ssl_ocspresponse.vtc135
-rw-r--r--reg-tests/ssl/simple.crt-list5
-rw-r--r--reg-tests/ssl/ssl_client_auth.vtc83
-rw-r--r--reg-tests/ssl/ssl_client_samples.vtc72
-rw-r--r--reg-tests/ssl/ssl_crt-list_filters.vtc64
-rw-r--r--reg-tests/ssl/ssl_curves.vtc134
-rw-r--r--reg-tests/ssl/ssl_default_server.vtc142
-rw-r--r--reg-tests/ssl/ssl_dh.vtc233
-rw-r--r--reg-tests/ssl/ssl_errors.vtc439
-rw-r--r--reg-tests/ssl/ssl_frontend_samples.vtc69
-rw-r--r--reg-tests/ssl/ssl_generate_certificate.vtc168
-rw-r--r--reg-tests/ssl/ssl_reuse.vtc141
-rw-r--r--reg-tests/ssl/ssl_server_samples.vtc73
-rw-r--r--reg-tests/ssl/ssl_simple_crt-list.vtc50
-rw-r--r--reg-tests/ssl/wrong_ctx_storage.vtc45
-rw-r--r--reg-tests/startup/automatic_maxconn.vtc104
-rw-r--r--reg-tests/startup/check_condition.vtc32
-rw-r--r--reg-tests/startup/common.pem117
-rw-r--r--reg-tests/startup/default_rules.vtc185
-rw-r--r--reg-tests/stick-table/converteers_ref_cnt_never_dec.vtc75
-rw-r--r--reg-tests/stick-table/src_conn_rate.vtc43
-rw-r--r--reg-tests/stick-table/unknown_key.vtc32
-rw-r--r--reg-tests/stickiness/lb-services.vtc291
-rw-r--r--reg-tests/stickiness/srvkey-addr.vtc260
-rw-r--r--reg-tests/stream/unique-id-from-proxy.vtc38
-rw-r--r--reg-tests/stream/unique-id.vtc49
-rw-r--r--reg-tests/tcp-rules/default_rules.vtc61
-rw-r--r--reg-tests/webstats/webstats-scope-and-post-change.vtc84
273 files changed, 25317 insertions, 0 deletions
diff --git a/reg-tests/README b/reg-tests/README
new file mode 100644
index 0000000..ef721fd
--- /dev/null
+++ b/reg-tests/README
@@ -0,0 +1,71 @@
+ * Regression testing for HAProxy with VTest *
+
+
+This little README file is about how to compile and run vtest test case files (VTC files)
+to test HAProxy for any regression.
+
+To do so, you will have to compile vtest program sources which depends on
+Varnish cache application sources. vtest, formerly varnishtest, is a very useful
+program which has been developed to test Varnish cache application. vtest has been
+modified in collaboration with Varnish cache conceptor Poul-Henning Kamp to support
+HAProxy in addition to Varnish cache.
+
+See also: doc/regression-testing.txt
+
+* vtest compilation *
+
+ $ git clone https://github.com/vtest/VTest
+
+ $ cd VTest
+
+ $ make vtest
+
+ Then vtest program may be found at the root directory of vtest sources directory.
+ The Varnish cache manuals are located in 'man' directory of Varnish cache sources
+ directory. You will have to have a look at varnishtest(7) and vtc(7) manuals to
+ use vtest.
+
+ Some information may also be found in doc/regression-testing.txt in HAProxy
+ sources.
+
+ Note that VTC files for Varnish cache may be found in bin/varnishtest/tests directory
+ of Varnish cache sources directory which may be found here:
+ https://github.com/varnishcache/varnish-cache
+
+
+* vtest execution *
+
+ You must set HAPROXY_PROGRAM environment variable to give the location
+ of the HAProxy program to test to vtest:
+
+ $ HAPROXY_PROGRAM=<my haproxy program> vtest ...
+
+ The HAProxy VTC files found in HAProxy sources may be run with the reg-tests
+ Makefile target. You must set the VTEST_PROGRAM environment variable to
+ give the location of the vtest program which has been previously compiled.
+
+ $ VTEST_PROGRAM=<my vtest program> make reg-tests
+
+ "reg-tests" Makefile target run scripts/run-regtest.sh script.
+ To get more information about this script run it with --help option.
+
+ Note that vtest is run with -t10 and -l option. -l option is to keep
+ keep vtest temporary directory in case of failed test cases. core files
+ may be found in this directory (if enabled by ulimit).
+
+
+* vtest patches for HAProxy VTC files *
+
+ When producing a patch to add a VTC regression testing file to reg-tests directory,
+ please follow these simple rules:
+
+ - If your VTC file needs others files, if possible, use the same basename as that
+ of the VTC file,
+ - Put these files in a directory with the same name as the code area concerned
+ by the bug ('peers', 'lua', 'acl' etc).
+
+Please note that most tests use a common set of timeouts defined by the
+environment variable HAPROXY_TEST_TIMEOUT. As much as possible, for regular I/O
+(i.e. not errors), please try to reuse that setting so that the value may
+easily be adjusted when running in some particularly slow environments, or be
+shortened to fail faster on developers' machines.
diff --git a/reg-tests/balance/balance-rr.vtc b/reg-tests/balance/balance-rr.vtc
new file mode 100644
index 0000000..908a4f9
--- /dev/null
+++ b/reg-tests/balance/balance-rr.vtc
@@ -0,0 +1,73 @@
+vtest "Test for balance roundrobin"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Server: s1"
+} -repeat 2 -start
+
+server s2 {
+ rxreq
+ txresp -hdr "Server: s2"
+} -repeat 2 -start
+
+server s3 {
+ rxreq
+ txresp -hdr "Server: s3"
+} -repeat 2 -start
+
+server s4 {
+ rxreq
+ txresp -hdr "Server: s4"
+} -repeat 2 -start
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ mode http
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen px
+ bind "fd@${px}"
+ balance roundrobin
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s2_addr}:${s2_port}
+ server srv3 ${s3_addr}:${s3_port}
+ server srv4 ${s4_addr}:${s4_port}
+} -start
+
+client c1 -connect ${h1_px_sock} {
+ txreq -url "/url1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -run
+
+client c2 -connect ${h1_px_sock} {
+ txreq -url "/url1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -run
+
+client c3 -connect ${h1_px_sock} {
+ txreq -url "/url1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s3
+} -run
+
+client c4 -connect ${h1_px_sock} {
+ txreq -url "/url1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s4
+} -run
+
+client c5 -connect ${h1_px_sock} {
+ txreq -url "/url1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -run
diff --git a/reg-tests/balance/balance-uri-path-only.vtc b/reg-tests/balance/balance-uri-path-only.vtc
new file mode 100644
index 0000000..f1291ee
--- /dev/null
+++ b/reg-tests/balance/balance-uri-path-only.vtc
@@ -0,0 +1,97 @@
+vtest "Test for balance URI"
+feature ignore_unknown_macro
+#REQUIRE_VERSION=2.3
+
+server s1 {
+ rxreq
+ txresp -hdr "Server: s1" -body "s1"
+} -repeat 5 -start
+
+server s2 {
+ rxreq
+ txresp -hdr "Server: s2" -body "s2"
+} -repeat 5 -start
+
+server s3 {
+ rxreq
+ txresp -hdr "Server: s3" -body "s3"
+} -repeat 5 -start
+
+server s4 {
+ rxreq
+ txresp -hdr "Server: s4" -body "s4"
+} -repeat 5 -start
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ mode http
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen px
+ bind "fd@${px}"
+ bind "fd@${pxh2}" proto h2
+ balance uri path-only
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s2_addr}:${s2_port}
+ server srv3 ${s3_addr}:${s3_port}
+ server srv4 ${s4_addr}:${s4_port}
+} -start
+
+client c1 -connect ${h1_px_sock} {
+ txreq -url "http://127.0.0.1/url1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -run
+
+client c2 -connect ${h1_px_sock} {
+ txreq -url "/url1?ignore=this-arg"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -run
+
+client c3 -connect ${h1_px_sock} {
+ txreq -url "http://127.0.0.1/url2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s3
+} -run
+
+client c4 -connect ${h1_px_sock} {
+ txreq -url "/url3"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s4
+} -run
+
+client c5 -connect ${h1_px_sock} {
+ txreq -url "/url4"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -run
+
+client c6h2 -connect ${h1_pxh2_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "GET" \
+ -scheme "https" \
+ -url "/url1"
+ rxhdrs
+ expect resp.status == 200
+ rxdata -all
+ expect resp.body == "s2"
+ } -run
+} -run
diff --git a/reg-tests/balance/balance-uri.vtc b/reg-tests/balance/balance-uri.vtc
new file mode 100644
index 0000000..cc65d64
--- /dev/null
+++ b/reg-tests/balance/balance-uri.vtc
@@ -0,0 +1,74 @@
+vtest "Test for balance URI"
+feature ignore_unknown_macro
+#REQUIRE_VERSION=2.0
+
+server s1 {
+ rxreq
+ txresp -hdr "Server: s1"
+} -repeat 2 -start
+
+server s2 {
+ rxreq
+ txresp -hdr "Server: s2"
+} -repeat 2 -start
+
+server s3 {
+ rxreq
+ txresp -hdr "Server: s3"
+} -repeat 2 -start
+
+server s4 {
+ rxreq
+ txresp -hdr "Server: s4"
+} -repeat 2 -start
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ mode http
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen px
+ bind "fd@${px}"
+ balance uri
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s2_addr}:${s2_port}
+ server srv3 ${s3_addr}:${s3_port}
+ server srv4 ${s4_addr}:${s4_port}
+} -start
+
+client c1 -connect ${h1_px_sock} {
+ txreq -url "/url1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -run
+
+client c2 -connect ${h1_px_sock} {
+ txreq -url "/url1?ignore=this-arg"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -run
+
+client c3 -connect ${h1_px_sock} {
+ txreq -url "/url2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s3
+} -run
+
+client c4 -connect ${h1_px_sock} {
+ txreq -url "/url3"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s4
+} -run
+
+client c5 -connect ${h1_px_sock} {
+ txreq -url "/url4"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -run
diff --git a/reg-tests/cache/basic.vtc b/reg-tests/cache/basic.vtc
new file mode 100644
index 0000000..d6a8c00
--- /dev/null
+++ b/reg-tests/cache/basic.vtc
@@ -0,0 +1,55 @@
+varnishtest "Basic cache test"
+
+#REQUIRE_VERSION=1.9
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Cache-Control: max-age=5"
+ chunkedlen 1
+ chunkedlen 1
+ chunkedlen 2
+ chunkedlen 3
+ chunkedlen 5
+ chunkedlen 8
+ chunkedlen 13
+ chunkedlen 21
+ chunkedlen 34
+ chunkedlen 55
+ chunkedlen 89
+ chunkedlen 144
+ chunkedlen 233
+ chunkedlen 0
+} -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}"
+ default_backend test
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 609
+} -repeat 4 -run
diff --git a/reg-tests/cache/caching_rules.vtc b/reg-tests/cache/caching_rules.vtc
new file mode 100644
index 0000000..61274b4
--- /dev/null
+++ b/reg-tests/cache/caching_rules.vtc
@@ -0,0 +1,256 @@
+varnishtest "Caching rules test"
+# A response will not be cached unless it has an explicit age (Cache-Control max-age of s-maxage, Expires) or a validator (Last-Modified, or ETag)
+# A response will not be cached either if it has an Age header that is either invalid (should be an integer) or greater than its max age.
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == "/max-age"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -bodylen 150
+
+ rxreq
+ expect req.url == "/s-maxage"
+ txresp -hdr "Cache-Control: s-maxage=5" \
+ -bodylen 160
+
+ rxreq
+ expect req.url == "/last-modified"
+ txresp -hdr "Last-Modified: Thu, 22 Oct 2020 16:51:12 GMT" \
+ -bodylen 180
+
+ rxreq
+ expect req.url == "/etag"
+ txresp -hdr "ETag: \"etag\"" \
+ -bodylen 190
+
+ rxreq
+ expect req.url == "/uncacheable"
+ txresp \
+ -bodylen 200
+
+ rxreq
+ expect req.url == "/uncacheable"
+ txresp \
+ -bodylen 210
+
+ # Age response header checks
+
+ # Invalid age
+ rxreq
+ expect req.url == "/invalid_age"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -hdr "Age: abc" -bodylen 120
+
+ rxreq
+ expect req.url == "/invalid_age"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -hdr "Age: abc" -bodylen 120
+
+ # Old age (greater than max age)
+ rxreq
+ expect req.url == "/old_age"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -hdr "Age: 10" -bodylen 130
+
+ rxreq
+ expect req.url == "/old_age"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -hdr "Age: 10" -bodylen 130
+
+ # Good age
+ rxreq
+ expect req.url == "/good_age"
+ txresp -hdr "Cache-Control: max-age=500" \
+ -hdr "Age: 100" -bodylen 140
+
+
+ # "Control-Cache: no-cache" on client request but still stored in cache
+ rxreq
+ expect req.url == "/nocache"
+ txresp -hdr "Cache-Control: max-age=500" \
+ -hdr "Age: 100" -bodylen 140
+
+ rxreq
+ expect req.url == "/nocache"
+ txresp -hdr "Cache-Control: max-age=500" \
+ -hdr "Age: 100" -bodylen 140
+
+} -start
+
+server s2 {
+ rxreq
+ expect req.url == "/expires"
+ # Expires header is filled directly by the expires_be backend"
+ txresp \
+ -bodylen 170
+} -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
+
+ 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}"
+ use_backend expires_be if { path_beg /expires }
+ default_backend test
+
+ backend expires_be
+ http-request cache-use my_cache
+ server www ${s2_addr}:${s2_port}
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+ # Expires value set in the future (current_time+5s)
+ http-response set-header Expires %[date(5),http_date]
+ http-response cache-store my_cache
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/max-age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 150
+
+ txreq -url "/max-age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 150
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/s-maxage"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 160
+
+ txreq -url "/s-maxage"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 160
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/expires"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 170
+
+ txreq -url "/expires"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 170
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/last-modified"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 180
+
+ txreq -url "/last-modified"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 180
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/etag"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 190
+
+ txreq -url "/etag"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 190
+ expect resp.http.X-Cache-Hit == 1
+
+ # The next response should not be cached
+ txreq -url "/uncacheable"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 200
+
+ txreq -url "/uncacheable"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 210
+ expect resp.http.X-Cache-Hit == 0
+
+ # Age header tests
+ txreq -url "/invalid_age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 120
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/invalid_age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 120
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/old_age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 130
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/old_age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 130
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/good_age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 140
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/good_age"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 140
+ expect resp.http.X-Cache-Hit == 1
+
+ # Cache-Control: no-cache
+ txreq -url "/nocache" -hdr "Cache-Control: no-cache"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 140
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/nocache" -hdr "Cache-Control: no-cache"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 140
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/nocache"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 140
+ expect resp.http.X-Cache-Hit == 1
+
+
+} -run
diff --git a/reg-tests/cache/expires.vtc b/reg-tests/cache/expires.vtc
new file mode 100644
index 0000000..ee5cd77
--- /dev/null
+++ b/reg-tests/cache/expires.vtc
@@ -0,0 +1,127 @@
+varnishtest "Expires support"
+
+#REQUIRE_VERSION=2.3
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Cache-Control: max-age=5"
+ chunkedlen 15
+ chunkedlen 15
+ chunkedlen 15
+ chunkedlen 0
+} -start
+
+server s2 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked"
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 0
+} -start
+
+server s3 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked"
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 0
+
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked"
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 0
+} -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
+
+ 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}"
+ use_backend cache_control_be if { path_beg /cache_control }
+ use_backend future_expires_be if { path_beg /future }
+ default_backend past_expires_be
+
+ backend cache_control_be
+ # Expires header should be ignored since a Cache-Control one is present
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+ http-response set-header Expires %[date(-1),http_date]
+ http-response cache-store my_cache
+
+ backend future_expires_be
+ # Expires value set in the future (current_time+5s)
+ http-request cache-use my_cache
+ server www ${s2_addr}:${s2_port}
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+ http-response set-header Expires %[date(5),http_date]
+ http-response cache-store my_cache
+
+ backend past_expires_be
+ # Expires value set in the past
+ http-request cache-use my_cache
+ server www ${s3_addr}:${s3_port}
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+ http-response set-header Expires %[date(-1),http_date]
+ http-response cache-store my_cache
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/cache_control"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 45
+
+ txreq -url "/cache_control"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/future"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+
+ txreq -url "/future"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/past"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+
+ txreq -url "/past"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 0
+
+} -run
+
diff --git a/reg-tests/cache/if-modified-since.vtc b/reg-tests/cache/if-modified-since.vtc
new file mode 100644
index 0000000..a49e473
--- /dev/null
+++ b/reg-tests/cache/if-modified-since.vtc
@@ -0,0 +1,154 @@
+varnishtest "If-Modified-Since support"
+
+#REQUIRE_VERSION=2.3
+
+feature ignore_unknown_macro
+
+server s1 {
+ # Response containing a "Last-Modified" field
+ rxreq
+ expect req.url == "/last_modified"
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Last-Modified: Thu, 15 Oct 2020 22:23:24 GMT"
+ chunkedlen 15
+ chunkedlen 15
+ chunkedlen 15
+ chunkedlen 0
+
+ # Response containing a "Date" field
+ rxreq
+ expect req.url == "/date"
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Date: Thu, 22 Oct 2020 16:51:12 GMT" \
+ -hdr "Cache-Control: max-age=5"
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 0
+
+ # Response containing both a "Last-Modified" and a "Date" fields
+ # Should behave the same way as if the "Date" field was not here.
+ rxreq
+ expect req.url == "/last_modified_and_date"
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Last-Modified: Thu, 15 Oct 2020 14:24:38 GMT" \
+ -hdr "Date: Thu, 22 Oct 2020 16:51:12 GMT"
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 0
+} -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
+
+ 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}"
+ default_backend test
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+
+ # Remove Transfer-Encoding header because of a vtest issue with
+ # 304-Not-Modified responses
+ http-after-response del-header transfer-encoding if { status eq 304 }
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/last_modified"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 45
+
+ txreq -url "/date"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+
+ txreq -url "/last_modified_and_date"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+
+
+ # Earlier date
+ # "Last-Modified" version
+ txreq -url "/last_modified" \
+ -hdr "If-Modified-Since: Thu, 15 Oct 2020 00:00:01 GMT"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 45
+ # "Date" version
+ txreq -url "/date" \
+ -hdr "If-Modified-Since: Thu, 01 Oct 2020 00:00:01 GMT"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+ # "Last-Modified" and "Date" version
+ txreq -url "/last_modified_and_date" \
+ -hdr "If-Modified-Since: Thu, 15 Oct 2020 00:00:01 GMT"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+
+
+ # Same date
+ # "Last-Modified" version
+ txreq -url "/last_modified" \
+ -hdr "If-Modified-Since: Thu, 15 Oct 2020 22:23:24 GMT"
+ rxresp
+ expect resp.status == 304
+ expect resp.bodylen == 0
+ # "Date" version
+ txreq -url "/date" \
+ -hdr "If-Modified-Since: Thu, 22 Oct 2020 16:51:12 GMT"
+ rxresp
+ expect resp.status == 304
+ expect resp.bodylen == 0
+ # "Last-Modified" and "Date" version
+ txreq -url "/last_modified_and_date" \
+ -hdr "If-Modified-Since: Thu, 15 Oct 2020 16:51:12 GMT"
+ rxresp
+ expect resp.status == 304
+ expect resp.bodylen == 0
+
+
+ # Later date
+ # "Last-Modified" version
+ txreq -url "/last_modified" \
+ -hdr "If-Modified-Since: Thu, 22 Oct 2020 23:00:00 GMT"
+ rxresp
+ expect resp.status == 304
+ expect resp.bodylen == 0
+ # "Date" version
+ txreq -url "/date" \
+ -hdr "If-Modified-Since: Thu, 22 Oct 2020 23:00:00 GMT"
+ rxresp
+ expect resp.status == 304
+ expect resp.bodylen == 0
+ # "Last-Modified" and "Date" version
+ txreq -url "/last_modified_and_date" \
+ -hdr "If-Modified-Since: Thu, 22 Oct 2020 23:00:00 GMT"
+ rxresp
+ expect resp.status == 304
+ expect resp.bodylen == 0
+
+} -run
diff --git a/reg-tests/cache/if-none-match.vtc b/reg-tests/cache/if-none-match.vtc
new file mode 100644
index 0000000..ff90ad4
--- /dev/null
+++ b/reg-tests/cache/if-none-match.vtc
@@ -0,0 +1,93 @@
+varnishtest "If-None-Match support"
+
+#REQUIRE_VERSION=2.3
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "ETag: \"etag\""
+ chunkedlen 1
+ chunkedlen 1
+ chunkedlen 2
+ chunkedlen 3
+ chunkedlen 5
+ chunkedlen 8
+ chunkedlen 13
+ chunkedlen 21
+ chunkedlen 34
+ chunkedlen 55
+ chunkedlen 89
+ chunkedlen 144
+ chunkedlen 233
+ chunkedlen 0
+} -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
+
+ 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}"
+ default_backend test
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+
+ # Remove Transfer-Encoding header because of a vtest issue with
+ # 304-Not-Modified responses
+ http-after-response del-header transfer-encoding if { status eq 304 }
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 609
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 609
+ txreq \
+ -hdr "if-none-match: *"
+ rxresp
+ expect resp.status == 304
+ txreq \
+ -hdr "if-none-match: \"etag\""
+ rxresp
+ expect resp.status == 304
+ txreq \
+ -hdr "if-none-match: W/\"etag\""
+ rxresp
+ expect resp.status == 304
+} -run
+
+client c2 -connect ${h1_fe_sock} {
+ txreq \
+ -hdr "if-none-match: \"wrong_etag\""
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 609
+ txreq \
+ -hdr "if-none-match: W/\"wrong_etag\", W/\"etag\""
+ rxresp
+ expect resp.status == 304
+} -run
diff --git a/reg-tests/cache/post_on_entry.vtc b/reg-tests/cache/post_on_entry.vtc
new file mode 100644
index 0000000..66c89e4
--- /dev/null
+++ b/reg-tests/cache/post_on_entry.vtc
@@ -0,0 +1,65 @@
+varnishtest "A successful unsafe method (POST for instance) on a cached entry must disable it."
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == "/cached"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -bodylen 150
+
+ rxreq
+ expect req.url == "/cached"
+ expect req.method == "POST"
+ txresp
+
+ rxreq
+ expect req.url == "/cached"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -bodylen 100
+
+} -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}"
+ default_backend test
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/cached"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 150
+
+ txreq -method "POST" -url "/cached" -bodylen 100
+ rxresp
+ expect resp.status == 200
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/cached"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 100
+ expect resp.http.X-Cache-Hit == 0
+} -run
diff --git a/reg-tests/cache/sample_fetches.vtc b/reg-tests/cache/sample_fetches.vtc
new file mode 100644
index 0000000..c2b1d15
--- /dev/null
+++ b/reg-tests/cache/sample_fetches.vtc
@@ -0,0 +1,139 @@
+
+varnishtest "Basic cache test"
+
+#REQUIRE_VERSION=1.9
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Cache-Control: max-age=5"
+ chunkedlen 15
+ chunkedlen 15
+ chunkedlen 15
+ chunkedlen 0
+} -start
+
+server s2 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Cache-Control: max-age=5"
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 16
+ chunkedlen 0
+} -start
+
+server s3 {
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Cache-Control: max-age=5"
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 0
+
+ rxreq
+ txresp -nolen -hdr "Transfer-Encoding: chunked" \
+ -hdr "Cache-Control: max-age=5"
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 17
+ chunkedlen 0
+} -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
+
+ 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}"
+ use_backend first_be if { path_beg /first }
+ use_backend nocache_be if { path_beg /nocache }
+ default_backend second_be
+
+ backend first_be
+ http-request cache-use first_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store first_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+ http-response set-header X-Cache-Name %[res.cache_name]
+
+ backend second_be
+ http-request cache-use second_cache
+ server www ${s2_addr}:${s2_port}
+ http-response cache-store second_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+ http-response set-header X-Cache-Name %[res.cache_name]
+
+ backend nocache_be
+ server www ${s3_addr}:${s3_port}
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+ http-response set-header X-Cache-Name %[res.cache_name]
+
+ cache first_cache
+ total-max-size 3
+ max-age 40
+ max-object-size 3000
+
+ cache second_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/first"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 0
+ expect resp.http.X-Cache-Name == ""
+
+ txreq -url "/second"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+ expect resp.http.X-Cache-Hit == 0
+ expect resp.http.X-Cache-Name == ""
+
+ txreq -url "/nocache"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 0
+ expect resp.http.X-Cache-Name == ""
+
+ # Response should come form the cache now
+ txreq -url "/nocache"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 0
+ expect resp.http.X-Cache-Name == ""
+
+ txreq -url "/first"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+ expect resp.http.X-Cache-Name == "first_cache"
+
+ txreq -url "/second"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+ expect resp.http.X-Cache-Hit == 1
+ expect resp.http.X-Cache-Name == "second_cache"
+} -run
diff --git a/reg-tests/cache/vary.vtc b/reg-tests/cache/vary.vtc
new file mode 100644
index 0000000..b1c1bda
--- /dev/null
+++ b/reg-tests/cache/vary.vtc
@@ -0,0 +1,410 @@
+varnishtest "Vary support"
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ # Response varying on "accept-encoding" with
+ # an unacceptable content-encoding
+ rxreq
+ expect req.url == "/accept-encoding"
+ txresp -hdr "Content-Encoding: gzip" \
+ -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 45
+
+ # Response varying on "accept-encoding"
+ rxreq
+ expect req.url == "/accept-encoding"
+ txresp -hdr "Content-Encoding: gzip" \
+ -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 45
+
+ # Response varying on "accept-encoding" with
+ # no content-encoding
+ rxreq
+ expect req.url == "/accept-encoding"
+ txresp -hdr "Content-Type: text/plain" \
+ -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 48
+
+ # Response varying on "accept-encoding" but having two different encodings
+ rxreq
+ expect req.url == "/accept-encoding-multiple"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 51
+
+
+ # Unmanaged vary
+ rxreq
+ expect req.url == "/unmanaged"
+ txresp -hdr "Vary: accept-encoding,unmanaged" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 51
+
+
+ rxreq
+ expect req.url == "/unmanaged"
+ txresp -hdr "Vary: accept-encoding,unmanaged" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 51
+
+
+
+ # Mixed Vary (Accept-Encoding + Referer)
+ rxreq
+ expect req.url == "/referer-accept-encoding"
+ txresp -hdr "Vary: accept-encoding,referer" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: gzip" \
+ -bodylen 51
+
+ rxreq
+ expect req.url == "/referer-accept-encoding"
+ txresp -hdr "Vary: referer,accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: br" \
+ -bodylen 54
+
+ rxreq
+ expect req.url == "/referer-accept-encoding"
+ txresp -hdr "Vary: referer,accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: gzip" \
+ -bodylen 57
+
+ # Multiple Accept-Encoding headers
+ rxreq
+ expect req.url == "/multiple_headers"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: br" \
+ -bodylen 155
+
+ rxreq
+ expect req.url == "/multiple_headers"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: br" \
+ -bodylen 166
+
+
+ # Too many Accept-Encoding values (we will not cache responses with more than 16 encodings)
+ rxreq
+ expect req.url == "/too_many_encodings"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: gzip" \
+ -bodylen 177
+
+ rxreq
+ expect req.url == "/too_many_encodings"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: gzip" \
+ -bodylen 188
+
+ rxreq
+ expect req.url == "/empty-vs-missing"
+ txresp -hdr "Content-Encoding: gzip" \
+ -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 234
+
+ rxreq
+ expect req.url == "/empty-vs-missing"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 256
+} -start
+
+server s2 {
+ # Responses that should not be cached
+ rxreq
+ expect req.url == "/no_vary_support"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 57
+
+ rxreq
+ expect req.url == "/no_vary_support"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 57
+} -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
+
+ 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}"
+ use_backend no_vary_be if { path_beg /no_vary_support }
+ default_backend test
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+
+ backend no_vary_be
+ http-request cache-use no_vary_cache
+ server www ${s2_addr}:${s2_port}
+ http-response cache-store no_vary_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+ process-vary on
+
+ cache no_vary_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+ process-vary off
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ # Accept-Encoding Vary
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: first_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+
+ # The response for the first request had an unacceptable `content-encoding`
+ # which might happen if that's the only thing the server supports, but
+ # we must not cache that and instead defer to the server.
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: first_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: second_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.X-Cache-Hit == 0
+
+ # This request matches the cache entry for the request above, despite
+ # matching the `accept-encoding` of the first request because the
+ # request above only has the `identity` encoding which is implicitly
+ # added, unless explicitly forbidden.
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: first_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: second_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 48
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.X-Cache-Hit == 1
+
+ # The accept-encoding normalizer function converts the header values
+ # to lower case then calculates the hash of every sub part before
+ # sorting the hashes and xor'ing them (while removing duplicates).
+ txreq -url "/accept-encoding-multiple" -hdr "Accept-Encoding: first,second"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/accept-encoding-multiple" -hdr "Accept-Encoding: first,second"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/accept-encoding-multiple" -hdr "Accept-Encoding: second,first"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/accept-encoding-multiple" -hdr "Accept-Encoding: FirsT,SECOND,first"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 1
+
+ # Unmanaged vary
+ txreq -url "/unmanaged" -hdr "Accept-Encoding: first_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/unmanaged" -hdr "Accept-Encoding: first_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 0
+
+
+ # Mixed Vary (Accept-Encoding + Referer)
+ txreq -url "/referer-accept-encoding" \
+ -hdr "Accept-Encoding: br, gzip" \
+ -hdr "Referer: referer"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/referer-accept-encoding" \
+ -hdr "Accept-Encoding: br" \
+ -hdr "Referer: other-referer"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 54
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/referer-accept-encoding" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Referer: other-referer"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 57
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/referer-accept-encoding" \
+ -hdr "Referer: referer" \
+ -hdr "Accept-Encoding: gzip, br"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 51
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/referer-accept-encoding" \
+ -hdr "Accept-Encoding: br" \
+ -hdr "Referer: other-referer"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 54
+ expect resp.http.X-Cache-Hit == 1
+
+ txreq -url "/referer-accept-encoding" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Referer: other-referer"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 57
+ expect resp.http.X-Cache-Hit == 1
+
+
+ # Multiple Accept-encoding headers
+ txreq -url "/multiple_headers" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Accept-Encoding: br, deflate"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 155
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/multiple_headers" \
+ -hdr "Accept-Encoding: deflate" \
+ -hdr "Accept-Encoding: br,gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 155
+ expect resp.http.X-Cache-Hit == 1
+
+ # Should not match a cache entry
+ txreq -url "/multiple_headers" \
+ -hdr "Accept-Encoding: first_encoding"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 166
+ expect resp.http.X-Cache-Hit == 0
+
+ # Too many accept encodings
+ txreq -url "/too_many_encodings" \
+ -hdr "Accept-Encoding: a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 177
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/too_many_encodings" \
+ -hdr "Accept-Encoding: a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 188
+ expect resp.http.X-Cache-Hit == 0
+
+ # A missing 'Accept-Encoding' implies that anything is acceptable,
+ # while an empty 'Accept-Encoding' implies nothing is acceptable.
+
+ # Start by caching a gzip response.
+ txreq -url "/empty-vs-missing" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 234
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.X-Cache-Hit == 0
+
+ # Check that it is cached.
+ txreq -url "/empty-vs-missing" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 234
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.X-Cache-Hit == 1
+
+ # Check that the cached response is returned when no accept-encoding is
+ # specified.
+ txreq -url "/empty-vs-missing"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 234
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.X-Cache-Hit == 1
+
+ # Check that the cached response is not returned when an empty
+ # accept-encoding is specified.
+ txreq -url "/empty-vs-missing" -hdr "Accept-Encoding:"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 256
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.X-Cache-Hit == 0
+
+ # The following requests are treated by a backend that does not cache
+ # responses containing a Vary header
+ txreq -url "/no_vary_support"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 57
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/no_vary_support"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 57
+ expect resp.http.X-Cache-Hit == 0
+
+
+} -run
diff --git a/reg-tests/cache/vary_accept_encoding.vtc b/reg-tests/cache/vary_accept_encoding.vtc
new file mode 100644
index 0000000..4b828a8
--- /dev/null
+++ b/reg-tests/cache/vary_accept_encoding.vtc
@@ -0,0 +1,333 @@
+varnishtest "Check the Accept-Encoding processing implemented in the Vary mechanism"
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ # Response varying on "accept-encoding" with a gzip content-encoding
+ rxreq
+ expect req.url == "/accept-encoding"
+ txresp -hdr "Content-Encoding: gzip" \
+ -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 45
+
+ # Response varying on "accept-encoding" with a deflate content-encoding
+ rxreq
+ expect req.url == "/accept-encoding"
+ txresp -hdr "Content-Encoding: deflate" \
+ -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 55
+
+
+ # Response varying on "accept-encoding" with no content-encoding (identity)
+ rxreq
+ expect req.url == "/accept-encoding-identity"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -bodylen 65
+
+ # Response varying on "accept-encoding" with refused identity encoding
+ rxreq
+ expect req.url == "/accept-encoding-identity"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: deflate" \
+ -bodylen 75
+
+
+ rxreq
+ expect req.url == "/accept-encoding-star"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: br" \
+ -bodylen 89
+
+ rxreq
+ expect req.url == "/accept-encoding-star"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: deflate" \
+ -bodylen 99
+
+
+ rxreq
+ expect req.url == "/multiple-content-encoding"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: deflate, gzip" \
+ -bodylen 109
+
+ rxreq
+ expect req.url == "/unknown-content-encoding"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: unknown_encoding" \
+ -bodylen 119
+
+ rxreq
+ expect req.url == "/unknown-content-encoding"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: unknown_encoding" \
+ -bodylen 119
+
+
+ rxreq
+ expect req.url == "/hash-collision"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: br" \
+ -bodylen 129
+
+ rxreq
+ expect req.url == "/hash-collision"
+ txresp -hdr "Vary: accept-encoding" \
+ -hdr "Cache-Control: max-age=5" \
+ -hdr "Content-Encoding: gzip" \
+ -bodylen 139
+} -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
+
+ 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}"
+ default_backend test
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+ process-vary on
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ #
+ # Accept-Encoding Vary
+ #
+
+ # First request
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+
+ # Regular case
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+
+ # Regular case with upper case encoding
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: GZIP"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+
+ # Multiple accepted encodings (all standard)
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: deflate,gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+
+ # Multiple accept-encoding headers + non-standard accepted encodings
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: first_encoding,second_encoding" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+
+ # Regular case with positive weight
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: gzip;q=0.8"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+
+ # Regular case with positive weight and extra whitespaces
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: gzip ; q=0.8"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 45
+ expect resp.http.X-Cache-Hit == 1
+
+ # Regular case with null weight
+ txreq -url "/accept-encoding" -hdr "Accept-Encoding: deflate;q=0.8, gzip;q=0"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "deflate"
+ expect resp.bodylen == 55
+ expect resp.http.X-Cache-Hit == 0
+
+
+ #
+ # Identity tests
+ #
+ txreq -url "/accept-encoding-identity"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.bodylen == 65
+ expect resp.http.X-Cache-Hit == 0
+
+ # Regular case
+ txreq -url "/accept-encoding-identity"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.bodylen == 65
+ expect resp.http.X-Cache-Hit == 1
+
+ # Identity is allowed by default even if another encoding is specified
+ txreq -url "/accept-encoding-identity" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.bodylen == 65
+ expect resp.http.X-Cache-Hit == 1
+
+ # Refused identity encoding (explicit null weight)
+ txreq -url "/accept-encoding-identity" -hdr "Accept-Encoding: deflate, identity;q=0"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "deflate"
+ expect resp.bodylen == 75
+ expect resp.http.X-Cache-Hit == 0
+
+
+ #
+ # Star tests
+ #
+ txreq -url "/accept-encoding-star" -hdr "Accept-Encoding: *"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "br"
+ expect resp.bodylen == 89
+ expect resp.http.X-Cache-Hit == 0
+
+ # Regular case
+ txreq -url "/accept-encoding-star" -hdr "Accept-Encoding: *"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "br"
+ expect resp.bodylen == 89
+ expect resp.http.X-Cache-Hit == 1
+
+ # Reject some encodings
+ txreq -url "/accept-encoding-star" -hdr "Accept-Encoding: gzip;q=0, deflate,*"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "br"
+ expect resp.bodylen == 89
+ expect resp.http.X-Cache-Hit == 1
+
+ # Weighted star
+ txreq -url "/accept-encoding-star" -hdr "Accept-Encoding: gzip;q=0, deflate,*;q=0.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "br"
+ expect resp.bodylen == 89
+ expect resp.http.X-Cache-Hit == 1
+
+ # Rejected identity
+ txreq -url "/accept-encoding-star" -hdr "Accept-Encoding: gzip;q=0, deflate,*;q=0.1,identity;q=0"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "br"
+ expect resp.bodylen == 89
+ expect resp.http.X-Cache-Hit == 1
+
+ # Rejected star and "br" not accepted
+ txreq -url "/accept-encoding-star" -hdr "Accept-Encoding: gzip;q=0, deflate,*;q=0"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "deflate"
+ expect resp.bodylen == 99
+ expect resp.http.X-Cache-Hit == 0
+
+
+ #
+ # Multiple content-encodings
+ #
+ txreq -url "/multiple-content-encoding" -hdr "Accept-Encoding: gzip;q=0.8, deflate"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "deflate, gzip"
+ expect resp.bodylen == 109
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/multiple-content-encoding" -hdr "Accept-Encoding: deflate,gzip;q=0.7"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "deflate, gzip"
+ expect resp.bodylen == 109
+ expect resp.http.X-Cache-Hit == 1
+
+
+ #
+ # Unknown content-encoding
+ # The response should not be cached since it has an unknown content encoding
+ #
+ txreq -url "/unknown-content-encoding" -hdr "Accept-Encoding: gzip;q=0.8, deflate, first_encoding"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "unknown_encoding"
+ expect resp.bodylen == 119
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/unknown-content-encoding" -hdr "Accept-Encoding: deflate,gzip;q=0.8, first_encoding"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "unknown_encoding"
+ expect resp.bodylen == 119
+ expect resp.http.X-Cache-Hit == 0
+
+ #
+ # Hash collision (https://github.com/haproxy/haproxy/issues/988)
+ #
+ # crc32(gzip) ^ crc32(br) ^ crc32(xxx) ^ crc32(jdcqiab) == crc32(gzip)
+ txreq -url "/hash-collision" -hdr "Accept-Encoding: br,gzip,xxx,jdcqiab"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "br"
+ expect resp.bodylen == 129
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/hash-collision" -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.bodylen == 139
+ expect resp.http.X-Cache-Hit == 0
+} -run
diff --git a/reg-tests/checks/1be_40srv_odd_health_checks.vtc b/reg-tests/checks/1be_40srv_odd_health_checks.vtc
new file mode 100644
index 0000000..d0f3be5
--- /dev/null
+++ b/reg-tests/checks/1be_40srv_odd_health_checks.vtc
@@ -0,0 +1,117 @@
+varnishtest "Health-checks: only for servers with 'check' set"
+feature ignore_unknown_macro
+
+# This test start 40 servers in the same backend, named srv0 up to srv39.
+# Only the odd servers have health-checks enabled.
+# The first health-checks passed tests are checked for all these servers
+# thanks to syslog messages.
+
+#REQUIRE_VERSION=2.4
+#EXCLUDE_TARGETS=freebsd
+#REGTEST_TYPE=slow
+
+syslog S -repeat 20 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv([13579]|[123][13579]) succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP"
+} -start
+
+server s0 {} -start
+server s1 {} -start
+server s2 {} -start
+server s3 {} -start
+server s4 {} -start
+server s5 {} -start
+server s6 {} -start
+server s7 {} -start
+server s8 {} -start
+server s9 {} -start
+server s10 {} -start
+server s11 {} -start
+server s12 {} -start
+server s13 {} -start
+server s14 {} -start
+server s15 {} -start
+server s16 {} -start
+server s17 {} -start
+server s18 {} -start
+server s19 {} -start
+server s20 {} -start
+server s21 {} -start
+server s22 {} -start
+server s23 {} -start
+server s24 {} -start
+server s25 {} -start
+server s26 {} -start
+server s27 {} -start
+server s28 {} -start
+server s29 {} -start
+server s30 {} -start
+server s31 {} -start
+server s32 {} -start
+server s33 {} -start
+server s34 {} -start
+server s35 {} -start
+server s36 {} -start
+server s37 {} -start
+server s38 {} -start
+server s39 {} -start
+
+haproxy h1 -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ default-server no-check inter 200ms downinter 100ms rise 1 fall 1
+
+ backend be1
+ option log-health-checks
+ log ${S_addr}:${S_port} daemon
+ server srv0 ${s0_addr}:${s0_port}
+ server srv1 ${s1_addr}:${s1_port} check
+ server srv2 ${s2_addr}:${s2_port}
+ server srv3 ${s3_addr}:${s3_port} check
+ server srv4 ${s4_addr}:${s4_port}
+ server srv5 ${s5_addr}:${s5_port} check
+ server srv6 ${s6_addr}:${s6_port}
+ server srv7 ${s7_addr}:${s7_port} check
+ server srv8 ${s8_addr}:${s8_port}
+ server srv9 ${s9_addr}:${s9_port} check
+ server srv10 ${s10_addr}:${s10_port}
+ server srv11 ${s11_addr}:${s11_port} check
+ server srv12 ${s12_addr}:${s12_port}
+ server srv13 ${s13_addr}:${s13_port} check
+ server srv14 ${s14_addr}:${s14_port}
+ server srv15 ${s15_addr}:${s15_port} check
+ server srv16 ${s16_addr}:${s16_port}
+ server srv17 ${s17_addr}:${s17_port} check
+ server srv18 ${s18_addr}:${s18_port}
+ server srv19 ${s19_addr}:${s19_port} check
+ server srv20 ${s20_addr}:${s20_port}
+ server srv21 ${s21_addr}:${s21_port} check
+ server srv22 ${s22_addr}:${s22_port}
+ server srv23 ${s23_addr}:${s23_port} check
+ server srv24 ${s24_addr}:${s24_port}
+ server srv25 ${s25_addr}:${s25_port} check
+ server srv26 ${s26_addr}:${s26_port}
+ server srv27 ${s27_addr}:${s27_port} check
+ server srv28 ${s28_addr}:${s28_port}
+ server srv29 ${s29_addr}:${s29_port} check
+ server srv30 ${s30_addr}:${s30_port}
+ server srv31 ${s31_addr}:${s31_port} check
+ server srv32 ${s32_addr}:${s32_port}
+ server srv33 ${s33_addr}:${s33_port} check
+ server srv34 ${s34_addr}:${s34_port}
+ server srv35 ${s35_addr}:${s35_port} check
+ server srv36 ${s36_addr}:${s36_port}
+ server srv37 ${s37_addr}:${s37_port} check
+ server srv38 ${s38_addr}:${s38_port}
+ server srv39 ${s39_addr}:${s39_port} check
+} -start
+
+syslog S -wait
+
+haproxy h1 -cli {
+ send "show servers state"
+ expect ~ "# be_id be_name srv_id srv_name srv_addr srv_op_state srv_admin_state srv_uweight srv_iweight srv_time_since_last_change srv_check_status srv_check_result srv_check_health srv_check_state srv_agent_state bk_f_forced_id srv_f_forced_id srv_fqdn srv_port srvrecord srv_use_ssl srv_check_port srv_check_addr srv_agent_addr srv_agent_port\n2 be1 1 srv0 ${s0_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s0_port} - 0 0 - - 0\n2 be1 2 srv1 ${s1_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s1_port} - 0 0 - - 0\n2 be1 3 srv2 ${s2_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s2_port} - 0 0 - - 0\n2 be1 4 srv3 ${s3_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s3_port} - 0 0 - - 0\n2 be1 5 srv4 ${s4_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s4_port} - 0 0 - - 0\n2 be1 6 srv5 ${s5_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s5_port} - 0 0 - - 0\n2 be1 7 srv6 ${s6_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s6_port} - 0 0 - - 0\n2 be1 8 srv7 ${s7_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s7_port} - 0 0 - - 0\n2 be1 9 srv8 ${s8_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s8_port} - 0 0 - - 0\n2 be1 10 srv9 ${s9_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s9_port} - 0 0 - - 0\n2 be1 11 srv10 ${s10_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s10_port} - 0 0 - - 0\n2 be1 12 srv11 ${s11_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s11_port} - 0 0 - - 0\n2 be1 13 srv12 ${s12_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s12_port} - 0 0 - - 0\n2 be1 14 srv13 ${s13_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s13_port} - 0 0 - - 0\n2 be1 15 srv14 ${s14_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s14_port} - 0 0 - - 0\n2 be1 16 srv15 ${s15_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s15_port} - 0 0 - - 0\n2 be1 17 srv16 ${s16_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s16_port} - 0 0 - - 0\n2 be1 18 srv17 ${s17_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s17_port} - 0 0 - - 0\n2 be1 19 srv18 ${s18_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s18_port} - 0 0 - - 0\n2 be1 20 srv19 ${s19_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s19_port} - 0 0 - - 0\n2 be1 21 srv20 ${s20_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s20_port} - 0 0 - - 0\n2 be1 22 srv21 ${s21_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s21_port} - 0 0 - - 0\n2 be1 23 srv22 ${s22_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s22_port} - 0 0 - - 0\n2 be1 24 srv23 ${s23_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s23_port} - 0 0 - - 0\n2 be1 25 srv24 ${s24_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s24_port} - 0 0 - - 0\n2 be1 26 srv25 ${s25_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s25_port} - 0 0 - - 0\n2 be1 27 srv26 ${s26_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s26_port} - 0 0 - - 0\n2 be1 28 srv27 ${s27_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s27_port} - 0 0 - - 0\n2 be1 29 srv28 ${s28_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s28_port} - 0 0 - - 0\n2 be1 30 srv29 ${s29_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s29_port} - 0 0 - - 0\n2 be1 31 srv30 ${s30_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s30_port} - 0 0 - - 0\n2 be1 32 srv31 ${s31_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s31_port} - 0 0 - - 0\n2 be1 33 srv32 ${s32_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s32_port} - 0 0 - - 0\n2 be1 34 srv33 ${s33_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s33_port} - 0 0 - - 0\n2 be1 35 srv34 ${s34_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s34_port} - 0 0 - - 0\n2 be1 36 srv35 ${s35_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s35_port} - 0 0 - - 0\n2 be1 37 srv36 ${s36_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s36_port} - 0 0 - - 0\n2 be1 38 srv37 ${s37_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s37_port} - 0 0 - - 0\n2 be1 39 srv38 ${s38_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s38_port} - 0 0 - - 0\n2 be1 40 srv39 ${s39_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s39_port} - 0 0 - - 0\n"
+}
+
diff --git a/reg-tests/checks/40be_2srv_odd_health_checks.vtc b/reg-tests/checks/40be_2srv_odd_health_checks.vtc
new file mode 100644
index 0000000..cbd4fc0
--- /dev/null
+++ b/reg-tests/checks/40be_2srv_odd_health_checks.vtc
@@ -0,0 +1,645 @@
+varnishtest "Health-checks"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+#EXCLUDE_TARGETS=freebsd,osx,generic
+#REGTEST_TYPE=slow
+
+# This script start 40 servers named s0 up to s39.
+# For 0 <= i <= 19:
+# - s(i) and s(i+1) belong to backend be(2*i+1),
+# - fe(2*i+1) backend is configured to used be(2*i+1) backend.
+# - only s(2*i+1) servers have health-checks enabled,
+# - we start 20 clients named s(2*i+1) which connect to fe(2*i+1) frontend,
+# - so that to ensure that health-checks do not consume any connection
+# (any varnishtest server without -repeat <n> with n > 1 accepts
+# only one connection).
+# - we take care of sending the clients to the unchecked servers using the
+# "first" lb algo so that servers always receive a valid request
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv3 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S5 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be5/srv5 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S7 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be7/srv7 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S9 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be9/srv9 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S11 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be11/srv11 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S13 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be13/srv13 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S15 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be15/srv15 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S17 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be17/srv17 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S19 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be19/srv19 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S21 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be21/srv21 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S23 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be23/srv23 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S25 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be25/srv25 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S27 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be27/srv27 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S29 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be29/srv29 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S31 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be31/srv31 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S33 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be33/srv33 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S35 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be35/srv35 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S37 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be37/srv37 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+syslog S39 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be39/srv39 succeeded, reason: Layer4 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP"
+} -start
+
+server s0 {
+ rxreq
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ txresp
+} -start
+
+server s4 {
+ rxreq
+ txresp
+} -start
+
+server s6 {
+ rxreq
+ txresp
+} -start
+
+server s8 {
+ rxreq
+ txresp
+} -start
+
+server s10 {
+ rxreq
+ txresp
+} -start
+
+server s12 {
+ rxreq
+ txresp
+} -start
+
+server s14 {
+ rxreq
+ txresp
+} -start
+
+server s16 {
+ rxreq
+ txresp
+} -start
+
+server s18 {
+ rxreq
+ txresp
+} -start
+
+server s20 {
+ rxreq
+ txresp
+} -start
+
+server s22 {
+ rxreq
+ txresp
+} -start
+
+server s24 {
+ rxreq
+ txresp
+} -start
+
+server s26 {
+ rxreq
+ txresp
+} -start
+
+server s28 {
+ rxreq
+ txresp
+} -start
+
+server s30 {
+ rxreq
+ txresp
+} -start
+
+server s32 {
+ rxreq
+ txresp
+} -start
+
+server s34 {
+ rxreq
+ txresp
+} -start
+
+server s36 {
+ rxreq
+ txresp
+} -start
+
+server s38 {
+ rxreq
+ txresp
+} -start
+
+server s1 {} -start
+server s3 {} -start
+server s5 {} -start
+server s7 {} -start
+server s9 {} -start
+server s11 {} -start
+server s13 {} -start
+server s15 {} -start
+server s17 {} -start
+server s19 {} -start
+server s21 {} -start
+server s23 {} -start
+server s25 {} -start
+server s27 {} -start
+server s29 {} -start
+server s31 {} -start
+server s33 {} -start
+server s35 {} -start
+server s37 {} -start
+server s39 {} -start
+
+haproxy h1 -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ balance first
+ default-server no-check inter 20ms downinter 1s rise 1 fall 1
+
+ backend be1
+ option log-health-checks
+ log ${S1_addr}:${S1_port} daemon
+ server srv0 ${s0_addr}:${s0_port}
+ server srv1 ${s1_addr}:${s1_port} check
+
+ backend be3
+ option log-health-checks
+ log ${S3_addr}:${S3_port} daemon
+ server srv2 ${s2_addr}:${s2_port}
+ server srv3 ${s3_addr}:${s3_port} check
+
+ backend be5
+ option log-health-checks
+ log ${S5_addr}:${S5_port} daemon
+ server srv4 ${s4_addr}:${s4_port}
+ server srv5 ${s5_addr}:${s5_port} check
+
+ backend be7
+ option log-health-checks
+ log ${S7_addr}:${S7_port} daemon
+ server srv6 ${s6_addr}:${s6_port}
+ server srv7 ${s7_addr}:${s7_port} check
+
+ backend be9
+ option log-health-checks
+ log ${S9_addr}:${S9_port} daemon
+ server srv8 ${s8_addr}:${s8_port}
+ server srv9 ${s9_addr}:${s9_port} check
+
+ backend be11
+ option log-health-checks
+ log ${S11_addr}:${S11_port} daemon
+ server srv10 ${s10_addr}:${s10_port}
+ server srv11 ${s11_addr}:${s11_port} check
+
+ backend be13
+ option log-health-checks
+ log ${S13_addr}:${S13_port} daemon
+ server srv12 ${s12_addr}:${s12_port}
+ server srv13 ${s13_addr}:${s13_port} check
+
+ backend be15
+ option log-health-checks
+ log ${S15_addr}:${S15_port} daemon
+ server srv14 ${s14_addr}:${s14_port}
+ server srv15 ${s15_addr}:${s15_port} check
+
+ backend be17
+ option log-health-checks
+ log ${S17_addr}:${S17_port} daemon
+ server srv16 ${s16_addr}:${s16_port}
+ server srv17 ${s17_addr}:${s17_port} check
+
+ backend be19
+ option log-health-checks
+ log ${S19_addr}:${S19_port} daemon
+ server srv18 ${s18_addr}:${s18_port}
+ server srv19 ${s19_addr}:${s19_port} check
+
+ backend be21
+ option log-health-checks
+ log ${S21_addr}:${S21_port} daemon
+ server srv20 ${s20_addr}:${s20_port}
+ server srv21 ${s21_addr}:${s21_port} check
+
+ backend be23
+ option log-health-checks
+ log ${S23_addr}:${S23_port} daemon
+ server srv22 ${s22_addr}:${s22_port}
+ server srv23 ${s23_addr}:${s23_port} check
+
+ backend be25
+ option log-health-checks
+ log ${S25_addr}:${S25_port} daemon
+ server srv24 ${s24_addr}:${s24_port}
+ server srv25 ${s25_addr}:${s25_port} check
+
+ backend be27
+ option log-health-checks
+ log ${S27_addr}:${S27_port} daemon
+ server srv26 ${s26_addr}:${s26_port}
+ server srv27 ${s27_addr}:${s27_port} check
+
+ backend be29
+ option log-health-checks
+ log ${S29_addr}:${S29_port} daemon
+ server srv28 ${s28_addr}:${s28_port}
+ server srv29 ${s29_addr}:${s29_port} check
+
+ backend be31
+ option log-health-checks
+ log ${S31_addr}:${S31_port} daemon
+ server srv30 ${s30_addr}:${s30_port}
+ server srv31 ${s31_addr}:${s31_port} check
+
+ backend be33
+ option log-health-checks
+ log ${S33_addr}:${S33_port} daemon
+ server srv32 ${s32_addr}:${s32_port}
+ server srv33 ${s33_addr}:${s33_port} check
+
+ backend be35
+ option log-health-checks
+ log ${S35_addr}:${S35_port} daemon
+ server srv34 ${s34_addr}:${s34_port}
+ server srv35 ${s35_addr}:${s35_port} check
+
+ backend be37
+ option log-health-checks
+ log ${S37_addr}:${S37_port} daemon
+ server srv36 ${s36_addr}:${s36_port}
+ server srv37 ${s37_addr}:${s37_port} check
+
+ backend be39
+ option log-health-checks
+ log ${S39_addr}:${S39_port} daemon
+ server srv38 ${s38_addr}:${s38_port}
+ server srv39 ${s39_addr}:${s39_port} check
+
+ frontend fe1
+ bind "fd@${fe1}"
+ use_backend be1
+
+ frontend fe3
+ bind "fd@${fe3}"
+ use_backend be3
+
+ frontend fe5
+ bind "fd@${fe5}"
+ use_backend be5
+
+ frontend fe7
+ bind "fd@${fe7}"
+ use_backend be7
+
+ frontend fe9
+ bind "fd@${fe9}"
+ use_backend be9
+
+ frontend fe11
+ bind "fd@${fe11}"
+ use_backend be11
+
+ frontend fe13
+ bind "fd@${fe13}"
+ use_backend be13
+
+ frontend fe15
+ bind "fd@${fe15}"
+ use_backend be15
+
+ frontend fe17
+ bind "fd@${fe17}"
+ use_backend be17
+
+ frontend fe19
+ bind "fd@${fe19}"
+ use_backend be19
+
+ frontend fe21
+ bind "fd@${fe21}"
+ use_backend be21
+
+ frontend fe23
+ bind "fd@${fe23}"
+ use_backend be23
+
+ frontend fe25
+ bind "fd@${fe25}"
+ use_backend be25
+
+ frontend fe27
+ bind "fd@${fe27}"
+ use_backend be27
+
+ frontend fe29
+ bind "fd@${fe29}"
+ use_backend be29
+
+ frontend fe31
+ bind "fd@${fe31}"
+ use_backend be31
+
+ frontend fe33
+ bind "fd@${fe33}"
+ use_backend be33
+
+ frontend fe35
+ bind "fd@${fe35}"
+ use_backend be35
+
+ frontend fe37
+ bind "fd@${fe37}"
+ use_backend be37
+
+ frontend fe39
+ bind "fd@${fe39}"
+ use_backend be39
+} -start
+
+# This is a sort of synchronization: after having waited for all the syslog
+# servers we are sure that all the health-checks have succeeded.
+syslog S1 -wait
+syslog S3 -wait
+syslog S5 -wait
+syslog S7 -wait
+syslog S9 -wait
+syslog S11 -wait
+syslog S13 -wait
+syslog S15 -wait
+syslog S17 -wait
+syslog S19 -wait
+syslog S21 -wait
+syslog S23 -wait
+syslog S25 -wait
+syslog S27 -wait
+syslog S29 -wait
+syslog S31 -wait
+syslog S33 -wait
+syslog S35 -wait
+syslog S37 -wait
+syslog S39 -wait
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c3 -connect ${h1_fe3_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c5 -connect ${h1_fe5_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c7 -connect ${h1_fe7_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c9 -connect ${h1_fe9_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c11 -connect ${h1_fe11_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c13 -connect ${h1_fe13_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c15 -connect ${h1_fe15_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c17 -connect ${h1_fe17_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c19 -connect ${h1_fe19_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c21 -connect ${h1_fe21_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c23 -connect ${h1_fe23_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c25 -connect ${h1_fe25_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c27 -connect ${h1_fe27_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c29 -connect ${h1_fe29_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c31 -connect ${h1_fe31_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c33 -connect ${h1_fe33_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c35 -connect ${h1_fe35_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c37 -connect ${h1_fe37_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c39 -connect ${h1_fe39_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+client c1 -wait
+client c3 -wait
+client c5 -wait
+client c7 -wait
+client c9 -wait
+client c11 -wait
+client c13 -wait
+client c15 -wait
+client c17 -wait
+client c19 -wait
+client c21 -wait
+client c23 -wait
+client c25 -wait
+client c27 -wait
+client c29 -wait
+client c31 -wait
+client c33 -wait
+client c35 -wait
+client c37 -wait
+client c39 -wait
+
+server s0 -wait
+server s2 -wait
+server s4 -wait
+server s6 -wait
+server s8 -wait
+server s10 -wait
+server s12 -wait
+server s14 -wait
+server s16 -wait
+server s18 -wait
+server s20 -wait
+server s22 -wait
+server s24 -wait
+server s26 -wait
+server s28 -wait
+server s30 -wait
+server s32 -wait
+server s34 -wait
+server s36 -wait
+server s38 -wait
+
+
+haproxy h1 -cli {
+ send "show servers state"
+ # output produced using the command below (warning, a bug inserts a "be0" every other line:
+ # for ((i=0;i<40;i++)); do id=$((i/2+2)); be=$((i|1)); si=$(((i&1)+1));
+ # if ((i&1)); then chk="6 ([[:digit:]]+ ){3}"; else chk="1 0 1 0 ";fi;
+ # printf "%d be%d %d srv%d \${s%d_addr} 2 0 1 1 [[:digit:]]+ %s0 0 0 - \${s%d_port} - 0 0 - - 0\n" "$id" "$be" "$si" "$i" "$i" "$chk" "$i" "$i" ;
+ # done|grep -v be0|sed 's,$,\\n,'| tr -d '\n'
+ expect ~ "# be_id be_name srv_id srv_name srv_addr srv_op_state srv_admin_state srv_uweight srv_iweight srv_time_since_last_change srv_check_status srv_check_result srv_check_health srv_check_state srv_agent_state bk_f_forced_id srv_f_forced_id srv_fqdn srv_port srvrecord srv_use_ssl srv_check_port srv_check_addr srv_agent_addr srv_agent_port\n2 be1 1 srv0 ${s0_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s0_port} - 0 0 - - 0\n2 be1 2 srv1 ${s1_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s1_port} - 0 0 - - 0\n3 be3 1 srv2 ${s2_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s2_port} - 0 0 - - 0\n3 be3 2 srv3 ${s3_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s3_port} - 0 0 - - 0\n4 be5 1 srv4 ${s4_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s4_port} - 0 0 - - 0\n4 be5 2 srv5 ${s5_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s5_port} - 0 0 - - 0\n5 be7 1 srv6 ${s6_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s6_port} - 0 0 - - 0\n5 be7 2 srv7 ${s7_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s7_port} - 0 0 - - 0\n6 be9 1 srv8 ${s8_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s8_port} - 0 0 - - 0\n6 be9 2 srv9 ${s9_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s9_port} - 0 0 - - 0\n7 be11 1 srv10 ${s10_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s10_port} - 0 0 - - 0\n7 be11 2 srv11 ${s11_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s11_port} - 0 0 - - 0\n8 be13 1 srv12 ${s12_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s12_port} - 0 0 - - 0\n8 be13 2 srv13 ${s13_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s13_port} - 0 0 - - 0\n9 be15 1 srv14 ${s14_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s14_port} - 0 0 - - 0\n9 be15 2 srv15 ${s15_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s15_port} - 0 0 - - 0\n10 be17 1 srv16 ${s16_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s16_port} - 0 0 - - 0\n10 be17 2 srv17 ${s17_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s17_port} - 0 0 - - 0\n11 be19 1 srv18 ${s18_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s18_port} - 0 0 - - 0\n11 be19 2 srv19 ${s19_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s19_port} - 0 0 - - 0\n12 be21 1 srv20 ${s20_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s20_port} - 0 0 - - 0\n12 be21 2 srv21 ${s21_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s21_port} - 0 0 - - 0\n13 be23 1 srv22 ${s22_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s22_port} - 0 0 - - 0\n13 be23 2 srv23 ${s23_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s23_port} - 0 0 - - 0\n14 be25 1 srv24 ${s24_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s24_port} - 0 0 - - 0\n14 be25 2 srv25 ${s25_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s25_port} - 0 0 - - 0\n15 be27 1 srv26 ${s26_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s26_port} - 0 0 - - 0\n15 be27 2 srv27 ${s27_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s27_port} - 0 0 - - 0\n16 be29 1 srv28 ${s28_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s28_port} - 0 0 - - 0\n16 be29 2 srv29 ${s29_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s29_port} - 0 0 - - 0\n17 be31 1 srv30 ${s30_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s30_port} - 0 0 - - 0\n17 be31 2 srv31 ${s31_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s31_port} - 0 0 - - 0\n18 be33 1 srv32 ${s32_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s32_port} - 0 0 - - 0\n18 be33 2 srv33 ${s33_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s33_port} - 0 0 - - 0\n19 be35 1 srv34 ${s34_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s34_port} - 0 0 - - 0\n19 be35 2 srv35 ${s35_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s35_port} - 0 0 - - 0\n20 be37 1 srv36 ${s36_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s36_port} - 0 0 - - 0\n20 be37 2 srv37 ${s37_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s37_port} - 0 0 - - 0\n21 be39 1 srv38 ${s38_addr} 2 0 1 1 [[:digit:]]+ 1 0 1 0 0 0 0 - ${s38_port} - 0 0 - - 0\n21 be39 2 srv39 ${s39_addr} 2 0 1 1 [[:digit:]]+ 6 ([[:digit:]]+ ){3}0 0 0 - ${s39_port} - 0 0 - - 0\n"
+}
+
diff --git a/reg-tests/checks/4be_1srv_health_checks.vtc b/reg-tests/checks/4be_1srv_health_checks.vtc
new file mode 100644
index 0000000..88b631a
--- /dev/null
+++ b/reg-tests/checks/4be_1srv_health_checks.vtc
@@ -0,0 +1,201 @@
+varnishtest "Health-check test"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+#EXCLUDE_TARGETS=freebsd
+#REGTEST_TYPE=slow
+
+# This script test health-checks for four backends with one server by backend.
+# A syslog server is attached to each backend to check the syslog messages
+# in the righ order.
+
+# First, we check a health-check has passed for all the servers thanks to the syslog
+# messages. Then each server is disabled. The health-check status are checked.
+# Then each server is re-enabled. Finally health-check status
+# verifications for each server terminate the execution of this script.
+
+# Note that the CLI is synchronized with the syslog servers so that
+# to be sure to receive the passed health-checks status messages before
+# disabling the servers. Same thing, when we check that the servers are down
+# before enabling the servers.
+
+# Cyclic barrier to synchronize the CLI with the syslog servers
+barrier b1 cond 5 -cyclic
+
+# These servers are there only for the health-check test.
+server s1 {
+} -start
+
+server s2 {
+} -start
+
+server s3 {
+} -start
+
+server s4 {
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP"
+ barrier b1 sync
+ recv alert
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Server be1/srv1 is going DOWN for maintenance. 0 active and 0 backup servers left. [01] sessions active, 0 requeued, 0 remaining in queue."
+ recv emerg
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: backend be1 has no server available!"
+ barrier b1 sync
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: (Server be1/srv1 is UP/READY \\(leaving forced maintenance\\).|Health check for server be1/srv1 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP)"
+ barrier b1 sync
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv2 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP"
+ barrier b1 sync
+ recv alert
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Server be2/srv2 is going DOWN for maintenance. 0 active and 0 backup servers left. [01] sessions active, 0 requeued, 0 remaining in queue."
+ recv emerg
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: backend be2 has no server available!"
+ barrier b1 sync
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: (Server be2/srv2 is UP/READY \\(leaving forced maintenance\\).|Health check for server be2/srv2 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP)"
+ barrier b1 sync
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv3 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP"
+ barrier b1 sync
+ recv alert
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Server be3/srv3 is going DOWN for maintenance. 0 active and 0 backup servers left. [01] sessions active, 0 requeued, 0 remaining in queue."
+ recv emerg
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: backend be3 has no server available!"
+ barrier b1 sync
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: (Server be3/srv3 is UP/READY \\(leaving forced maintenance\\).|Health check for server be3/srv3 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP)"
+ barrier b1 sync
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv4 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP"
+ barrier b1 sync
+ recv alert
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Server be4/srv4 is going DOWN for maintenance. 0 active and 0 backup servers left. [01] sessions active, 0 requeued, 0 remaining in queue."
+ recv emerg
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: backend be4 has no server available!"
+ barrier b1 sync
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: (Server be4/srv4 is UP/READY \\(leaving forced maintenance\\).|Health check for server be4/srv4 succeeded.+reason: Layer4 check passed.+check duration: [[:digit:]]+ms.+status: 1/1 UP)"
+ barrier b1 sync
+} -start
+
+
+haproxy h1 -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ default-server check inter 200ms downinter 100s rise 1 fall 1
+
+ frontend fe1
+ bind "fd@${fe1}"
+ use_backend be1
+
+ frontend fe2
+ bind "fd@${fe2}"
+ use_backend be2
+
+ frontend fe3
+ bind "fd@${fe3}"
+ use_backend be3
+
+ frontend fe4
+ bind "fd@${fe4}"
+ use_backend be4
+
+ backend be1
+ option log-health-checks
+ log ${S1_addr}:${S1_port} daemon
+ server srv1 ${s1_addr}:${s1_port}
+
+ backend be2
+ option log-health-checks
+ log ${S2_addr}:${S2_port} daemon
+ server srv2 ${s2_addr}:${s2_port}
+
+ backend be3
+ option log-health-checks
+ log ${S3_addr}:${S3_port} daemon
+ server srv3 ${s3_addr}:${s3_port}
+
+ backend be4
+ option log-health-checks
+ log ${S4_addr}:${S4_port} daemon
+ server srv4 ${s4_addr}:${s4_port}
+} -start
+
+haproxy h1 -cli {
+ barrier b1 sync
+ send "show servers state"
+ expect ~ "# be_id be_name srv_id srv_name srv_addr srv_op_state srv_admin_state srv_uweight srv_iweight srv_time_since_last_change srv_check_status srv_check_result srv_check_health srv_check_state srv_agent_state bk_f_forced_id srv_f_forced_id srv_fqdn srv_port srvrecord srv_use_ssl srv_check_port srv_check_addr srv_agent_addr srv_agent_port\n6 be1 1 srv1 ${s1_addr} 2 0 1 1 [[:digit:]]+ 6 3 1 [67] 0 0 0 - ${s1_port} - 0 0 - - 0\n7 be2 1 srv2 ${s2_addr} 2 0 1 1 [[:digit:]]+ 6 3 1 [67] 0 0 0 - ${s2_port} - 0 0 - - 0\n8 be3 1 srv3 ${s3_addr} 2 0 1 1 [[:digit:]]+ 6 3 1 [67] 0 0 0 - ${s3_port} - 0 0 - - 0\n9 be4 1 srv4 ${s4_addr} 2 0 1 1 [[:digit:]]+ 6 3 1 [67] 0 0 0 - ${s4_port} - 0 0 - - 0"
+}
+
+haproxy h1 -cli {
+ send "disable server be1/srv1"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ send "disable server be2/srv2"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ send "disable server be3/srv3"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ send "disable server be4/srv4"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ barrier b1 sync
+ send "show servers state"
+ expect ~ "# be_id be_name srv_id srv_name srv_addr srv_op_state srv_admin_state srv_uweight srv_iweight srv_time_since_last_change srv_check_status srv_check_result srv_check_health srv_check_state srv_agent_state bk_f_forced_id srv_f_forced_id srv_fqdn srv_port srvrecord srv_use_ssl srv_check_port srv_check_addr srv_agent_addr srv_agent_port\n6 be1 1 srv1 ${s1_addr} 0 1 1 1 [[:digit:]]+ 6 3 [01] 1[45] 0 0 0 - ${s1_port} - 0 0 - - 0\n7 be2 1 srv2 ${s2_addr} 0 1 1 1 [[:digit:]]+ 6 3 [01] 1[45] 0 0 0 - ${s2_port} - 0 0 - - 0\n8 be3 1 srv3 ${s3_addr} 0 1 1 1 [[:digit:]]+ 6 3 [01] 1[45] 0 0 0 - ${s3_port} - 0 0 - - 0\n9 be4 1 srv4 ${s4_addr} 0 1 1 1 [[:digit:]]+ 6 3 [01] 1[45] 0 0 0 - ${s4_port} - 0 0 - - 0"
+}
+
+haproxy h1 -cli {
+ send "enable server be1/srv1"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ send "enable server be2/srv2"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ send "enable server be3/srv3"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ send "enable server be4/srv4"
+ expect ~ .*
+}
+
+haproxy h1 -cli {
+ barrier b1 sync
+ send "show servers state"
+ expect ~ "# be_id be_name srv_id srv_name srv_addr srv_op_state srv_admin_state srv_uweight srv_iweight srv_time_since_last_change srv_check_status srv_check_result srv_check_health srv_check_state srv_agent_state bk_f_forced_id srv_f_forced_id srv_fqdn srv_port srvrecord srv_use_ssl srv_check_port srv_check_addr srv_agent_addr srv_agent_port\n6 be1 1 srv1 ${s1_addr} 2 0 1 1 [[:digit:]]+ 6 [03] 1 [67] 0 0 0 - ${s1_port} - 0 0 - - 0\n7 be2 1 srv2 ${s2_addr} 2 0 1 1 [[:digit:]]+ 6 [03] 1 [67] 0 0 0 - ${s2_port} - 0 0 - - 0\n8 be3 1 srv3 ${s3_addr} 2 0 1 1 [[:digit:]]+ 6 [03] 1 [67] 0 0 0 - ${s3_port} - 0 0 - - 0\n9 be4 1 srv4 ${s4_addr} 2 0 1 1 [[:digit:]]+ 6 [03] 1 [67] 0 0 0 - ${s4_port} - 0 0 - - 0"
+}
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
+
diff --git a/reg-tests/checks/4be_1srv_smtpchk_httpchk_layer47errors.vtc b/reg-tests/checks/4be_1srv_smtpchk_httpchk_layer47errors.vtc
new file mode 100644
index 0000000..3c4fcd8
--- /dev/null
+++ b/reg-tests/checks/4be_1srv_smtpchk_httpchk_layer47errors.vtc
@@ -0,0 +1,99 @@
+varnishtest "Check: smptchk option"
+feature ignore_unknown_macro
+
+#EXCLUDE_TARGETS=freebsd,osx,generic
+#REGTEST_TYPE=slow
+
+barrier b cond 3
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 succeeded.+reason: Layer7 check passed.+code: 221.+check duration: [[:digit:]]+ms.+status: 1/1 UP."
+ barrier b sync
+ recv
+ expect ~ "Health check for server be1/srv1 failed.+reason: Layer7 timeout.+check duration: [[:digit:]]+ms.+status: 0/1 DOWN"
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv2 succeeded.+reason: Layer7 check passed.+code: 200.+.+check duration: [[:digit:]]+ms.+status: 1/1 UP."
+ barrier b sync
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv2 failed.+reason: Layer7 timeout.+check duration: [[:digit:]]+ms.+status: 0/1 DOWN"
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv3 failed.+reason: Layer4 connection problem.+info: \"General socket error \\(Network is unreachable\\)\".+check duration: [[:digit:]]+ms.+status: 0/1 DOWN."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv4 failed.+reason: Layer4 connection problem.+info: \"Connection refused\".+check duration: [[:digit:]]+ms.+status: 0/1 DOWN."
+} -start
+
+server s1 {
+ send "2"
+ send "2"
+ send "0"
+ send "\r\n"
+ recv 16
+ send "2"
+ send "4"
+ send "8"
+ send "\r\n"
+ recv 6
+ send "2"
+ send "2"
+ send "1"
+ send " ok\r\n"
+} -start
+
+server s2 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ option log-health-checks
+ default-server inter 200ms downinter 100ms rise 1 fall 1
+
+ backend be1
+ option smtpchk
+ log ${S1_addr}:${S1_port} daemon
+ server srv1 ${s1_addr}:${s1_port} check
+
+ backend be2
+ mode tcp
+ log ${S2_addr}:${S2_port} daemon
+ option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www
+ server srv2 ${s2_addr}:${s2_port} check
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ server srv3 255.255.255.255:11111 check
+
+ backend be4
+ log ${S4_addr}:${S4_port} daemon
+ server srv4 localhost:11111 check
+} -start
+
+haproxy h1 -cli {
+ barrier b sync
+ send "show servers state"
+ expect ~ .*
+}
+
+server s1 -wait
+server s2 -wait
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
+
+
diff --git a/reg-tests/checks/agent-check.vtc b/reg-tests/checks/agent-check.vtc
new file mode 100644
index 0000000..5cf51c6
--- /dev/null
+++ b/reg-tests/checks/agent-check.vtc
@@ -0,0 +1,42 @@
+varnishtest "Health-checks: agent-check"
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+barrier b1 cond 2 -cyclic
+barrier b2 cond 2 -cyclic
+
+server s1 {
+ barrier b1 sync
+ recv 5
+ send "75%,maxconn:30,maint,down\n"
+ expect_close
+ barrier b2 sync
+} -start
+
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ server srv ${s1_addr}:${s1_port} weight 100 agent-check agent-addr ${s1_addr} agent-port ${s1_port} agent-send "pouet" agent-inter 100ms
+} -start
+
+haproxy h1 -cli {
+ send "show servers state"
+ expect ~ "be1 1 srv 127.0.0.1 2 0 100 100 [[:digit:]]+ 1 0 [[:digit:]] 0 [[:digit:]]+ 0 0 - ${s1_port} -"
+ send "show stat"
+ expect ~ "be1,srv,0,0,0,0,,"
+
+ barrier b1 sync
+ barrier b2 sync
+ send "show servers state"
+ expect ~ "be1 1 srv 127.0.0.1 0 1 75 100 [[:digit:]]+ 1 0 [[:digit:]] 0 [[:digit:]]+ 0 0 - ${s1_port} -"
+ send "show stat"
+ expect ~ "be1,srv,0,0,0,0,30"
+}
diff --git a/reg-tests/checks/common.pem b/reg-tests/checks/common.pem
new file mode 120000
index 0000000..a4433d5
--- /dev/null
+++ b/reg-tests/checks/common.pem
@@ -0,0 +1 @@
+../ssl/common.pem \ No newline at end of file
diff --git a/reg-tests/checks/http-check-expect.vtc b/reg-tests/checks/http-check-expect.vtc
new file mode 100644
index 0000000..637eec6
--- /dev/null
+++ b/reg-tests/checks/http-check-expect.vtc
@@ -0,0 +1,64 @@
+varnishtest "Health-checks: some http-check expect tests"
+feature ignore_unknown_macro
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+# This script tests http-check expect rules.
+
+server s1 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ txresp -status 202 \
+ -hdr "x-test1: true, next value" \
+ -hdr "x-test2: true, begin-value, value-end, value-sub-string, value-reg-123ABC" \
+ -hdr "x-begin-test: 1" \
+ -hdr "x-test-end: 1" \
+ -hdr "x-sub-test: 1" \
+ -hdr "x-reg-test1: 1" \
+ -hdr "x-hdr-name: x-test1"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded.*code: 202"
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ option log-health-checks
+
+ backend be1
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk
+ http-check expect status 200-399
+
+ http-check expect hdr name "x-test1"
+ http-check expect hdr name -m str "X-Test2"
+ http-check expect hdr name -m beg "X-Begin-"
+ http-check expect hdr name -m end "-End"
+ http-check expect hdr name -m sub "-Sub-"
+ http-check expect hdr name -m reg "^[a-z]+-Reg-[a-z]+[0-9]\$"
+ http-check set-var(check.hdr_name) res.fhdr(x-hdr-name)
+ http-check expect hdr name-lf -m str "%[var(check.hdr_name)]"
+ http-check expect hdr name-lf -m str "%[res.fhdr(x-hdr-name)]"
+
+ http-check expect fhdr name "x-test1" value "true, next value"
+ http-check expect hdr name "x-test2" value -m str "true"
+ http-check expect hdr name -m beg "x-test" value -m beg "begin-"
+ http-check expect hdr name -m beg "x-test" value -m end "-end"
+ http-check expect hdr name -m beg "x-test" value -m sub "-sub-"
+ http-check expect hdr name -m beg "x-test" value -m reg "^value-reg-[A-Z0-9]+\$"
+ http-check expect fhdr name -m beg "x-test" value -m reg "value-reg-[A-Z0-9]+"
+ http-check set-var(check.hdr_value) str(x-test1)
+ http-check expect hdr name -m beg "x-" value-lf -m str "%[var(check.hdr_value)]"
+ http-check expect fhdr name -m beg "x-" value-lf -m str "%[res.fhdr(x-hdr-name)]"
+
+ server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1
+} -start
+
+syslog S1 -wait
diff --git a/reg-tests/checks/http-check-send.vtc b/reg-tests/checks/http-check-send.vtc
new file mode 100644
index 0000000..0970ee4
--- /dev/null
+++ b/reg-tests/checks/http-check-send.vtc
@@ -0,0 +1,168 @@
+varnishtest "Health-checks: http-check send test"
+#REGTEST_TYPE=slow
+#REQUIRE_VERSION=2.4
+feature ignore_unknown_macro
+
+# This script tests HTTP health-checks and more particularly the "http-check
+# send" directive.
+
+server s1 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ expect req.method == GET
+ expect req.url == /test
+ expect req.proto == HTTP/1.1
+ expect req.http.connection == "close"
+ txresp
+} -start
+
+server s3 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ expect req.http.hdr == <undef>
+ expect req.http.host == <undef>
+ expect req.http.x-test == <undef>
+ expect req.bodylen == 0
+ txresp
+} -start
+
+server s4 {
+ rxreq
+ expect req.method == GET
+ expect req.url == /status
+ expect req.proto == HTTP/1.1
+ expect req.http.connection == "close"
+ expect req.http.hdr == <undef>
+ expect req.http.host == "my-www-host"
+ expect req.http.x-test == true
+ expect req.http.content-length == 4
+ expect req.bodylen == 4
+ expect req.body == "test"
+ txresp
+} -start
+
+server s5 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ expect req.http.hdr == <undef>
+ expect req.http.host == "other-www-host"
+ expect req.http.x-test == <undef>
+ expect req.http.x-new-test == true
+ expect req.http.content-length == 10
+ expect req.bodylen == 10
+ expect req.body == "other test"
+ txresp
+} -start
+
+server s6 {
+ rxreq
+ expect req.method == GET
+ expect req.url == /
+ expect req.proto == HTTP/1.1
+ expect req.http.host == "ws-host"
+ expect req.http.connection == "upgrade"
+ expect req.http.upgrade == "raw-proto"
+ txresp \
+ -status 101 \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: raw-proto"
+} -start
+
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded.*code: 200"
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv succeeded.*code: 200"
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv succeeded.*code: 200"
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv succeeded.*code: 200"
+} -start
+
+syslog S5 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be5/srv succeeded.*code: 200"
+} -start
+
+syslog S6 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be6_ws/srv succeeded.*code: 101"
+} -start
+
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ option httpchk
+ option log-health-checks
+
+ backend be1
+ log ${S1_addr}:${S1_port} len 2048 local0
+ server srv ${s1_addr}:${s1_port} check inter 200ms rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} len 2048 local0
+ option httpchk GET /test HTTP/1.1
+ server srv ${s2_addr}:${s2_port} check inter 200ms rise 1 fall 1
+
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ option httpchk GET /status HTTP/1.1\r\nHdr:\ must-be-removed
+ option log-health-checks
+ http-check send hdr Host "my-www-host" hdr X-test true body "test"
+
+ backend be3
+ option httpchk
+ log ${S3_addr}:${S3_port} len 2048 local0
+ server srv ${s3_addr}:${s3_port} check inter 200ms rise 1 fall 1
+
+ backend be4
+ log ${S4_addr}:${S4_port} len 2048 local0
+ server srv ${s4_addr}:${s4_port} check inter 200ms rise 1 fall 1
+
+ backend be5
+ log ${S5_addr}:${S5_port} len 2058 local0
+ http-check send hdr Host "other-www-host" hdr X-New-Test true body "other test"
+ server srv ${s5_addr}:${s5_port} check inter 200ms rise 1 fall 1
+
+ backend be6_ws
+ log ${S6_addr}:${S6_port} len 2048 local0
+ http-check send meth GET uri / ver HTTP/1.1 hdr host ws-host hdr connection upgrade hdr upgrade raw-proto
+ http-check expect status 101
+ server srv ${s6_addr}:${s6_port} check inter 200ms rise 1 fall 1
+
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
+syslog S5 -wait
+syslog S6 -wait
diff --git a/reg-tests/checks/http-check.vtc b/reg-tests/checks/http-check.vtc
new file mode 100644
index 0000000..3353060
--- /dev/null
+++ b/reg-tests/checks/http-check.vtc
@@ -0,0 +1,157 @@
+varnishtest "Health-checks: some http-check tests"
+feature ignore_unknown_macro
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+# This script tests HTTP health-checks.
+
+server s1 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ expect req.method == GET
+ expect req.url == /status
+ expect req.proto == HTTP/1.1
+ txresp
+} -start
+
+server s3 {
+ rxreq
+ expect req.method == GET
+ expect req.url == /status
+ expect req.proto == HTTP/1.1
+ txresp
+} -start
+
+server s4 {
+ rxreq
+ expect req.method == GET
+ expect req.url == /req1
+ expect req.proto == HTTP/1.1
+ expect req.http.x-test == "server=srv"
+ expect req.http.x-haproxy-server-state ~ "UP.+name=be4/srv"
+ expect req.bodylen == 0
+ txresp
+
+ accept
+ rxreq
+ expect req.method == GET
+ expect req.url == /req2
+ expect req.proto == HTTP/1.1
+ expect req.http.x-test == "server="
+ expect req.http.x-haproxy-server-state ~ "UP.+name=be4/srv"
+ expect req.http.content-length == 17
+ expect req.bodylen == 17
+ expect req.body == "health-check body"
+ txresp
+
+ accept
+ rxreq
+ expect req.method == GET
+ expect req.url == /req3
+ expect req.proto == HTTP/1.0
+ expect req.http.x-test == <undef>
+ expect req.http.x-haproxy-server-state ~ "UP.+name=be4/srv"
+ expect req.bodylen == 0
+ txresp
+
+ accept
+ rxreq
+ expect req.method == GET
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ expect req.http.x-test == <undef>
+ expect req.http.x-haproxy-server-state ~ "UP.+name=be4/srv"
+ expect req.bodylen == 23
+ expect req.body == "health-check on be4-srv"
+ txresp
+
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ option log-health-checks
+
+ backend be1
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk
+ server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1
+
+ backend be2
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk GET /status HTTP/1.1
+ server srv ${s2_addr}:${s2_port} check inter 100ms rise 1 fall 1
+
+ backend be3
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk
+ http-check send meth GET uri /status ver HTTP/1.1
+ server srv ${s3_addr}:${s3_port} check inter 100ms rise 1 fall 1
+
+ backend be4
+ mode tcp
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk
+ http-check send-state
+ http-check connect addr ${s4_addr}:${s4_port}
+ http-check set-var(check.server) "str(srv)"
+ http-check set-var(check.path) "str(/req1)"
+ http-check send meth GET uri-lf "%[var(check.path)]" ver HTTP/1.1 hdr x-test "server=%[var(check.server)]"
+ http-check expect status 200
+ http-check connect addr ${s4_addr} port ${s4_port}
+ http-check unset-var(check.server)
+ http-check set-var(check.path) "str(/req2)"
+ http-check send meth GET uri-lf "%[var(check.path)]" ver HTTP/1.1 hdr x-test "server=%[var(check.server)]" body "health-check body"
+ http-check expect rstatus "^2[0-9]{2}"
+ http-check connect addr ${s4_addr} port ${s4_port}
+ http-check set-var(check.path) "str(/req3)"
+ http-check send meth GET uri-lf "%[var(check.path)]"
+ http-check expect rstatus "^2[0-9]{2}"
+ http-check connect addr ${s4_addr} port ${s4_port}
+ http-check unset-var(check.path)
+ http-check send meth GET uri-lf "%[var(check.path)]" body-lf "health-check on %[be_name]-%[srv_name]"
+ ## implicit expect rule
+ server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1
+
+ backend be5
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk
+ server srv ${h1_li1_addr}:${h1_li1_port} proto h2 check inter 100ms rise 1 fall 1
+
+ backend be6
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk GET /status HTTP/1.1
+ server srv ${h1_li1_addr}:${h1_li1_port} check check-proto h2 inter 100ms rise 1 fall 1
+
+ listen li1
+ mode http
+ bind "fd@${li1}" proto h2
+ http-request return status 200
+
+} -start
+
+syslog S1 -wait
diff --git a/reg-tests/checks/http-monitor-uri.vtc b/reg-tests/checks/http-monitor-uri.vtc
new file mode 100644
index 0000000..b6c8ccb
--- /dev/null
+++ b/reg-tests/checks/http-monitor-uri.vtc
@@ -0,0 +1,56 @@
+varnishtest "Test the HTTP directive monitor-uri"
+#REQUIRE_VERSION=2.2
+
+# This config tests the HTTP directive monitor-uri. Especially the path matching
+# when an absolute-form uri is received from the client. But also the
+# case-sensitivity of the matching.
+
+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}"
+ monitor-uri /health
+
+ frontend fe2
+ bind "fd@${fe2}"
+ monitor-uri http://www.haproxy.org/health
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /health
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -connect ${h1_fe1_sock} {
+ txreq -req GET -url http://www.haproxy.org/health \
+ -hdr "Host: www.haproxy.org"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /hEAlth
+ rxresp
+ expect resp.status == 503
+} -run
+
+client c4 -connect ${h1_fe2_sock} {
+ txreq -req GET -url http://www.haproxy.org/health \
+ -hdr "Host: www.haproxy.org"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c5 -connect ${h1_fe2_sock} {
+ txreq -req GET -url /health
+ rxresp
+ expect resp.status == 503
+} -run
diff --git a/reg-tests/checks/ldap-check.vtc b/reg-tests/checks/ldap-check.vtc
new file mode 100644
index 0000000..a0e5509
--- /dev/null
+++ b/reg-tests/checks/ldap-check.vtc
@@ -0,0 +1,96 @@
+varnishtest "Health-checks: LDAP health-check"
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests health-checks for LDAP application, enabled using
+# "option ldap-check" line. A intermediate listener is used to validate
+# the request because it is impossible with VTEST to read and match raw
+# text.
+
+server s1 {
+ recv 14
+ sendhex "300C020101 61 070A01 00 04000400"
+} -start
+
+server s2 {
+ recv 14
+ sendhex "300C020101 60 070A01 00 04000400"
+} -start
+
+server s3 {
+ recv 14
+ sendhex "300C020101 61 070A01 01 04000400"
+} -start
+
+server s4 {
+ recv 14
+ sendhex "308400000010020101 61 84000000070A01"
+ delay 0.1
+ sendhex "00 04000400"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer7 check passed.+info: \"Success\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv failed, reason: Layer7 invalid response.+info: \"Not LDAPv3 protocol\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv failed, reason: Layer7 wrong status.+code: 1.+info: \"See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv succeeded, reason: Layer7 check passed.+info: \"Success\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option ldap-check
+ server srv ${h1_ldap1_addr}:${h1_ldap1_port} check inter 1s rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ option log-health-checks
+ option ldap-check
+ server srv ${s2_addr}:${s2_port} check inter 1s rise 1 fall 1
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ option log-health-checks
+ option ldap-check
+ server srv ${s3_addr}:${s3_port} check inter 1s rise 1 fall 1
+
+ backend be4
+ log ${S4_addr}:${S4_port} daemon
+ option log-health-checks
+ option ldap-check
+ server srv ${s4_addr}:${s4_port} check inter 1s rise 1 fall 1
+
+ listen ldap1
+ bind "fd@${ldap1}"
+ tcp-request inspect-delay 100ms
+ tcp-request content accept if { req.len eq 14 } { req.payload(0,14) -m bin "300C020101600702010304008000" }
+ tcp-request content reject
+ server srv ${s1_addr}:${s1_port}
+
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
diff --git a/reg-tests/checks/mysql-check.vtc b/reg-tests/checks/mysql-check.vtc
new file mode 100644
index 0000000..b2348c3
--- /dev/null
+++ b/reg-tests/checks/mysql-check.vtc
@@ -0,0 +1,123 @@
+varnishtest "Health-checks: MySQL health-check"
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests health-checks for MySQL application, enabled using
+# "option mysql-check" line. A intermediate listener is used to validate
+# the request because it is impossible with VTEST to read and match raw
+# text.
+
+server s1 {
+ sendhex "4A0000000A382E302E3139000A0000006F3C025E6249410D00FFFFFF0200FFC715000000000000000000007C182159106E2761144322200063616368696E675F736861325F70617373776F726400"
+ expect_close
+} -start
+
+server s2 {
+ sendhex "4A0000000A382E302E3139000A0000006F3C025E6249410D00FFFFFF0200FFC715000000000000000000007C182159106E2761144322200063616368696E675F736861325F70617373776F726400"
+ recv 20
+ sendhex "03000002000000"
+} -start
+
+server s3 {
+ sendhex "4A0000000A382E302E3139000A0000006F3C025E6249410D00FFFFFF0200FFC715000000000000000000007C182159106E2761144322200063616368696E675F736861325F70617373776F726400"
+ recv 47
+ sendhex "0700000200000002000000"
+} -start
+
+server s4 {
+ sendhex "4A0000000A382E302E3139000A0000006F3C025E6249410D00FFFFFF0200FFC715000000000000000000007C182159106E2761144322200063616368696E675F736861325F70617373776F726400"
+ recv 21
+ sendhex "67000002FFE304436C69656E7420646F6573206E6F7420737570706F72742061757468656E7469636174696F6E2070726F746F636F6C20726571756573746564206279207365727665723B20636F6E736964657220757067726164696E67204D7953514C20636C69656E74"
+} -start
+
+server s5 {
+ sendhex "4A0000000A382E302E3139000A0000006F3C025E6249410D00FFFFFF0200FFC715000000000000000000007C182159106E2761144322200063616368696E675F736861325F70617373776F726400"
+ recv 48
+ sendhex "67000002FFE304436C69656E7420646F6573206E6F7420737570706F72742061757468656E7469636174696F6E2070726F746F636F6C20726571756573746564206279207365727665723B20636F6E736964657220757067726164696E67204D7953514C20636C69656E74"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer7 check passed.+info: \"8.0.19\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv succeeded, reason: Layer7 check passed.+info: \"8.0.19\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv succeeded, reason: Layer7 check passed.+info: \"8.0.19\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv failed, reason: Layer7 wrong status.+code: 1251.+info: \"Client does not support authentication protocol requested by server; consider upgrading MySQL client\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S5 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be5/srv failed, reason: Layer7 wrong status.+code: 1251.+info: \"Client does not support authentication protocol requested by server; consider upgrading MySQL client\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option mysql-check
+ server srv ${s1_addr}:${s1_port} check inter 1s rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ option log-health-checks
+ option mysql-check user user pre-41
+ server srv ${h1_mysql1_addr}:${h1_mysql1_port} check inter 1s rise 1 fall 1
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ option log-health-checks
+ option mysql-check user user
+ server srv ${h1_mysql2_addr}:${h1_mysql2_port} check inter 1s rise 1 fall 1
+
+ backend be4
+ log ${S4_addr}:${S4_port} daemon
+ option log-health-checks
+ option mysql-check user pouet
+ server srv ${s4_addr}:${s4_port} check inter 1s rise 1 fall 1
+
+ backend be5
+ log ${S5_addr}:${S5_port} daemon
+ option log-health-checks
+ option mysql-check user pouet post-41
+ server srv ${s5_addr}:${s5_port} check inter 1s rise 1 fall 1
+
+ listen mysql1
+ bind "fd@${mysql1}"
+ tcp-request inspect-delay 100ms
+ tcp-request content accept if { req.len eq 20 } { req.payload(0,20) -m bin "0B00000100800000017573657200000100000001" }
+ tcp-request content reject
+ server srv ${s2_addr}:${s2_port}
+
+ listen mysql2
+ bind "fd@${mysql2}"
+ tcp-request inspect-delay 100ms
+ tcp-request content accept if { req.len eq 47 } { req.payload(0,47) -m bin "2600000100820000008000012100000000000000000000000000000000000000000000007573657200000100000001" }
+ tcp-request content reject
+ server srv ${s3_addr}:${s3_port}
+
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
+syslog S5 -wait
diff --git a/reg-tests/checks/pgsql-check.vtc b/reg-tests/checks/pgsql-check.vtc
new file mode 100644
index 0000000..2c9c65b
--- /dev/null
+++ b/reg-tests/checks/pgsql-check.vtc
@@ -0,0 +1,93 @@
+varnishtest "Health-checks: PostgreSQL health-check"
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests health-checks for PostgreSQL application, enabled using
+# "option pgsql-check" line. A intermediate listener is used to validate
+# the request because it is impossible with VTEST to read and match raw
+# text.
+
+server s1 {
+ recv 23
+ sendhex "520000000800000000"
+} -start
+
+server s2 {
+ recv 23
+ sendhex "450000000B53464154414C00"
+} -start
+
+server s3 {
+ recv 23
+ send "Not a PostgreSQL response"
+} -start
+
+server s4 {
+ recv 23
+ sendhex "52000000170000000A534352414D2D5348412D3235360000"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer7 check passed.+info: \"PostgreSQL server is ok\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv failed, reason: Layer7 invalid response.+info: \"FATAL\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv failed, reason: Layer7 wrong status.+info: \"PostgreSQL unknown error\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv succeeded, reason: Layer7 check passed.+info: \"PostgreSQL server is ok\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option pgsql-check user postgres
+ server srv ${h1_pgsql_addr}:${h1_pgsql_port} check inter 1s rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ option log-health-checks
+ option pgsql-check user postgres
+ server srv ${s2_addr}:${s2_port} check inter 1s rise 1 fall 1
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ option log-health-checks
+ option pgsql-check user postgres
+ server srv ${s3_addr}:${s3_port} check inter 1s rise 1 fall 1
+
+ backend be4
+ log ${S4_addr}:${S4_port} daemon
+ option log-health-checks
+ option pgsql-check user postgres
+ server srv ${s4_addr}:${s4_port} check inter 1s rise 1 fall 1
+
+ listen pgsql1
+ bind "fd@${pgsql}"
+ tcp-request inspect-delay 100ms
+ tcp-request content accept if { req.len eq 23 } { req.payload(0,23) -m bin "00000017000300007573657200706f7374677265730000" }
+ tcp-request content reject
+ server srv ${s1_addr}:${s1_port}
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
diff --git a/reg-tests/checks/redis-check.vtc b/reg-tests/checks/redis-check.vtc
new file mode 100644
index 0000000..78b6ed3
--- /dev/null
+++ b/reg-tests/checks/redis-check.vtc
@@ -0,0 +1,61 @@
+varnishtest "Health-checks: Redis health-check"
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests health-checks for Redis application, enabled using
+# "option redis-check" line. A intermediate listener is used to validate
+# the request because it is impossible with VTEST to read and match raw
+# text.
+
+server s1 {
+ recv 14
+ send "+PONG\r\n"
+} -start
+
+server s2 {
+ recv 14
+ send "-Error message\r\n"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer7 check passed.+info: \"Redis server is ok\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv failed, reason: Layer7 wrong status.+info: \"-Error message\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option redis-check
+ server srv ${h1_redis_addr}:${h1_redis_port} check inter 1s rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ option log-health-checks
+ option redis-check
+ server srv ${s2_addr}:${s2_port} check inter 1s rise 1 fall 1
+
+ listen redis1
+ bind "fd@${redis}"
+ tcp-request inspect-delay 100ms
+ tcp-request content accept if { req.len eq 14 } { req.payload(0,14) -m str "*1\r\n\$4\r\nPING\r\n" }
+ tcp-request content reject
+ server srv ${s1_addr}:${s1_port}
+
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
diff --git a/reg-tests/checks/smtp-check.vtc b/reg-tests/checks/smtp-check.vtc
new file mode 100644
index 0000000..723f5f0
--- /dev/null
+++ b/reg-tests/checks/smtp-check.vtc
@@ -0,0 +1,110 @@
+varnishtest "Health-checks: SMTP health-check"
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests health-checks for SMTP servers, enabled using
+# "option smtpchk" line.
+
+server s1 {
+ send "220 smtp-check.vtc SMTP Server\r\n"
+ recv 16
+ send "250 smtp-check.vtc\r\n"
+ recv 6
+ send "221 smtp-check.vtc closing\r\n"
+} -start
+
+server s2 {
+ send "220 smtp-check.vtc SMTP Server\r\n"
+ recv 17
+ send "250-smtp-check.vtc\r\n"
+ send "250-KEYWORD\r\n"
+ send "250 LAST KEYWORD\r\n"
+ recv 6
+ send "221 smtp-check.vtc closing\r\n"
+} -start
+
+server s3 {
+ send "I'm not a SMTP server\r\n"
+} -start
+
+server s4 {
+ send "421 Try again later\r\n"
+} -start
+
+server s5 {
+ send "220 smtp-check.vtc SMTP Server\r\n"
+ recv 16
+ send "512 DNS error\r\n"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer7 check passed.+code: 221.+info: \"smtp-check.vtc closing\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv succeeded, reason: Layer7 check passed.+code: 221.+info: \"smtp-check.vtc closing\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv failed, reason: Layer7 invalid response.+info: \"I'm not a SMTP server\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv failed, reason: Layer7 wrong status.+code: 421.+info: \"Try again later\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S5 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be5/srv failed, reason: Layer7 wrong status.+code: 512.+info: \"DNS error\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option smtpchk
+ server srv ${s1_addr}:${s1_port} check inter 1s rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ option log-health-checks
+ option smtpchk EHLO domain.tld
+ server srv ${s2_addr}:${s2_port} check inter 1s rise 1 fall 1
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ option log-health-checks
+ option smtpchk
+ server srv ${s3_addr}:${s3_port} check inter 1s rise 1 fall 1
+
+ backend be4
+ log ${S4_addr}:${S4_port} daemon
+ option log-health-checks
+ option smtpchk
+ server srv ${s4_addr}:${s4_port} check inter 1s rise 1 fall 1
+
+ backend be5
+ log ${S5_addr}:${S5_port} daemon
+ option log-health-checks
+ option smtpchk EHLO domain.tld
+ server srv ${s5_addr}:${s5_port} check inter 1s rise 1 fall 1
+
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
+syslog S5 -wait
diff --git a/reg-tests/checks/spop-check.vtc b/reg-tests/checks/spop-check.vtc
new file mode 100644
index 0000000..93cef59
--- /dev/null
+++ b/reg-tests/checks/spop-check.vtc
@@ -0,0 +1,94 @@
+varnishtest "Health-checks: SPOP health-check"
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests health-checks for SPOE agent, enabled using
+# "option spop-check" line. A intermediate listener is used to validate
+# the request because it is impossible with VTEST to read and match raw
+# text.
+
+server s1 {
+ recv 82
+ sendhex "00000036 65 00000001 0000 0776657273696F6E 0803322E30 0E6D61782D6672616D652D73697A65 03FCF0 060C6361706162696C6974696573 0800"
+} -start
+
+server s2 {
+ recv 82
+ sendhex "00000000"
+} -start
+
+server s3 {
+ recv 82
+ sendhex "00000007 65 00000000 0000"
+} -start
+
+server s4 {
+ recv 82
+ sendhex "00000014 65 00000001 0000 0776657273696F6E 0803312E30"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer7 check passed.+info: \"SPOA server is ok\".+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv failed, reason: Layer7 invalid response.+info: \"invalid frame received\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv failed, reason: Layer7 invalid response.+info: \"fragmentation not supported\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv failed, reason: Layer7 invalid response.+info: \"unsupported version\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option spop-check
+ server srv ${h1_spop1_addr}:${h1_spop1_port} check inter 1s rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ option log-health-checks
+ option spop-check
+ server srv ${s2_addr}:${s2_port} check inter 1s rise 1 fall 1
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ option log-health-checks
+ option spop-check
+ server srv ${s3_addr}:${s3_port} check inter 1s rise 1 fall 1
+
+ backend be4
+ log ${S4_addr}:${S4_port} daemon
+ option log-health-checks
+ option spop-check
+ server srv ${s4_addr}:${s4_port} check inter 1s rise 1 fall 1
+
+ listen spop1
+ bind "fd@${spop1}"
+ tcp-request inspect-delay 100ms
+ tcp-request content accept if { req.len eq 82 } { req.payload(0,4) -m bin "0000004E" } #{ req.payload(4,4) -m bin "00000001" } { req.payload(8,2) -m bin "0000" } { req.payload(12,17) -m str "supported-version" }
+ tcp-request content reject
+ server srv ${s1_addr}:${s1_port}
+
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
diff --git a/reg-tests/checks/ssl-hello-check.vtc b/reg-tests/checks/ssl-hello-check.vtc
new file mode 100644
index 0000000..49abc0b
--- /dev/null
+++ b/reg-tests/checks/ssl-hello-check.vtc
@@ -0,0 +1,76 @@
+varnishtest "Health-checks: ssl-hello health-check"
+#REQUIRE_OPTION=OPENSSL
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests health-checks for SSL application, enabled using
+# "option ssl-hello-chk" line.
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv failed, reason: Layer6 invalid response.+info: \"TCPCHK got an empty response at step 2\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv failed, reason: Layer6 invalid response.+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+haproxy htst -conf {
+ global
+ tune.ssl.default-dh-param 2048
+
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}" ssl crt ${testdir}/common.pem
+
+ frontend fe2
+ bind "fd@${fe2}"
+
+ frontend fe3
+ mode http
+ bind "fd@${fe3}"
+
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option ssl-hello-chk
+ server srv ${htst_fe1_addr}:${htst_fe1_port} check inter 1s rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ option log-health-checks
+ option ssl-hello-chk
+ server srv ${htst_fe2_addr}:${htst_fe2_port} check inter 1s rise 1 fall 1
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ option log-health-checks
+ option ssl-hello-chk
+ server srv ${htst_fe3_addr}:${htst_fe3_port} check inter 1s rise 1 fall 1
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
diff --git a/reg-tests/checks/tcp-check-ssl.vtc b/reg-tests/checks/tcp-check-ssl.vtc
new file mode 100644
index 0000000..6ac1782
--- /dev/null
+++ b/reg-tests/checks/tcp-check-ssl.vtc
@@ -0,0 +1,118 @@
+varnishtest "Health-checks: tcp-check health-check with ssl options"
+#REQUIRE_OPTION=OPENSSL
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+syslog S_ok -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S3 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv failed, reason: Layer6 invalid response.+info: \"(Connection closed during SSL handshake|SSL handshake failure)\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv failed, reason: Layer6 invalid response.+info: \"(Connection closed during SSL handshake|SSL handshake failure) at step 1 of tcp-check \\(connect\\)\".+check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+} -start
+
+
+haproxy htst -conf {
+ global
+ tune.ssl.default-dh-param 2048
+
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen li1
+ bind "fd@${li1}"
+ tcp-request inspect-delay 100ms
+ tcp-request content reject if { req.ssl_hello_type 0 }
+ tcp-request content accept if { req.ssl_sni check.haproxy.org }
+ tcp-request content accept if { req.ssl_sni connect.haproxy.org }
+ tcp-request content reject
+ server fe1 ${htst_fe1_addr}:${htst_fe1_port}
+
+ listen li2
+ bind "fd@${li2}"
+ tcp-request inspect-delay 100ms
+ tcp-request content reject if { req.ssl_hello_type 0 }
+ tcp-request content accept if { req.ssl_alpn h2 }
+ tcp-request content accept if { req.ssl_alpn http/1.1 }
+ tcp-request content reject
+ server fe1 ${htst_fe1_addr}:${htst_fe1_port}
+
+ frontend fe1
+ bind "fd@${fe1}" ssl crt ${testdir}/common.pem
+
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S_ok_addr}:${S_ok_port} daemon
+ option log-health-checks
+ server srv ${htst_li1_addr}:${htst_li1_port} check check-ssl check-sni check.haproxy.org inter 1s rise 1 fall 1 verify none
+
+ backend be2
+ log ${S_ok_addr}:${S_ok_port} daemon
+ option log-health-checks
+ option tcp-check
+ tcp-check connect ssl sni connect.haproxy.org
+ server srv ${htst_li1_addr}:${htst_li1_port} check inter 1s rise 1 fall 1 verify none
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ option log-health-checks
+ server srv ${htst_li1_addr}:${htst_li1_port} check check-ssl check-sni bad.haproxy.org inter 1s rise 1 fall 1 verify none
+
+ backend be4
+ log ${S4_addr}:${S4_port} daemon
+ option log-health-checks
+ option tcp-check
+ tcp-check connect ssl sni bad.haproxy.org
+ server srv ${htst_li1_addr}:${htst_li1_port} check inter 1s rise 1 fall 1 verify none
+
+ backend be5
+ log ${S_ok_addr}:${S_ok_port} daemon
+ option log-health-checks
+ option tcp-check
+ tcp-check connect default
+ server srv ${htst_li1_addr}:${htst_li1_port} check check-ssl check-sni check.haproxy.org inter 1s rise 1 fall 1 verify none
+
+ backend be6
+ log ${S_ok_addr}:${S_ok_port} daemon
+ option log-health-checks
+ server srv ${htst_li2_addr}:${htst_li2_port} check check-ssl check-alpn "h2,http/1.1" inter 1s rise 1 fall 1 verify none
+
+ backend be7
+ log ${S_ok_addr}:${S_ok_port} daemon
+ option log-health-checks
+ option tcp-check
+ tcp-check connect ssl alpn "h2,http/1.1"
+ server srv ${htst_li2_addr}:${htst_li2_port} check inter 1s rise 1 fall 1 verify none
+
+} -start
+
+syslog S_ok -wait
+syslog S3 -wait
+syslog S4 -wait
diff --git a/reg-tests/checks/tcp-check_min-recv.vtc b/reg-tests/checks/tcp-check_min-recv.vtc
new file mode 100644
index 0000000..81f93e0
--- /dev/null
+++ b/reg-tests/checks/tcp-check_min-recv.vtc
@@ -0,0 +1,68 @@
+varnishtest "tcp-check negative bounded regex match"
+#EXCLUDE_TARGETS=freebsd,osx,generic
+#REGTEST_TYPE=slow
+#REQUIRE_VERSION=2.2
+# This test use a negative expect rule and verify that setting a required
+# minimum amount of data to match.
+feature ignore_unknown_macro
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 failed, reason: Layer7 timeout.*at step 2 of tcp-check"
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv1 succeeded"
+} -start
+
+server s1 {
+ send "valid"
+ delay 0.2
+ expect_close
+} -start
+
+server s2 {
+ send "valid"
+ recv 10
+ send "valid"
+ delay 0.2
+ expect_close
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout check "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ # must fail fast
+ timeout check 1
+ timeout server 1
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option tcp-check
+ option log-health-checks
+ tcp-check connect
+ tcp-check expect !rstring "^error" comment "negative check"
+ tcp-check expect string "valid" comment "positive check"
+ tcp-check send "0123456789"
+ tcp-check expect string "valid" comment "positive check"
+ server srv1 ${s1_addr}:${s1_port} check inter 200ms rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} len 2048 local0
+ option tcp-check
+ option log-health-checks
+ tcp-check connect
+ tcp-check expect min-recv 5 !rstring "^error" comment "negative check"
+ tcp-check expect string "valid" comment "positive check"
+ tcp-check send "0123456789"
+ tcp-check expect string "valid" comment "positive check"
+ server srv1 ${s2_addr}:${s2_port} check inter 200ms rise 1 fall 1
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
diff --git a/reg-tests/checks/tcp-check_multiple_ports.vtc b/reg-tests/checks/tcp-check_multiple_ports.vtc
new file mode 100644
index 0000000..356ddf6
--- /dev/null
+++ b/reg-tests/checks/tcp-check_multiple_ports.vtc
@@ -0,0 +1,48 @@
+varnishtest "tcp-check multiple ports"
+#EXCLUDE_TARGETS=freebsd,osx,generic
+#REGTEST_TYPE=slow
+# This test uses multiple tcp-check connect rules to perform health checking on
+# a target. It relies on port 1 being unbound on the local system.
+feature ignore_unknown_macro
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 failed.*Connection refused at step 2 of tcp-check.*connect port 1"
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv1 failed.*Connection refused at step 1 of tcp-check.*connect port 1"
+} -start
+
+server s1 {
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout check "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ #default-server check inter 200ms rise 1 fall 1
+
+ backend be1
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option tcp-check
+ option log-health-checks
+ tcp-check connect port ${s1_port}
+ tcp-check connect port 1
+ server srv1 ${s1_addr}:${s1_port} check inter 200ms rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} len 2048 local0
+ option tcp-check
+ option log-health-checks
+ tcp-check connect port 1
+ tcp-check connect port ${s1_port}
+ server srv1 ${s1_addr}:${s1_port} check inter 200ms rise 1 fall 1
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
diff --git a/reg-tests/checks/tcp-checks-socks4.vtc b/reg-tests/checks/tcp-checks-socks4.vtc
new file mode 100644
index 0000000..04c23ec
--- /dev/null
+++ b/reg-tests/checks/tcp-checks-socks4.vtc
@@ -0,0 +1,61 @@
+varnishtest "Health-checks: basic HTTP health-check though a socks4 proxy"
+#REQUIRE_VERSION=2.0
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+# This scripts tests a simple HTTP health-checks though a socks4 proxy.
+
+server s1 {
+} -start
+
+server socks {
+ ## get socks4 request
+ recv 16
+
+ ## send socks4 response :
+ ## constant(1): 0x00
+ ## statut(1) : 0x5a (success)
+ ## padding(6) : ignored
+ sendhex "005A000000000000"
+
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ txresp
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded, reason: Layer7 check passed.+code: 200.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ option log-health-checks
+ option httpchk
+ server srv ${s1_addr}:${s1_port} socks4 ${h1_socks_addr}:${h1_socks_port} check-via-socks4 check inter 1s rise 1 fall 1
+
+ listen socks
+ bind "fd@${socks}"
+ tcp-request inspect-delay 500ms
+ ## Accept socks4 request on 16 bytes :
+ ## version(1) : 0x04
+ ## command(1) : 0x01
+ ## port(2) : ${s1_port}
+ ## addr(4) : ${s1_addr}
+ ## user-id : "HAProxy\0"
+ tcp-request content accept if { req.len eq 16 } { req.payload(0,1) -m bin "04" } { req.payload(1,1) -m bin "01" } { req.payload(2,2),hex,hex2i eq ${s1_port} } { req.payload(4,4),hex,hex2i -m ip ${s1_addr} } { req.payload(8,8) -m bin "484150726F787900" }
+ tcp-request content reject
+ server srv ${socks_addr}:${socks_port}
+
+} -start
+
+syslog S1 -wait
diff --git a/reg-tests/checks/tls_health_checks.vtc b/reg-tests/checks/tls_health_checks.vtc
new file mode 100644
index 0000000..1989d65
--- /dev/null
+++ b/reg-tests/checks/tls_health_checks.vtc
@@ -0,0 +1,118 @@
+varnishtest "Health-check test over TLS/SSL"
+#REQUIRE_OPTIONS=OPENSSL
+#REGTEST_TYPE=slow
+feature ignore_unknown_macro
+
+
+# This script tests health-checks for a TLS/SSL backend with "option httpchk"
+# and "check-ssl" option enabled attached to h2 haproxy process. This haproxy
+# h2 process is chained to h1 other one.
+#
+server s1 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == *
+ expect req.proto == HTTP/1.1
+ txresp
+} -start
+
+server s2 {
+} -start
+
+server s3 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == *
+ expect req.proto == HTTP/1.1
+ txresp
+} -start
+
+syslog S1 -level notice {
+ recv info
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* fe1~ be1/srv1 .* 200 [[:digit:]]+ - - ---- .* \"OPTIONS \\* HTTP/1.1\""
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ server srv1 ${s1_addr}:${s1_port}
+
+ backend be2
+ server srv2 ${s2_addr}:${s2_port}
+
+ backend be3
+ server srv3 ${s3_addr}:${s3_port}
+
+ frontend fe1
+ option httplog
+ log ${S1_addr}:${S1_port} len 2048 local0 debug err
+ bind "fd@${fe1}" ssl crt ${testdir}/common.pem
+ use_backend be1
+
+ frontend fe2
+ option tcplog
+ bind "fd@${fe2}" ssl crt ${testdir}/common.pem
+ use_backend be2
+
+ frontend fe3
+ option httplog
+ bind "fd@${fe3}" ssl crt ${testdir}/common.pem
+ use_backend be3
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be2/srv1 succeeded, reason: Layer7 check passed.+code: 200.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S4 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be4/srv2 succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+syslog S6 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be6/srv3 succeeded, reason: Layer7 check passed.+code: 200.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
+haproxy h2 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ default-server downinter 1s inter 500 rise 1 fall 1
+
+ backend be2
+ option log-health-checks
+ option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www
+ log ${S2_addr}:${S2_port} daemon
+ server srv1 ${h1_fe1_addr}:${h1_fe1_port} ssl crt ${testdir}/common.pem verify none check
+
+ backend be4
+ option log-health-checks
+ log ${S4_addr}:${S4_port} daemon
+ server srv2 ${h1_fe2_addr}:${h1_fe2_port} ssl crt ${testdir}/common.pem verify none check-ssl check
+
+ backend be6
+ option log-health-checks
+ option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www
+ log ${S6_addr}:${S6_port} daemon
+ server srv3 127.0.0.1:80 crt ${testdir}/common.pem verify none check check-ssl port ${h1_fe3_port} addr ${h1_fe3_addr}:80
+} -start
+
+syslog S1 -wait
+
+syslog S2 -wait
+syslog S4 -wait
+syslog S6 -wait
diff --git a/reg-tests/compression/basic.vtc b/reg-tests/compression/basic.vtc
new file mode 100644
index 0000000..76ad43d
--- /dev/null
+++ b/reg-tests/compression/basic.vtc
@@ -0,0 +1,378 @@
+varnishtest "Basic compression test"
+
+#REQUIRE_VERSION=1.9
+#REQUIRE_OPTION=ZLIB|SLZ
+
+feature ignore_unknown_macro
+
+server s1 {
+ # client c1 - request 1
+ rxreq
+ expect req.url == "/c1.1"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ # client c1 - request 2
+ rxreq
+ expect req.url == "/c1.2"
+ expect req.http.user-agent == "Mozilla/4"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ # client c1 - request 3
+ rxreq
+ expect req.url == "/c1.3"
+ expect req.proto == "HTTP/1.0"
+ expect req.http.accept-encoding == "gzip"
+ txresp -bodylen 100
+
+ # client c1 - request 4
+ rxreq
+ expect req.url == "/c1.4"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -proto "HTTP/1.0" \
+ -hdr "Connection: keep-alive" \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ # client c1 - request 5
+ rxreq
+ expect req.url == "/c1.5"
+ expect req.method == "HEAD"
+ expect req.http.accept-encoding == "gzip"
+ txresp -nolen \
+ -hdr "Content-Length: 100" \
+ -hdr "Content-Type: text/plain" \
+
+ # client c1 - request 6
+ rxreq
+ expect req.url == "/c1.6"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -status 400 \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ # client c1 - request 7
+ rxreq
+ expect req.url == "/c1.7"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "Content-Encoding: something" \
+ -body "FOO"
+
+ # client c1 - request 8
+ rxreq
+ expect req.url == "/c1.8"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "Cache-Control: no-transform" \
+ -bodylen 100
+
+ # client c1 - request 9
+ rxreq
+ expect req.url == "/c1.9"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/css" \
+ -bodylen 100
+
+ # client c1 - request 10
+ rxreq
+ expect req.url == "/c1.10"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: multipart/mixed; boundary=\"aaa\"" \
+ -bodylen 100
+
+ # Close the connection with HAProxy and wait for a new one
+ # (C1 has finished and C2 will start)
+ accept
+
+ # client c2 - request 1
+ rxreq
+ expect req.url == "/c2.1"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ # client c2 - request 2
+ rxreq
+ expect req.url == "/c2.2"
+ expect req.http.accept-encoding == "gzip"
+ txresp -nolen \
+ -hdr "Content-Type: text/plain" \
+ -hdr "Transfer-Encoding: chunked"
+ chunkedlen 1
+ chunkedlen 1
+ chunkedlen 2
+ chunkedlen 3
+ chunkedlen 5
+ chunkedlen 8
+ chunkedlen 13
+ chunkedlen 21
+ chunkedlen 34
+ chunkedlen 55
+ chunkedlen 89
+ chunkedlen 144
+ chunkedlen 233
+ chunkedlen 0
+
+ # Close the connection with HAProxy and wait for a new one
+ # (C2 has finished and C3 will start)
+ accept
+
+ # client c3 - request 1
+ rxreq
+ expect req.url == "/c3.1"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 50000
+
+ # client c3 - request 2
+ rxreq
+ expect req.url == "/c3.2"
+ expect req.http.accept-encoding == "<undef>"
+ txresp -nolen \
+ -hdr "Content-Type: text/plain" \
+ -hdr "Transfer-Encoding: chunked"
+ chunkedlen 1000
+ chunkedlen 1000
+ chunkedlen 1000
+ chunkedlen 1000
+ chunkedlen 1000
+ chunkedlen 5000
+ chunkedlen 10000
+ chunkedlen 30000
+ chunkedlen 0
+
+ # Close the connection with HAProxy and wait for a new one
+ # (C3 has finished and C4 will start)
+ accept
+
+ # client c4 - request 1
+ rxreq
+ expect req.url == "/c4.1"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ # client c4 - request 2
+ rxreq
+ expect req.url == "/c4.2"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+} -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-gzip
+ bind "fd@${fe_gzip}"
+ default_backend be-gzip
+
+ frontend fe-identity
+ bind "fd@${fe_identity}"
+ default_backend be-identity
+
+ frontend fe-gzip-deflate
+ bind "fd@${fe_gzip_deflate}"
+ default_backend be-gzip-defalte
+
+ backend be-gzip
+ compression algo gzip
+ compression type text/html text/plain
+ server www ${s1_addr}:${s1_port}
+
+ backend be-identity
+ compression algo identity
+ server www ${s1_addr}:${s1_port}
+
+ backend be-gzip-defalte
+ compression algo gzip deflate
+ compression offload
+ server www ${s1_addr}:${s1_port}
+
+} -start
+
+# No compression expected because not supported by the client or because
+# something in the request or response headers forbids it.
+client c1 -connect ${h1_fe_gzip_sock} {
+ # 1. no "Accept-Encoding header"
+ txreq -url "/c1.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.bodylen == 100
+
+ # 2. Buggy User-Agent
+ txreq -url "/c1.2" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "User-Agent: Mozilla/4"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.bodylen == 100
+
+ # 3. HTTP/1.0 request
+ txreq -url "/c1.3" \
+ -proto "HTTP/1.0" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Connection: keep-alive"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.bodylen == 100
+
+ # 4. HTTP/1.0 response
+ txreq -url "/c1.4" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.proto == "HTTP/1.0"
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.bodylen == 100
+
+ # 5. HEAD method
+ txreq -req "HEAD" -url "/c1.5" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp -no_obj
+ expect resp.status == 200
+ expect resp.http.content-length == "100"
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+
+ # 6. Response status code != 20[0-3]
+ txreq -url "/c1.6" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 400
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.bodylen == 100
+
+ # 7. Response alerady compressed by the server (with "Accept-Encoding")
+ txreq -url "/c1.7" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "something"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.bodylen == 3
+ expect resp.body == "FOO"
+
+ # 8. Response with "Cache-Control: no-transform"
+ txreq -url "/c1.8" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.cache-control == "no-transform"
+ expect resp.bodylen == 100
+
+ # 9. Response with uncompressable content-type
+ txreq -url "/c1.9" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.content-type == "text/css"
+ expect resp.bodylen == 100
+
+ # 10. Response with uncompressable content-type
+ txreq -url "/c1.10" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.content-type == "multipart/mixed; boundary=\"aaa\""
+ expect resp.bodylen == 100
+} -run
+
+# GZIP Compression expected (small body)
+client c2 -connect ${h1_fe_gzip_sock} {
+ # 1. response from the server with a small body with a C-L
+ txreq -url "/c2.1" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "chunked"
+ gunzip
+ expect resp.bodylen == 100
+
+ # 2. response from the server with a small chunked body
+ txreq -url "/c2.2" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "chunked"
+ gunzip
+ expect resp.bodylen == 609
+} -run
+
+# Identity compression expect (Huge body)
+# Identity is used because of a limitation of vtest (the uncompressed body size
+# must be lower than 10 times of the compressed one)
+client c3 -connect ${h1_fe_identity_sock} {
+ # 1. response from the server with a huge body with a C-L
+ txreq -url "/c3.1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.bodylen == 50000
+
+ # 2. response from the server with a huge chunked body
+ txreq -url "/c3.2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.bodylen == 50000
+} -run
+
+
+# Compression expected with priority
+client c4 -connect ${h1_fe_gzip_deflate_sock} {
+ # 1. response from the server with a small body with a C-L
+ txreq -url "/c4.1" \
+ -hdr "Accept-Encoding: *;q=0, gzip;q=0.750, deflate;q=0.500"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "chunked"
+
+ # 2. response from the server with a small body with a C-L
+ txreq -url "/c4.2" \
+ -hdr "Accept-Encoding: *;q=0, gzip;q=0.500, deflate;q=0.750"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "deflate"
+ expect resp.http.transfer-encoding == "chunked"
+} -run
diff --git a/reg-tests/compression/common.pem b/reg-tests/compression/common.pem
new file mode 120000
index 0000000..a4433d5
--- /dev/null
+++ b/reg-tests/compression/common.pem
@@ -0,0 +1 @@
+../ssl/common.pem \ No newline at end of file
diff --git a/reg-tests/compression/etags_conversion.vtc b/reg-tests/compression/etags_conversion.vtc
new file mode 100644
index 0000000..c5684a2
--- /dev/null
+++ b/reg-tests/compression/etags_conversion.vtc
@@ -0,0 +1,231 @@
+varnishtest "Compression converts strong ETags to weak ETags"
+
+#REQUIRE_VERSION=1.9
+#REQUIRE_OPTION=ZLIB|SLZ
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == "/strong"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: \"123\"" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/weak"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: W/\"456\"" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/weak-incorrect-quoting"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: \"W/789\"" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/empty-strong"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: \"\"" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/empty-weak"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: W/\"\"" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/invalid1"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: \"invalid" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/invalid2"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: invalid\"" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/invalid3"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: invalid" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/invalid4"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: W/\"invalid" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/invalid5"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: W/invalid\"" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/invalid6"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: W/invalid" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/multiple"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: \"one\"" \
+ -hdr "ETag: \"two\"" \
+ -bodylen 100
+} -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
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe-gzip
+ bind "fd@${fe_gzip}"
+ default_backend be-gzip
+
+ backend be-gzip
+ compression algo gzip
+ compression type text/html text/plain
+ server www ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_gzip_sock} {
+ txreq -url "/strong" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.etag == "W/\"123\""
+ gunzip
+ expect resp.bodylen == 100
+
+ txreq -url "/weak" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.etag == "W/\"456\""
+ gunzip
+ expect resp.bodylen == 100
+
+ txreq -url "/weak-incorrect-quoting" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.etag == "W/\"W/789\""
+ gunzip
+ expect resp.bodylen == 100
+
+ txreq -url "/empty-strong" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.etag == "W/\"\""
+ gunzip
+ expect resp.bodylen == 100
+
+ txreq -url "/empty-weak" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.etag == "W/\"\""
+ gunzip
+ expect resp.bodylen == 100
+
+ txreq -url "/invalid1" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.etag == "\"invalid"
+ expect resp.bodylen == 100
+
+ txreq -url "/invalid2" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.etag == "invalid\""
+ expect resp.bodylen == 100
+
+ txreq -url "/invalid3" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.etag == "invalid"
+ expect resp.bodylen == 100
+
+ txreq -url "/invalid4" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.etag == "W/\"invalid"
+ expect resp.bodylen == 100
+
+ txreq -url "/invalid5" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.etag == "W/invalid\""
+ expect resp.bodylen == 100
+
+ txreq -url "/invalid6" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.etag == "W/invalid"
+ expect resp.bodylen == 100
+
+ txreq -url "/multiple" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.bodylen == 100
+} -run
diff --git a/reg-tests/compression/lua_validation.lua b/reg-tests/compression/lua_validation.lua
new file mode 100644
index 0000000..2cc874b
--- /dev/null
+++ b/reg-tests/compression/lua_validation.lua
@@ -0,0 +1,19 @@
+
+local data = "abcdefghijklmnopqrstuvwxyz"
+local responseblob = ""
+for i = 1,10000 do
+ responseblob = responseblob .. "\r\n" .. i .. data:sub(1, math.floor(i % 27))
+end
+
+http01applet = function(applet)
+ local response = responseblob
+ applet:set_status(200)
+ applet:add_header("Content-Type", "application/javascript")
+ applet:add_header("Content-Length", string.len(response)*10)
+ applet:start_response()
+ for i = 1,10 do
+ applet:send(response)
+ end
+end
+
+core.register_service("fileloader-http01", "http", http01applet)
diff --git a/reg-tests/compression/lua_validation.vtc b/reg-tests/compression/lua_validation.vtc
new file mode 100644
index 0000000..b10cbd9
--- /dev/null
+++ b/reg-tests/compression/lua_validation.vtc
@@ -0,0 +1,59 @@
+# Checks that compression doesn't cause corruption..
+
+varnishtest "Compression validation"
+#REQUIRE_OPTIONS=ZLIB|SLZ,LUA,OPENSSL
+#REGTEST_TYPE=slow
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+global
+# log stdout format short daemon
+ lua-load ${testdir}/lua_validation.lua
+
+defaults
+ mode http
+ log global
+ option httplog
+
+frontend main-https
+ bind "fd@${fe1}" ssl crt ${testdir}/common.pem
+ compression algo gzip
+ compression type text/html text/plain application/json application/javascript
+ compression offload
+ use_backend TestBack if TRUE
+
+backend TestBack
+ server LocalSrv ${h1_fe2_addr}:${h1_fe2_port}
+
+listen fileloader
+ mode http
+ bind "fd@${fe2}"
+ http-request use-service lua.fileloader-http01
+} -start
+
+shell {
+ HOST=${h1_fe1_addr}
+ if [ "${h1_fe1_addr}" = "::1" ] ; then
+ HOST="\[::1\]"
+ fi
+
+ md5=$(command -v md5 || command -v md5sum)
+
+ if [ -z $md5 ] ; then
+ echo "MD5 checksum utility not found"
+ exit 1
+ fi
+
+ expectchecksum="4d9c62aa5370b8d5f84f17ec2e78f483"
+
+ for opt in "" "--limit-rate 300K" "--limit-rate 500K" ; do
+ checksum=$(curl --max-time 15 --compressed -k "https://$HOST:${h1_fe1_port}" $opt | $md5 | cut -d ' ' -f1)
+ if [ "$checksum" != "$expectchecksum" ] ; then
+ echo "Expecting checksum $expectchecksum"
+ echo "Received checksum: $checksum"
+ exit 1;
+ fi
+ done
+
+} -run
diff --git a/reg-tests/compression/vary.vtc b/reg-tests/compression/vary.vtc
new file mode 100644
index 0000000..8219c73
--- /dev/null
+++ b/reg-tests/compression/vary.vtc
@@ -0,0 +1,192 @@
+varnishtest "Compression sets Vary header"
+
+#REQUIRE_VERSION=1.9
+#REQUIRE_OPTION=ZLIB|SLZ
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == "/plain/accept-encoding-gzip"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/plain/accept-encoding-invalid"
+ expect req.http.accept-encoding == "invalid"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/plain/accept-encoding-null"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/html/accept-encoding-gzip"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/html" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/html/accept-encoding-invalid"
+ expect req.http.accept-encoding == "invalid"
+ txresp \
+ -hdr "Content-Type: text/html" \
+ -bodylen 100
+
+
+ rxreq
+ expect req.url == "/html/accept-encoding-null"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/html" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/dup-etag/accept-encoding-gzip"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: \"123\"" \
+ -hdr "ETag: \"123\"" \
+ -bodylen 100
+} -repeat 2 -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
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe-gzip
+ bind "fd@${fe_gzip}"
+ default_backend be-gzip
+
+ backend be-gzip
+ compression algo gzip
+ compression type text/plain
+ server www ${s1_addr}:${s1_port}
+
+ frontend fe-nothing
+ bind "fd@${fe_nothing}"
+ default_backend be-nothing
+
+ backend be-nothing
+ server www ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_gzip_sock} {
+ txreq -url "/plain/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.vary == "Accept-Encoding"
+ gunzip
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/dup-etag/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+} -run
+
+# This Client duplicates c1, against the "nothing" frontend, ensuring no Vary header is ever set.
+client c2 -connect ${h1_fe_nothing_sock} {
+ txreq -url "/plain/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/dup-etag/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+} -run
diff --git a/reg-tests/connection/cli_src_dst.vtc b/reg-tests/connection/cli_src_dst.vtc
new file mode 100644
index 0000000..6809d39
--- /dev/null
+++ b/reg-tests/connection/cli_src_dst.vtc
@@ -0,0 +1,290 @@
+varnishtest "Test multi-level client source and destination addresses"
+
+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}"
+ tcp-request connection set-src ipv4(10.0.0.1)
+ tcp-request connection set-dst ipv4(10.0.0.2)
+
+ tcp-request session set-var(sess.sess_fc_src) fc_src
+ tcp-request session set-var(sess.sess_fc_dst) fc_dst
+ tcp-request session set-var(sess.sess_src) src
+ tcp-request session set-var(sess.sess_dst) dst
+
+ tcp-request inspect-delay 100ms
+ tcp-request content set-var(txn.strm_fc_src) fc_src
+ tcp-request content set-var(txn.strm_fc_dst) fc_dst
+ tcp-request content set-var(txn.strm_src) src
+ tcp-request content set-var(txn.strm_dst) dst
+
+ http-after-response set-header sess-fc-src %[var(sess.sess_fc_src)]
+ http-after-response set-header sess-src %[var(sess.sess_src)]
+ http-after-response set-header sess-fc-dst %[var(sess.sess_fc_dst)]
+ http-after-response set-header sess-dst %[var(sess.sess_dst)]
+ http-after-response set-header strm-fc-src %[var(txn.strm_fc_src)]
+ http-after-response set-header strm-src %[var(txn.strm_src)]
+ http-after-response set-header strm-fc-dst %[var(txn.strm_fc_dst)]
+ http-after-response set-header strm-dst %[var(txn.strm_dst)]
+
+ default_backend be
+
+ frontend fe2
+ bind "fd@${fe2}"
+ tcp-request connection set-src ipv4(10.0.0.1)
+ tcp-request connection set-dst ipv4(10.0.0.2)
+
+ tcp-request session set-src ipv4(10.1.0.1)
+ tcp-request session set-dst ipv4(10.1.0.2)
+ tcp-request session set-var(sess.sess_fc_src) fc_src
+ tcp-request session set-var(sess.sess_fc_dst) fc_dst
+ tcp-request session set-var(sess.sess_src) src
+ tcp-request session set-var(sess.sess_dst) dst
+
+ tcp-request inspect-delay 100ms
+ tcp-request content set-var(txn.strm_fc_src) fc_src
+ tcp-request content set-var(txn.strm_fc_dst) fc_dst
+ tcp-request content set-var(txn.strm_src) src
+ tcp-request content set-var(txn.strm_dst) dst
+
+ http-after-response set-header sess-fc-src %[var(sess.sess_fc_src)]
+ http-after-response set-header sess-src %[var(sess.sess_src)]
+ http-after-response set-header sess-fc-dst %[var(sess.sess_fc_dst)]
+ http-after-response set-header sess-dst %[var(sess.sess_dst)]
+ http-after-response set-header strm-fc-src %[var(txn.strm_fc_src)]
+ http-after-response set-header strm-src %[var(txn.strm_src)]
+ http-after-response set-header strm-fc-dst %[var(txn.strm_fc_dst)]
+ http-after-response set-header strm-dst %[var(txn.strm_dst)]
+
+ default_backend be
+
+ frontend fe3
+ bind "fd@${fe3}"
+ tcp-request connection set-src ipv4(10.0.0.1)
+ tcp-request connection set-dst ipv4(10.0.0.2)
+
+ tcp-request session set-src ipv4(10.1.0.1)
+ tcp-request session set-dst ipv4(10.1.0.2)
+ tcp-request session set-var(sess.sess_fc_src) fc_src
+ tcp-request session set-var(sess.sess_fc_dst) fc_dst
+ tcp-request session set-var(sess.sess_src) src
+ tcp-request session set-var(sess.sess_dst) dst
+
+ tcp-request inspect-delay 100ms
+ tcp-request content set-src ipv4(10.2.0.1)
+ tcp-request content set-dst ipv4(10.2.0.2)
+ tcp-request content set-var(txn.strm_fc_src) fc_src
+ tcp-request content set-var(txn.strm_fc_dst) fc_dst
+ tcp-request content set-var(txn.strm_src) src
+ tcp-request content set-var(txn.strm_dst) dst
+
+ http-after-response set-header sess-fc-src %[var(sess.sess_fc_src)]
+ http-after-response set-header sess-src %[var(sess.sess_src)]
+ http-after-response set-header sess-fc-dst %[var(sess.sess_fc_dst)]
+ http-after-response set-header sess-dst %[var(sess.sess_dst)]
+ http-after-response set-header strm-fc-src %[var(txn.strm_fc_src)]
+ http-after-response set-header strm-src %[var(txn.strm_src)]
+ http-after-response set-header strm-fc-dst %[var(txn.strm_fc_dst)]
+ http-after-response set-header strm-dst %[var(txn.strm_dst)]
+
+
+ frontend fe4
+ bind "fd@${fe4}"
+
+ tcp-request connection set-src ipv4(10.0.0.1)
+ tcp-request connection set-dst ipv4(10.0.0.2)
+
+ tcp-request session set-var(sess.sess_fc_src) fc_src
+ tcp-request session set-var(sess.sess_fc_dst) fc_dst
+ tcp-request session set-var(sess.sess_src) src
+ tcp-request session set-var(sess.sess_dst) dst
+
+ http-request set-src hdr(x-forwarded-for)
+ http-request set-dst hdr(x-original-to)
+ http-request set-var(txn.strm_fc_src) fc_src
+ http-request set-var(txn.strm_fc_dst) fc_dst
+ http-request set-var(txn.strm_src) src
+ http-request set-var(txn.strm_dst) dst
+
+ http-after-response set-header sess-fc-src %[var(sess.sess_fc_src)]
+ http-after-response set-header sess-src %[var(sess.sess_src)]
+ http-after-response set-header sess-fc-dst %[var(sess.sess_fc_dst)]
+ http-after-response set-header sess-dst %[var(sess.sess_dst)]
+ http-after-response set-header strm-fc-src %[var(txn.strm_fc_src)]
+ http-after-response set-header strm-src %[var(txn.strm_src)]
+ http-after-response set-header strm-fc-dst %[var(txn.strm_fc_dst)]
+ http-after-response set-header strm-dst %[var(txn.strm_dst)]
+
+ default_backend be
+
+ backend be
+ http-request return status 200
+
+ listen li1
+ bind "fd@${li1}"
+
+ tcp-request connection set-src ipv4(10.0.0.1)
+ tcp-request connection set-dst ipv4(10.0.0.2)
+
+ http-request set-src ipv4(192.168.0.1)
+ http-request set-dst ipv4(192.168.0.2)
+
+ http-after-response set-header li1-fc-src %[fc_src]
+ http-after-response set-header li1-src %[src]
+ http-after-response set-header li1-fc-dst %[fc_dst]
+ http-after-response set-header li1-dst %[dst]
+
+
+ server srv ${h1_li3_addr}:${h1_li3_port} send-proxy
+
+ listen li2
+ bind "fd@${li2}"
+
+ tcp-request connection set-src ipv4(10.0.0.1)
+ tcp-request connection set-dst ipv4(10.0.0.2)
+
+ http-request set-src ipv4(192.168.0.1)
+ http-request set-dst ipv4(192.168.0.2)
+
+ http-after-response set-header li2-fc-src %[fc_src]
+ http-after-response set-header li2-src %[src]
+ http-after-response set-header li2-fc-dst %[fc_dst]
+ http-after-response set-header li2-dst %[dst]
+
+ server srv ${h1_li3_addr}:${h1_li3_port} send-proxy-v2
+
+ listen li3
+ bind "fd@${li3}" accept-proxy
+
+ tcp-request connection set-src ipv4(10.1.0.1)
+ tcp-request connection set-dst ipv4(10.1.0.2)
+
+ http-after-response set-header li3-fc-src %[fc_src]
+ http-after-response set-header li3-src %[src]
+ http-after-response set-header li3-fc-dst %[fc_dst]
+ http-after-response set-header li3-dst %[dst]
+
+ http-request return status 200
+
+} -start
+
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq
+ rxresp
+ expect resp.http.sess-fc-src == 10.0.0.1
+ expect resp.http.sess-src == 10.0.0.1
+ expect resp.http.strm-fc-src == 10.0.0.1
+ expect resp.http.strm-src == 10.0.0.1
+
+ expect resp.http.sess-fc-dst == 10.0.0.2
+ expect resp.http.sess-dst == 10.0.0.2
+ expect resp.http.strm-fc-dst == 10.0.0.2
+ expect resp.http.strm-dst == 10.0.0.2
+} -run
+
+client c2 -connect ${h1_fe2_sock} {
+ txreq
+ rxresp
+ expect resp.http.sess-fc-src == 10.0.0.1
+ expect resp.http.sess-src == 10.1.0.1
+ expect resp.http.strm-fc-src == 10.0.0.1
+ expect resp.http.strm-src == 10.1.0.1
+
+ expect resp.http.sess-fc-dst == 10.0.0.2
+ expect resp.http.sess-dst == 10.1.0.2
+ expect resp.http.strm-fc-dst == 10.0.0.2
+ expect resp.http.strm-dst == 10.1.0.2
+} -run
+
+client c3 -connect ${h1_fe3_sock} {
+ txreq
+ rxresp
+ expect resp.http.sess-fc-src == 10.0.0.1
+ expect resp.http.sess-src == 10.1.0.1
+ expect resp.http.strm-fc-src == 10.0.0.1
+ expect resp.http.strm-src == 10.2.0.1
+
+ expect resp.http.sess-fc-dst == 10.0.0.2
+ expect resp.http.sess-dst == 10.1.0.2
+ expect resp.http.strm-fc-dst == 10.0.0.2
+ expect resp.http.strm-dst == 10.2.0.2
+} -run
+
+client c4 -connect ${h1_fe4_sock} {
+ txreq \
+ -hdr "x-forwarded-for: 192.168.0.1" \
+ -hdr "x-original-to: 192.168.0.2"
+ rxresp
+ expect resp.http.sess-fc-src == 10.0.0.1
+ expect resp.http.sess-src == 10.0.0.1
+ expect resp.http.strm-fc-src == 10.0.0.1
+ expect resp.http.strm-src == 192.168.0.1
+
+ expect resp.http.sess-fc-dst == 10.0.0.2
+ expect resp.http.sess-dst == 10.0.0.2
+ expect resp.http.strm-fc-dst == 10.0.0.2
+ expect resp.http.strm-dst == 192.168.0.2
+
+ txreq \
+ -hdr "x-forwarded-for: 192.168.1.1" \
+ -hdr "x-original-to: 192.168.1.2"
+ rxresp
+ expect resp.http.sess-fc-src == 10.0.0.1
+ expect resp.http.sess-src == 10.0.0.1
+ expect resp.http.strm-fc-src == 10.0.0.1
+ expect resp.http.strm-src == 192.168.1.1
+
+ expect resp.http.sess-fc-dst == 10.0.0.2
+ expect resp.http.sess-dst == 10.0.0.2
+ expect resp.http.strm-fc-dst == 10.0.0.2
+ expect resp.http.strm-dst == 192.168.1.2
+
+ txreq
+ rxresp
+ expect resp.http.sess-fc-src == 10.0.0.1
+ expect resp.http.sess-src == 10.0.0.1
+ expect resp.http.strm-fc-src == 10.0.0.1
+ expect resp.http.strm-src == 10.0.0.1
+
+ expect resp.http.sess-fc-dst == 10.0.0.2
+ expect resp.http.sess-dst == 10.0.0.2
+ expect resp.http.strm-fc-dst == 10.0.0.2
+ expect resp.http.strm-dst == 10.0.0.2
+} -run
+
+client c5 -connect ${h1_li1_sock} {
+ txreq
+ rxresp
+ expect resp.http.li1-fc-src == 10.0.0.1
+ expect resp.http.li1-src == 192.168.0.1
+ expect resp.http.li1-fc-dst == 10.0.0.2
+ expect resp.http.li1-dst == 192.168.0.2
+
+ expect resp.http.li3-fc-src == 10.1.0.1
+ expect resp.http.li3-src == 192.168.0.1
+ expect resp.http.li3-fc-dst == 10.1.0.2
+ expect resp.http.li3-dst == 192.168.0.2
+} -run
+
+client c6 -connect ${h1_li2_sock} {
+ txreq
+ rxresp
+ expect resp.http.li2-fc-src == 10.0.0.1
+ expect resp.http.li2-src == 192.168.0.1
+ expect resp.http.li2-fc-dst == 10.0.0.2
+ expect resp.http.li2-dst == 192.168.0.2
+
+ expect resp.http.li3-fc-src == 10.1.0.1
+ expect resp.http.li3-src == 192.168.0.1
+ expect resp.http.li3-fc-dst == 10.1.0.2
+ expect resp.http.li3-dst == 192.168.0.2
+} -run
diff --git a/reg-tests/connection/common.pem b/reg-tests/connection/common.pem
new file mode 120000
index 0000000..a4433d5
--- /dev/null
+++ b/reg-tests/connection/common.pem
@@ -0,0 +1 @@
+../ssl/common.pem \ No newline at end of file
diff --git a/reg-tests/connection/dispatch.vtc b/reg-tests/connection/dispatch.vtc
new file mode 100644
index 0000000..8696b50
--- /dev/null
+++ b/reg-tests/connection/dispatch.vtc
@@ -0,0 +1,42 @@
+varnishtest "Validate proper operation of the 'dispatch' mode"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+defaults
+ log global
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+listen fe_tcp
+ bind "fd@${fe_tcp}"
+ mode tcp
+ dispatch ${s1_addr}:${s1_port}
+
+listen fe_http
+ bind "fd@${fe_http}"
+ mode http
+ dispatch ${s2_addr}:${s2_port}
+} -start
+
+client c1 -connect ${h1_fe_tcp_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -connect ${h1_fe_http_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/connection/http_reuse_aggressive.vtc b/reg-tests/connection/http_reuse_aggressive.vtc
new file mode 100644
index 0000000..71f4cee
--- /dev/null
+++ b/reg-tests/connection/http_reuse_aggressive.vtc
@@ -0,0 +1,45 @@
+varnishtest "Test the http-reuse aggressive mode"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ listen sender
+ bind "fd@${feS}"
+ http-reuse aggressive
+ server srv ${h1_feR_addr}:${h1_feR_port}
+
+ listen receiver
+ bind "fd@${feR}"
+ http-request return status 200
+ http-after-response set-header http_first_request %[http_first_req]
+} -start
+
+# bootstrap
+client c1 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "1"
+} -run
+
+# first request should not be reused as no safe connection for the moment
+client c2 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+} -run
+
+# first request must be reused with the safe connection
+client c3 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+} -run
diff --git a/reg-tests/connection/http_reuse_always.vtc b/reg-tests/connection/http_reuse_always.vtc
new file mode 100644
index 0000000..136fe4e
--- /dev/null
+++ b/reg-tests/connection/http_reuse_always.vtc
@@ -0,0 +1,43 @@
+varnishtest "Test the http-reuse always mode"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ listen sender
+ bind "fd@${feS}"
+ http-reuse always
+ server srv ${h1_feR_addr}:${h1_feR_port}
+
+ listen receiver
+ bind "fd@${feR}"
+ http-request return status 200
+ http-after-response set-header http_first_request %[http_first_req]
+} -start
+
+# bootstrap
+client c1 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "1"
+} -run
+
+client c2 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+} -run
+
+client c3 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+} -run
diff --git a/reg-tests/connection/http_reuse_be_transparent.vtc b/reg-tests/connection/http_reuse_be_transparent.vtc
new file mode 100644
index 0000000..1cadb35
--- /dev/null
+++ b/reg-tests/connection/http_reuse_be_transparent.vtc
@@ -0,0 +1,81 @@
+varnishtest "Test the proper interaction between http-reuse and backend in transparent mode"
+
+# If backend is used with the transparent mode, the connection are considered
+# as private and should only be reused for requests of the same session.
+# This is similar to the http-reuse never mode
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ listen sender
+ bind "fd@${feS}"
+ http-request set-header client-id %[req.hdr(client-id)] if { req.hdr(client-id) -m found }
+ option transparent
+ http-request set-dst-port int(${h1_feR_port})
+
+ listen receiver
+ bind "fd@${feR}"
+ http-request set-var(sess.client_id) req.hdr(client-id)
+ http-request return status 200
+ http-after-response set-header http_first_request %[http_first_req]
+ http-after-response set-header client-id %[var(sess.client_id)]
+} -start
+
+client c1 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c1"
+ rxresp
+ expect resp.http.http_first_request == "1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+} -run
+
+client c2 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c2"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c2"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c2"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c2"
+} -run
+
+client c3 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c3"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c3"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c3"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c3"
+} -run
+
diff --git a/reg-tests/connection/http_reuse_conn_hash.vtc b/reg-tests/connection/http_reuse_conn_hash.vtc
new file mode 100644
index 0000000..991e86f
--- /dev/null
+++ b/reg-tests/connection/http_reuse_conn_hash.vtc
@@ -0,0 +1,163 @@
+varnishtest "Test the http-reuse with special connection parameters"
+#REQUIRE_VERSION=2.4
+#REQUIRE_OPTIONS=OPENSSL
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ # sni
+ listen sender-sni
+ bind "fd@${feS_sni}"
+ server srv2 ${h1_feR_ssl_addr}:${h1_feR_ssl_port} ssl sni "req.hdr(x-sni)" verify none pool-low-conn 2
+
+ # set-dst
+ # specify dst1_addr for server, which should be identical to dst2_addr
+ # port is specified by the client in header x-dst-port
+ listen sender-set-dst
+ bind "fd@${feS_dst}"
+ http-request set-dst-port hdr(x-dst-port)
+ server srv2 ${h1_feR_dst1_addr}:0 pool-low-conn 2
+
+ # proxy protocol
+ # must use reuse always as consecutive requests are from different client
+ listen sender-proxy
+ bind "fd@${feS_proxy}" accept-proxy
+ http-reuse always
+ server srv2 ${h1_feR_proxy_addr}:${h1_feR_proxy_port} send-proxy pool-low-conn 2
+
+ listen receiver
+ bind "fd@${feR_ssl}" ssl crt ${testdir}/common.pem
+ bind "fd@${feR_proxy}" accept-proxy
+ http-request return status 200
+ http-after-response set-header http_first_request %[http_first_req]
+
+ listen receiver-dst1
+ bind "fd@${feR_dst1}"
+ http-request return status 200 hdr "x-dst" "dst1"
+ http-after-response set-header http_first_request %[http_first_req]
+
+ listen receiver-dst2
+ bind "fd@${feR_dst2}"
+ http-request return status 200 hdr "x-dst" "dst2"
+ http-after-response set-header http_first_request %[http_first_req]
+} -start
+
+# http-reuse with sni parameters
+client c_sni -connect ${h1_feS_sni_sock} {
+ # first request
+ txreq \
+ -hdr "x-sni: www.custom.com"
+ rxresp
+ expect resp.http.http_first_request == "1"
+
+ # second request with same sni, connection must be reused
+ txreq \
+ -hdr "x-sni: www.custom.com"
+ rxresp
+ expect resp.http.http_first_request == "0"
+
+ # third request with a different sni, a new connection must be used
+ txreq \
+ -hdr "x-sni: www.custom2.com"
+ rxresp
+ expect resp.http.http_first_request == "1"
+
+ # fourth request, reuse sni2
+ txreq \
+ -hdr "x-sni: www.custom2.com"
+ rxresp
+ expect resp.http.http_first_request == "0"
+} -run
+
+# http-reuse with destination address
+client c_dst1 -connect ${h1_feS_dst_sock} {
+ txreq \
+ -hdr "x-dst-port: ${h1_feR_dst1_port}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-dst == "dst1"
+ expect resp.http.http_first_request == "1"
+
+ txreq \
+ -hdr "x-dst-port: ${h1_feR_dst1_port}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-dst == "dst1"
+ expect resp.http.http_first_request == "0"
+
+ txreq \
+ -hdr "x-dst-port: ${h1_feR_dst2_port}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-dst == "dst2"
+ expect resp.http.http_first_request == "1"
+
+ txreq \
+ -hdr "x-dst-port: ${h1_feR_dst1_port}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-dst == "dst1"
+ expect resp.http.http_first_request == "0"
+
+ txreq \
+ -hdr "x-dst-port: ${h1_feR_dst2_port}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-dst == "dst2"
+ expect resp.http.http_first_request == "0"
+} -run
+
+## first request with proxy protocol
+client c_proxy -connect ${h1_feS_proxy_sock} -proxy1 "127.0.0.1:40000 ${h1_feS_proxy_addr}:${h1_feS_proxy_port}" {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.http_first_request == "1"
+
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.http_first_request == "0"
+} -run
+
+## second request with different proxy protocol
+# this have the nice effect to fill the server pool to 2 connection
+# (pool-low-conn value) to allow takeover on multi thread run
+client c_proxy -connect ${h1_feS_proxy_sock} -proxy1 "127.0.0.1:50000 ${h1_feS_proxy_addr}:${h1_feS_proxy_port}" {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.http_first_request == "1"
+} -run
+
+## third request, reuse same proxy protocol entry
+client c_proxy -connect ${h1_feS_proxy_sock} -proxy1 "127.0.0.1:40000 ${h1_feS_proxy_addr}:${h1_feS_proxy_port}" {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.http_first_request == "0"
+} -run
+
+## fourth request with different proxy protocol entry, no reuse
+client c_proxy -connect ${h1_feS_proxy_sock} -proxy1 "127.0.0.1:60000 ${h1_feS_proxy_addr}:${h1_feS_proxy_port}" {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.http_first_request == "1"
+} -run
+
+## fifth request, reuse proxy protocol
+client c_proxy -connect ${h1_feS_proxy_sock} -proxy1 "127.0.0.1:50000 ${h1_feS_proxy_addr}:${h1_feS_proxy_port}" {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.http_first_request == "0"
+
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.http_first_request == "0"
+} -run
diff --git a/reg-tests/connection/http_reuse_dispatch.vtc b/reg-tests/connection/http_reuse_dispatch.vtc
new file mode 100644
index 0000000..a419727
--- /dev/null
+++ b/reg-tests/connection/http_reuse_dispatch.vtc
@@ -0,0 +1,79 @@
+varnishtest "Test the proper interaction between http-reuse and dispatch mode"
+
+# With dispatch, the connection are considered as private and should only be
+# reused for requests of the same session
+# This is similar to the http-reuse never mode
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ listen sender
+ bind "fd@${feS}"
+ http-request set-header client-id %[req.hdr(client-id)] if { req.hdr(client-id) -m found }
+ dispatch ${h1_feR_addr}:${h1_feR_port}
+
+ listen receiver
+ bind "fd@${feR}"
+ http-request set-var(sess.client_id) req.hdr(client-id)
+ http-request return status 200
+ http-after-response set-header http_first_request %[http_first_req]
+ http-after-response set-header client-id %[var(sess.client_id)]
+} -start
+
+client c1 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c1"
+ rxresp
+ expect resp.http.http_first_request == "1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+} -run
+
+client c2 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c2"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c2"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c2"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c2"
+} -run
+
+client c3 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c3"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c3"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c3"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c3"
+} -run
diff --git a/reg-tests/connection/http_reuse_never.vtc b/reg-tests/connection/http_reuse_never.vtc
new file mode 100644
index 0000000..fc74631
--- /dev/null
+++ b/reg-tests/connection/http_reuse_never.vtc
@@ -0,0 +1,79 @@
+varnishtest "Test the http-reuse never mode"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ # limit idle pool to one connection
+ # this is to mirror http-reuse safe test, but in this case to ensure that
+ # connection are never reused as expected
+ listen sender
+ bind "fd@${feS}"
+ http-reuse never
+ http-request set-header client-id %[req.hdr(client-id)] if { req.hdr(client-id) -m found }
+ server srv ${h1_feR_addr}:${h1_feR_port} pool-max-conn 1
+
+ listen receiver
+ bind "fd@${feR}"
+ http-request set-var(sess.client_id) req.hdr(client-id)
+ http-request return status 200
+ http-after-response set-header http_first_request %[http_first_req]
+ http-after-response set-header client-id %[var(sess.client_id)]
+} -start
+
+client c1 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c1"
+ rxresp
+ expect resp.http.http_first_request == "1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+} -run
+
+client c2 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c2"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c2"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c2"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c2"
+} -run
+
+client c3 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c3"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c3"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c3"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c3"
+} -run
diff --git a/reg-tests/connection/http_reuse_safe.vtc b/reg-tests/connection/http_reuse_safe.vtc
new file mode 100644
index 0000000..fa00dd4
--- /dev/null
+++ b/reg-tests/connection/http_reuse_safe.vtc
@@ -0,0 +1,78 @@
+varnishtest "Test the http-reuse safe mode"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ # limit idle pool to one connection
+ # this forces connection reuse for the transaction after the first one
+ listen sender
+ bind "fd@${feS}"
+ http-reuse safe
+ http-request set-header client-id %[req.hdr(client-id)] if { req.hdr(client-id) -m found }
+ server srv ${h1_feR_addr}:${h1_feR_port} pool-max-conn 1
+
+ listen receiver
+ bind "fd@${feR}"
+ http-request set-var(sess.client_id) req.hdr(client-id)
+ http-request return status 200
+ http-after-response set-header http_first_request %[http_first_req]
+ http-after-response set-header client-id %[var(sess.client_id)]
+} -start
+
+client c1 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c1"
+ rxresp
+ expect resp.http.http_first_request == "1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+} -run
+
+client c2 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c2"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c2"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+} -run
+
+client c3 -connect ${h1_feS_sock} {
+ txreq \
+ -hdr "client-id: c3"
+ rxresp
+ expect resp.http.http_first_request == "1"
+ expect resp.http.client-id == "c3"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+
+ txreq
+ rxresp
+ expect resp.http.http_first_request == "0"
+ expect resp.http.client-id == "c1"
+} -run
diff --git a/reg-tests/connection/proxy_protocol_random_fail.vtc b/reg-tests/connection/proxy_protocol_random_fail.vtc
new file mode 100644
index 0000000..bcaa03c
--- /dev/null
+++ b/reg-tests/connection/proxy_protocol_random_fail.vtc
@@ -0,0 +1,60 @@
+#commit b406b87
+# BUG/MEDIUM: connection: don't store recv() result into trash.data
+#
+# Cyril Bonté discovered that the proxy protocol randomly fails since
+# commit 843b7cb ("MEDIUM: chunks: make the chunk struct's fields match
+# the buffer struct"). This is because we used to store recv()'s return
+# code into trash.data which is now unsigned, so it never compares as
+# negative against 0. Let's clean this up and test the result itself
+# without storing it first.
+
+varnishtest "PROXY protocol random failures"
+#REQUIRE_OPTIONS=OPENSSL
+
+feature ignore_unknown_macro
+
+#REGTEST_TYPE=broken
+
+syslog Slog_1 -repeat 8 -level info {
+ recv
+ expect ~ "Connect from .* to ${h1_ssl_addr}:${h1_ssl_port}"
+ recv
+ expect ~ "ssl-offload-http/http .* \"POST (https://.*:${h1_ssl_port})?/[1-8] HTTP/(2\\.0|1\\.1)\""
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ stats bind-process 1
+ log ${Slog_1_addr}:${Slog_1_port} len 2048 local0 debug err
+
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ log global
+
+ listen http
+ bind unix@"${tmpdir}/http.socket" accept-proxy name ssl-offload-http
+ option forwardfor
+
+ listen ssl-offload-http
+ option httplog
+ bind "fd@${ssl}" ssl crt ${testdir}/common.pem ssl no-sslv3 alpn h2,http/1.1
+ server http unix@"${tmpdir}/http.socket" send-proxy
+} -start
+
+
+shell {
+ HOST=${h1_ssl_addr}
+ if [ "$HOST" = "::1" ] ; then
+ HOST="\[::1\]"
+ fi
+ for i in 1 2 3 4 5 6 7 8 ; do
+ urls="$urls https://$HOST:${h1_ssl_port}/$i"
+ done
+ curl -i -k -d 'x=x' $urls & wait $!
+}
+
+syslog Slog_1 -wait
diff --git a/reg-tests/connection/proxy_protocol_send_unique_id.vtc b/reg-tests/connection/proxy_protocol_send_unique_id.vtc
new file mode 100644
index 0000000..4f6b848
--- /dev/null
+++ b/reg-tests/connection/proxy_protocol_send_unique_id.vtc
@@ -0,0 +1,42 @@
+varnishtest "Check that the unique ID TLV is properly sent"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ log global
+ unique-id-format %{+X}o\ TEST-%[req.hdr(in)]
+
+ listen sender
+ bind "fd@${feS}"
+
+ unique-id-header unique_id
+
+ server example ${h1_feR_addr}:${h1_feR_port} send-proxy-v2 proxy-v2-options unique-id
+
+ listen receiver
+ bind "fd@${feR}" accept-proxy
+
+ http-request set-var(txn.http_unique_id) req.hdr(unique_id)
+ http-request set-var(txn.proxy_unique_id) fc_pp_unique_id
+ http-after-response set-header http_unique_id %[var(txn.http_unique_id)]
+ http-after-response set-header proxy_unique_id %[var(txn.proxy_unique_id)]
+ http-request return status 200
+} -start
+
+# Validate that a correct header passes
+client c1 -connect ${h1_feS_sock} {
+ txreq -url "/" \
+ -hdr "in: foo"
+ rxresp
+ expect resp.http.http_unique_id == "TEST-foo"
+ expect resp.http.proxy_unique_id == "TEST-foo"
+ txreq -url "/" \
+ -hdr "in: bar"
+ rxresp
+ expect resp.http.http_unique_id == "TEST-bar"
+ expect resp.http.proxy_unique_id == "TEST-bar"
+} -run
diff --git a/reg-tests/connection/proxy_protocol_send_unique_id_alpn.vtc b/reg-tests/connection/proxy_protocol_send_unique_id_alpn.vtc
new file mode 100644
index 0000000..87e590a
--- /dev/null
+++ b/reg-tests/connection/proxy_protocol_send_unique_id_alpn.vtc
@@ -0,0 +1,33 @@
+varnishtest "Check that the unique ID TLV is properly sent for servers with ALPN option"
+
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ log global
+ unique-id-format %{+X}o\ TEST-%[req.hdr(in)]
+
+ listen sender
+ bind "fd@${feS}"
+
+ server example ${h1_feR_addr}:${h1_feR_port} send-proxy-v2 proxy-v2-options unique-id ssl alpn XXX verify none
+
+ listen receiver
+ bind "fd@${feR}" ssl crt ${testdir}/common.pem accept-proxy
+
+ http-request set-var(txn.proxy_unique_id) fc_pp_unique_id
+ http-after-response set-header proxy_unique_id %[var(txn.proxy_unique_id)]
+ http-request return status 200
+} -start
+
+# Validate that a correct header passes
+client c1 -connect ${h1_feS_sock} {
+ txreq -url "/" \
+ -hdr "in: foo"
+ rxresp
+ expect resp.http.proxy_unique_id == "TEST-foo"
+} -run
diff --git a/reg-tests/connection/proxy_protocol_tlv_validation.vtc b/reg-tests/connection/proxy_protocol_tlv_validation.vtc
new file mode 100644
index 0000000..8c7d734
--- /dev/null
+++ b/reg-tests/connection/proxy_protocol_tlv_validation.vtc
@@ -0,0 +1,142 @@
+varnishtest "Check that the TLVs are properly validated"
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+# We need one HAProxy for each test, because apparently the connection by
+# the client is reused, leading to connection resets.
+
+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 a
+ bind "fd@${fe1}" accept-proxy
+ http-after-response set-header echo %[fc_pp_authority,hex]
+ http-request return status 200
+} -start
+
+# Validate that a correct header passes
+client c1 -connect ${h1_fe1_sock} {
+ # PROXY v2 signature
+ sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+ # version + PROXY
+ sendhex "21"
+ # TCP4
+ sendhex "11"
+ # length of the address (12) + length of the TLV (8)
+ sendhex "00 14"
+ # 127.0.0.1 42 127.0.0.1 1337
+ sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+ # PP2_TYPE_AUTHORITY + length of the value + "12345"
+ sendhex "02 00 05 31 32 33 34 35"
+
+ txreq -url "/"
+ rxresp
+ expect resp.http.echo == "3132333435"
+} -run
+
+haproxy h2 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend a
+ bind "fd@${fe1}" accept-proxy
+ http-after-response set-header echo %[fc_pp_authority,hex]
+ http-request return status 200
+} -start
+
+# Validate that a TLV after the end of the PROXYv2 header is not parsed
+# and handle by the HTTP parser, leading to a 400 bad request error
+client c2 -connect ${h2_fe1_sock} {
+ # PROXY v2 signature
+ sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+ # version + PROXY
+ sendhex "21"
+ # TCP4
+ sendhex "11"
+ # length of the address (12) + length of the TLV (8)
+ sendhex "00 14"
+ # 127.0.0.1 42 127.0.0.1 1337
+ sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+ # PP2_TYPE_AUTHORITY + length of the value + "12345"
+ sendhex "02 00 05 31 32 33 34 35"
+ # after the end of the PROXYv2 header: PP2_TYPE_AUTHORITY + length of the value + "54321"
+ sendhex "02 00 05 35 34 33 32 31"
+
+ txreq -url "/"
+ rxresp
+ expect resp.status == 400
+ expect resp.http.echo == <undef>
+} -run
+
+haproxy h3 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend a
+ bind "fd@${fe1}" accept-proxy
+ http-after-response set-header echo %[fc_pp_authority,hex]
+ http-request return status 200
+} -start
+
+# Validate that a TLV length exceeding the PROXYv2 length fails
+client c3 -connect ${h3_fe1_sock} {
+ # PROXY v2 signature
+ sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+ # version + PROXY
+ sendhex "21"
+ # TCP4
+ sendhex "11"
+ # length of the address (12) + too small length of the TLV (8)
+ sendhex "00 14"
+ # 127.0.0.1 42 127.0.0.1 1337
+ sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+ # PP2_TYPE_AUTHORITY + length of the value + "1234512345"
+ sendhex "02 00 0A 31 32 33 34 35 31 32 33 34 35"
+
+ txreq -url "/"
+ expect_close
+} -run
+
+haproxy h4 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend a
+ bind "fd@${fe1}" accept-proxy
+ http-after-response set-header echo %[fc_pp_authority,hex]
+ http-request return status 200
+} -start
+
+# Validate that TLVs not ending with the PROXYv2 header fail
+client c4 -connect ${h4_fe1_sock} {
+ # PROXY v2 signature
+ sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+ # version + PROXY
+ sendhex "21"
+ # TCP4
+ sendhex "11"
+ # length of the address (12) + too big length of the TLV (8)
+ sendhex "00 14"
+ # 127.0.0.1 42 127.0.0.1 1337
+ sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+ # PP2_TYPE_AUTHORITY + length of the value + "1234"
+ sendhex "02 00 04 31 32 33 34"
+
+ txreq -url "/"
+ expect_close
+} -run
diff --git a/reg-tests/connection/tcp_to_http_upgrade.vtc b/reg-tests/connection/tcp_to_http_upgrade.vtc
new file mode 100644
index 0000000..093ba48
--- /dev/null
+++ b/reg-tests/connection/tcp_to_http_upgrade.vtc
@@ -0,0 +1,162 @@
+varnishtest "Test connection upgrades from TCP to HTTP"
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ # TCP > H1 using "switch-mode http"
+ rxreq
+ expect req.http.x-stream-mode == tcp
+ expect req.http.x-name == fe1
+ txresp
+ rxreq
+ expect req.http.x-stream-mode == http
+ expect req.http.x-name == fe1
+ txresp
+
+ accept
+
+ # TCP > H1 using backend mode
+ rxreq
+ expect req.http.x-name == be
+ txresp
+ rxreq
+ expect req.http.x-name == be
+ txresp
+
+ accept
+
+ # TCP > H2 using "switch-mode http"
+ rxreq
+ expect req.http.x-stream-mode == http
+ expect req.http.x-name == fe1
+ txresp
+ rxreq
+ expect req.http.x-stream-mode == http
+ expect req.http.x-name == fe1
+ txresp
+
+ # To be sure no other request was received
+ accept
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ frontend fe1
+ mode tcp
+ bind "fd@${fe1h1}"
+
+ tcp-request inspect-delay 1s
+ tcp-request content set-var(req.stream_mode) internal.strm.is_htx,iif(http,tcp)
+ tcp-request content switch-mode http if HTTP
+ tcp-request content reject # never reached
+
+ http-request set-header x-stream-mode %[var(req.stream_mode)]
+ http-request set-header x-name %[fe_name]
+
+ default_backend be
+
+ frontend fe2
+ mode tcp
+ bind "fd@${fe2h1}"
+ default_backend be
+
+ backend be
+ mode http
+ http-request set-header x-name %[be_name] unless { req.fhdr(x-name) -m found }
+ server s1 ${s1_addr}:${s1_port}
+
+ listen li1
+ mode http
+ bind "fd@${li1h1}"
+ server s1 ${h1_fe1h1_addr}:${h1_fe1h1_port} proto h2
+
+ listen err1
+ mode http
+ bind "fd@${err1h1}" proto h1
+ server s1 ${s1_addr}:${s1_port}
+
+ listen err2
+ mode tcp
+ bind "fd@${err2h1}"
+
+ tcp-request inspect-delay 1s
+ tcp-request content switch-mode http proto h1 if HTTP
+ tcp-request content reject # never reached
+
+ default_backend be
+
+ listen err3
+ mode tcp
+ bind "fd@${err3h1}" proto none
+
+ tcp-request inspect-delay 1s
+ tcp-request content switch-mode http if HTTP
+ tcp-request content reject # never reached
+
+ default_backend be
+} -start
+
+# TCP > H1 using "switch-mode http"
+client c1 -connect ${h1_fe1h1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+# TCP > H1 using backend mode
+client c2 -connect ${h1_fe2h1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+# TCP > H2 using "switch-mode http"
+client c3 -connect ${h1_li1h1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+# implicit H1 > H2 upgrade not performed
+client c_err1 -connect ${h1_err1h1_sock} {
+ send "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+ rxresp
+ expect resp.status == 400
+} -run
+
+
+# TCP > H1 > H2 upgrade not allowed
+client c_err2 -connect ${h1_err2h1_sock} {
+ send "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+ rxresp
+ expect resp.status == 400
+} -run
+
+
+# TCP > HTTP upgrade not allowed
+client c_err3 -connect ${h1_err3h1_sock} {
+ txreq
+ expect_close
+} -run
+
+# To be sure no other request was received by the server
+client c_end -connect ${s1_sock} {
+ txreq
+ rxresp
+} -run
diff --git a/reg-tests/contrib/prometheus.vtc b/reg-tests/contrib/prometheus.vtc
new file mode 100644
index 0000000..a481240
--- /dev/null
+++ b/reg-tests/contrib/prometheus.vtc
@@ -0,0 +1,113 @@
+varnishtest "prometheus exporter test"
+
+#REQUIRE_VERSION=2.4
+#REQUIRE_SERVICES=prometheus-exporter
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ 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}"
+ option socket-stats
+
+ listen stats
+ bind "fd@${stats}"
+ http-request use-service prometheus-exporter if { path /metrics }
+
+ frontend fe
+ bind "fd@${fe}"
+ default_backend be
+
+ backend be
+ stick-table type ip size 1m expire 10s store http_req_rate(10s)
+ option httpchk
+ server s1 ${s1_addr}:${s1_port}
+ server s2 ${s2_addr}:${s2_port} check inter 5s maxqueue 10 maxconn 12 pool-max-conn 42
+} -start
+
+client c1 -connect ${h1_stats_sock} {
+ txreq -url "/metrics"
+ rxresp
+ # test general metrics
+ expect resp.status == 200
+ expect resp.body ~ ".*haproxy_process.*"
+ expect resp.body ~ ".*haproxy_frontend.*"
+ expect resp.body ~ ".*haproxy_listener.*"
+ expect resp.body ~ ".*haproxy_backend.*"
+ expect resp.body ~ ".*haproxy_server.*"
+ expect resp.body ~ ".*haproxy_sticktable.*"
+
+ # test expected NaN values
+ expect resp.body ~ ".*haproxy_server_check_failures_total{proxy=\"be\",server=\"s1\"} NaN.*"
+ expect resp.body ~ ".*haproxy_server_check_up_down_total{proxy=\"be\",server=\"s1\"} NaN.*"
+ expect resp.body ~ ".*haproxy_server_check_failures_total{proxy=\"be\",server=\"s2\"} 0.*"
+ expect resp.body ~ ".*haproxy_server_check_up_down_total{proxy=\"be\",server=\"s2\"} 0.*"
+
+ expect resp.body ~ ".*haproxy_server_queue_limit{proxy=\"be\",server=\"s1\"} NaN.*"
+ expect resp.body ~ ".*haproxy_server_queue_limit{proxy=\"be\",server=\"s2\"} 10.*"
+
+ expect resp.body ~ ".*haproxy_server_limit_sessions{proxy=\"be\",server=\"s1\"} NaN.*"
+ expect resp.body ~ ".*haproxy_server_limit_sessions{proxy=\"be\",server=\"s2\"} 12.*"
+
+ expect resp.body ~ ".*haproxy_backend_downtime_seconds_total{proxy=\"stats\"} NaN.*"
+ expect resp.body ~ ".*haproxy_backend_downtime_seconds_total{proxy=\"be\"} 0.*"
+ expect resp.body ~ ".*haproxy_server_downtime_seconds_total{proxy=\"be\",server=\"s1\"} NaN.*"
+ expect resp.body ~ ".*haproxy_server_downtime_seconds_total{proxy=\"be\",server=\"s2\"} 0.*"
+
+ expect resp.body ~ ".*haproxy_server_current_throttle{proxy=\"be\",server=\"s1\"} NaN.*"
+
+ expect resp.body ~ ".*haproxy_server_idle_connections_limit{proxy=\"be\",server=\"s1\"} NaN.*"
+ expect resp.body ~ ".*haproxy_server_idle_connections_limit{proxy=\"be\",server=\"s2\"} 42.*"
+
+ # test well known labels presence
+ expect resp.body ~ ".*haproxy_process_build_info{version=\".*\"} 1.*"
+ expect resp.body ~ ".*haproxy_frontend_http_responses_total{proxy=\"stats\",code=\"4xx\"} 0.*"
+ expect resp.body ~ ".*haproxy_frontend_status{proxy=\"fe\",state=\"UP\"} 1.*"
+ expect resp.body ~ ".*haproxy_listener_status{proxy=\"stats\",listener=\"sock-1\",state=\"WAITING\"} 0.*"
+ expect resp.body ~ ".*haproxy_backend_status{proxy=\"be\",state=\"UP\"} 1.*"
+ expect resp.body ~ ".*haproxy_server_status{proxy=\"be\",server=\"s1\",state=\"DOWN\"} 0.*"
+ expect resp.body ~ ".*haproxy_server_check_status{proxy=\"be\",server=\"s2\",state=\"HANA\"} 0.*"
+
+ # test scope
+ txreq -url "/metrics?scope="
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 0
+
+ txreq -url "/metrics?scope=server"
+ rxresp
+ expect resp.status == 200
+ expect resp.body !~ ".*haproxy_process.*"
+ expect resp.body !~ ".*haproxy_frontend.*"
+ expect resp.body !~ ".*haproxy_listener.*"
+ expect resp.body !~ ".*haproxy_backend.*"
+ expect resp.body ~ ".*haproxy_server.*"
+ expect resp.body !~ ".*haproxy_sticktable.*"
+
+ txreq -url "/metrics?scope=frontend&scope=backend"
+ rxresp
+ expect resp.status == 200
+ expect resp.body !~ ".*haproxy_process.*"
+ expect resp.body ~ ".*haproxy_frontend.*"
+ expect resp.body !~ ".*haproxy_listener.*"
+ expect resp.body ~ ".*haproxy_backend.*"
+ expect resp.body !~ ".*haproxy_server.*"
+ expect resp.body !~ ".*haproxy_sticktable.*"
+
+ txreq -url "/metrics?scope"
+ rxresp
+ expect resp.status == 400
+} -run
diff --git a/reg-tests/converter/add_item.vtc b/reg-tests/converter/add_item.vtc
new file mode 100644
index 0000000..474ad7b
--- /dev/null
+++ b/reg-tests/converter/add_item.vtc
@@ -0,0 +1,50 @@
+varnishtest "be2dec converter Test"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.6-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 3 -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}"
+
+ #### requests
+ http-request set-var(txn.input) req.hdr(input)
+ http-request set-var(txn.var) str("var_content")
+
+ http-response set-header add_item-1 "%[var(txn.input),add_item(',',txn.var,_suff_)]"
+ http-response set-header add_item-2 "%[var(txn.input),add_item(',',txn.var)]"
+ http-response set-header add_item-3 "%[var(txn.input),add_item(',',,_suff_)]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "input:"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.add_item-1 == "var_content_suff_"
+ expect resp.http.add_item-2 == "var_content"
+ expect resp.http.add_item-3 == "_suff_"
+ txreq -url "/" \
+ -hdr "input: input_string"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.add_item-1 == "input_string,var_content_suff_"
+ expect resp.http.add_item-2 == "input_string,var_content"
+ expect resp.http.add_item-3 == "input_string,_suff_"
+} -run
diff --git a/reg-tests/converter/be2dec.vtc b/reg-tests/converter/be2dec.vtc
new file mode 100644
index 0000000..a0b7104
--- /dev/null
+++ b/reg-tests/converter/be2dec.vtc
@@ -0,0 +1,56 @@
+varnishtest "be2dec converter Test"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 3 -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}"
+
+ #### requests
+ http-request set-var(txn.input) req.hdr(input)
+
+ http-response set-header be2dec-1 "%[var(txn.input),be2dec(:,1)]"
+ http-response set-header be2dec-2 "%[var(txn.input),be2dec(-,3)]"
+ http-response set-header be2dec-3 "%[var(txn.input),be2dec(::,3,1)]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "input:"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.be2dec-1 == ""
+ expect resp.http.be2dec-2 == ""
+ expect resp.http.be2dec-3 == ""
+ txreq -url "/" \
+ -hdr "input: 0123456789"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.be2dec-1 == "48:49:50:51:52:53:54:55:56:57"
+ expect resp.http.be2dec-2 == "3158322-3355701-3553080-57"
+ expect resp.http.be2dec-3 == "3158322::3355701::3553080"
+ txreq -url "/" \
+ -hdr "input: abcdefghijklmnopqrstuvwxyz"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.be2dec-1 == "97:98:99:100:101:102:103:104:105:106:107:108:109:110:111:112:113:114:115:116:117:118:119:120:121:122"
+ expect resp.http.be2dec-2 == "6382179-6579558-6776937-6974316-7171695-7369074-7566453-7763832-31098"
+ expect resp.http.be2dec-3 == "6382179::6579558::6776937::6974316::7171695::7369074::7566453::7763832"
+} -run
diff --git a/reg-tests/converter/be2hex.vtc b/reg-tests/converter/be2hex.vtc
new file mode 100644
index 0000000..4cf3dc1
--- /dev/null
+++ b/reg-tests/converter/be2hex.vtc
@@ -0,0 +1,60 @@
+varnishtest "be2hex converter Test"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 3 -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}"
+
+ #### requests
+ http-request set-var(txn.input) req.hdr(input)
+
+ http-response set-header be2hex "%[var(txn.input),be2hex,lower]"
+ http-response set-header be2hex-1 "%[var(txn.input),be2hex(:,1),lower]"
+ http-response set-header be2hex-2 "%[var(txn.input),be2hex(--,3),lower]"
+ http-response set-header be2hex-3 "%[var(txn.input),be2hex(.,3,1),lower]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "input:"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.be2hex == ""
+ expect resp.http.be2hex-1 == ""
+ expect resp.http.be2hex-2 == ""
+ expect resp.http.be2hex-3 == ""
+ txreq -url "/" \
+ -hdr "input: 0123456789"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.be2hex == "30313233343536373839"
+ expect resp.http.be2hex-1 == "30:31:32:33:34:35:36:37:38:39"
+ expect resp.http.be2hex-2 == "303132--333435--363738--39"
+ expect resp.http.be2hex-3 == "303132.333435.363738"
+ txreq -url "/" \
+ -hdr "input: abcdefghijklmnopqrstuvwxyz"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.be2hex == "6162636465666768696a6b6c6d6e6f707172737475767778797a"
+ expect resp.http.be2hex-1 == "61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:70:71:72:73:74:75:76:77:78:79:7a"
+ expect resp.http.be2hex-2 == "616263--646566--676869--6a6b6c--6d6e6f--707172--737475--767778--797a"
+ expect resp.http.be2hex-3 == "616263.646566.676869.6a6b6c.6d6e6f.707172.737475.767778"
+} -run
diff --git a/reg-tests/converter/digest.vtc b/reg-tests/converter/digest.vtc
new file mode 100644
index 0000000..e911ff4
--- /dev/null
+++ b/reg-tests/converter/digest.vtc
@@ -0,0 +1,57 @@
+varnishtest "digest converter Test"
+
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTION=OPENSSL
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -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@${fe}"
+
+ #### requests
+ http-request set-var(txn.hash) req.hdr(hash)
+
+ http-response set-header SHA1 "%[var(txn.hash),digest(sha1),hex,lower]"
+ http-response set-header SHA224 "%[var(txn.hash),digest(sha224),hex,lower]"
+ http-response set-header SHA256 "%[var(txn.hash),digest(sha256),hex,lower]"
+ http-response set-header SHA384 "%[var(txn.hash),digest(sha384),hex,lower]"
+ http-response set-header SHA512 "%[var(txn.hash),digest(sha512),hex,lower]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "Hash: 1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.sha1 == "356a192b7913b04c54574d18c28d46e6395428ab"
+ expect resp.http.sha224 == "e25388fde8290dc286a6164fa2d97e551b53498dcbf7bc378eb1f178"
+ expect resp.http.sha256 == "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"
+ expect resp.http.sha384 == "47f05d367b0c32e438fb63e6cf4a5f35c2aa2f90dc7543f8a41a0f95ce8a40a313ab5cf36134a2068c4c969cb50db776"
+ expect resp.http.sha512 == "4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a"
+ txreq -url "/" \
+ -hdr "Hash: 2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.sha1 == "da4b9237bacccdf19c0760cab7aec4a8359010b0"
+ expect resp.http.sha224 == "58b2aaa0bfae7acc021b3260e941117b529b2e69de878fd7d45c61a9"
+ expect resp.http.sha256 == "d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35"
+ expect resp.http.sha384 == "d063457705d66d6f016e4cdd747db3af8d70ebfd36badd63de6c8ca4a9d8bfb5d874e7fbd750aa804dcaddae7eeef51e"
+ expect resp.http.sha512 == "40b244112641dd78dd4f93b6c9190dd46e0099194d5a44257b7efad6ef9ff4683da1eda0244448cb343aa688f5d3efd7314dafe580ac0bcbf115aeca9e8dc114"
+} -run
diff --git a/reg-tests/converter/field.vtc b/reg-tests/converter/field.vtc
new file mode 100644
index 0000000..1243728
--- /dev/null
+++ b/reg-tests/converter/field.vtc
@@ -0,0 +1,39 @@
+varnishtest "field converter Test"
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 3 -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}"
+
+ #### requests
+ http-request set-var(txn.uri) path
+ http-response set-header Found %[var(txn.uri),field(3,/)] if { var(txn.uri),field(3,/) -m found }
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/foo/bar/baz"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.found == "bar"
+ txreq -url "/foo"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.found == "<undef>"
+} -run
diff --git a/reg-tests/converter/fix.vtc b/reg-tests/converter/fix.vtc
new file mode 100644
index 0000000..8206da3
--- /dev/null
+++ b/reg-tests/converter/fix.vtc
@@ -0,0 +1,235 @@
+varnishtest "fix converters Test"
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ # Valid FIX-4.0 logon
+ recv 92
+ # 8=FIX|4.0|9=66|35=A|34=1|49=EXECUTOR|52=20201029-10:54:19|56=CLIENT1|98=0|108=30|10=147|
+ sendhex "383d4649582e342e3001393d36360133353d410133343d310134393d4558454355544f520135323d32303230313032392d31303a35343a31390135363d434c49454e54310139383d30013130383d33300131303d31343701"
+ close
+
+ # Valid FIX-4.1 logon
+ accept
+ recv 98
+ # 8=FIX.4.1|9=72|35=A|34=1|49=EXECUTOR|52=20201029-12:43:07|56=CLIENT1|98=0|108=30|141=Y|10=187|
+ sendhex "383d4649582e342e3101393d37320133353d410133343d310134393d4558454355544f520135323d32303230313032392d31323a34333a30370135363d434c49454e54310139383d30013130383d3330013134313d590131303d31383701"
+ close
+
+ # Valid FIX-4.2 logon
+ accept
+ recv 98
+ # 8=FIX.4.2|9=79|35=A|34=1|49=EXECUTOR|52=20201029-12:55:12.101414|56=CLIENT1|98=0|108=30|141=Y|10=027|
+ sendhex "383d4649582e342e3201393d37390133353d410133343d310134393d4558454355544f520135323d32303230313032392d31323a35353a31322e3130313431340135363d434c49454e54310139383d30013130383d3330013134313d590131303d30323701"
+ close
+
+ # Valid FIX-4.3 logon
+ accept
+ recv 125
+ # 8=FIX.4.3|9=79|35=A|34=1|49=EXECUTOR|52=20201029-12:58:50.891371|56=CLIENT1|98=0|108=30|141=Y|10=051|
+ sendhex "383d4649582e342e3301393d37390133353d410133343d310134393d4558454355544f520135323d32303230313032392d31323a35383a35302e3839313337310135363d434c49454e54310139383d30013130383d3330013134313d590131303d30353101"
+ close
+
+ # Valid FIX-4.4 logon
+ accept
+ recv 125
+ # 8=FIX.4.4|9=79|35=A|34=1|49=EXECUTOR|52=20201029-13:02:44.535360|56=CLIENT1|98=0|108=30|141=Y|10=038|
+ sendhex "383d4649582e342e3401393d37390133353d410133343d310134393d4558454355544f520135323d32303230313032392d31333a30323a34342e3533353336300135363d434c49454e54310139383d30013130383d3330013134313d590131303d30333801"
+ close
+
+ # Valid FIX-5.0 logon
+ accept
+ recv 140
+ # 8=FIXT.1.1|9=86|35=A|34=1|49=EXECUTOR|52=20201029-13:13:22.626384|56=CLIENT1|98=0|108=30|141=Y|1137=7|10=184|
+ sendhex "383d464958542e312e3101393d38360133353d410133343d310134393d4558454355544f520135323d32303230313032392d31333a31333a32322e3632363338340135363d434c49454e54310139383d30013130383d3330013134313d5901313133373d370131303d31383401"
+} -start
+
+server s2 {
+ # Valid FIX-4.4 logon
+ recv 125
+ # 8=FIX.4.4|9=79|35=A|34=1|49=EXECUTOR|52=20201029-13:02:44.535360|56=CLIENT1|98=0|108=30|141=Y|10=038|
+ sendhex "383d4649582e342e3401393d37390133353d410133343d310134393d4558454355544f520135323d32303230313032392d31333a30323a34342e3533353336300135363d434c49454e54310139383d30013130383d3330013134313d590131303d30333801"
+
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ tcp-request inspect-delay 1s
+ tcp-request content reject unless { req.payload(0,0),fix_is_valid }
+ default_backend be1
+
+ frontend fe2
+ bind "fd@${fe2}"
+ tcp-request inspect-delay 1s
+ tcp-request content reject unless { req.payload(0,0),fix_is_valid }
+ tcp-request content set-var(req.fix_vsn) req.payload(0,0),fix_tag_value(BeginString)
+ tcp-request content set-var(req.fix_len) req.payload(0,0),fix_tag_value(BodyLength)
+ tcp-request content set-var(req.fix_type) req.payload(0,0),fix_tag_value(MsgType)
+ tcp-request content set-var(req.fix_sender) req.payload(0,0),fix_tag_value(SenderCompID)
+ tcp-request content set-var(req.fix_target) req.payload(0,0),fix_tag_value(TargetCompID)
+ tcp-request content set-var(req.fix_chksum) req.payload(0,0),fix_tag_value(CheckSum)
+ tcp-request content reject if ! { var(req.fix_vsn) -m str "FIX.4.4" } || ! { var(req.fix_len) -m str "102" }
+ tcp-request content reject if ! { var(req.fix_type) -m str "A" } || ! { var(req.fix_sender) -m str "CLIENT1" }
+ tcp-request content reject if ! { var(req.fix_target) -m str "EXECUTOR" } || ! { var(req.fix_chksum) -m str "252" }
+ default_backend be2
+
+ backend be1
+ server s1 ${s1_addr}:${s1_port}
+ tcp-response inspect-delay 1s
+ tcp-response content reject unless { res.payload(0,0),fix_is_valid }
+
+ backend be2
+ server s2 ${s2_addr}:${s2_port}
+ tcp-response inspect-delay 1s
+ tcp-response content reject unless { res.payload(0,0),fix_is_valid }
+ tcp-response content set-var(res.fix_vsn) res.payload(0,0),fix_tag_value(8)
+ tcp-response content set-var(res.fix_len) res.payload(0,0),fix_tag_value(9)
+ tcp-response content set-var(res.fix_type) res.payload(0,0),fix_tag_value(35)
+ tcp-response content set-var(res.fix_sender) res.payload(0,0),fix_tag_value(49)
+ tcp-response content set-var(res.fix_target) res.payload(0,0),fix_tag_value(56)
+ tcp-response content set-var(res.fix_chksum) res.payload(0,0),fix_tag_value(10)
+ tcp-response content reject if ! { var(res.fix_vsn) -m str "FIX.4.4" } || ! { var(res.fix_len) -m str "79" }
+ tcp-response content reject if ! { var(res.fix_type) -m str "A" } || ! { var(res.fix_sender) -m str "EXECUTOR" }
+ tcp-response content reject if ! { var(res.fix_target) -m str "CLIENT1" } || ! { var(res.fix_chksum) -m str "038" }
+} -start
+
+client c1_4_0 -connect ${h1_fe1_sock} {
+ # Valid FIX-4.0 logon
+ # 8=FIX|4.0|9=70|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-10:54:19.617|98=0|108=30|10=090|
+ sendhex "383d4649582e342e3001393d37300133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31303a35343a31392e3631370139383d30013130383d33300131303d30393001"
+ recv 88
+ expect_close
+} -run
+
+client c1_4_1 -connect ${h1_fe1_sock} {
+ # Valid FIX-4.1 logon
+ # 8=FIX.4.1|9=76|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-12:43:07.940|98=0|108=30|141=Y|10=138|
+ sendhex "383d4649582e342e3101393d37360133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31323a34333a30372e3934300139383d30013130383d3330013134313d590131303d31333801"
+ recv 94
+ expect_close
+} -run
+
+client c1_4_2 -connect ${h1_fe1_sock} {
+ # Valid FIX-4.2 logon
+ # 8=FIX.4.2|9=76|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-12:55:12.100|98=0|108=30|141=Y|10=126|
+ sendhex "383d4649582e342e3201393d37360133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31323a35353a31322e3130300139383d30013130383d3330013134313d590131303d31323601"
+ recv 101
+ expect_close
+} -run
+
+client c1_4_3 -connect ${h1_fe1_sock} {
+ # Valid FIX-4.3 logon
+ # 8=FIX.4.3|9=102|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-12:58:50.889|98=0|108=30|141=Y|553=Username|554=Password|10=012|
+ sendhex "383d4649582e342e3301393d3130320133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31323a35383a35302e3838390139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d30313201"
+ recv 101
+ expect_close
+} -run
+
+client c1_4_4 -connect ${h1_fe1_sock} {
+ # Valid FIX-4.4 logon
+ # 8=FIX.4.4|9=102|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e3401393d3130320133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ recv 101
+ expect_close
+} -run
+
+client c1_5_0 -connect ${h1_fe1_sock} {
+ # Valid FIX-5.0 logon
+ # 8=FIXT.1.1|9=116|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:13:22.624|1128=7|98=0|108=30|141=Y|553=Username|554=Password|1137=7|10=204|
+ sendhex "383d464958542e312e3101393d3131360133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a31333a32322e36323401313132383d370139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f726401313133373d370131303d32303401"
+ recv 109
+ expect_close
+} -run
+
+client c2_1 -connect ${h1_fe1_sock} {
+ # InValid FIX-4.4: Empty TagName (missing EncryptMethod <98> tag name)
+ # 8=FIX.4.4|9=100|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|=0|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e3401393d3130300133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e353238013d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ expect_close
+} -run
+
+client c2_2 -connect ${h1_fe1_sock} {
+ # InValid FIX-4.4: Empty TagValue (missing EncryptMethod <98> tag value)
+ # 8=FIX.4.4|9=101|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e3401393d3130310133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ expect_close
+} -run
+
+client c2_3 -connect ${h1_fe1_sock} {
+ # InValid FIX-4.4: Empty Tag no delimiter (missing delimiter for EncryptMethod <98> tag)
+ # 8=FIX.4.4|9=101|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e3401393d3130300133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e353238013938013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ expect_close
+} -run
+
+client c2_4 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: First tag != BeginString
+ # 9=102|8=FIX.4.4|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "393d31303201383d4649582e342e340133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ expect_close
+} -run
+
+client c2_5 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: Second tag != BodyLength
+ # 8=FIX.4.4|35=A|9=102|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e340133353d4101393d3130320134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ expect_close
+} -run
+
+client c2_6 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: Third tag != MsgType
+ # 8=FIX.4.4|9=102|49=CLIENT1|35=A|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e3401393d3130320134393d434c49454e54310133353d410135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ expect_close
+} -run
+
+client c2_7 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: Bad body length (too short 100 != 102)
+ # 8=FIX.4.4|9=100|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e3401393d3130300133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ expect_close
+} -run
+
+client c2_8 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: Bad body length (too long 105 != 102)
+ # 8=FIX.4.4|9=105|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=252|XXX
+ sendhex "383d4649582e342e3401393d3130350133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201585858"
+ expect_close
+} -run
+
+client c2_9 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: Too short checksum value (< 3 digit)
+ # 8=FIX.4.4|9=102|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=25|
+ sendhex "383d4649582e342e3401393d3130320133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d323501"
+ expect_close
+} -run
+
+client c2_10 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: Too long checksum value (> 3 digit)
+ # 8=FIX.4.4|9=102|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=2520|
+ sendhex "383d4649582e342e3401393d3130320133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d3235323001"
+ expect_close
+} -run
+
+client c2_11 -connect ${h1_fe1_sock} {
+ # Invalid FIX-4.4: invalid checksum value (253 != 252)
+ # 8=FIX.4.4|9=102|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=253|
+ sendhex "383d4649582e342e3401393d3130320133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353301"
+ expect_close
+} -run
+
+
+client c3_1 -connect ${h1_fe2_sock} {
+ # 8=FIX.4.4|9=102|35=A|49=CLIENT1|56=EXECUTOR|34=1|52=20201029-13:02:44.528|98=0|108=30|141=Y|553=Username|554=Password|10=252|
+ sendhex "383d4649582e342e3401393d3130320133353d410134393d434c49454e54310135363d4558454355544f520133343d310135323d32303230313032392d31333a30323a34342e3532380139383d30013130383d3330013134313d59013535333d557365726e616d65013535343d50617373776f72640131303d32353201"
+ recv 101
+ expect_close
+} -run
diff --git a/reg-tests/converter/hmac.vtc b/reg-tests/converter/hmac.vtc
new file mode 100644
index 0000000..230a44d
--- /dev/null
+++ b/reg-tests/converter/hmac.vtc
@@ -0,0 +1,55 @@
+varnishtest "HMAC converter Test"
+
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTION=OPENSSL
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -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@${fe}"
+
+ #### requests
+ http-request set-var(txn.hash) req.hdr(hash)
+ http-request set-var(txn.key) str(my_super_secret_long_key),base64
+
+ http-response set-header SHA1-short "%[var(txn.hash),hmac(sha1,a2V5),hex,lower]"
+ http-response set-header SHA1-long "%[var(txn.hash),hmac(sha1,txn.key),hex,lower]"
+ http-response set-header SHA256-short "%[var(txn.hash),hmac(sha256,a2V5),hex,lower]"
+ http-response set-header SHA256-long "%[var(txn.hash),hmac(sha256,txn.key),hex,lower]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "Hash: 1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.sha1-short == "e23feb105f9622241bf23db1638cd2b4208b1f53"
+ expect resp.http.sha1-long == "87b10ddcf39e26f6bd7c3b0e38e0125997b255be"
+ expect resp.http.sha256-short == "6da91fb91517be1f5cdcf3af91d7d40c717dd638a306157606fb2e584f7ae926"
+ expect resp.http.sha256-long == "2fb3de6a462c54d1803f946b52202f3a8cd46548ffb3f789b4ac11a4361ffef2"
+ txreq -url "/" \
+ -hdr "Hash: 2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.sha1-short == "311219c4a80c5ef81b1cee5505236c1d0ab1922c"
+ expect resp.http.sha1-long == "c5758af565ba4b87b3db49c8b32d4a94d430cb78"
+ expect resp.http.sha256-short == "ae7b3ee87b8c9214f714df1c2042c7a985b9d711e9938a063937ad1636775a88"
+ expect resp.http.sha256-long == "c073191a2ebf29f510444b92c187d62199d84b58f58dceeadb91994c170a9a16"
+} -run
diff --git a/reg-tests/converter/iif.vtc b/reg-tests/converter/iif.vtc
new file mode 100644
index 0000000..f412daf
--- /dev/null
+++ b/reg-tests/converter/iif.vtc
@@ -0,0 +1,46 @@
+varnishtest "iif converter Test"
+#REQUIRE_VERSION=2.3
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 3 -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}"
+
+ #### requests
+ http-request set-var(txn.iif) req.hdr_cnt(count),iif(ok,ko)
+ http-response set-header iif %[var(txn.iif)]
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.iif == "ko"
+ txreq \
+ -hdr "count: 1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.iif == "ok"
+ txreq \
+ -hdr "count: 1,2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.iif == "ok"
+} -run
diff --git a/reg-tests/converter/json.vtc b/reg-tests/converter/json.vtc
new file mode 100644
index 0000000..1f37c9f
--- /dev/null
+++ b/reg-tests/converter/json.vtc
@@ -0,0 +1,40 @@
+varnishtest "json converter test"
+
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -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@${fe}"
+
+ http-response set-header json0 "%[str(foo 1/2),json]"
+ # bad UTF-8 sequence
+ http-response set-header json1 "%[str(\xE0),json(utf8)]"
+ # bad UTF-8 sequence, but removes them
+ http-response set-header json2 "%[str(-\xE0-),json(utf8s)]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.http.json0 == "foo 1\\/2"
+ expect resp.http.json1 == ""
+ expect resp.http.json2 == "--"
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/converter/json_query.vtc b/reg-tests/converter/json_query.vtc
new file mode 100644
index 0000000..f78c393
--- /dev/null
+++ b/reg-tests/converter/json_query.vtc
@@ -0,0 +1,101 @@
+varnishtest "JSON Query converters Test"
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 8 -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
+
+ 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 fe
+ bind "fd@${fe}"
+ tcp-request inspect-delay 1s
+
+ http-request set-var(sess.header_json) req.hdr(Authorization),json_query('$.iss')
+ http-request set-var(sess.pay_json) req.body,json_query('$.iss')
+ http-request set-var(sess.pay_int) req.body,json_query('$.integer',"int"),add(1)
+ http-request set-var(sess.pay_neg_int) req.body,json_query('$.negativ-integer',"int"),add(1)
+ http-request set-var(sess.pay_double) req.body,json_query('$.double')
+ http-request set-var(sess.pay_boolean_true) req.body,json_query('$.boolean-true')
+ http-request set-var(sess.pay_boolean_false) req.body,json_query('$.boolean-false')
+ http-request set-var(sess.pay_mykey) req.body,json_query('$.my\\.key')
+
+ http-response set-header x-var_header %[var(sess.header_json)]
+ http-response set-header x-var_body %[var(sess.pay_json)]
+ http-response set-header x-var_body_int %[var(sess.pay_int)]
+ http-response set-header x-var_body_neg_int %[var(sess.pay_neg_int)]
+ http-response set-header x-var_body_double %[var(sess.pay_double)]
+ http-response set-header x-var_body_boolean_true %[var(sess.pay_boolean_true)]
+ http-response set-header x-var_body_boolean_false %[var(sess.pay_boolean_false)]
+ http-response set-header x-var_body_mykey %[var(sess.pay_mykey)]
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "Authorization: {\"iss\":\"kubernetes.io/serviceaccount\"}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_header ~ "kubernetes.io/serviceaccount"
+
+ txreq -url "/" \
+ -body "{\"iss\":\"kubernetes.io/serviceaccount\"}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_body ~ "kubernetes.io/serviceaccount"
+
+ txreq -url "/" \
+ -body "{\"integer\":4}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_body_int ~ "5"
+
+ txreq -url "/" \
+ -body "{\"integer\":-4}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_body_int ~ "-3"
+
+ txreq -url "/" \
+ -body "{\"double\":4.5}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_body_double ~ "4.5"
+
+ txreq -url "/" \
+ -body "{\"boolean-true\":true}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_body_boolean_true == 1
+
+ txreq -url "/" \
+ -body "{\"boolean-false\":false}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_body_boolean_false == 0
+
+ txreq -url "/" \
+ -body "{\"my.key\":\"myvalue\"}"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var_body_mykey ~ "myvalue"
+} -run \ No newline at end of file
diff --git a/reg-tests/converter/mqtt.vtc b/reg-tests/converter/mqtt.vtc
new file mode 100644
index 0000000..fc3daca
--- /dev/null
+++ b/reg-tests/converter/mqtt.vtc
@@ -0,0 +1,238 @@
+varnishtest "mqtt converters Test"
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ # MQTT 3.1.1 CONNECT packet (id: test_subaaaaaa... [len = 200])
+ recv 215
+ sendhex "20020000"
+ close
+
+ # MQTT 3.1.1 CONNECT packet (id: <empty> - username: test - passwd: passwd)
+ accept
+ recv 28
+ sendhex "20020000"
+ close
+
+ # MQTT 3.1.1 CONNECT packet (id: test_sub - username: test - passwd: passwd - will_topic: willtopic - will_payload: willpayload)
+ accept
+ recv 60
+ sendhex "20020000"
+ close
+
+ # MQTT 5.0 CONNECT packet (id: test_sub)
+ accept
+ recv 26
+ sendhex "200600000322000a"
+
+ # MQTT 5.0 CONNECT packet (id: test_sub - username: test - passwd: passwd)
+ accept
+ recv 40
+ sendhex "200600000322000a"
+
+ # MQTT 5.0 complex CONNECT/CONNACK packet
+ accept
+ recv 128
+ sendhex "20250000221100000078217fff24012501270000ffff22000a2600016100016226000163000164"
+ close
+
+ # Invalid MQTT 3.1.1 CONNACK packet with invalid flags (!= 0x00)
+ accept
+ recv 22
+ sendhex "21020000"
+ expect_close
+
+ # MQTT 3.1 CONNECT packet (id: test_sub - username: test - passwd: passwd)
+ accept
+ recv 38
+ sendhex "20020000"
+} -start
+
+server s2 {
+ # MQTT 5.0 complex CONNECT packet
+ recv 128
+ sendhex "20250000221100000078217fff24012501270000ffff22000a2600016100016226000163000164"
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ tcp-request inspect-delay 1s
+ tcp-request content reject unless { req.payload(0,0),mqtt_is_valid }
+ default_backend be1
+
+ frontend fe2
+ bind "fd@${fe2}"
+ tcp-request inspect-delay 1s
+ tcp-request content reject unless { req.payload(0,0),mqtt_is_valid }
+ tcp-request content set-var(req.flags) req.payload(0,0),mqtt_field_value(connect,flags)
+ tcp-request content set-var(req.protoname) req.payload(0,0),mqtt_field_value(connect,protocol_name)
+ tcp-request content set-var(req.protovsn) req.payload(0,0),mqtt_field_value(connect,protocol_version)
+ tcp-request content set-var(req.clientid) req.payload(0,0),mqtt_field_value(connect,client_identifier)
+ tcp-request content set-var(req.willtopic) req.payload(0,0),mqtt_field_value(connect,will_topic)
+ tcp-request content set-var(req.willbody) req.payload(0,0),mqtt_field_value(connect,will_payload)
+ tcp-request content set-var(req.user) req.payload(0,0),mqtt_field_value(connect,username)
+ tcp-request content set-var(req.pass) req.payload(0,0),mqtt_field_value(connect,password)
+ tcp-request content set-var(req.maxpktsz) req.payload(0,0),mqtt_field_value(connect,39)
+ tcp-request content set-var(req.reqpbinfo) req.payload(0,0),mqtt_field_value(connect,23)
+ tcp-request content set-var(req.ctype) req.payload(0,0),mqtt_field_value(connect,3)
+ tcp-request content set-var(req.willrsptopic) req.payload(0,0),mqtt_field_value(connect,8)
+ tcp-request content reject if ! { var(req.protoname) -m str "MQTT" } || ! { var(req.protovsn) -m str "5" }
+ tcp-request content reject if ! { var(req.flags) -m str "238" } || ! { var(req.clientid) -m str "test_sub" }
+ tcp-request content reject if ! { var(req.user) -m str "test" } || ! { var(req.pass) -m str "passwd" }
+ tcp-request content reject if ! { var(req.willtopic) -m str "willtopic" } || ! { var(req.willbody) -m str "willpayload" }
+ tcp-request content reject if ! { var(req.maxpktsz) -m str "20" } || ! { var(req.reqpbinfo) -m str "1" }
+ tcp-request content reject if ! { var(req.ctype) -m str "text/plain" } || ! { var(req.willrsptopic) -m str "willrsptopic" }
+ default_backend be2
+
+ backend be1
+ server s1 ${s1_addr}:${s1_port}
+ tcp-response inspect-delay 1s
+ tcp-response content reject unless { res.payload(0,0),mqtt_is_valid }
+
+ backend be2
+ server s2 ${s2_addr}:${s2_port}
+ tcp-response inspect-delay 1s
+ tcp-response content reject unless { res.payload(0,0),mqtt_is_valid }
+ tcp-response content set-var(res.flags) res.payload(0,0),mqtt_field_value(connack,flags)
+ tcp-response content set-var(res.protovsn) res.payload(0,0),mqtt_field_value(connack,protocol_version)
+ tcp-response content set-var(res.rcode) res.payload(0,0),mqtt_field_value(connack,reason_code)
+ tcp-response content set-var(res.sessexpint) res.payload(0,0),mqtt_field_value(connack,17)
+ tcp-response content set-var(res.recvmax) res.payload(0,0),mqtt_field_value(connack,33)
+ tcp-response content set-var(res.maxqos) res.payload(0,0),mqtt_field_value(connack,36)
+ tcp-response content set-var(res.retainavail) res.payload(0,0),mqtt_field_value(connack,37)
+ tcp-response content set-var(res.maxpktsz) res.payload(0,0),mqtt_field_value(connack,39)
+ tcp-response content set-var(res.topicaliasmax) res.payload(0,0),mqtt_field_value(connack,34)
+ tcp-response content reject if ! { var(res.protovsn) -m str "5" } || ! { var(res.flags) -m str "0" }
+ tcp-response content reject if ! { var(res.rcode) -m str "0" } || ! { var(res.sessexpint) -m str "120" }
+ tcp-response content reject if ! { var(res.recvmax) -m str "32767" } || ! { var(res.maxqos) -m str "1" }
+ tcp-response content reject if ! { var(res.retainavail) -m str "1" } || ! { var(res.maxpktsz) -m str "65535" }
+ tcp-response content reject if ! { var(res.topicaliasmax) -m str "10" }
+} -start
+
+client c1_311_1 -connect ${h1_fe1_sock} {
+ # Valid MQTT 3.1.1 CONNECT packet (id: test_sub)
+ sendhex "10d40100044d5154540402003c00c8746573745f737562616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161"
+ recv 4
+ expect_close
+} -run
+
+client c1_311_2 -connect ${h1_fe1_sock} {
+ # Valid MQTT 3.1.1 CONNECT packet (id: <empty> - username: test - passwd: passwd)
+ sendhex "101a00044d51545404c2003c00000004746573740006706173737764"
+ recv 4
+ expect_close
+} -run
+
+client c1_311_3 -connect ${h1_fe1_sock} {
+ # Valid MQTT 3.1.1 CONNECT packet (id: test_sub - username: test - passwd: passwd - will_topic: willtopic - will_payload: willpayload)
+ sendhex "103a00044d51545404ee003c0008746573745f737562000977696c6c746f706963000b77696c6c7061796c6f61640004746573740006706173737764"
+ recv 4
+ expect_close
+} -run
+
+client c1_50_1 -connect ${h1_fe1_sock} {
+ # Valid MQTT 5.0 CONNECT packet (id: test_sub)
+ sendhex "101800044d5154540502003c032100140008746573745f737562"
+ recv 8
+ expect_close
+} -run
+
+client c1_50_2 -connect ${h1_fe1_sock} {
+ # Valid MQTT 5.0 CONNECT packet (id: test_sub - username: test - passwd: passwd)
+ sendhex "102600044d51545405c2003c032100140008746573745f7375620004746573740006706173737764"
+ recv 8
+ expect_close
+} -run
+
+client c1_50_3 -connect ${h1_fe1_sock} {
+ # Valid MQTT 5.0 complex CONNECT/CONNACK packet
+ sendhex "107e00044d51545405ee003c182700000014170126000161000162260001630001642100140008746573745f7375622a03000a746578742f706c61696e08000c77696c6c727370746f7069632600016500016626000167000168000977696c6c746f706963000b77696c6c7061796c6f61640004746573740006706173737764"
+ recv 39
+ expect_close
+} -run
+
+client c2_311_1 -connect ${h1_fe1_sock} {
+ # Invalid MQTT 3.1.1 PINREQ
+ sendhex "d000"
+ expect_close
+} -run
+
+client c2_311_2 -connect ${h1_fe1_sock} {
+ # Invalid MQTT 3.1.1 CONNECT packet with invalid flags (!= 0x00)
+ sendhex "111400044d5154540402003c0008746573745f737562"
+ expect_close
+} -run
+
+client c2_311_3 -connect ${h1_fe1_sock} {
+ # Invalid MQTT 3.1.1 CONNACK packet with invalid flags (!= 0x00)
+ sendhex "101400044d5154540402003c0008746573745f737562"
+ expect_close
+} -run
+
+client c2_311_4 -connect ${h1_fe1_sock} {
+ # Invalid MQTT 3.1.1 CONNECT with too long remaing_length ( > 4 bytes)
+ sendhex "10ffffffff1400044d5154540402003c0008746573745f737562"
+ expect_close
+} -run
+
+client c2_311_4 -connect ${h1_fe1_sock} {
+ # Invalid MQTT 3.1.1 CONNECT with not matching ( 0x13 != 0x14)
+ sendhex "101300044d5154540402003c000874657374a5f737562"
+ expect_close
+} -run
+
+client c2_311_4 -connect ${h1_fe1_sock} {
+ # Invalid MQTT 3.1.1 CONNECT with not matching ( 0x18 != 0x14)
+ sendhex "101800044d5154540402003c000874657374a5f737562ffffffff"
+ expect_close
+} -run
+
+
+client c2_50_1 -connect ${h1_fe2_sock} {
+ # complex MQTT 5.0 CONNECT/CONNACK packet
+ # - CONNECT :
+ # client-id : test_sub
+ # username : test
+ # password : passwd
+ # will-topic : willtopic
+ # will-payload: willpayload
+ # connect props:
+ # maximum-packet-size : 20
+ # request-problem-information: 1
+ # user-property : name=a value=b
+ # user-property : name=c value=d
+ # will props:
+ # content-type : text/plain
+ # response-topic: willrsptopic
+ # user-property : name=e value=f
+ # user-property : name=g value=h
+ # - CONNACK :
+ # flags : 0x00
+ # reason-code: 0x00
+ # connack props:
+ # session-Expiry-interval: 120
+ # receive-maximum : 32767
+ # maximum-qos : 1
+ # retain-available : 1
+ # maximum-packet-size : 65535
+ # topic-alias-maximum : 10
+ # user-property : name=a value=b
+ # user-property : name=c value=d
+ sendhex "107e00044d51545405ee003c182700000014170126000161000162260001630001642100140008746573745f7375622a03000a746578742f706c61696e08000c77696c6c727370746f7069632600016500016626000167000168000977696c6c746f706963000b77696c6c7061796c6f61640004746573740006706173737764"
+ recv 39
+ expect_close
+} -run
+
+client c3_31_1 -connect ${h1_fe1_sock} {
+ # Valid MQTT 3.1 CONNECT packet (id: test_sub - username: test - passwd: passwd)
+ sendhex "102400064d514973647003c200000008746573745f7375620004746573740006706173737764"
+ recv 4
+} -run \ No newline at end of file
diff --git a/reg-tests/converter/secure_memcmp.vtc b/reg-tests/converter/secure_memcmp.vtc
new file mode 100644
index 0000000..6ff74e6
--- /dev/null
+++ b/reg-tests/converter/secure_memcmp.vtc
@@ -0,0 +1,143 @@
+varnishtest "secure_memcmp converter Test"
+
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTION=OPENSSL
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 4 -start
+
+server s2 {
+ rxreq
+ txresp -hdr "Connection: close"
+} -repeat 7 -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
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe
+ # This frontend matches two base64 encoded values and does not need to
+ # handle null bytes.
+
+ bind "fd@${fe}"
+
+ #### requests
+ http-request set-var(txn.hash) req.hdr(hash)
+ http-request set-var(txn.raw) req.hdr(raw)
+
+ acl is_match var(txn.raw),sha1,base64,secure_memcmp(txn.hash)
+
+ http-response set-header Match true if is_match
+ http-response set-header Match false if !is_match
+
+ default_backend be
+
+ frontend fe2
+ # This frontend matches two binary values, needing to handle null
+ # bytes.
+ bind "fd@${fe2}"
+
+ #### requests
+ http-request set-var(txn.hash) req.hdr(hash),b64dec
+ http-request set-var(txn.raw) req.hdr(raw)
+
+ acl is_match var(txn.raw),sha1,secure_memcmp(txn.hash)
+
+ http-response set-header Match true if is_match
+ http-response set-header Match false if !is_match
+
+ default_backend be2
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+
+ backend be2
+ server s2 ${s2_addr}:${s2_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "Raw: 1" \
+ -hdr "Hash: NWoZK3kTsExUV00Ywo1G5jlUKKs="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "true"
+ txreq -url "/" \
+ -hdr "Raw: 2" \
+ -hdr "Hash: 2kuSN7rMzfGcB2DKt67EqDWQELA="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "true"
+ txreq -url "/" \
+ -hdr "Raw: 2" \
+ -hdr "Hash: 2kuSN7rMzfGcB2DKt67EqDWQELX="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "false"
+ txreq -url "/" \
+ -hdr "Raw: 3" \
+ -hdr "Hash: 2kuSN7rMzfGcB2DKt67EqDWQELA="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "false"
+} -run
+
+client c2 -connect ${h1_fe2_sock} {
+ txreq -url "/" \
+ -hdr "Raw: 1" \
+ -hdr "Hash: NWoZK3kTsExUV00Ywo1G5jlUKKs="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "true"
+ txreq -url "/" \
+ -hdr "Raw: 2" \
+ -hdr "Hash: 2kuSN7rMzfGcB2DKt67EqDWQELA="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "true"
+ txreq -url "/" \
+ -hdr "Raw: 2" \
+ -hdr "Hash: 2kuSN7rMzfGcB2DKt67EqDWQELX="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "false"
+ txreq -url "/" \
+ -hdr "Raw: 3" \
+ -hdr "Hash: 2kuSN7rMzfGcB2DKt67EqDWQELA="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "false"
+
+ # Test for values with leading nullbytes.
+ txreq -url "/" \
+ -hdr "Raw: 6132845" \
+ -hdr "Hash: AAAAVaeL9nNcSok1j6sd40EEw8s="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "true"
+ txreq -url "/" \
+ -hdr "Raw: 49177200" \
+ -hdr "Hash: AAAA9GLglTNv2JoMv2n/w9Xadhc="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "true"
+ txreq -url "/" \
+ -hdr "Raw: 6132845" \
+ -hdr "Hash: AAAA9GLglTNv2JoMv2n/w9Xadhc="
+ rxresp
+ expect resp.status == 200
+ expect resp.http.match == "false"
+} -run
diff --git a/reg-tests/converter/sha2.vtc b/reg-tests/converter/sha2.vtc
new file mode 100644
index 0000000..e90e274
--- /dev/null
+++ b/reg-tests/converter/sha2.vtc
@@ -0,0 +1,57 @@
+varnishtest "sha2 converter Test"
+
+#REQUIRE_VERSION=2.1
+#REQUIRE_OPTION=OPENSSL
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -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@${fe}"
+
+ #### requests
+ http-request set-var(txn.hash) req.hdr(hash)
+
+ http-response set-header SHA2 "%[var(txn.hash),sha2,hex,lower]"
+ http-response set-header SHA2-224 "%[var(txn.hash),sha2(224),hex,lower]"
+ http-response set-header SHA2-256 "%[var(txn.hash),sha2(256),hex,lower]"
+ http-response set-header SHA2-384 "%[var(txn.hash),sha2(384),hex,lower]"
+ http-response set-header SHA2-512 "%[var(txn.hash),sha2(512),hex,lower]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "Hash: 1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.sha2 == "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"
+ expect resp.http.sha2-224 == "e25388fde8290dc286a6164fa2d97e551b53498dcbf7bc378eb1f178"
+ expect resp.http.sha2-256 == "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"
+ expect resp.http.sha2-384 == "47f05d367b0c32e438fb63e6cf4a5f35c2aa2f90dc7543f8a41a0f95ce8a40a313ab5cf36134a2068c4c969cb50db776"
+ expect resp.http.sha2-512 == "4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a"
+ txreq -url "/" \
+ -hdr "Hash: 2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.sha2 == "d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35"
+ expect resp.http.sha2-224 == "58b2aaa0bfae7acc021b3260e941117b529b2e69de878fd7d45c61a9"
+ expect resp.http.sha2-256 == "d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35"
+ expect resp.http.sha2-384 == "d063457705d66d6f016e4cdd747db3af8d70ebfd36badd63de6c8ca4a9d8bfb5d874e7fbd750aa804dcaddae7eeef51e"
+ expect resp.http.sha2-512 == "40b244112641dd78dd4f93b6c9190dd46e0099194d5a44257b7efad6ef9ff4683da1eda0244448cb343aa688f5d3efd7314dafe580ac0bcbf115aeca9e8dc114"
+} -run
diff --git a/reg-tests/converter/url_dec.vtc b/reg-tests/converter/url_dec.vtc
new file mode 100644
index 0000000..d5e317b
--- /dev/null
+++ b/reg-tests/converter/url_dec.vtc
@@ -0,0 +1,37 @@
+varnishtest "url_dec converter Test"
+
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -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@${fe}"
+
+ http-request set-var(txn.url) url
+ http-response set-header url_dec0 "%[var(txn.url),url_dec]"
+ http-response set-header url_dec1 "%[var(txn.url),url_dec(1)]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/bla+%20?foo%3Dbar%2B42+42%20"
+ rxresp
+ expect resp.http.url_dec0 == "/bla+ ?foo=bar+42 42 "
+ expect resp.http.url_dec1 == "/bla ?foo=bar+42 42 "
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/converter/url_enc.vtc b/reg-tests/converter/url_enc.vtc
new file mode 100644
index 0000000..74acac8
--- /dev/null
+++ b/reg-tests/converter/url_enc.vtc
@@ -0,0 +1,43 @@
+varnishtest "url_enc converter test"
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -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@${fe}"
+
+ http-request set-var(txn.url0) "str(foo=bar+42 42 )"
+ http-request set-var(txn.url1) "var(txn.url0),url_enc"
+ http-request set-var(txn.url2) "var(txn.url1),url_dec"
+ http-request set-var(txn.url3) "var(txn.url2),url_enc(query)"
+ http-response set-header url_enc0 "%[var(txn.url1)]"
+ http-response set-header url_dec "%[var(txn.url2)]"
+ http-response set-header url_enc1 "%[var(txn.url3)]"
+
+ default_backend be
+
+ backend be
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.http.url_enc0 == "foo%3Dbar%2B42%2042%20"
+ expect resp.http.url_dec == "foo=bar+42 42 "
+ expect resp.http.url_enc1 == "foo%3Dbar%2B42%2042%20"
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/filters/random-forwarding.vtc b/reg-tests/filters/random-forwarding.vtc
new file mode 100644
index 0000000..87e29a6
--- /dev/null
+++ b/reg-tests/filters/random-forwarding.vtc
@@ -0,0 +1,139 @@
+varnishtest "Filtering test with several filters and random forwarding (via trace filter)"
+
+#REQUIRE_VERSION=2.4
+#REQUIRE_OPTION=ZLIB|SLZ
+#REGTEST_TYPE=slow
+
+feature ignore_unknown_macro
+
+barrier b1 cond 2 -cyclic
+
+server s1 {
+ rxreq
+ expect req.url == "/"
+ expect req.bodylen == 1048576
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 1048576
+
+ rxreq
+ expect req.url == "127.0.0.1:80"
+ txresp \
+ -hdr "Content-Length: 0"
+ recv 36000
+ send_n 1000 "0123456789abcdefghijklmnopqrstuvwxyz"
+ barrier b1 sync
+
+ accept
+ rxreq
+ expect req.url == "/"
+ txresp -nolen \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 20480
+ close
+
+ accept
+ rxreq
+ expect req.url == "/"
+ txresp -nolen
+ close
+
+ accept
+ rxreq
+ expect req.url == "/"
+ expect req.bodylen == 20480
+ txresp -nolen \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 20480
+
+} -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}"
+
+ compression offload
+ compression algo gzip
+
+ filter trace name "BEFORE" random-forwarding quiet
+ filter compression
+ filter trace name "AFTER" random-forwarding quiet
+ default_backend be1
+
+ backend be1
+ server www ${s1_addr}:${s1_port}
+
+ listen li1
+ mode tcp
+ bind "fd@${li1}"
+ # Validate nothing is blocked in TCP mode
+ filter compression
+ server www ${s1_addr}:${s1_port}
+
+
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -url "/" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 1048576
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.transfer-encoding == "chunked"
+ gunzip
+ expect resp.bodylen == 1048576
+
+ txreq -method "CONNECT" -url "127.0.0.1:80" -nolen
+ rxresp
+ expect resp.status == 200
+ send_n 1000 "0123456789abcdefghijklmnopqrstuvwxyz"
+ recv 36000
+ barrier b1 sync
+} -run
+
+client c2 -connect ${h1_fe1_sock} {
+ txreq -url "/" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Content-Type: text/plain"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.content-length == "<undef>"
+ expect resp.bodylen == 20480
+} -run
+
+client c3 -connect ${h1_fe1_sock} {
+ txreq -url "/" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Content-Type: text/plain"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.content-length == "<undef>"
+ expect resp.bodylen == 0
+} -run
+
+client c4 -connect ${h1_li1_sock} {
+ txreq -url "/" \
+ -hdr "Accept-Encoding: gzip" \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 20480
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "<undef>"
+ expect resp.http.transfer-encoding == "<undef>"
+ expect resp.http.content-length == "<undef>"
+ expect resp.bodylen == 20480
+ expect_close
+} -run
diff --git a/reg-tests/http-capture/multiple_headers.vtc b/reg-tests/http-capture/multiple_headers.vtc
new file mode 100644
index 0000000..1ae210b
--- /dev/null
+++ b/reg-tests/http-capture/multiple_headers.vtc
@@ -0,0 +1,91 @@
+varnishtest "Tests for 'capture (request|response) header"
+feature ignore_unknown_macro
+
+# This script checks that the last occurrences of "fooresp" and "fooreq" header
+# are correctly captured and added to the logs.
+# Note that varnishtest does not support more than MAX_HDR header.
+
+syslog S -level info {
+ recv
+ expect ~ "[^:\\[ ]\\[${h_pid}\\]: .* .* fe be/srv .* 200 1[0-9]{4} - - ---- .* .* {HPhx8n59qjjNBLjP} {htb56qDdCcbRVTfS} \"GET / HTTP/1\\.1\""
+} -start
+
+server s {
+ rxreq
+ txresp -hdr "fooresp: HnFDGJ6KvhSG5QjX" -hdr "fooresp: 8dp7vBMQjTMkVwtG" \
+ -hdr "fooresp: NTpxWmvsNKGxvH6K" -hdr "fooresp: sPKNNJ5VRBDz9qXP" \
+ -hdr "fooresp: HnFDGJ6KvhSG5QjX" -hdr "fooresp: 8dp7vBMQjTMkVwtG" \
+ -hdr "fooresp: VSNnccbGkvfM9JK9" -hdr "fooresp: 9D5cjwtK3LCxmg4F" \
+ -hdr "fooresp: dsbxGqlBPRWGP3vX" -hdr "fooresp: xf6VK6GXlgdj5mwc" \
+ -hdr "fooresp: 8jzM3clRKtdL2WWb" -hdr "fooresp: v7ZHrTPjDR6lm6Bg" \
+ -hdr "fooresp: FQT6th9whMqQ7Z6C" -hdr "fooresp: KM22HH6lRBw6SHQT" \
+ -hdr "fooresp: PmRHphHXmTV9kZNS" -hdr "fooresp: CkGRbTJrD5nSVpFk" \
+ -hdr "fooresp: KQ9mzmMHpmmZ2SXP" -hdr "fooresp: W5FqfFDN6dqBxjK7" \
+ -hdr "fooresp: bvcxNPK4gpnTvn3z" -hdr "fooresp: BSXRLSWMsgQN54cC" \
+ -hdr "fooresp: ZX9ttTnlbXtJK55d" -hdr "fooresp: KH9StjMHF73NqzL8" \
+ -hdr "fooresp: W2q2m6MvMLcnXsX7" -hdr "fooresp: wtrjnJgFzHDvMg5r" \
+ -hdr "fooresp: Vpk2c2DsbWf2Gtwh" -hdr "fooresp: sCcW2qpRhFHHRDpH" \
+ -hdr "fooresp: P4mltXtvxLsnPcNS" -hdr "fooresp: TXgdSKNMmsJ8x9zq" \
+ -hdr "fooresp: n5t8pdZgnGFXZDd3" -hdr "fooresp: pD84GCtkWZqWbCM9" \
+ -hdr "fooresp: wx2FPxsGqSRjNVws" -hdr "fooresp: TXmtBCqPTVGFc3NK" \
+ -hdr "fooresp: 4DrFTLxpcPk2n3Zv" -hdr "fooresp: vrcFr9MWpqJWhK4h" \
+ -hdr "fooresp: HMsCHMZnHT3q8qD2" -hdr "fooresp: HsCXQGTxDpsMf4z6" \
+ -hdr "fooresp: 9rb2vjvvd2SzCQVT" -hdr "fooresp: qn5C2fZTWHVp7NkC" \
+ -hdr "fooresp: ZVd5ltcngZFHXfvr" -hdr "fooresp: j6BZVdV8fkz5tgjR" \
+ -hdr "fooresp: 6qfVwfHqdfntQjmP" -hdr "fooresp: RRr9nTnwjG6d2x7X" \
+ -hdr "fooresp: RJXtWtdJRTss6JgZ" -hdr "fooresp: zzHZWm6bqXDN9k47" \
+ -hdr "fooresp: htb56qDdCcbRVTfS" \
+ -bodylen 16384
+} -start
+
+haproxy h -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be
+ server srv ${s_addr}:${s_port}
+
+ frontend fe
+ option httplog
+ log ${S_addr}:${S_port} local0 debug err
+ capture request header fooreq len 25
+ capture response header fooresp len 25
+
+ bind "fd@${fe}"
+ use_backend be
+} -start
+
+client c1 -connect ${h_fe_sock} {
+ txreq -hdr "fooreq: c8Ck8sx8qfXk5pSS" -hdr "fooreq: TGNXbG2DF3TmLWK3" \
+ -hdr "fooreq: mBxq9Cgr8GN6hkt6" -hdr "fooreq: MHZ6VBCPgs564KfR" \
+ -hdr "fooreq: BCCwX2kL9BSMCqvt" -hdr "fooreq: 8rXw87xVTphpRQb7" \
+ -hdr "fooreq: gJ3Tp9kXQlqLC8Qp" -hdr "fooreq: dFnLs6wpMl2M5N7c" \
+ -hdr "fooreq: r3f9WgQ8Brqw37Kj" -hdr "fooreq: dbJzSSdCqV3ZVtXK" \
+ -hdr "fooreq: 5HxHd6g4n2Rj2CNG" -hdr "fooreq: HNqQSNfkt6q4zK26" \
+ -hdr "fooreq: rzqNcfskPR7vW4jG" -hdr "fooreq: 9c7txWhsdrwmkR6d" \
+ -hdr "fooreq: 3v8Nztg9l9vLSKJm" -hdr "fooreq: lh4WDxMX577h4z3l" \
+ -hdr "fooreq: mFtHj5SKDvfcGzfq" -hdr "fooreq: PZ5B5wRM9D7GLm7W" \
+ -hdr "fooreq: fFpN4zCkLTxzp5Dz" -hdr "fooreq: J5XMdfCCHmmwkr2f" \
+ -hdr "fooreq: KqssZ3SkZnZJF8mz" -hdr "fooreq: HrGgsnBnslKN7Msz" \
+ -hdr "fooreq: d8TQltZ39xFZBNx2" -hdr "fooreq: mwDt2k2tvqM8x5kQ" \
+ -hdr "fooreq: 7Qh6tM7s7z3P8XCl" -hdr "fooreq: S3mTVbbPhJbLR7n2" \
+ -hdr "fooreq: zr7hMDvrrwfvpmTT" -hdr "fooreq: lV9TnZX2CtSnr4k8" \
+ -hdr "fooreq: bMdJx8pVDG2nWFNg" -hdr "fooreq: FkGvB2cBwNrB3cm4" \
+ -hdr "fooreq: 5ckNn3m6m8r2CXLF" -hdr "fooreq: sk4pJGTSZ5HMPJP5" \
+ -hdr "fooreq: HgVgQ73zhLwX6Wzq" -hdr "fooreq: T5k2QbFKvCVJlz4c" \
+ -hdr "fooreq: SKcNPw8CXGKhtxNP" -hdr "fooreq: n9fFrcR2kRQJrCpZ" \
+ -hdr "fooreq: hrJ2MXCdcSCDhQ6n" -hdr "fooreq: 9xsWQ8srzLDvG9F4" \
+ -hdr "fooreq: GNcP9NBTFJkg4hbk" -hdr "fooreq: Vg8B8MNwz4T7q5Tj" \
+ -hdr "fooreq: XXns3qPCzZmt9j4G" -hdr "fooreq: hD7TnP43bcPHm5g2" \
+ -hdr "fooreq: wZbxVq7MwmfBSqb5" -hdr "fooreq: HPhx8n59qjjNBLjP" \
+ -bodylen 16384
+ rxresp
+ expect resp.status == 200
+} -start
+
+server s -wait
+syslog S -wait
+
diff --git a/reg-tests/http-cookies/cookie_insert_indirect.vtc b/reg-tests/http-cookies/cookie_insert_indirect.vtc
new file mode 100644
index 0000000..6b86360
--- /dev/null
+++ b/reg-tests/http-cookies/cookie_insert_indirect.vtc
@@ -0,0 +1,54 @@
+varnishtest "HTTP cookie basic test"
+feature ignore_unknown_macro
+
+# This script tests "cookie <name> insert indirect" directive.
+# The client sends a wrong "SRVID=s2" cookie.
+# haproxy removes it.
+# The server replies with "SRVID=S1" after having checked that
+# no cookies were sent by haproxy.
+# haproxy replies "SRVID=server-one" to the client.
+# We log the HTTP request to a syslog server and check their "--II"
+# (invalid, insert) flags.
+
+syslog S1 -level notice {
+ recv info
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* fe1 be1/srv1 .* --II .* \"GET / HTTP/1\\.1\""
+} -start
+
+server s1 {
+ rxreq
+ expect req.http.cookie == <undef>
+ txresp -hdr "Cookie: SRVID=S1"
+} -start
+
+haproxy h1 -conf {
+ global
+ log ${S1_addr}:${S1_port} len 2048 local0 debug err
+
+ defaults
+ mode http
+ option httplog
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ log global
+
+ backend be1
+ cookie SRVID insert indirect
+ server srv1 ${s1_addr}:${s1_port} cookie server-one
+
+ frontend fe1
+ option httplog
+ bind "fd@${fe1}"
+ use_backend be1
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -hdr "Cookie: SRVID=s2"
+ rxresp
+ expect resp.http.Set-Cookie ~ "^SRVID=server-one;.*"
+} -start
+
+
+client c1 -wait
+syslog S1 -wait
diff --git a/reg-tests/http-cookies/h2_cookie_concat.vtc b/reg-tests/http-cookies/h2_cookie_concat.vtc
new file mode 100644
index 0000000..e2e6d81
--- /dev/null
+++ b/reg-tests/http-cookies/h2_cookie_concat.vtc
@@ -0,0 +1,42 @@
+varnishtest "HTTP/2 cookie concatenation"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.http.cookie == "c1=foo; c2=bar; c3=baz"
+ txresp
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ frontend fe1
+ bind "fd@${fe1}" proto h2
+ use_backend be1
+
+ backend be1
+ server srv1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "GET" \
+ -scheme "http" \
+ -url "/" \
+ -hdr "cookie" "c1=foo" \
+ -hdr "cookie" "c2=bar" \
+ -hdr "cookie" "c3=baz"
+ rxhdrs
+ } -run
+} -run
diff --git a/reg-tests/http-errorfiles/errorfiles.vtc b/reg-tests/http-errorfiles/errorfiles.vtc
new file mode 100644
index 0000000..1ace744
--- /dev/null
+++ b/reg-tests/http-errorfiles/errorfiles.vtc
@@ -0,0 +1,51 @@
+varnishtest "Test the errofile directive in proxy sections"
+
+# This config tests the errorfile directive in proxy sections (including the
+# defaults section).
+
+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}"
+ errorfile 400 ${testdir}/errors/400.http
+ errorfile 403 ${testdir}/errors/403.http
+ errorfile 408 /dev/null
+
+ frontend fe1
+ bind "fd@${fe1}"
+
+ errorfile 403 ${testdir}/errors/403-1.http
+ errorfile 500 /dev/null
+
+ http-request deny deny_status 400 if { path /400 }
+ http-request deny deny_status 403 if { path /403 }
+ http-request deny deny_status 408 if { path /408 }
+ http-request deny deny_status 500 if { path /500 }
+
+} -start
+
+client c1r1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /400
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == "default"
+} -run
+client c1r2 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /403
+ rxresp
+ expect resp.status == 403
+ expect resp.http.x-err-type == "errors-1"
+} -run
+client c1r3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /408
+ expect_close
+} -run
+client c1r4 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /500
+ expect_close
+} -run
diff --git a/reg-tests/http-errorfiles/errors/400-1.http b/reg-tests/http-errorfiles/errors/400-1.http
new file mode 100644
index 0000000..86a2e69
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400-1.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-1
+
+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/400-2.http b/reg-tests/http-errorfiles/errors/400-2.http
new file mode 100644
index 0000000..c108510
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400-2.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-2
+
+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/400-3.http b/reg-tests/http-errorfiles/errors/400-3.http
new file mode 100644
index 0000000..1fe1841
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400-3.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-3
+
+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/400.http b/reg-tests/http-errorfiles/errors/400.http
new file mode 100644
index 0000000..ce229aa
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: default
+
+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/403-1.http b/reg-tests/http-errorfiles/errors/403-1.http
new file mode 100644
index 0000000..08bdf02
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/403-1.http
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-1
+
+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/403-2.http b/reg-tests/http-errorfiles/errors/403-2.http
new file mode 100644
index 0000000..9c07e5d
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/403-2.http
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-2
+
+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/403.http b/reg-tests/http-errorfiles/errors/403.http
new file mode 100644
index 0000000..fd969b2
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/403.http
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: default
+
+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404-1.http b/reg-tests/http-errorfiles/errors/404-1.http
new file mode 100644
index 0000000..154ed0b
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404-1.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-1
+
+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404-2.http b/reg-tests/http-errorfiles/errors/404-2.http
new file mode 100644
index 0000000..e26f91d
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404-2.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-2
+
+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404-3.http b/reg-tests/http-errorfiles/errors/404-3.http
new file mode 100644
index 0000000..4bc1661
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404-3.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-3
+
+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404.http b/reg-tests/http-errorfiles/errors/404.http
new file mode 100644
index 0000000..8dacd95
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: default
+
+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/500-1.http b/reg-tests/http-errorfiles/errors/500-1.http
new file mode 100644
index 0000000..4e4f7e4
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/500-1.http
@@ -0,0 +1,9 @@
+HTTP/1.0 500 Internal Server Error
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: errors-1
+
+<html><body><h1>500 Internal Server Error</h1>
+An internal server error occurred.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/500.http b/reg-tests/http-errorfiles/errors/500.http
new file mode 100644
index 0000000..68a31ff
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/500.http
@@ -0,0 +1,9 @@
+HTTP/1.0 500 Internal Server Error
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+x-err-type: default
+
+<html><body><h1>500 Internal Server Error</h1>
+An internal server error occurred.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/lf-403.txt b/reg-tests/http-errorfiles/errors/lf-403.txt
new file mode 100644
index 0000000..3a3c3aa
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/lf-403.txt
@@ -0,0 +1 @@
+The path "%[path]" is forbidden
diff --git a/reg-tests/http-errorfiles/http-error.vtc b/reg-tests/http-errorfiles/http-error.vtc
new file mode 100644
index 0000000..1af909b
--- /dev/null
+++ b/reg-tests/http-errorfiles/http-error.vtc
@@ -0,0 +1,75 @@
+varnishtest "Test the http-error directive"
+#REQUIRE_VERSION=2.2
+
+# This config tests the http-error directive.
+
+feature ignore_unknown_macro
+
+
+haproxy h1 -conf {
+ http-errors errors-1
+ errorfile 400 ${testdir}/errors/400-1.http
+ errorfile 403 ${testdir}/errors/403-1.http
+ errorfile 404 ${testdir}/errors/404-1.http
+ errorfile 500 ${testdir}/errors/500-1.http
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ errorfile 400 ${testdir}/errors/400.http
+ errorfile 404 ${testdir}/errors/404.http
+
+ frontend fe1
+ bind "fd@${fe1}"
+
+ http-error status 400
+ http-error status 403 default-errorfiles
+ http-error status 404 errorfiles errors-1
+ http-error status 500 errorfile ${testdir}/errors/500.http
+ http-error status 200 content-type "text/plain" hdr x-path "path=%[path]" lf-string "The path is \"%[path]\""
+
+ http-request return status 200 default-errorfiles if { path /200 }
+ http-request deny deny_status 400 if { path /400 }
+ http-request deny deny_status 403 if { path /403 }
+ http-request deny deny_status 404 if { path /404 }
+ http-request deny deny_status 500 if { path /500 }
+
+} -start
+
+client c1r1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /200
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-path == "path=/200"
+ expect resp.http.content-type == "text/plain"
+ expect resp.body == "The path is \"/200\""
+} -run
+client c1r2 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /400
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == <undef>
+ expect resp.http.content-length == 0
+} -run
+client c1r3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /403
+ rxresp
+ expect resp.status == 403
+ expect resp.http.x-err-type == <undef>
+ expect resp.http.content-length == 93
+ expect resp.http.content-type == "text/html"
+} -run
+client c1r3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /404
+ rxresp
+ expect resp.status == 404
+ expect resp.http.x-err-type == "errors-1"
+} -run
+client c1r4 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /500
+ rxresp
+ expect resp.status == 500
+ expect resp.http.x-err-type == "default"
+} -run
diff --git a/reg-tests/http-errorfiles/http_deny_errors.vtc b/reg-tests/http-errorfiles/http_deny_errors.vtc
new file mode 100644
index 0000000..353045d
--- /dev/null
+++ b/reg-tests/http-errorfiles/http_deny_errors.vtc
@@ -0,0 +1,77 @@
+varnishtest "Test the custom errors for HTTP deny rules"
+#REQUIRE_VERSION=2.2
+
+# This config tests the custom errors for HTTP deny rules.
+
+feature ignore_unknown_macro
+
+
+haproxy h1 -conf {
+ http-errors errors-1
+ errorfile 400 ${testdir}/errors/400-1.http
+ errorfile 403 ${testdir}/errors/403-1.http
+ errorfile 404 ${testdir}/errors/404-1.http
+ errorfile 500 /dev/null
+
+ 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 deny deny_status 400 if { path /400 }
+ http-request deny deny_status 403 errorfile ${testdir}/errors/403.http if { path /403 }
+ http-request deny deny_status 404 errorfiles errors-1 if { path /404 }
+ http-request deny deny_status 500 errorfile /dev/null if { path /500-1 }
+ http-request deny deny_status 500 errorfiles errors-1 if { path /500-2 }
+
+ http-request deny status 500 hdr x-err-info "path=%[path]" content-type "text/plain" string "Internal Error" if { path /int-err }
+ http-request deny status 403 hdr x-err-info "path=%[path]" content-type "text/plain" lf-file ${testdir}/errors/lf-403.txt if { path /forbidden }
+
+} -start
+
+client c1r1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /400
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == <undef>
+} -run
+client c1r2 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /403
+ rxresp
+ expect resp.status == 403
+ expect resp.http.x-err-type == "default"
+} -run
+client c1r3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /404
+ rxresp
+ expect resp.status == 404
+ expect resp.http.x-err-type == "errors-1"
+} -run
+client c1r4 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /500-1
+ expect_close
+} -run
+client c1r5 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /500-2
+ expect_close
+} -run
+client c1r6 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /int-err
+ rxresp
+ expect resp.status == 500
+ expect resp.http.x-err-info == "path=/int-err"
+ expect resp.http.content-type == "text/plain"
+ expect resp.http.content-length == 14
+ expect resp.body == "Internal Error"
+} -run
+client c1r7 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /forbidden
+ rxresp
+ expect resp.status == 403
+ expect resp.http.x-err-info == "path=/forbidden"
+ expect resp.http.content-type == "text/plain"
+ expect resp.body == "The path \"/forbidden\" is forbidden\n"
+} -run
diff --git a/reg-tests/http-errorfiles/http_errors.vtc b/reg-tests/http-errorfiles/http_errors.vtc
new file mode 100644
index 0000000..6b20be7
--- /dev/null
+++ b/reg-tests/http-errorfiles/http_errors.vtc
@@ -0,0 +1,134 @@
+varnishtest "Test the errorfiles directive"
+#REQUIRE_VERSION=2.2
+
+# This config tests the errorfiles directive.
+
+feature ignore_unknown_macro
+
+
+haproxy h1 -conf {
+ http-errors errors-1
+ errorfile 400 ${testdir}/errors/400-1.http
+ errorfile 403 ${testdir}/errors/403-1.http
+ errorfile 404 ${testdir}/errors/404-1.http
+ errorfile 500 ${testdir}/errors/500-1.http
+
+ http-errors errors-2
+ errorfile 400 ${testdir}/errors/400-2.http
+ errorfile 403 ${testdir}/errors/403-2.http
+ errorfile 404 ${testdir}/errors/404-2.http
+
+ http-errors errors-3
+ errorfile 400 ${testdir}/errors/400-3.http
+ errorfile 404 ${testdir}/errors/404-3.http
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ errorfiles errors-2
+ errorfile 400 ${testdir}/errors/400.http
+ errorfile 404 ${testdir}/errors/404.http
+
+ frontend fe1
+ bind "fd@${fe1}"
+ http-request deny deny_status 400 if { path /400 }
+ http-request deny if { path /403 }
+ http-request deny deny_status 404 if { path /404 }
+ http-request deny deny_status 500 if { path /500 }
+
+ frontend fe2
+ bind "fd@${fe2}"
+ errorfiles errors-1
+ errorfile 500 ${testdir}/errors/500.http
+ http-request deny deny_status 400 if { path /400 }
+ http-request deny if { path /403 }
+ http-request deny deny_status 404 if { path /404 }
+ http-request deny deny_status 500 if { path /500 }
+
+ frontend fe3
+ bind "fd@${fe3}"
+ errorfile 500 ${testdir}/errors/500.http
+ errorfiles errors-1 500
+ errorfiles errors-3 400
+ http-request deny deny_status 400 if { path /400 }
+ http-request deny if { path /403 }
+ http-request deny deny_status 404 if { path /404 }
+ http-request deny deny_status 500 if { path /500 }
+} -start
+
+client c1r1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /400
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == "default"
+} -run
+client c1r2 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /403
+ rxresp
+ expect resp.status == 403
+ expect resp.http.x-err-type == "errors-2"
+} -run
+client c1r3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /404
+ rxresp
+ expect resp.status == 404
+ expect resp.http.x-err-type == "default"
+} -run
+client c1r4 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /500
+ rxresp
+ expect resp.status == 500
+ expect resp.http.x-err-type == <undef>
+} -run
+
+client c2r1 -connect ${h1_fe2_sock} {
+ txreq -req GET -url /400
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == "errors-1"
+} -run
+client c2r2 -connect ${h1_fe2_sock} {
+ txreq -req GET -url /403
+ rxresp
+ expect resp.status == 403
+ expect resp.http.x-err-type == "errors-1"
+} -run
+client c2r3 -connect ${h1_fe2_sock} {
+ txreq -req GET -url /404
+ rxresp
+ expect resp.status == 404
+ expect resp.http.x-err-type == "errors-1"
+} -run
+client c2r4 -connect ${h1_fe2_sock} {
+ txreq -req GET -url /500
+ rxresp
+ expect resp.status == 500
+ expect resp.http.x-err-type == "default"
+} -run
+
+client c3r1 -connect ${h1_fe3_sock} {
+ txreq -req GET -url /400
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == "errors-3"
+} -run
+client c3r2 -connect ${h1_fe3_sock} {
+ txreq -req GET -url /403
+ rxresp
+ expect resp.status == 403
+ expect resp.http.x-err-type == "errors-2"
+} -run
+client c3r3 -connect ${h1_fe3_sock} {
+ txreq -req GET -url /404
+ rxresp
+ expect resp.status == 404
+ expect resp.http.x-err-type == "default"
+} -run
+client c3r4 -connect ${h1_fe3_sock} {
+ txreq -req GET -url /500
+ rxresp
+ expect resp.status == 500
+ expect resp.http.x-err-type == "errors-1"
+} -run
diff --git a/reg-tests/http-errorfiles/http_return.vtc b/reg-tests/http-errorfiles/http_return.vtc
new file mode 100644
index 0000000..8db77a9
--- /dev/null
+++ b/reg-tests/http-errorfiles/http_return.vtc
@@ -0,0 +1,46 @@
+varnishtest "Test the HTTP return action with errorfiles"
+#REQUIRE_VERSION=2.2
+
+# This config tests the HTTP return action when error files are used to reply to
+# the client.
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ http-errors errors-2
+ errorfile 400 ${testdir}/errors/400-2.http
+
+ 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}"
+ errorfile 400 ${testdir}/errors/400.http
+ http-request return status 400 default-errorfiles if { path /def }
+ http-request return status 400 errorfile ${testdir}/errors/400-1.http if { path /400-1 }
+ http-request return status 400 errorfiles errors-2 if { path /400-2 }
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /def
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == "default"
+} -run
+
+client c1r2 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /400-1
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == "errors-1"
+} -run
+
+client c1r3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /400-2
+ rxresp
+ expect resp.status == 400
+ expect resp.http.x-err-type == "errors-2"
+} -run
diff --git a/reg-tests/http-messaging/common.pem b/reg-tests/http-messaging/common.pem
new file mode 120000
index 0000000..a4433d5
--- /dev/null
+++ b/reg-tests/http-messaging/common.pem
@@ -0,0 +1 @@
+../ssl/common.pem \ No newline at end of file
diff --git a/reg-tests/http-messaging/h1_host_normalization.vtc b/reg-tests/http-messaging/h1_host_normalization.vtc
new file mode 100644
index 0000000..5855034
--- /dev/null
+++ b/reg-tests/http-messaging/h1_host_normalization.vtc
@@ -0,0 +1,603 @@
+varnishtest "H1 authority validation and host normalizarion based on the scheme (rfc3982 6.3.2) or the method (connect)"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.6-dev0)'"
+feature ignore_unknown_macro
+
+syslog S1 -level info {
+ # C1
+ recv
+ expect ~ "^.* uri: GET http://toto:poue@hostname/c1 HTTP/1.1; host: {hostname}$"
+
+ # C2
+ recv
+ expect ~ "^.* uri: GET http://hostname:8080/c2 HTTP/1.1; host: {hostname:8080}$"
+
+ # C3
+ recv
+ expect ~ "^.* uri: GET https://hostname/c3 HTTP/1.1; host: {hostname}$"
+
+ # C4
+ recv
+ expect ~ "^.* uri: GET https://hostname:80/c4 HTTP/1.1; host: {hostname:80}$"
+
+ # C5
+ recv
+ expect ~ "^.* uri: CONNECT hostname:80 HTTP/1.1; host: {hostname}$"
+ recv
+ expect ~ "^.* uri: CONNECT hostname:80 HTTP/1.1; host: {hostname}$"
+ recv
+ expect ~ "^.* uri: CONNECT hostname:80 HTTP/1.1; host: {hostname:}$"
+
+ # C6
+ recv
+ expect ~ "^.* uri: CONNECT hostname:443 HTTP/1.1; host: {hostname}$"
+ recv
+ expect ~ "^.* uri: CONNECT hostname:443 HTTP/1.1; host: {hostname}$"
+ recv
+ expect ~ "^.* uri: CONNECT hostname:443 HTTP/1.1; host: {hostname:}$"
+
+ # C7
+ recv
+ expect ~ "^.* uri: CONNECT hostname:8443 HTTP/1.1; host: {hostname:8443}$"
+
+ # C8
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C9
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C10
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C11
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C12
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C13
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C14
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C15
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C16
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C17
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C18
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C19
+ recv
+ expect ~ "^.* uri: <BADREQ>; host: $"
+
+ # C20
+ recv
+ expect ~ "^.* uri: GET http://hostname/c20 HTTP/1.1; host: {hostname}$"
+
+ # C21
+ recv
+ expect ~ "^.* uri: GET https://hostname/c21 HTTP/1.1; host: {hostname}$"
+
+ # C22
+ recv
+ expect ~ "^.* uri: GET http://hostname/c22 HTTP/1.1; host: {hostname:80}$"
+
+ # C23
+ recv
+ expect ~ "^.* uri: GET https://hostname/c23 HTTP/1.1; host: {hostname:443}$"
+
+ # C24
+ recv
+ expect ~ "^.* uri: GET http://hostname/c24 HTTP/1.1; host: {hostname}$"
+
+ # C25
+ recv
+ expect ~ "^.* uri: GET https://hostname/c25 HTTP/1.1; host: {hostname}$"
+
+ # C26
+ recv
+ expect ~ "^.* uri: GET http://hostname/c26 HTTP/1.1; host: {hostname:}$"
+
+ # C27
+ recv
+ expect ~ "^.* uri: GET https://hostname/c27 HTTP/1.1; host: {hostname:}$"
+
+ # C28
+ recv
+ expect ~ "^.* uri: GET http://hostname/c28 HTTP/1.1; host: {hostname}$"
+
+ # C29
+ recv
+ expect ~ "^.* uri: GET http://hostname/c29 HTTP/1.1; host: {hostname}$"
+
+ # C30
+ recv
+ expect ~ "^.* uri: GET https://hostname/c30 HTTP/1.1; host: {hostname}$"
+
+ # C31
+ recv
+ expect ~ "^.* uri: GET https://hostname/c31 HTTP/1.1; host: {hostname}$"
+
+ # C32
+ recv
+ expect ~ "^.* uri: GET http:// HTTP/1.1; host: {}$"
+
+ # C33
+ recv
+ expect ~ "^.* uri: GET https:// HTTP/1.1; host: {}$"
+
+ # C34
+ recv
+ expect ~ "^.* uri: GET http:// HTTP/1.1; host: {}$"
+
+ # C35
+ recv
+ expect ~ "^.* uri: GET https:// HTTP/1.1; host: {}$"
+} -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 capture req.hdr(host) len 512
+ log-format "uri: %r; host: %hr"
+ log ${S1_addr}:${S1_port} len 2048 local0 debug err
+
+ http-request return status 200
+} -start
+
+# default port 80 with http scheme => should be normalized
+# Be sure userinfo are skipped
+client c1 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://toto:poue@hostname:80/c1" \
+ -hdr "host: hostname:80"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# port 8080 with http scheme => no normalization
+client c2 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname:8080/c2" \
+ -hdr "host: hostname:8080"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# default port 443 with https scheme => should be normalized
+client c3 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname:443/c3" \
+ -hdr "host: hostname:443"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# port 80 with https scheme => no normalization
+client c4 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname:80/c4" \
+ -hdr "host: hostname:80"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# CONNECT on port 80 => should be normalized
+client c5 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:80" \
+ -hdr "host: hostname:80"
+
+ rxresp
+ expect resp.status == 200
+} -run
+client c5 -connect ${h1_fe_sock} {
+
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:80" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 200
+} -run
+client c5 -connect ${h1_fe_sock} {
+
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:80" \
+ -hdr "host: hostname:"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# CONNECT on port 443 => should be normalized
+client c6 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:443" \
+ -hdr "host: hostname:443"
+
+ rxresp
+ expect resp.status == 200
+} -run
+client c6 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:443" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 200
+} -run
+client c6 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:443" \
+ -hdr "host: hostname:"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# CONNECT on port non-default port => no normalization
+client c7 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:8443" \
+ -hdr "host: hostname:8443"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# host miss-match => error
+client c8 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname1/" \
+ -hdr "host: hostname2"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# port miss-match => error
+client c9 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname:80/" \
+ -hdr "host: hostname:81"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# no host port with a non-default port in abs-uri => error
+client c10 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname:8080/" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# non-default host port with a default in abs-uri => error
+client c11 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname/" \
+ -hdr "host: hostname:81"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# miss-match between host headers => error
+client c12 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname1/" \
+ -hdr "host: hostname1" \
+ -hdr "host: hostname2"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# miss-match between host headers but with a normalization => error
+client c13 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname1/" \
+ -hdr "host: hostname1:80" \
+ -hdr "host: hostname1"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# CONNECT authoriy without port => error
+client c14 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# host miss-match with CONNECT => error
+client c15 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname1:80" \
+ -hdr "host: hostname2:80"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# port miss-match with CONNECT => error
+client c16 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:80" \
+ -hdr "host: hostname:443"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# no host port with non-default port in CONNECT authority => error
+client c17 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:8080" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# no authority => error
+client c18 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "/" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# no authority => error
+client c19 -connect ${h1_fe_sock} {
+ txreq \
+ -req "CONNECT" \
+ -url "hostname:" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+
+# default port 80 with http scheme but no port for host value => should be normalized
+client c20 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname:80/c20" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+# default port 443 with https scheme but no port for host value => should be normalized
+client c21 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname:443/c21" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+# http scheme, no port for the authority but default port for host value => no normalization
+client c22 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname/c22" \
+ -hdr "host: hostname:80"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# https scheme, no port for the authority but default port for host value => no normalization
+client c23 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname/c23" \
+ -hdr "host: hostname:443"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+# http scheme, empty port for the authority and no port for host value => should be normalized
+client c24 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname:/c24" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# https scheme, empty port for the authority and no port for host value => should be normalized
+client c25 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname:/c25" \
+ -hdr "host: hostname"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# http scheme, no port for the authority and empty port for host value => no normalization
+client c26 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname/c26" \
+ -hdr "host: hostname:"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# https scheme, no port for the authority and empty port for host value => no normalization
+client c27 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname/c27" \
+ -hdr "host: hostname:"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# http scheme, default port for the authority and empty port for host value => should be normalized
+client c28 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname:80/c28" \
+ -hdr "host: hostname:"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# http scheme, empty port for the authority and default port for host value => should be normalized
+client c29 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://hostname:/c29" \
+ -hdr "host: hostname:80"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# https scheme, default port for the authority and empty port for host value => should be normalized
+client c30 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname:443/c30" \
+ -hdr "host: hostname:"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# https scheme, empty port for the authority and default port for host value => should be normalized
+client c31 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://hostname:/c31" \
+ -hdr "host: hostname:443"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# Strange cases
+client c32 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://:" \
+ -hdr "host: :80"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+client c33 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://:" \
+ -hdr "host: :443"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+# Strange cases
+client c34 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "http://:" \
+ -hdr "host: :"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+client c35 -connect ${h1_fe_sock} {
+ txreq \
+ -req "GET" \
+ -url "https://:" \
+ -hdr "host: :"
+
+ rxresp
+ expect resp.status == 200
+} -run
+
+syslog S1 -wait
diff --git a/reg-tests/http-messaging/h1_to_h1.vtc b/reg-tests/http-messaging/h1_to_h1.vtc
new file mode 100644
index 0000000..0d65366
--- /dev/null
+++ b/reg-tests/http-messaging/h1_to_h1.vtc
@@ -0,0 +1,275 @@
+varnishtest "HTTP request tests: H1 to H1 (HTX mode supported only for HAProxy >= 1.9)"
+
+# Run it with HAPROXY_PROGRAM=$PWD/haproxy varnishtest -l -k -t 1 "$1"
+
+feature ignore_unknown_macro
+
+server s1 {
+ ##
+ ## Handle GET requests
+ ##
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 1"
+
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 2"
+
+ rxreq
+ expect req.bodylen == 38
+ expect req.body == "this must be delivered, like it or not"
+ txresp \
+ -status 200 \
+ -body "response 3"
+
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 4"
+
+ accept
+
+ ##
+ ## Handle HEAD requests
+ ##
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 1"
+
+ accept
+
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 2"
+
+ accept
+
+ rxreq
+ expect req.bodylen == 38
+ expect req.body == "this must be delivered, like it or not"
+ txresp \
+ -status 200 \
+ -body "response 3"
+
+ accept
+
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 4"
+
+ accept
+
+ ##
+ ## Handle POST requests
+ ##
+ # POST request without body
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 1"
+
+ # POST request without body
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 2"
+
+ # POST request with a body
+ rxreq
+ expect req.bodylen == 12
+ expect req.body == "this is sent"
+ txresp \
+ -status 200 \
+ -body "response 3"
+
+ # POST request without body
+ rxreq
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp \
+ -status 200 \
+ -body "response 4"
+} -repeat 3 -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
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen feh1
+ bind "fd@${feh1}"
+ #bind "fd@${feh2}" proto h2
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+# GET requests
+client c1h1 -connect ${h1_feh1_sock} {
+ # first request is valid
+ txreq \
+ -req "GET" \
+ -url "/test1.html"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 1"
+
+ # second request is valid and advertises C-L:0
+ txreq \
+ -req "GET" \
+ -url "/test2.html" \
+ -hdr "content-length: 0"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 2"
+
+ # third request sends a body with a GET
+ txreq \
+ -req "GET" \
+ -url "/test3.html" \
+ -body "this must be delivered, like it or not"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 3"
+
+ # fourth request is valid and advertises C-L:0, and close, and is
+ # followed by a string "this is not sent\r\n\r\n" which must be
+ # dropped.
+ txreq \
+ -req "GET" \
+ -url "/test4.html" \
+ -hdr "content-length: 0" \
+ -hdr "connection: close"
+ # "this is not sent"
+ sendhex "74787973207973206E6F742073656E740D0A0D0A"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 4"
+
+ # the connection is expected to be closed and no more response must
+ # arrive here.
+ expect_close
+} -run
+
+# HEAD requests
+# Note: for now they fail with varnishtest, which expects the amount of
+# data advertised in the content-length response.
+client c2h1 -connect ${h1_feh1_sock} {
+ # first request is valid
+ txreq \
+ -req "HEAD" \
+ -url "/test11.html"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ # second request is valid and advertises C-L:0
+ txreq \
+ -req "HEAD" \
+ -url "/test12.html" \
+ -hdr "content-length: 0"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ # third request sends a body with a HEAD
+ txreq \
+ -req "HEAD" \
+ -url "/test13.html" \
+ -body "this must be delivered, like it or not"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ # fourth request is valid and advertises C-L:0, and close, and is
+ # followed by a string "this is not sent\r\n\r\n" which must be
+ # dropped.
+ txreq \
+ -req "HEAD" \
+ -url "/test14.html" \
+ -hdr "content-length: 0" \
+ -hdr "connection: close"
+ # "this is not sent"
+ sendhex "74787973207973206E6F742073656E740D0A0D0A"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ # the connection is expected to be closed and no more response must
+ # arrive here.
+ expect_close
+} -run
+
+client c3h1 -connect ${h1_feh1_sock} {
+ # first request is valid
+ txreq \
+ -req "POST" \
+ -url "/test21.html"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 1"
+
+ # second request is valid and advertises C-L:0
+ txreq \
+ -req "POST" \
+ -url "/test22.html" \
+ -hdr "content-length: 0"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 2"
+
+ # third request is valid and advertises (and sends) some contents
+ txreq \
+ -req "POST" \
+ -url "/test23.html" \
+ -body "this is sent"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 3"
+
+ # fourth request is valid and advertises C-L:0, and close, and is
+ # followed by a string "this is not sent\r\n\r\n" which must be
+ # dropped.
+ txreq \
+ -req "POST" \
+ -url "/test24.html" \
+ -hdr "content-length: 0" \
+ -hdr "connection: close"
+ # "this is not sent"
+ sendhex "74787973207973206E6F742073656E740D0A0D0A"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "response 4"
+
+ # the connection is expected to be closed and no more response must
+ # arrive here.
+ expect_close
+} -run
diff --git a/reg-tests/http-messaging/h2_desync_attacks.vtc b/reg-tests/http-messaging/h2_desync_attacks.vtc
new file mode 100644
index 0000000..112bc60
--- /dev/null
+++ b/reg-tests/http-messaging/h2_desync_attacks.vtc
@@ -0,0 +1,167 @@
+# This test ensures that h2 requests with invalid pseudo-headers are properly
+# rejected. Also, the host header must be ignored if authority is present. This
+# is necessary to avoid http/2 desync attacks through haproxy as described here
+# https://portswigger.net/research/http2
+
+varnishtest "h2 desync attacks"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.http.host == "hostname"
+ txresp
+} -start
+
+# haproxy frontend
+haproxy hap -conf {
+ defaults
+ mode http
+
+ listen feSrvH1
+ bind "fd@${feSrvH1}"
+ http-request return status 200
+
+ listen feSrvH2
+ bind "fd@${feSrvH2}" proto h2
+ http-request return status 200
+
+ listen fe1
+ bind "fd@${fe1}" proto h2
+ server srv-hapSrv "${hap_feSrvH1_addr}:${hap_feSrvH1_port}"
+
+ listen fe2
+ bind "fd@${fe2}" proto h2
+ server srv-hapSrv "${hap_feSrvH2_addr}:${hap_feSrvH2_port}" proto h2
+
+ listen fe3
+ bind "fd@${fe3}" proto h2
+ server s1 "${s1_addr}:${s1_port}"
+} -start
+
+# valid request
+client c1 -connect ${hap_fe1_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -method "GET" \
+ -scheme "http" \
+ -url "/"
+ rxresp
+ expect resp.status == 200
+ } -run
+} -run
+
+# valid request
+client c2 -connect ${hap_fe2_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -method "GET" \
+ -scheme "http" \
+ -url "/"
+ rxresp
+ expect resp.status == 200
+ } -run
+} -run
+
+# invalid path
+client c3-path -connect ${hap_fe1_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -method "GET" \
+ -scheme "http" \
+ -url "hello-world"
+ rxrst
+ } -run
+} -run
+
+# invalid scheme
+client c4-scheme -connect ${hap_fe1_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -method "GET" \
+ -scheme "http://localhost/?" \
+ -url "/"
+ rxresp
+ expect resp.status == 400
+ } -run
+} -run
+
+# invalid method
+client c5-method -connect ${hap_fe2_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -method "GET?" \
+ -scheme "http" \
+ -url "/"
+ rxresp
+ expect resp.status == 400
+ } -run
+} -run
+
+# different authority and host headers
+# in this case, host should be ignored in favor of the authority
+client c6-host-authority -connect ${hap_fe3_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -method "GET" \
+ -scheme "http" \
+ -url "/" \
+ -hdr ":authority" "hostname" \
+ -hdr "host" "other_host"
+ } -run
+} -run
+
+server s1 -wait
diff --git a/reg-tests/http-messaging/h2_to_h1.vtc b/reg-tests/http-messaging/h2_to_h1.vtc
new file mode 100644
index 0000000..852ee4c
--- /dev/null
+++ b/reg-tests/http-messaging/h2_to_h1.vtc
@@ -0,0 +1,265 @@
+varnishtest "HTTP request tests: H2 to H1 (HTX and legacy mode)"
+#REQUIRE_VERSION=1.9
+
+# Run it with HAPROXY_PROGRAM=$PWD/haproxy varnishtest -l -k -t 1 "$1"
+
+feature ignore_unknown_macro
+
+# synchronize requests between streams
+barrier b1 cond 2 -cyclic
+barrier b2 cond 2 -cyclic
+barrier b3 cond 2 -cyclic
+barrier b4 cond 2 -cyclic
+
+server s1 {
+ rxreq
+ txresp \
+ -status 200 \
+ -body "response 1"
+
+ barrier b2 sync
+ rxreq
+ txresp \
+ -status 200 \
+ -body "response 2"
+
+ barrier b3 sync
+ rxreq
+ txresp \
+ -status 200 \
+ -body "response 3"
+
+ barrier b4 sync
+ # the next request is never received
+} -repeat 2 -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
+
+ defaults
+ #log stdout format raw daemon
+ mode http
+ option http-buffer-request
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen feh1
+ bind "fd@${feh1}"
+ bind "fd@${feh2}" proto h2
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1h2 -connect ${h1_feh2_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ # first request is valid
+ stream 1 {
+ txreq \
+ -req "GET" \
+ -scheme "https" \
+ -url "/test1.html"
+ rxhdrs
+ expect resp.status == 200
+ rxdata -all
+ expect resp.body == "response 1"
+ } -run
+
+ # second request is valid and advertises C-L:0
+ stream 3 {
+ barrier b2 sync
+ txreq \
+ -req "GET" \
+ -scheme "https" \
+ -url "/test2.html" \
+ -hdr "content-length" "0"
+ rxhdrs
+ expect resp.status == 200
+ rxdata -all
+ expect resp.body == "response 2"
+ } -run
+
+ # third request sends a body with a GET
+ stream 5 {
+ barrier b3 sync
+ txreq \
+ -req "GET" \
+ -scheme "https" \
+ -url "/test3.html" \
+ -nostrend \
+ -body "this must be delivered, like it or not"
+ rxwinup
+ rxhdrs
+ expect resp.status == 200
+ rxdata -all
+ expect resp.body == "response 3"
+ } -run
+
+ # fourth request is valid and advertises C-L:2, and close, and is
+ # followed by a string "this is not sent\r\n\r\n" which causes a
+ # stream error of type PROTOCOL_ERROR.
+ stream 7 {
+ barrier b4 sync
+ txreq \
+ -req "GET" \
+ -scheme "https" \
+ -url "/test4.html" \
+ -hdr "content-length" "2" \
+ -nostrend
+ txdata -data "this is sent and ignored"
+ rxrst
+ } -run
+} -run
+
+# HEAD requests : don't work well yet
+#client c2h2 -connect ${h1_feh2_sock} {
+# txpri
+# stream 0 {
+# txsettings
+# rxsettings
+# txsettings -ack
+# rxsettings
+# expect settings.ack == true
+# } -run
+#
+# # first request is valid
+# stream 1 {
+# txreq \
+# -req "HEAD" \
+# -scheme "https" \
+# -url "/test11.html"
+# rxhdrs
+# expect resp.status == 200
+# rxdata -all
+# expect resp.bodylen == 0
+# } -run
+#
+# # second request is valid and advertises C-L:0
+# stream 3 {
+# barrier b2 sync
+# txreq \
+# -req "HEAD" \
+# -scheme "https" \
+# -url "/test12.html" \
+# -hdr "content-length" "0"
+# rxhdrs
+# expect resp.status == 200
+# rxdata -all
+# expect resp.bodylen == 0
+# } -run
+#
+# # third request sends a body with a GET
+# stream 5 {
+# barrier b3 sync
+# txreq \
+# -req "HEAD" \
+# -scheme "https" \
+# -url "/test13.html" \
+# -nostrend \
+# -body "this must be delivered, like it or not"
+# rxwinup
+# rxhdrs
+# expect resp.status == 200
+# rxdata -all
+# expect resp.bodylen == 0
+# } -run
+#
+# # fourth request is valid and advertises C-L:0, and close, and is
+# # followed by a string "this is not sent\r\n\r\n" which must be
+# # dropped.
+# stream 7 {
+# barrier b4 sync
+# txreq \
+# -req "HEAD" \
+# -scheme "https" \
+# -url "/test14.html" \
+# -hdr "content-length" "0" \
+# -nostrend
+# txdata -data "this is sent and ignored"
+# rxwinup
+# rxhdrs
+# expect resp.status == 200
+# rxdata -all
+# expect resp.bodylen == 0
+# } -run
+#} -run
+
+# POST requests
+client c3h2 -connect ${h1_feh2_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ # first request is valid
+ stream 1 {
+ txreq \
+ -req "POST" \
+ -scheme "https" \
+ -url "/test21.html"
+ rxhdrs
+ expect resp.status == 200
+ rxdata -all
+ expect resp.body == "response 1"
+ } -run
+
+ # second request is valid and advertises C-L:0
+ stream 3 {
+ barrier b2 sync
+ txreq \
+ -req "POST" \
+ -scheme "https" \
+ -url "/test22.html" \
+ -hdr "content-length" "0"
+ rxhdrs
+ expect resp.status == 200
+ rxdata -all
+ expect resp.body == "response 2"
+ } -run
+
+ # third request sends a body with a GET
+ stream 5 {
+ barrier b3 sync
+ txreq \
+ -req "POST" \
+ -scheme "https" \
+ -url "/test23.html" \
+ -nostrend \
+ -body "this must be delivered, like it or not"
+ rxwinup
+ rxhdrs
+ expect resp.status == 200
+ rxdata -all
+ expect resp.body == "response 3"
+ } -run
+
+ # fourth request is valid and advertises C-L:2, and close, and is
+ # followed by a string "this is not sent\r\n\r\n" which results
+ # in a stream error.
+ stream 7 {
+ barrier b4 sync
+ txreq \
+ -req "POST" \
+ -scheme "https" \
+ -url "/test24.html" \
+ -hdr "content-length" "2" \
+ -nostrend
+ txdata -data "this is sent and ignored"
+ rxrst
+ } -run
+} -run
diff --git a/reg-tests/http-messaging/http_abortonclose.vtc b/reg-tests/http-messaging/http_abortonclose.vtc
new file mode 100644
index 0000000..767a185
--- /dev/null
+++ b/reg-tests/http-messaging/http_abortonclose.vtc
@@ -0,0 +1,142 @@
+varnishtest "A test for the abortonclose option (H1 only)"
+feature ignore_unknown_macro
+
+# NOTE : This test may fail if too many vtest are running in parallel because
+# the port reserved for closed s1 server may be reused by another vtest
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.0-dev0)'"
+#REGTEST_TYPE=slow
+
+# b0 : Wait s1 was detected as DOWN to be sure it is stopped
+# b1 : Don't send /c4 before /c3 was received by s2 server
+# b2 : Don't finish c2 before c1 and c3 before c4 (from syslog POV)
+# b3 : finish c3 before s2
+
+barrier b0 cond 2 -cyclic
+barrier b1 cond 2 -cyclic
+barrier b2 cond 2 -cyclic
+barrier b3 cond 2 -cyclic
+
+server s1 {
+} -start
+server s1 -break
+
+server s2 {
+ rxreq
+
+ # unlock c4
+ barrier b1 sync
+
+ # wait end of c3
+ barrier b3 sync
+
+ expect_close
+} -start
+
+syslog S -level info {
+ recv alert
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: Server check/srv1 is DOWN.*"
+ barrier b0 sync
+
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1_1/srv1 [0-9]*/[0-9]*/-1/-1/[0-9]* 503 .* - - SC-- .* .* \"GET /c1 HTTP/1\\.1\""
+ barrier b2 sync
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1_2/srv1 [0-9]*/[0-9]*/-1/-1/[0-9]* -1 .* - - CC-- .* .* \"GET /c2 HTTP/1\\.1\""
+ barrier b2 sync
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe2 be2/<NOSRV> [0-9]*/[0-9]*/-1/-1/[0-9]* -1 .* - - CQ-- .* .* \"GET /c4 HTTP/1\\.1\""
+ barrier b2 sync
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe2 be2/srv1 [0-9]*/[0-9]*/[0-9]*/-1/[0-9]* 400 .* - - CH-- .* .* \"GET /c3 HTTP/1\\.1\""
+} -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
+
+ defaults
+ mode http
+ option abortonclose
+ retries 1
+ timeout client 10s
+ timeout server 10s
+ timeout connect 100ms
+ timeout queue 5s
+
+ frontend fe1
+ option httplog
+ log ${S_addr}:${S_port} local0 debug err
+ bind "fd@${fe1}"
+ use_backend be1_1 if { path /c1 }
+ use_backend be1_2 if { path /c2 }
+
+ frontend fe2
+ option httplog
+ log ${S_addr}:${S_port} local0 debug err
+ bind "fd@${fe2}"
+ use_backend be2
+
+ backend be1_1
+ server srv1 ${s1_addr}:${s1_port}
+
+ backend be1_2
+ timeout connect 1s
+ retries 10
+ server srv1 ${s1_addr}:${s1_port}
+
+ backend be2
+ server srv1 ${s2_addr}:${s2_port} maxconn 1
+
+ backend check
+ server srv1 ${s1_addr}:${s1_port} check
+ log ${S_addr}:${S_port} local0 debug alert
+} -start
+
+# Wait s1 was detected as DOWN
+barrier b0 sync
+
+# No server, wait all connection retries : SC--
+client c1 -connect ${h1_fe1_sock} {
+ txreq -url /c1
+ rxresp
+ expect resp.status == 503
+} -run
+
+# Wait c1 log entry
+barrier b2 sync
+
+# No server, abort during connections retries : CC--
+client c2 -connect ${h1_fe1_sock} {
+ txreq -url /c2
+} -run
+
+# Wait c2 log entry
+barrier b2 sync
+
+# server with maxconn=1, abort waiting the server reply : CH--
+client c3 -connect ${h1_fe2_sock} {
+ txreq -url /c3
+
+ # Wait c4 log entry
+ barrier b2 sync
+} -start
+
+# server with maxconn=1, abort waiting in the queue (c3 still attached) : CQ--
+client c4 -connect ${h1_fe2_sock} {
+ # Wait s2 receives c3 request
+ barrier b1 sync
+
+ txreq -url /c4
+ delay .2
+} -run
+
+client c3 -wait
+
+# unlock s2
+barrier b3 sync
+
+syslog S -wait
diff --git a/reg-tests/http-messaging/http_bodyless_response.vtc b/reg-tests/http-messaging/http_bodyless_response.vtc
new file mode 100644
index 0000000..9e0ce1c
--- /dev/null
+++ b/reg-tests/http-messaging/http_bodyless_response.vtc
@@ -0,0 +1,127 @@
+varnishtest "A test to be sure payload is skipped for bodyless responses"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+
+server s1 {
+ rxreq
+ txresp \
+ -status 200 \
+ -body "skipped data"
+
+ rxreq
+ txresp \
+ -status 200 \
+ -bodylen 20000
+
+ rxreq
+ txresp \
+ -status 200 \
+ -nolen -hdr "Transfer-Encoding: chunked"
+ chunkedlen 15
+ chunkedlen 1024
+ chunkedlen 4048
+ chunkedlen 0
+
+ rxreq
+ txresp \
+ -status 200 \
+ -body "last response"
+} -repeat 2 -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
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen fe1
+ bind "fd@${fe1}"
+ # Rewrite the method to be sure to get the response payload
+ # on the server side
+ http-request set-method GET
+ server s1 ${s1_addr}:${s1_port}
+
+ listen int
+ bind "fd@${int}" proto h2
+ # Rewrite the method to be sure to get the response payload
+ # on the server side
+ http-request set-method GET
+ server s1 ${s1_addr}:${s1_port}
+ #server s1 ${h1_fe1_addr}:${h1_fe1_port}
+
+ listen fe2
+ bind "fd@${fe2}"
+ server s1 ${h1_int_addr}:${h1_int_port} proto h2
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq \
+ -req "HEAD" \
+ -url "/req1"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ txreq \
+ -req "HEAD" \
+ -url "/req2"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ txreq \
+ -req "HEAD" \
+ -url "/req3"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ # The last one have a body and validate the connection was not closed
+ # unexpectedly and no payload was received for previous requests
+ txreq \
+ -req "GET" \
+ -url "/req4"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "last response"
+} -run
+
+client c2 -connect ${h1_fe2_sock} {
+ txreq \
+ -req "HEAD" \
+ -url "/req1"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ txreq \
+ -req "HEAD" \
+ -url "/req2"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ txreq \
+ -req "HEAD" \
+ -url "/req3"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == ""
+
+ # The last one have a body and validate the connection was not closed
+ # unexpectedly and no payload was received for previous requests
+ txreq \
+ -req "GET" \
+ -url "/req4"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "last response"
+} -run
diff --git a/reg-tests/http-messaging/http_msg_full_on_eom.vtc b/reg-tests/http-messaging/http_msg_full_on_eom.vtc
new file mode 100644
index 0000000..2edba7d
--- /dev/null
+++ b/reg-tests/http-messaging/http_msg_full_on_eom.vtc
@@ -0,0 +1,62 @@
+varnishtest "cannot add the HTX EOM block because the buffer is full"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.2
+#REQUIRE_VERSION_BELOW=2.4
+#REGTEST_TYPE=devel
+
+# This test checks that an HTTP message is properly processed when we failed to
+# add the HTX EOM block in an HTX message during the parsing because the buffer
+# is full. Some space must be released in the buffer to make it possible. This
+# requires an extra pass in the H1 multiplexer. Here, we must be sure the mux is
+# called while there is no more incoming data.
+
+server s1 {
+ rxreq
+ expect req.bodylen == 15200
+ txresp -bodylen 15220
+} -start
+
+syslog S -level info {
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1/srv1 [0-9]*/[0-9]*/[0-9]*/[0-9]*/[0-9]* 200 .* - - ---- .* .* \"GET / HTTP/1\\.1\""
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.bufsize 16384
+ tune.maxrewrite 1024
+
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ tcp-response inspect-delay 100ms
+ tcp-response content accept if { res.len gt 15272 }
+ tcp-response content reject
+
+ http-response deny if { internal.htx.has_eom -m bool } or { internal.htx.free_data gt 1024 }
+ server srv1 ${s1_addr}:${s1_port}
+
+ frontend fe1
+ option httplog
+ option http-buffer-request
+ log ${S_addr}:${S_port} local0 debug err
+ bind "fd@${fe1}"
+ http-request deny if ! { req.body_len eq 15200 } or { internal.htx.has_eom -m bool } or { internal.htx.free_data gt 1024 }
+ use_backend be1
+} -start
+
+haproxy h1 -cli {
+ send "trace h1 sink stderr; trace h1 level developer; trace h1 verbosity complete; trace h1 start now"
+}
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -bodylen 15200
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 15220
+} -run
diff --git a/reg-tests/http-messaging/http_request_buffer.vtc b/reg-tests/http-messaging/http_request_buffer.vtc
new file mode 100644
index 0000000..15ec540
--- /dev/null
+++ b/reg-tests/http-messaging/http_request_buffer.vtc
@@ -0,0 +1,135 @@
+varnishtest "A test for http-request-buffer option"
+feature ignore_unknown_macro
+
+
+# This test checks HTTP request buffering feature.
+# We run one server s1 which can serve only one client (no -repeat argument here).
+# c1 client uses a malformed request which is not transferred to s1 server
+# thanks to "http-buffer-request". If this was the case, c2 client
+# could not connect to s1 server and this would lead to make this test fail.
+
+barrier b1 cond 2 -cyclic
+
+server s1 {
+ rxreq
+ expect req.bodylen == 257
+ txresp
+
+ accept
+
+ rxreq
+ expect req.bodylen == 2
+ txresp
+} -start
+
+syslog S -level info {
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 fe1/<NOSRV> .* 408 .* - - cR-- .* .* \"GET /this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url HTTP/1\\.1\""
+ barrier b1 sync
+
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1/srv1 [0-9]*/[0-9]*/[0-9]*/[0-9]*/[0-9]* 200 .* - - ---- .* .* \"GET / HTTP/1\\.1\""
+ barrier b1 sync
+
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe2 be1/srv1 [0-9]*/[0-9]*/[0-9]*/[0-9]*/[0-9]* 200 .* - - ---- .* .* \"POST /1 HTTP/1\\.1\""
+ barrier b1 sync
+
+ recv
+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe2 be1/<NOSRV> [0-9]*/-1/-1/-1/[0-9]* -1 .* - - CR-- .* .* \"POST /2 HTTP/1\\.1\""
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout client 100
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend be1
+ server srv1 ${s1_addr}:${s1_port}
+
+ frontend fe1
+ option httplog
+ option http-buffer-request
+ log ${S_addr}:${S_port} local0 debug err
+ bind "fd@${fe1}"
+ use_backend be1
+
+ frontend fe2
+ timeout client 10s
+ option httplog
+ option http-buffer-request
+ log ${S_addr}:${S_port} local0 debug err
+ bind "fd@${fe2}"
+ use_backend be1
+} -start
+
+# 1 byte of the payload is missing.
+# ==> The request must timed out with a 408 response
+client c1 -connect ${h1_fe1_sock} {
+ send "GET"
+ send " "
+ send "/this-is-a-long-url"
+ send "-this-is-a-long-url"
+ send "-this-is-a-long-url"
+ send "-this-is-a-long-url"
+ send "-this-is-a-long-url"
+ send "-this-is-a-long-url"
+ send "-this-is-a-long-url"
+ send " HTT"
+ send "P/1.1"
+ send "\r"
+ send "\n"
+ send "Content-Length: 209\r\n\r\n"
+ send "abcdefghijklmnopqrstuvwxyz"
+ send "abcdefghijklmnopqrstuvwxyz"
+ send "abcdefghijklmnopqrstuvwxyz"
+ send "abcdefghijklmnopqrstuvwxyz"
+ send "abcdefghijklmnopqrstuvwxyz"
+ send "abcdefghijklmnopqrstuvwxyz"
+ send "abcdefghijklmnopqrstuvwxyz"
+ send "abcdefghijklmnopqrstuvwxyz"
+ rxresp
+ expect resp.status == 408
+} -run
+
+# Wait matching on log message
+barrier b1 sync
+
+# Payload is fully sent
+# ==> Request must be sent to the server. A 200 must be received
+client c2 -connect ${h1_fe1_sock} {
+ txreq -bodylen 257
+ rxresp
+ expect resp.status == 200
+} -run
+
+# Wait matching on log message
+barrier b1 sync
+
+# Payload is fully sent in 2 steps (with a small delay, smaller than the client
+# timeout) and split on a chunk size.
+# ==> Request must be sent to the server. A 200 must be received
+client c3 -connect ${h1_fe2_sock} {
+ send "POST /1 HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n1\r\n1\r\n1"
+ delay 0.01
+ send "\r\n1\r\n0\r\n\r\n"
+ rxresp
+ expect resp.status == 200
+} -run
+
+# Wait matching on log message
+barrier b1 sync
+
+# Last CRLF of the request payload is missing but payload is sent in 2 steps
+# (with a small delay, smaller than the client timeout) and split on a chunk
+# size. The client aborts before sending the last CRLF.
+# ==> Request must be handled as an error with 'CR--' termination state.
+client c4 -connect ${h1_fe2_sock} {
+ send "POST /2 HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n1\r\n1\r\n1"
+ delay 0.01
+ send "\r\n1\r\n0\r\n"
+} -run
+
+syslog S -wait
diff --git a/reg-tests/http-messaging/http_splicing.vtc b/reg-tests/http-messaging/http_splicing.vtc
new file mode 100644
index 0000000..2456cdd
--- /dev/null
+++ b/reg-tests/http-messaging/http_splicing.vtc
@@ -0,0 +1,75 @@
+# This reg-test checks splicing support for the H1 multiplexer
+
+varnishtest "A test to validate h1 splicing support"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.6-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(LINUX_SPLICE)'"
+feature ignore_unknown_macro
+
+#REGTEST_TYPE=slow
+
+server s1 {
+ rxreq
+ expect req.http.content-length == "1048576"
+ expect req.bodylen == 1048576
+ txresp -status 200 -bodylen 1048576
+} -start
+
+server s2 {
+ rxreq
+ txresp -status 200 -nolen -bodylen 1048576
+} -start
+
+haproxy h1 -conf {
+ global
+ log stderr len 4096 local0 debug
+
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ option splice-request
+ option splice-response
+ log global
+ option httplog
+
+ listen li1
+ bind "fd@${li1}"
+ id 10
+ server srv1 ${s1_addr}:${s1_port}
+
+ listen li2
+ bind "fd@${li2}"
+ id 20
+ server srv2 ${s2_addr}:${s2_port}
+} -start
+
+
+client c1 -connect ${h1_li1_sock} {
+ txreq -method POST -url "/" -bodylen 1048576
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == "1048576"
+ expect resp.bodylen == 1048576
+} -run
+
+client c2 -connect ${h1_li2_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == <undef>
+ expect resp.bodylen == 1048576
+} -run
+
+haproxy h1 -cli {
+ send "show stat typed"
+ expect ~ "F.10.0.[[:digit:]]+.h1_spliced_bytes_in.1:MCP:u64:[1-9][[:digit:]]+\nF.10.0.[[:digit:]]+.h1_spliced_bytes_out.1:MCP:u64:[1-9][[:digit:]]+"
+ send "show stat typed"
+ expect ~ "B.10.0.[[:digit:]]+.h1_spliced_bytes_in.1:MCP:u64:[1-9][[:digit:]]+\nB.10.0.[[:digit:]]+.h1_spliced_bytes_out.1:MCP:u64:[1-9][[:digit:]]+"
+
+ send "show stat typed"
+ expect ~ "F.20.0.[[:digit:]]+.h1_spliced_bytes_in.1:MCP:u64:0\nF.20.0.[[:digit:]]+.h1_spliced_bytes_out.1:MCP:u64:[1-9][[:digit:]]+"
+ send "show stat typed"
+ expect ~ "B.20.0.[[:digit:]]+.h1_spliced_bytes_in.1:MCP:u64:[1-9][[:digit:]]+\nB.20.0.[[:digit:]]+.h1_spliced_bytes_out.1:MCP:u64:0"
+}
diff --git a/reg-tests/http-messaging/http_transfer_encoding.vtc b/reg-tests/http-messaging/http_transfer_encoding.vtc
new file mode 100644
index 0000000..322dfe2
--- /dev/null
+++ b/reg-tests/http-messaging/http_transfer_encoding.vtc
@@ -0,0 +1,202 @@
+varnishtest "A test to validate Transfer-Encoding header conformance to the spec"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.http.content-length == <undef>
+ expect req.http.transfer-encoding == "chunked"
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp -status 200
+
+ accept
+ rxreq
+ expect req.http.content-length == <undef>
+ expect req.http.transfer-encoding == "chunked"
+ expect req.bodylen == 0
+ expect req.body == ""
+ txresp -status 200
+
+ accept
+ rxreq
+ send "HTTP/1.0 200 Ok\r\n"
+ send "Transfer-Encoding: chunked\r\n\r\n"
+ send "0\r\n\r\n"
+
+ accept
+ rxreq
+ send "HTTP/1.1 200 Ok\r\n"
+ send "Transfer-Encoding: chunked\r\n"
+ send "Content-Length: 30\r\n\r\n"
+ send "0\r\n\r\nResponse splitting attach"
+
+ accept
+ rxreq
+ expect req.url == "/1"
+ expect req.http.transfer-encoding == "chunked"
+ expect req.http.te == "trailers"
+ txresp
+
+ rxreq
+ expect req.url == "/2"
+ expect req.http.transfer-encoding == "chunked"
+ expect req.http.te == <undef>
+ txresp
+
+ rxreq
+ expect req.url == "/3"
+ expect req.http.transfer-encoding == "chunked"
+ expect req.http.te == <undef>
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ txresp -nolen \
+ -hdr "Transfer-Encoding: chunked, chunked" \
+ -body "0\r\n\r\n"
+
+ accept
+ rxreq
+ txresp -nolen \
+ -hdr "Transfer-Encoding: chunked, gzip, chunked" \
+ -body "0\r\n\r\n"
+
+ accept
+ rxreq
+ txresp -nolen \
+ -hdr "Transfer-Encoding: chunked, gzip" \
+ -body "0\r\n\r\n"
+
+ accept
+ rxreq
+ txresp \
+ -hdr "Transfer-Encoding: gzip"
+} -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}"
+
+ listen fe1
+ bind "fd@${fe1}"
+ server s1 ${s1_addr}:${s1_port}
+
+ listen fe2
+ bind "fd@${fe2}"
+ server s2 ${s2_addr}:${s2_port}
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -method POST -nolen \
+ -hdr "Transfer-Encoding: chunked" \
+ -hdr "Content-Length: 31" \
+ -body "0\r\n\r\nGET /smuggled HTTP/1.1\r\n\r\n"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.connection == "close"
+ expect_close
+} -run
+
+client c2 -connect ${h1_fe1_sock} {
+ send "POST / HTTP/1.0\r\n"
+ send "Transfer-Encoding: chunked\r\n\r\n"
+ send "0\r\n\r\n"
+ rxresp
+ expect resp.status == 200
+ expect_close
+} -run
+
+client c3 -connect ${h1_fe1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == <undef>
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.bodylen == 0
+ expect resp.body == ""
+ expect_close
+} -run
+
+client c4 -connect ${h1_fe1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-length == <undef>
+ expect resp.http.transfer-encoding == "chunked"
+ expect resp.bodylen == 0
+ expect resp.body == ""
+} -run
+
+client c5 -connect ${h1_fe1_sock} {
+ txreq -method POST -url "/1" -nolen \
+ -hdr "Transfer-Encoding: chunked" \
+ -hdr "TE: trailers, gzip" \
+ -body "0\r\n\r\n"
+ rxresp
+ expect resp.status == 200
+
+ txreq -method POST -url "/2" -nolen \
+ -hdr "Transfer-Encoding: chunked" \
+ -hdr "TE: gzip" \
+ -body "0\r\n\r\n"
+ rxresp
+ expect resp.status == 200
+
+ txreq -method POST -url "/3" -nolen \
+ -hdr "Transfer-Encoding: chunked" \
+ -hdr "TE: trailers;q=0.5" \
+ -body "0\r\n\r\n"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c6 -connect ${h1_fe1_sock} {
+ txreq -nolen \
+ -hdr "Transfer-Encoding: chunked, chunked" \
+ -body "0\r\n\r\n"
+ rxresp
+ expect resp.status == 400
+} -run
+
+client c7 -connect ${h1_fe1_sock} {
+ txreq -nolen \
+ -hdr "Transfer-Encoding: chunked, gzip, chunked" \
+ -body "0\r\n\r\n"
+ rxresp
+ expect resp.status == 400
+} -run
+
+client c8 -connect ${h1_fe1_sock} {
+ txreq -nolen \
+ -hdr "Transfer-Encoding: chunked, gzip" \
+ -body "0\r\n\r\n"
+ rxresp
+ expect resp.status == 400
+} -run
+
+client c9 -connect ${h1_fe1_sock} {
+ txreq \
+ -hdr "Transfer-Encoding: gzip"
+ rxresp
+ expect resp.status == 400
+} -run
+
+client c10 -connect ${h1_fe1_sock} {
+ txreq -nolen \
+ -hdr "Transfer-Encoding: gzip, chunked" \
+ -body "0\r\n\r\n"
+ rxresp
+ expect resp.status == 422
+} -run
+
+client c11 -connect ${h1_fe2_sock} {
+ txreq
+ rxresp
+ expect resp.status == 502
+} -run -repeat 4
diff --git a/reg-tests/http-messaging/http_wait_for_body.vtc b/reg-tests/http-messaging/http_wait_for_body.vtc
new file mode 100644
index 0000000..a9f8191
--- /dev/null
+++ b/reg-tests/http-messaging/http_wait_for_body.vtc
@@ -0,0 +1,171 @@
+varnishtest "A test for the wait-for-body HTTP action"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+#REGTEST_TYPE=slow
+
+server s1 {
+ rxreq
+ expect req.bodylen == 1001
+ txresp
+
+ rxreq
+ expect req.bodylen == 1001
+ txresp
+} -start
+
+
+server s2 {
+ rxreq
+ send "HTTP/1.1 200 OK\r\n"
+ send "Content-Length: 1001\r\n\r\n"
+ delay 0.01
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+
+ expect_close
+ accept
+
+ rxreq
+ send "HTTP/1.1 200 OK\r\n"
+ send "Content-Length: 1001\r\n\r\n"
+ delay 0.01
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ delay 0.01
+ send "1"
+
+ rxreq
+ send "HTTP/1.1 201 OK\r\n"
+ send "Content-Length: 1001\r\n\r\n"
+ delay 0.01
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ delay 0.1
+ send "1"
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ http-request wait-for-body time 100ms if { path /a }
+ http-request wait-for-body time 100ms at-least 1000 if { path /b }
+ use_backend be1
+
+ backend be1
+ server srv1 ${s1_addr}:${s1_port}
+
+ frontend fe2
+ bind "fd@${fe2}"
+ use_backend be2
+
+ backend be2
+ http-response wait-for-body time 100ms if { status eq 200 }
+ http-response wait-for-body time 100ms at-least 1000 if { status eq 201 }
+ server srv1 ${s2_addr}:${s2_port}
+} -start
+
+
+client c1 -connect ${h1_fe1_sock} {
+ send "GET /a HTTP/1.1\r\n"
+ send "Content-Length: 1001\r\n\r\n"
+ delay 0.01
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ rxresp
+ expect resp.status == 408
+} -run
+
+client c2 -connect ${h1_fe1_sock} {
+ send "GET /a HTTP/1.1\r\n"
+ send "Content-Length: 1001\r\n\r\n"
+ delay 0.01
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ delay 0.01
+ send "1"
+ rxresp
+ expect resp.status == 200
+
+ send "GET /b HTTP/1.1\r\n"
+ send "Content-Length: 1001\r\n\r\n"
+ delay 0.01
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ send "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=====================================\n"
+ delay 0.1
+ send "1"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c3 -connect ${h1_fe2_sock} {
+ txreq
+ rxresp
+ expect resp.status == 504
+} -run
+
+client c4 -connect ${h1_fe2_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 1001
+
+ txreq
+ rxresp
+ expect resp.status == 201
+ expect resp.bodylen == 1001
+} -run
diff --git a/reg-tests/http-messaging/protocol_upgrade.vtc b/reg-tests/http-messaging/protocol_upgrade.vtc
new file mode 100644
index 0000000..ebb6328
--- /dev/null
+++ b/reg-tests/http-messaging/protocol_upgrade.vtc
@@ -0,0 +1,228 @@
+# This reg-test checks the full support of HTTP protocol upgrade, using a GET
+# method and a Connection: Upgrade header. The equivalent mechanism has been
+# defined in rfc8441 for HTTP/2 using CONNECT and a new pseudo-header
+# :protocol. Check that haproxy handles properly h1/h2 translation of protocol
+# upgrade requests and responses.
+
+varnishtest "h1/h2 support for protocol upgrade test"
+
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+
+# http/1.1 server
+server srv_h1 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.connection == "upgrade"
+ expect req.http.upgrade == "custom_protocol"
+
+ txresp \
+ -status 101 \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: custom_protocol"
+} -repeat 2 -start
+
+# http2 server
+server srv_h2 {
+ rxpri
+
+ stream 0 {
+ # manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL
+ sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 01"
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ rxhdrs
+ expect req.method == "CONNECT"
+ expect req.http.:scheme == "https"
+ expect req.http.:path == "/"
+ expect req.http.:authority == "127.0.0.1"
+ expect req.http.:protocol == "custom_protocol"
+
+ txresp \
+ -status 200
+ } -run
+} -repeat 2 -start
+
+# http2 server without support for RFC8441
+server srv_h2_no_ws {
+ rxpri
+
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ rxrst
+ } -run
+} -start
+
+# http2 server without support for RFC8441 : settings announced with value 0
+server srv_h2_no_ws2 {
+ rxpri
+
+ stream 0 {
+ # manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL with a value of 0
+ sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 00"
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ rxrst
+ } -run
+} -start
+
+haproxy hap -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ # h1 frontend connected to h2 frontend
+ listen frt_h1_h2
+ bind "fd@${frt_h1_h2}"
+ server feh2_srv ${hap_frt_h2_addr}:${hap_frt_h2_port} proto h2
+
+ # h2 frontend connected to srv_h1
+ listen frt_h2
+ bind "fd@${frt_h2}" proto h2
+ server srv_h1 ${srv_h1_addr}:${srv_h1_port}
+
+ # h1 frontend connected to srv_h2
+ listen frt_h1
+ bind "fd@${frt_h1}"
+ server srv_h2 ${srv_h2_addr}:${srv_h2_port} proto h2
+
+ # h1 frontend connected to srv_h2_no_ws
+ listen frt_h1_no_ws
+ bind "fd@${frt_h1_no_ws}"
+ server srv_h2_no_ws ${srv_h2_no_ws_addr}:${srv_h2_no_ws_port} proto h2
+
+ # h1 frontend connected to srv_h2_no_ws2
+ listen frt_h1_no_ws2
+ bind "fd@${frt_h1_no_ws2}"
+ server srv_h2_no_ws2 ${srv_h2_no_ws2_addr}:${srv_h2_no_ws2_port} proto h2
+
+ # h2 frontend connected to h1 frontend
+ listen frt_h2_h1
+ bind "fd@${frt_h2_h1}" proto h2
+ server frt_h1 ${hap_frt_h1_addr}:${hap_frt_h1_port}
+} -start
+
+## connect to h1 translation frontend
+client c1_h1_h2 -connect ${hap_frt_h1_h2_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: custom_protocol"
+
+ rxresp
+ expect resp.status == 101
+ expect resp.http.connection == "upgrade"
+ expect resp.http.upgrade == "custom_protocol"
+} -run
+
+# connect to h2 server frontend
+client c2_h2 -connect ${hap_frt_h2_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "CONNECT" \
+ -scheme "http" \
+ -url "/" \
+ -hdr ":authority" "127.0.0.1" \
+ -hdr ":protocol" "custom_protocol"
+
+ rxhdrs
+ expect resp.status == 200
+ } -run
+} -run
+
+# connect to h2 translation frontend
+client c3_h2_h1 -connect ${hap_frt_h2_h1_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "CONNECT" \
+ -scheme "http" \
+ -url "/" \
+ -hdr ":authority" "127.0.0.1" \
+ -hdr ":protocol" "custom_protocol"
+
+ rxhdrs
+ expect resp.status == 200
+ } -run
+} -run
+
+# connect to h1 server frontend
+client c4_h1 -connect ${hap_frt_h1_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: custom_protocol"
+
+ rxresp
+ expect resp.status == 101
+ expect resp.http.connection == "upgrade"
+ expect resp.http.upgrade == "custom_protocol"
+} -run
+
+# connect via h1 server frontend to h2 server without RFC8441 support
+client c5 -connect ${hap_frt_h1_no_ws_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: custom_protocol"
+
+ rxresp
+ expect resp.status == 502
+} -run
+
+# connect via h1 server frontend to h2 server without RFC8441 support
+client c6 -connect ${hap_frt_h1_no_ws2_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: custom_protocol"
+
+ rxresp
+ expect resp.status == 502
+} -run
diff --git a/reg-tests/http-messaging/scheme_based_normalize.vtc b/reg-tests/http-messaging/scheme_based_normalize.vtc
new file mode 100644
index 0000000..3edbafb
--- /dev/null
+++ b/reg-tests/http-messaging/scheme_based_normalize.vtc
@@ -0,0 +1,125 @@
+varnishtest "scheme based normalization (rfc3982 6.3.2)"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+syslog S1 -level info {
+ recv
+ expect ~ "^.* uri: GET http://hostname/ HTTP/2.0; host: {hostname}$"
+
+ recv
+ expect ~ "^.* uri: GET http://hostname:8080/ HTTP/2.0; host: {hostname:8080}$"
+
+ recv
+ expect ~ "^.* uri: GET https://hostname/ HTTP/2.0; host: {hostname}$"
+
+ recv
+ expect ~ "^.* uri: GET https://hostname:80/ HTTP/2.0; host: {hostname:80}$"
+} -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}" proto h2
+
+ http-request capture req.hdr(host) len 512
+ log-format "uri: %r; host: %hr"
+ log ${S1_addr}:${S1_port} len 2048 local0 debug err
+
+ http-request return status 200
+} -start
+
+# default port 80 with http scheme => should be normalized
+client c1 -connect ${h1_fe_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "GET" \
+ -scheme "http" \
+ -url "/" \
+ -hdr ":authority" "hostname:80"
+ rxhdrs
+ expect resp.status == 200
+ } -run
+} -run
+
+# port 8080 with http scheme => no normalization
+client c2 -connect ${h1_fe_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "GET" \
+ -scheme "http" \
+ -url "/" \
+ -hdr ":authority" "hostname:8080"
+ rxhdrs
+ expect resp.status == 200
+ } -run
+} -run
+
+# default port 443 with https scheme => should be normalized
+client c3 -connect ${h1_fe_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "GET" \
+ -scheme "https" \
+ -url "/" \
+ -hdr ":authority" "hostname:443"
+ rxhdrs
+ expect resp.status == 200
+ } -run
+} -run
+
+# port 80 with https scheme => no normalization
+client c4 -connect ${h1_fe_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "GET" \
+ -scheme "https" \
+ -url "/" \
+ -hdr ":authority" "hostname:80"
+ rxhdrs
+ expect resp.status == 200
+ } -run
+} -run
+
+syslog S1 -wait
diff --git a/reg-tests/http-messaging/srv_ws.vtc b/reg-tests/http-messaging/srv_ws.vtc
new file mode 100644
index 0000000..b26908e
--- /dev/null
+++ b/reg-tests/http-messaging/srv_ws.vtc
@@ -0,0 +1,180 @@
+# This reg-test checks websocket support in regards with the server keyword
+# 'ws'
+
+varnishtest "h2 backend websocket management via server keyword"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature ignore_unknown_macro
+
+# haproxy server
+haproxy hapsrv -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}"
+ bind "fd@${fessl}" ssl crt ${testdir}/common.pem alpn h2,http/1.1
+ capture request header sec-websocket-key len 128
+ http-request set-var(txn.ver) req.ver
+ use_backend be
+
+ backend be
+ # define websocket ACL
+ acl ws_handshake hdr(upgrade) -m str websocket
+
+ # handle non-ws streams
+ http-request return status 200 if !ws_handshake
+
+ # handle ws streams
+ #capture request header sec-websocket-key len 128
+ http-request return status 200 hdr connection upgrade hdr upgrade websocket hdr sec-websocket-accept "%[capture.req.hdr(0),concat(258EAFA5-E914-47DA-95CA-C5AB0DC85B11,,),sha1,base64]" if ws_handshake
+ http-after-response set-status 101 if { status eq 200 } { res.hdr(upgrade) -m str websocket }
+ http-after-response set-header x-backend-protocol "%[var(txn.ver)]"
+} -start
+
+# haproxy LB
+haproxy hap -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ # proto X ws h1 -> websocket on h1
+ listen li
+ bind "fd@${li}"
+ server hap_srv ${hapsrv_fe_addr}:${hapsrv_fe_port} proto h2 ws h1
+
+ # proto X ws h2 -> websocket on h2
+ listen lih2
+ bind "fd@${lih2}"
+ server hap_srv ${hapsrv_fe_addr}:${hapsrv_fe_port} proto h2 ws h2
+
+ # alpn h2,http/1.1 ws h2 -> websocket on h2
+ listen lisslh2
+ bind "fd@${lisslh2}"
+ server hap_srv ${hapsrv_fessl_addr}:${hapsrv_fessl_port} ssl verify none alpn h2,http/1.1 ws h2
+ http-response set-header x-alpn "%[ssl_bc_alpn]"
+
+ # ws auto -> websocket on h1
+ listen liauto
+ bind "fd@${liauto}"
+ server hap_srv ${hapsrv_fe_addr}:${hapsrv_fe_port}
+
+ # alpn h2,http/1.1 ws auto -> websocket on h1
+ listen lissl
+ bind "fd@${lissl}"
+ server hap_srv ${hapsrv_fessl_addr}:${hapsrv_fessl_port} ssl verify none alpn h2,http/1.1 ws auto
+ http-response set-header x-alpn "%[ssl_bc_alpn]"
+ # alpn h2,http/1.1 ws auto -> websocket on h1
+ listen lisslauto
+ bind "fd@${lisslauto}"
+ server hap_srv ${hapsrv_fessl_addr}:${hapsrv_fessl_port} ssl verify none alpn h2,http/1.1
+ http-response set-header x-alpn "%[ssl_bc_alpn]"
+
+ # proto h2 ws auto -> websocket on h2
+ listen liauto2
+ bind "fd@${liauto2}"
+ server hap_srv ${hapsrv_fe_addr}:${hapsrv_fe_port} proto h2
+
+ # alpn h2 ws auto -> websocket on h2
+ listen lisslauto2
+ bind "fd@${lisslauto2}"
+ server hap_srv ${hapsrv_fessl_addr}:${hapsrv_fessl_port} ssl verify none alpn h2 ws auto
+ http-response set-header x-alpn "%[ssl_bc_alpn]"
+} -start
+
+client c1 -connect ${hap_li_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.x-backend-protocol == "1.1"
+} -run
+
+client c1.2 -connect ${hap_lih2_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.x-backend-protocol == "2.0"
+} -run
+
+client c1.3 -connect ${hap_liauto_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.x-backend-protocol == "1.1"
+} -run
+
+client c1.4 -connect ${hap_liauto2_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.x-backend-protocol == "2.0"
+} -run
+
+client c2 -connect ${hap_lisslauto_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.x-alpn == "http/1.1"
+} -run
+
+client c2.2 -connect ${hap_lisslauto2_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.x-alpn == "h2"
+} -run
+
+client c2.3 -connect ${hap_lisslh2_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.x-alpn == "h2"
+} -run
diff --git a/reg-tests/http-messaging/websocket.vtc b/reg-tests/http-messaging/websocket.vtc
new file mode 100644
index 0000000..5f4b960
--- /dev/null
+++ b/reg-tests/http-messaging/websocket.vtc
@@ -0,0 +1,205 @@
+# This reg-test is uses to test respect of the websocket protocol according to
+# rfc6455.
+#
+# In particular, a request/response without a websocket key must be rejected by
+# haproxy. Note that in the tested case (h1 on both sides), haproxy does not
+# validate the key of the server but only checks its presence.
+#
+# For the case h2 client/h1 server, haproxy would add the key and validates it.
+# However, there is no way to check this case quickly at the moment using vtest.
+
+varnishtest "WebSocket test"
+
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+
+# valid websocket server
+server s1 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.connection == "upgrade"
+ expect req.http.upgrade == "websocket"
+ expect req.http.sec-websocket-key == "dGhlIHNhbXBsZSBub25jZQ=="
+
+ txresp \
+ -status 101 \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
+} -repeat 2 -start
+
+# non-conformant server: no websocket key
+server s2 {
+ rxreq
+ expect req.method == "GET"
+ expect req.http.connection == "upgrade"
+ expect req.http.upgrade == "websocket"
+
+ txresp \
+ -status 101 \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket"
+} -start
+
+# haproxy instance used as a server
+# generate a http/1.1 websocket response with the valid key
+haproxy hap_srv -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen fe1
+ bind "fd@${fe1}"
+
+ # reject if the request does not contains a websocket key
+ acl ws_handshake hdr(sec-websocket-key) -m found
+ http-request reject unless ws_handshake
+
+ # return a valid websocket handshake response
+ capture request header sec-websocket-key len 128
+ http-request return status 200 hdr connection upgrade hdr upgrade websocket hdr sec-websocket-accept "%[capture.req.hdr(0),concat(258EAFA5-E914-47DA-95CA-C5AB0DC85B11,,),sha1,base64]"
+ http-after-response set-status 101 if { status eq 200 }
+} -start
+
+# haproxy instance used as a server
+# generate a http/1.1 websocket response with an invalid key
+haproxy hap_srv_bad_key -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen fe1
+ bind "fd@${fe1}"
+
+ # reject if the request does not contains a websocket key
+ acl ws_handshake hdr(sec-websocket-key) -m found
+ http-request reject unless ws_handshake
+
+ # return an invalid websocket handshake response
+ capture request header sec-websocket-key len 128
+ http-request return status 200 hdr connection upgrade hdr upgrade websocket hdr sec-websocket-accept "invalid_key"
+ http-after-response set-status 101 if { status eq 200 }
+} -start
+
+haproxy hap -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen fe1
+ bind "fd@${fe1}"
+ server s1 ${s1_addr}:${s1_port}
+
+ listen fe2
+ bind "fd@${fe2}"
+ server s2 ${s2_addr}:${s2_port}
+
+ listen fe3
+ bind "fd@${fe3}" proto h2
+ server hap_srv ${hap_srv_fe1_addr}:${hap_srv_fe1_port}
+
+ listen fe4
+ bind "fd@${fe4}" proto h2
+ server hap_srv_bad_key ${hap_srv_bad_key_fe1_addr}:${hap_srv_bad_key_fe1_port}
+} -start
+
+# standard request
+client c1 -connect ${hap_fe1_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+ rxresp
+ expect resp.status == 101
+ expect resp.http.connection == "upgrade"
+ expect resp.http.upgrade == "websocket"
+ expect resp.http.sec-websocket-accept == "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
+} -run
+
+# missing websocket key
+client c2 -connect ${hap_fe1_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket"
+
+ rxresp
+ expect resp.status == 400
+} -run
+
+# missing key on server side
+client c3 -connect ${hap_fe2_sock} {
+ txreq \
+ -req "GET" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: websocket" \
+ -hdr "sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
+
+ rxresp
+ expect resp.status == 502
+} -run
+
+# connect with http/2 on a http/1.1 websocket server
+# the key must be provided by haproxy
+client c4 -connect ${hap_fe3_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "CONNECT" \
+ -scheme "http" \
+ -url "/" \
+ -hdr ":authority" "127.0.0.1" \
+ -hdr ":protocol" "websocket"
+
+ rxhdrs
+ expect resp.status == 200
+ } -run
+} -run
+
+# connect with http/2 on a http/1.1 websocket server
+# however, the server will respond with an invalid key
+# haproxy is responsible to reject the request and returning a 502 to the client
+client c5 -connect ${hap_fe4_sock} {
+ txpri
+ stream 0 {
+ txsettings
+ rxsettings
+ txsettings -ack
+ rxsettings
+ expect settings.ack == true
+ } -run
+
+ stream 1 {
+ txreq \
+ -req "CONNECT" \
+ -scheme "http" \
+ -url "/" \
+ -hdr ":authority" "127.0.0.1" \
+ -hdr ":protocol" "websocket"
+
+ rxhdrs
+ expect resp.status == 502
+ } -run
+} -run
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..e61176a
--- /dev/null
+++ b/reg-tests/http-rules/acl_cli_spaces.vtc
@@ -0,0 +1,79 @@
+varnishtest "haproxy ACL, CLI and mCLI spaces"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.0
+
+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..97469b5
--- /dev/null
+++ b/reg-tests/http-rules/converters_ipmask_concat_strcmp_field_word.vtc
@@ -0,0 +1,218 @@
+varnishtest "Minimal tests for 1.9 converters: ipmask,concat,strcmp,field,word"
+feature ignore_unknown_macro
+
+# concat,strcmp,ipmask(ipv6mask) need 1.9
+#REQUIRE_VERSION=1.9
+
+# ipmask 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"
+ 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 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
+
+ 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 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"
+ 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:]]+ gpc0=0 conn_cnt=1\\n0x[a-f0-9]+: key=::ffff:192\\.168\\.1\\.101 use=0 exp=[[:digit:]]+ gpc0=0 conn_cnt=1\\n0x[a-f0-9]+: key=2001:db8:c001:c01a:[0:]+ use=0 exp=[[:digit:]]+ 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/h1_to_h1c.vtc b/reg-tests/http-rules/h1_to_h1c.vtc
new file mode 100644
index 0000000..9ae73f7
--- /dev/null
+++ b/reg-tests/http-rules/h1_to_h1c.vtc
@@ -0,0 +1,186 @@
+varnishtest "Composite HTTP manipulation test (H1 clear to H1 clear)"
+#REQUIRE_VERSION_BELOW=1.9
+
+# 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 == 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 "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ 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
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..9e7eb60
--- /dev/null
+++ b/reg-tests/http-rules/h1or2_to_h1c.vtc
@@ -0,0 +1,226 @@
+varnishtest "Composite HTTP manipulation test (H1 and H2 clear to H1 clear)"
+#REQUIRE_VERSION=1.9
+
+# 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 == 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
+} -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-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 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 %[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 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]"
+ 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 "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 "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/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_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..82c8107
--- /dev/null
+++ b/reg-tests/http-rules/normalize_uri.vtc
@@ -0,0 +1,536 @@
+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}"
+
+ 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}"
+
+ 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
+
+ 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
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
diff --git a/reg-tests/http-set-timeout/set_timeout.vtc b/reg-tests/http-set-timeout/set_timeout.vtc
new file mode 100644
index 0000000..ebaa6a3
--- /dev/null
+++ b/reg-tests/http-set-timeout/set_timeout.vtc
@@ -0,0 +1,82 @@
+varnishtest "http-request set-timeout test"
+
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+
+server srv_h1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+syslog Slog1 -level info {
+ recv
+ expect ~ "^.*timeout: 5000 5000.*$"
+} -start
+
+syslog Slog2 -level info {
+ recv
+ expect ~ "^.*timeout: 5000 5000.*$"
+} -start
+
+syslog Slog3 -level info {
+ recv
+ expect ~ "^.*timeout: 5000 3000.*$"
+} -start
+
+haproxy hap -conf {
+ defaults
+ timeout connect 5s
+ timeout client 5s
+ timeout server 5s
+ log global
+
+ listen li1
+ mode http
+ bind "fd@${li1}"
+ log-format "timeout: %[be_server_timeout] %[cur_server_timeout]"
+ log ${Slog1_addr}:${Slog1_port} len 2048 local0 debug err
+ server srv_h1 ${srv_h1_addr}:${srv_h1_port}
+
+ listen li2
+ mode http
+ bind "fd@${li2}"
+ log-format "timeout: %[be_server_timeout] %[cur_server_timeout]"
+ log ${Slog2_addr}:${Slog2_port} len 2048 local0 debug err
+ http-request set-timeout server 5s
+ server srv_h1 ${srv_h1_addr}:${srv_h1_port}
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ log-format "timeout: %[be_server_timeout] %[cur_server_timeout]"
+ log ${Slog3_addr}:${Slog3_port} len 2048 local0 debug err
+ default_backend be1
+
+ backend be1
+ mode http
+ http-request set-timeout server int(3),mul(1000)
+ server srv_h1 ${srv_h1_addr}:${srv_h1_port}
+} -start
+
+client c1 -connect ${hap_li1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -connect ${hap_li2_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c3 -connect ${hap_fe1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+syslog Slog1 -wait
+syslog Slog2 -wait
+syslog Slog3 -wait
diff --git a/reg-tests/jwt/build_token.py b/reg-tests/jwt/build_token.py
new file mode 100755
index 0000000..2f368ab
--- /dev/null
+++ b/reg-tests/jwt/build_token.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+
+# JWT package can be installed via 'pip install pyjwt' command
+
+import sys
+import jwt
+import json
+
+if len(sys.argv) != 4:
+ print(sys.argv[0],"<alg> <json_to_sign> <priv_key>")
+ quit()
+
+
+alg=sys.argv[1]
+json_to_sign=sys.argv[2]
+priv_key_file=sys.argv[3]
+
+with open(priv_key_file) as file:
+ priv_key = file.read()
+
+print(jwt.encode(json.loads(json_to_sign),priv_key,algorithm=alg))
+
diff --git a/reg-tests/jwt/es256-public.pem b/reg-tests/jwt/es256-public.pem
new file mode 100644
index 0000000..ac69e6d
--- /dev/null
+++ b/reg-tests/jwt/es256-public.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjq7vv/FURryqr7ukvkrn1ek5rjCM
+hOngjD17uQTZN7fo1QRIV18lPx5O2Ed5ok/j8j/hZaFOB6TNshNmthk3dA==
+-----END PUBLIC KEY-----
diff --git a/reg-tests/jwt/es384-public.pem b/reg-tests/jwt/es384-public.pem
new file mode 100644
index 0000000..b726e12
--- /dev/null
+++ b/reg-tests/jwt/es384-public.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEm1LU84aybo84c3LWQtaILtzzQsU9sT1b
+uda6u6NBJ9FrVEAQkk5tABimCcn60bxSe7s1+oM8xLsu2RuGibQzbTuL75pEs5kx
+HPQW4nmOz0zXCjvAvtQTA7vMirb/Oste
+-----END PUBLIC KEY-----
diff --git a/reg-tests/jwt/es512-public.pem b/reg-tests/jwt/es512-public.pem
new file mode 100644
index 0000000..46520ac
--- /dev/null
+++ b/reg-tests/jwt/es512-public.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAMJ5MagTv8l+AjWZLLJ+xxV9/iNhb
+xE52xa8uMCiuBM5VHPcBLEPi1haY17abA6j0F173bK/AN7MBOpT4pAFP07IAEpF7
+QWzw+YH7hrWcT66gzfPysgpzktY+xpMFYhmLH1h9DGiJE+5t5FF5+mCg4GXi1Aez
+UzHc9yLw+6meeTWKcv4=
+-----END PUBLIC KEY-----
diff --git a/reg-tests/jwt/jws_verify.vtc b/reg-tests/jwt/jws_verify.vtc
new file mode 100644
index 0000000..d9a6328
--- /dev/null
+++ b/reg-tests/jwt/jws_verify.vtc
@@ -0,0 +1,379 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the JSON Web Token (JWT) converters to verify a token's signature.
+# It uses the http_auth_bearer sample fetch to fetch a token contained in an
+# HTTP Authorization header (with the Bearer scheme) which is the common way of
+# transmitting a token (see RFC6750). It then uses the jwt_header_query
+# converter to get the "alg" field declared in the token's JOSE header and
+# gives it to the jwt_verify converter with the appropriate certificate.
+#
+# All the supported algorithms are tested at least once (HMAC, RSA and ECDSA)
+# and the errors codes returned by jwt_verify are tested as well.
+
+varnishtest "Test the 'set ssl ca-file' feature of the CLI"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 22 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen main-fe
+ bind "fd@${mainfe}"
+
+ use_backend hsXXX_be if { path_beg /hs }
+ use_backend rsXXX_be if { path_beg /rs }
+ use_backend esXXX_be if { path_beg /es }
+ use_backend auth_bearer_be if { path /auth_bearer }
+ default_backend dflt_be
+
+
+ backend hsXXX_be
+ http-request set-var(txn.bearer) http_auth_bearer
+ http-request set-var(txn.jwt_alg) var(txn.bearer),jwt_header_query('$.alg')
+
+ http-request deny unless { var(txn.jwt_alg) -m beg "HS" }
+
+ http-response set-header x-jwt-token %[var(txn.bearer)]
+ http-response set-header x-jwt-alg %[var(txn.jwt_alg)]
+
+ http-response set-header x-jwt-verify-HS256 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"hmac key hs256")] if { var(txn.jwt_alg) -m str "HS256" }
+ http-response set-header x-jwt-verify-HS384 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"hmac key hs384")] if { var(txn.jwt_alg) -m str "HS384" }
+ http-response set-header x-jwt-verify-HS512 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"hmac key hs512")] if { var(txn.jwt_alg) -m str "HS512" }
+ server s1 ${s1_addr}:${s1_port}
+
+ backend rsXXX_be
+ http-request set-var(txn.bearer) http_auth_bearer
+ http-request set-var(txn.jwt_alg) var(txn.bearer),jwt_header_query('$.alg')
+
+ http-request deny unless { var(txn.jwt_alg) -m beg "RS" }
+
+ http-response set-header x-jwt-token %[var(txn.bearer)]
+ http-response set-header x-jwt-alg %[var(txn.jwt_alg)]
+
+ http-response set-header x-jwt-verify-RS256 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"${testdir}/rsa-public.pem")] if { var(txn.jwt_alg) -m str "RS256" }
+ http-response set-header x-jwt-verify-RS384 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"${testdir}/rsa-public.pem")] if { var(txn.jwt_alg) -m str "RS384" }
+ http-response set-header x-jwt-verify-RS512 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"${testdir}/rsa-public.pem")] if { var(txn.jwt_alg) -m str "RS512" }
+ server s1 ${s1_addr}:${s1_port}
+
+ backend esXXX_be
+ http-request set-var(txn.bearer) http_auth_bearer
+ http-request set-var(txn.jwt_alg) var(txn.bearer),jwt_header_query('$.alg')
+
+ http-request deny unless { var(txn.jwt_alg) -m beg "ES" }
+
+ http-response set-header x-jwt-token %[var(txn.bearer)]
+ http-response set-header x-jwt-alg %[var(txn.jwt_alg)]
+
+ http-response set-header x-jwt-verify-ES256 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"${testdir}/es256-public.pem")] if { var(txn.jwt_alg) -m str "ES256" }
+ http-response set-header x-jwt-verify-ES384 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"${testdir}/es384-public.pem")] if { var(txn.jwt_alg) -m str "ES384" }
+ http-response set-header x-jwt-verify-ES512 %[var(txn.bearer),jwt_verify(txn.jwt_alg,"${testdir}/es512-public.pem")] if { var(txn.jwt_alg) -m str "ES512" }
+ server s1 ${s1_addr}:${s1_port}
+
+
+ # This backend will only be used to test the http_auth_bearer sample fetch.
+ # No jwt_verify will then be performed.
+ backend auth_bearer_be
+ http-request set-var(txn.bearer) http_auth_bearer("Custom-Authorization")
+
+ http-response set-header x-jwt-token %[var(txn.bearer)]
+
+ server s1 ${s1_addr}:${s1_port}
+
+ # This backend will mostly be used to test error cases (invalid tokens, algorithm and so on)
+ backend dflt_be
+ http-request set-var(txn.bearer) http_auth_bearer
+ http-request set-var(txn.jwt_alg) var(txn.bearer),jwt_header_query('$.alg')
+
+ http-request set-var(txn.jwt_verify) var(txn.bearer),jwt_verify(txn.jwt_alg,"unknown_cert.pem")
+
+ http-response set-header x-jwt-token %[var(txn.bearer)]
+ http-response set-header x-jwt-alg %[var(txn.jwt_alg)]
+ http-response set-header x-jwt-verify %[var(txn.jwt_verify)]
+
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+
+client c1 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"HS256","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # HMAC key : 'hmac key hs256'
+ # OpenSSL cmd : openssl dgst -sha256 -mac HMAC -macopt key:'hmac key hs256' data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/hs256" -hdr "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.hhj1mbYgezxFoYwinThsZQbckYHt4jJlRoQ7W8ksrFM"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "HS256"
+ expect resp.http.x-jwt-verify-HS256 == "1"
+} -run
+
+client c2 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"HS384","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # HMAC key : 'hmac key hs384'
+ # OpenSSL cmd : openssl dgst -sha384 -mac HMAC -macopt key:'hmac key hs384' data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/hs384" -hdr "Authorization: Bearer eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.3EsbLfl6DDh5nZMkLWg3ssCurFHyOhXP28a4PDS48aPAIoYLzHchtXmNaYI8He-R"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "HS384"
+ expect resp.http.x-jwt-verify-HS384 == "1"
+} -run
+
+client c3 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"HS512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # HMAC key : 'hmac key hs512'
+ # OpenSSL cmd : openssl dgst -sha512 -mac HMAC -macopt key:'hmac key hs512' data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/hs512" -hdr "Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.K4Yze5N7jeJrDbJymphaH1YsFlYph5F-U75HzBRKDybrN7WBO494EgNG77mAQj4CVci_xbTD_IsqY2umO0f47A"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "HS512"
+ expect resp.http.x-jwt-verify-HS512 == "1"
+} -run
+
+# The following token is invalid (it has three extra characters at the end of the signature)
+client c4 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"HS512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # HMAC key : 'hmac key hs512'
+ # OpenSSL cmd : openssl dgst -sha512 -mac HMAC -macopt key:'hmac key hs512' data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/hs512" -hdr "Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.K4Yze5N7jeJrDbJymphaH1YsFlYph5F-U75HzBRKDybrN7WBO494EgNG77mAQj4CVci_xbTD_IsqY2umO0f47AAAA"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "HS512"
+ expect resp.http.x-jwt-verify-HS512 == "-3"
+} -run
+
+
+client c5 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"RS256","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # OpenSSL cmd : openssl dgst -sha256 -sign rsa-private.pem data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/rs256" -hdr "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.hRqFM87JzV_YinYhdERp2E9BLhl6s7I5J37GTXAeT5fixJx-OCjTFvwKssyVo7fWAFcQMdQU7vGEXDOiWbNaMUFGIsMxx0Uflk0BeNwk6pWvNGk8KZGMtiqOv-IuPdAiaSW_xhxLHIk7eOwVefvBfk8j2hgU9yoHN87AYnl8oEnzrkzwWvEt-x-P2zB4s_VwhF0gbL1G4FsP5hxWL1HWmSFLBpvWaL5Lx3OJE7mLRLRf8TpMwEe4ROakzMpiv9Xk1H3mZth6d2a91F5Bm65MIJpJ7P2kEL3tdS62VRx8DM_SlsFuWcsqryO3CDQquMbwzAvfRgLPy8PBLRLT64wM3mZtue5GI2KUlqSYsSwKwK580b4drosLvAS75l_4jJwdwuQEvVd8Gry3DWS2mKJSMefmGfD-cdty1vvszs5sUa96Gf7Ro5DvkgXtVCKYk8KJLI62YgZd5S3M0ucP5NLBc_flUi4A2B_aSkd7NDM0ELddk0y48pcF95tejcvliGIy1GRRwevdqensXXQrFweFSZVvuKo8c9pcCBVfKTSllgL0lFGyI_vz6dUYt69I1gqWBDeGcA2XQUBJqfX3o9nkhZspA7b7QxMESatoATsM_XmfhbwsyY-sTq25XIGC4awaZHViZr1YFVD6BwNZWBCEBvW5zObiD5h5A5AgWoBv14E"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "RS256"
+ expect resp.http.x-jwt-verify-RS256 == "1"
+} -run
+
+client c6 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"RS384","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # OpenSSL cmd : openssl dgst -sha384 -sign rsa-private.pem data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/rs384" -hdr "Authorization: Bearer eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.GuR-v91EMCVvvTTLiE56O0oDAKeQ5JdLqvHtrgOp2MbUtF7zCDutV0LTmMo4qDNVpvTnD3GZfTTGaVUTvW7kIQ3_1iEVAg61qVWkT9rtHHxifDX70RDBKkvNcMWyQH-dFP_FUvCmhCu7q-AzgBT6PHvs5ZqYyQvlQ1gSWZEPFi184dhvcUQrQC6CySEAdOzIryIHH2oQjN_a9lA9V9M_CH3P-AAwFE7NwUE1H1SGIYM4NHcngEZ3B4lBCHOhhgQMpfagcxQjjXv7VfeSqza6OZDpupwlOl34bb0gnFDGMh4hHSS6iHvvwCeCkclbyvKV0Vq0MaRtJuoKRF-_Oww-nKT_bfNtbF6MeOQLNRlYjGCHerWoBtjv3w2KjoLvQ5iGIFI3cEguyrrKNimpovF4Y5uINH0pWdRF99zOwVUlcJBk3RivIb--Y6s47aNFIVWimUpSn-8MSHTla20TYbcdVaZaMur09Cw500jPrOy6jFqVydSnmU6r13NkmCD5-Bl0mgwGtpZcOQExrnIcPQky12kQJAIrffVblvtkd-8FIBPBy1uBKCgkE-q9_suEvBTdvaoTocBmPcIxfPjZUVXeU3UmnRrXEz17pue0YfrwK9CUR9UoP0F5C7O5eSbAtZNm4Hpkiah0w7qugWG3esMgku3-xx0B2xwg6Ul7bAgEJFg"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "RS384"
+ expect resp.http.x-jwt-verify-RS384 == "1"
+} -run
+
+client c7 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"RS512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # OpenSSL cmd : openssl dgst -sha512 -sign rsa-private.pem data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/rs512" -hdr "Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.dgUDvxbWXV-q9lVFDVDt6zffrAjCMkKL7UURz-vvc6utCNMEgt8jSkDWi-mt-jmttkD5mwHqUf3HxWPhfjYNmkTok_XL79F5RXhiF_cu_2oDLDc-RuXdrHaRt9xjUIyZhVJMhaMLdmpcAokQlZxc2W6aj92HKzk3EjyHwfdwfKQNgMooXNzxjE9vCHUbahyLZvtPwiqDtYUSnvN_XOpAMUilxByJStwNqdB7MaOxeAzn76nITh6DqD1bNtxBiLzA7MxYdfsUSmXHMLpkWNAhlrcEIJui9PKm9E0OLFD3M7cCqi6rVvzDxvHqXz3-fcXiSJSRrSmSTu1_ok35TT4WwA9SkHpGe2MJ3uc-8CRlYmjDTcLyXWs_d8i3iNozo6xgiwqIkty4HqScTjhXndRQdmiK-RcUfNLM0Iqm6wYgOifWj728_9GCtdjup-C2uVPdwVwuOjwLbzctZLlFqH3i5IGrCfuOOCAcc_vN3REFqSrDEi4-9qpXuh7yk5pOaiCZYr3-uVhmY5neo55_eV8N3NooDyztwkzRtB_DdbaNrqxk3WEHU79Hseg7c1mkXGm6Djqt3dkkrdpbltzRLrnGKxA4-FzccKOT_P27UYmxQSkyfpAQhfH3jpOE0n9-UYyULbMOY7ZIypXUTquJnrZM3rD_NypU7Jg8uBBGqcziZFc"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "RS512"
+ expect resp.http.x-jwt-verify-RS512 == "1"
+} -run
+
+# The following token is invalid (the signature used SHA384 instead of SHA512)
+client c8 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"RS512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # OpenSSL cmd : openssl dgst -sha512 -sign rsa-private.pem data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/rs512" -hdr "Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.GuR-v91EMCVvvTTLiE56O0oDAKeQ5JdLqvHtrgOp2MbUtF7zCDutV0LTmMo4qDNVpvTnD3GZfTTGaVUTvW7kIQ3_1iEVAg61qVWkT9rtHHxifDX70RDBKkvNcMWyQH-dFP_FUvCmhCu7q-AzgBT6PHvs5ZqYyQvlQ1gSWZEPFi184dhvcUQrQC6CySEAdOzIryIHH2oQjN_a9lA9V9M_CH3P-AAwFE7NwUE1H1SGIYM4NHcngEZ3B4lBCHOhhgQMpfagcxQjjXv7VfeSqza6OZDpupwlOl34bb0gnFDGMh4hHSS6iHvvwCeCkclbyvKV0Vq0MaRtJuoKRF-_Oww-nKT_bfNtbF6MeOQLNRlYjGCHerWoBtjv3w2KjoLvQ5iGIFI3cEguyrrKNimpovF4Y5uINH0pWdRF99zOwVUlcJBk3RivIb--Y6s47aNFIVWimUpSn-8MSHTla20TYbcdVaZaMur09Cw500jPrOy6jFqVydSnmU6r13NkmCD5-Bl0mgwGtpZcOQExrnIcPQky12kQJAIrffVblvtkd-8FIBPBy1uBKCgkE-q9_suEvBTdvaoTocBmPcIxfPjZUVXeU3UmnRrXEz17pue0YfrwK9CUR9UoP0F5C7O5eSbAtZNm4Hpkiah0w7qugWG3esMgku3-xx0B2xwg6Ul7bAgEJFg"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "RS512"
+ expect resp.http.x-jwt-verify-RS512 == "0"
+} -run
+
+
+
+client c9 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES256","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # Key gen process : openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out es256-private.pem; openssl ec -in es256-private.pem -pubout -out es256-public.pem
+ # Token creation : ./build_token.py ES256 '{"sub":"1234567890","name":"John Doe","iat":1516239022}' es256-private.pem
+
+ txreq -url "/es256" -hdr "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.pNI_c5mHE3mLV0YDpstlP4l3t5XARLl6OmcKLuvF5r60m-C63mbgfKWdPjmJPMTCmX_y50YW_v2SKw0ju0tJHw"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES256"
+ expect resp.http.x-jwt-verify-ES256 == "1"
+} -run
+
+client c10 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES384","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # Key gen process : openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out es384-private.pem; openssl ec -in es384-private.pem -pubout -out es384-public.pem
+ # Token creation : ./build_token.py ES384 '{"sub":"1234567890","name":"John Doe","iat":1516239022}' es384-private.pem
+
+ txreq -url "/es384" -hdr "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cs59CQiCI_Pl8J-PKQ2y73L5IJascZXkf7MfRXycO1HkT9pqDW2bFr1bh7pFyPA85GaML4BPYVH_zDhcmjSMn_EIvUV8cPDuuUu69Au7n9LYGVkVJ-k7qN4DAR5eLCiU"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES384"
+ expect resp.http.x-jwt-verify-ES384 == "1"
+} -run
+
+client c11 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # Key gen process : openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-521 -out es512-private.pem; openssl ec -in es512-private.pem -pubout -out es512-public.pem
+ # Token creation : ./build_token.py ES512 '{"sub":"1234567890","name":"John Doe","iat":1516239022}' es512-private.pem
+
+ txreq -url "/es512" -hdr "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.AJcyt0OYf2wg7SggJJVKYysLUkBQA0f0Zc0EbKgud2fQLeT65n42A9l9hhGje79VLWhEyisQmDpFXTpfFXeD_NiaAXyNnX5b8TbZALqxbjx8iIpbcObgUh_g5Gi81bKmRmfXUHW7L5iAwoNjYbUpXGipCpCD0N6-8zCrjcFD2UX01f0Y"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES512"
+ expect resp.http.x-jwt-verify-ES512 == "1"
+} -run
+
+# The following token is invalid (too short)
+client c12 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # OpenSSL cmd : openssl dgst -sha512 -sign es512-private.pem data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/es512" -hdr "Authorization: Bearer eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.MIGHAkEEPEgIrFKIDofBpFKX_mtya55QboGr09P6--v8uO85DwQWR0iKgMNSzYkL3K1lwyExG0Vtwfnife0lNe7Fn5TigAJCAY95NShiTn3tvleXVGCkkD0-HcribnMhd34QPGRc4rlwTkUg9umIUhxnEhPR--OohlmhJyIYGHuH8Ksm5f"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES512"
+ # Invalid token
+ expect resp.http.x-jwt-verify-ES512 == "-3"
+} -run
+
+
+# Unmanaged algorithm
+client c13 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"PS512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.MIGHAkEEPEgIrFKIDofBpFKX_mtya55QboGr09P6--v8uO85DwQWR0iKgMNSzYkL3K1lwyExG0Vtwfnife0lNe7Fn5TigAJCAY95NShiTn3tvleXVGCkkD0-HcribnMhd34QPGRc4rlwTkUg9umIUhxnEhPR--OohlmhJyIYGHuH8Ksm5f"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "PS512"
+ # Unmanaged algorithm
+ expect resp.http.x-jwt-verify == "-2"
+} -run
+
+# Unknown algorithm
+client c14 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"UNKNOWN_ALG","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJVTktOT1dOX0FMRyIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.MIGHAkEEPEgIrFKIDofBpFKX_mtya55QboGr09P6--v8uO85DwQWR0iKgMNSzYkL3K1lwyExG0Vtwfnife0lNe7Fn5TigAJCAY95NShiTn3tvleXVGCkkD0-HcribnMhd34QPGRc4rlwTkUg9umIUhxnEhPR--OohlmhJyIYGHuH8Ksm5f"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "UNKNOWN_ALG"
+ # Unmanaged algorithm
+ expect resp.http.x-jwt-verify == "-1"
+} -run
+
+# Invalid token (not enough fields)
+client c15 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES512"
+ # Invalid token
+ expect resp.http.x-jwt-verify == "-3"
+} -run
+
+# Invalid token (too many fields)
+client c16 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.MIGHAkEEPEgIrFKIDofBpFKX_mtya55QboGr09P6--v8uO85DwQWR0iKgMNSzYkL3K1lwyExG0Vtwfnife0lNe7Fn5TigAJCAY95NShiTn3tvleXVGCkkD0-HcribnMhd34QPGRc4rlwTkUg9umIUhxnEhPR--OohlmhJyIYGHuH8Ksm5f.unexpectedextrafield"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES512"
+ # Invalid token
+ expect resp.http.x-jwt-verify == "-3"
+} -run
+
+# Invalid token (empty signature)
+client c17 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES512"
+ # Invalid token
+ expect resp.http.x-jwt-verify == "-3"
+} -run
+
+# Unknown certificate
+client c18 -connect ${h1_mainfe_sock} {
+ # Token content : {"alg":"ES512","typ":"JWT"}
+ # {"sub":"1234567890","name":"John Doe","iat":1516239022}
+ # Key gen process : openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-521 -out es512-private.pem; openssl ec -in es512-private.pem -pubout -out es512-public.pem
+ # OpenSSL cmd : openssl dgst -sha512 -sign es512-private.pem data.txt | base64 | tr -d '=\n' | tr '/+' '_-'
+
+ txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.MIGHAkEEPEgIrFKIDofBpFKX_mtya55QboGr09P6--v8uO85DwQWR0iKgMNSzYkL3K1lwyExG0Vtwfnife0lNe7Fn5TigAJCAY95NShiTn3tvleXVGCkkD0-HcribnMhd34QPGRc4rlwTkUg9umIUhxnEhPR--OohlmhJyIYGHuH8Ksm5fSIWfRa"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-alg == "ES512"
+ # Unknown certificate
+ expect resp.http.x-jwt-verify == "-5"
+} -run
+
+
+# Test the http_auth_bearer special cases (other header than the default "Authorization" one)
+client c19 -connect ${h1_mainfe_sock} {
+ txreq -url "/auth_bearer" -hdr "Custom-Authorization: Bearer random_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-token == "random_value"
+} -run
+
+# Test the http_auth_bearer special cases (multiple spaces after the scheme)
+client c20 -connect ${h1_mainfe_sock} {
+ txreq -url "/auth_bearer" -hdr "Custom-Authorization: Bearer random_value"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-token == "random_value"
+} -run
+
+# Test the http_auth_bearer special cases (no value after the scheme)
+client c21 -connect ${h1_mainfe_sock} {
+ txreq -url "/auth_bearer" -hdr "Custom-Authorization: Bearer "
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-token == ""
+} -run
+
+# Test the http_auth_bearer special cases (no value after the scheme)
+client c22 -connect ${h1_mainfe_sock} {
+ txreq -url "/errors" -hdr "Authorization: Bearer "
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-jwt-token == ""
+} -run
diff --git a/reg-tests/jwt/rsa-public.pem b/reg-tests/jwt/rsa-public.pem
new file mode 100644
index 0000000..a87a89d
--- /dev/null
+++ b/reg-tests/jwt/rsa-public.pem
@@ -0,0 +1,14 @@
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxCPdKRUDpwNqrka4OYaI
+9bweoN/YoMYR8sddqK39S0pmzVIWZpZ51wXJU7oT4umSGAP0VpexxKNZdKnq6b9S
+caIfLCazl8EaU3Wg16l5ZD/OmHggaD5iHtI3lV2JhxTFlIdLI6sGoJxaDne0oelv
+tsE2dbBZBPT0OPKWyXgL2qQHCtYnqZI7d9czA61rg1PfiUqV6zh9MC7NW5mKPVS9
+5/MCIILyP4smljh5cUGkzhZaBy/mfKobTRe5xTP+DJ78wZhTAapOY/GmyQ4rFWZF
+ISH2tVQ7Ic32lbxeYXycTcPxEUcijNklnFHfpZ3Hhbz9hBuCWTaujcdYVxkRfMoc
+nz9InY8FCic3vgcOPrpqhZMxjeuVwUV9cjJhsWTjZeIne5P4l6DHmDIdoVJVatKR
++O4AL2q+VZ+d5euSmUe6bwrz1ufczIcRYAo1mnYD+USwjT5rGWSjG8brtfxtrzJz
+QP4oqMgLH2QBEgVDKlvsHiEC2K16tTf1pSEAh9Lyo2t8Tbc1BbuuJPafixNGFEQI
+J7sAwYoWNkncGOfwrPUpU13KtAGoW8hMBlLSuGb70FLbei/Qiz/YsWi86ybetN4W
+MpF096lcgqa/JH8IeYvGa/MQYoavloGv05OhaGrvGRy0GV6I9elnLEaSdBROnA4k
+yPaHW8jKmj04T8EBFmx5Lu0CAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/reg-tests/log/last_rule.vtc b/reg-tests/log/last_rule.vtc
new file mode 100644
index 0000000..e695166
--- /dev/null
+++ b/reg-tests/log/last_rule.vtc
@@ -0,0 +1,165 @@
+varnishtest "Verify logging of last final rule"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.6-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -repeat 15 -start
+
+syslog Slg_1 -level info {
+ recv
+ # /trqacc1
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:30"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:31"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:32"
+ recv
+ # /trsacc1
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:36"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:37"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:38"
+ recv
+ # /hrqvar
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*-:-"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:41"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:42"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:43"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:44"
+ recv
+ # /hrsacc1
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:46"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:47"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:48"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* lr=.*/h1/cfg:49"
+} -start
+
+haproxy h1 -conf {
+ global
+ nbthread 1
+
+ defaults
+ mode http
+ option httplog
+ option http-server-close
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe_1}"
+ log ${Slg_1_addr}:${Slg_1_port} local0
+ log-format "ci:%cp [%tr] lr=%[last_rule_file]:%[last_rule_line]"
+ default_backend be
+
+ backend be
+ # handle these URLs:
+ # /trqacc1, /trqrej1, /trqrej2, /trsacc1, /trsrej1, /trsrej2
+ # /hrqvar, /hrqacc1, /hrqred1, /hrqrej1, /hrqrej2,
+ # /hrsacc1, /hrsred1, /hrsrej1, /hrsrej2
+
+ tcp-response inspect-delay 10s
+ tcp-request content set-var(txn.path) path # must have no effect
+ tcp-request content accept if { var(txn.path) -m beg /trqacc1 /hrqrej1 }
+ tcp-request content reject if { var(txn.path) -m beg /trqrej1 }
+ tcp-request content reject if { var(txn.path) -m beg /trqrej2 }
+
+ tcp-response content reject unless WAIT_END
+ tcp-response content set-var(txn.foo) var(txn.path) # must have no effect
+ tcp-response content accept if { var(txn.path) -m beg /trsacc1 /hrsrej1 }
+ tcp-response content reject if { var(txn.path) -m beg /trsrej1 }
+ tcp-response content reject if { var(txn.path) -m beg /trsrej2 }
+
+ http-request set-var(txn.bar) var(txn.path) if { path_beg /hrqvar } # must have no effect
+ http-request allow if { var(txn.path) -m beg /hrqacc1 /hrsrej2 }
+ http-request redirect location / if { var(txn.path) -m beg /hrqred1 }
+ http-request deny if { var(txn.path) -m beg /hrqrej1 } # accepted by tcp-rq above
+ http-request deny if { var(txn.path) -m beg /hrqrej2 }
+
+ http-response allow if { var(txn.path) -m beg /hrsacc1 }
+ http-response redirect location / if { var(txn.path) -m beg /hrsred1 }
+ http-response deny if { var(txn.path) -m beg /hrsrej1 } # accepted by tcp-rs above
+ http-response deny if { var(txn.path) -m beg /hrsrej2 } # accepted by http-rq above
+ http-response deny if { var(txn.path) -m beg /hrsrej3 }
+
+ server app1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_1_sock} {
+ txreq -url /trqacc1
+ rxresp
+
+ txreq -url /trqrej1
+ expect_close
+} -run
+
+# The following client are started in background and synchronized
+client c2 -connect ${h1_fe_1_sock} {
+ txreq -url /trqrej2
+ expect_close
+} -run
+
+client c3 -connect ${h1_fe_1_sock} {
+ txreq -url /trsacc1
+ rxresp
+ expect resp.status == 200
+
+ txreq -url /trsrej1
+ expect_close
+} -run
+
+client c4 -connect ${h1_fe_1_sock} {
+ txreq -url /trsrej2
+ expect_close
+} -run
+
+client c5 -connect ${h1_fe_1_sock} {
+ txreq -url /hrqvar
+ rxresp
+ expect resp.status == 200
+
+ txreq -url /hrqacc1
+ rxresp
+ expect resp.status == 200
+
+ txreq -url /hrqred1
+ rxresp
+ expect resp.status == 302
+
+ txreq -url /hrqrej1
+ rxresp
+ expect resp.status == 403
+
+ txreq -url /hrqrej2
+ rxresp
+ expect resp.status == 403
+
+ txreq -url /hrsacc1
+ rxresp
+ expect resp.status == 200
+
+ txreq -url /hrsred1
+ rxresp
+ expect resp.status == 302
+
+ txreq -url /hrsrej1
+ rxresp
+ expect resp.status == 502
+
+ txreq -url /hrsrej2
+ rxresp
+ expect resp.status == 502
+} -run
+
+syslog Slg_1 -wait
diff --git a/reg-tests/log/load_balancing.vtc b/reg-tests/log/load_balancing.vtc
new file mode 100644
index 0000000..22aacae
--- /dev/null
+++ b/reg-tests/log/load_balancing.vtc
@@ -0,0 +1,161 @@
+varnishtest "Basic log load-balancing test"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.0
+
+barrier b1 cond 2 -cyclic
+barrier b2 cond 2 -cyclic
+barrier b3 cond 2 -cyclic
+barrier b4 cond 2 -cyclic
+barrier b5 cond 2 -cyclic
+
+server s1 {
+ rxreq
+ txresp
+} -repeat 500 -start
+
+syslog Slg_1 -level info {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c1 HTTP/1.1\""
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c2 HTTP/1.1\""
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c3 HTTP/1.1\""
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c4 HTTP/1.1\""
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c5 HTTP/1.1\""
+} -repeat 50 -start
+
+# Here are the syslog messages received by Slg_2:
+syslog Slg_2 -level info {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c6 HTTP/1.1\""
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c8 HTTP/1.1\""
+} -repeat 50 -start
+
+haproxy h1 -conf {
+ global
+ nbthread 1
+
+ defaults
+ mode http
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe_1}"
+ log ${Slg_1_addr}:${Slg_1_port} local0
+ default_backend be
+
+ frontend fe2
+ bind "fd@${fe_2}"
+ log ${Slg_2_addr}:${Slg_2_port} sample 1,3:5 local0
+ default_backend be
+
+ backend be
+ server app1 ${s1_addr}:${s1_port}
+} -start
+
+# The following client are started in background and synchronized
+client c1 -connect ${h1_fe_1_sock} {
+ txreq -url "/client_c1"
+ rxresp
+ expect resp.status == 200
+ barrier b1 sync
+ barrier b5 sync
+} -repeat 50 -start
+
+client c2 -connect ${h1_fe_1_sock} {
+ barrier b1 sync
+ txreq -url "/client_c2"
+ rxresp
+ expect resp.status == 200
+ barrier b2 sync
+} -repeat 50 -start
+
+client c3 -connect ${h1_fe_1_sock} {
+ barrier b2 sync
+ txreq -url "/client_c3"
+ rxresp
+ expect resp.status == 200
+ barrier b3 sync
+} -repeat 50 -start
+
+client c4 -connect ${h1_fe_1_sock} {
+ barrier b3 sync
+ txreq -url "/client_c4"
+ rxresp
+ expect resp.status == 200
+ barrier b4 sync
+} -repeat 50 -start
+
+client c5 -connect ${h1_fe_1_sock} {
+ barrier b4 sync
+ txreq -url "/client_c5"
+ rxresp
+ expect resp.status == 200
+ barrier b5 sync
+} -repeat 50 -start
+
+syslog Slg_1 -wait
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
+client c4 -wait
+client c5 -wait
+
+# Same test as before but with fe2 frontend.
+# The following client are started in background and synchronized
+client c6 -connect ${h1_fe_2_sock} {
+ txreq -url "/client_c6"
+ rxresp
+ expect resp.status == 200
+ barrier b1 sync
+ barrier b5 sync
+} -repeat 50 -start
+
+client c7 -connect ${h1_fe_2_sock} {
+ barrier b1 sync
+ txreq -url "/client_c7"
+ rxresp
+ expect resp.status == 200
+ barrier b2 sync
+} -repeat 50 -start
+
+client c8 -connect ${h1_fe_2_sock} {
+ barrier b2 sync
+ txreq -url "/client_c8"
+ rxresp
+ expect resp.status == 200
+ barrier b3 sync
+} -repeat 50 -start
+
+client c9 -connect ${h1_fe_2_sock} {
+ barrier b3 sync
+ txreq -url "/client_c9"
+ rxresp
+ expect resp.status == 200
+ barrier b4 sync
+} -repeat 50 -start
+
+client c10 -connect ${h1_fe_2_sock} {
+ barrier b4 sync
+ txreq -url "/client_c10"
+ rxresp
+ expect resp.status == 200
+ barrier b5 sync
+} -repeat 50 -start
+
+syslog Slg_2 -wait
+
+client c6 -wait
+client c7 -wait
+client c8 -wait
+client c9 -wait
+client c10 -wait
+
diff --git a/reg-tests/log/log_forward.vtc b/reg-tests/log/log_forward.vtc
new file mode 100644
index 0000000..3977f4c
--- /dev/null
+++ b/reg-tests/log/log_forward.vtc
@@ -0,0 +1,57 @@
+varnishtest "Test the TCP load-forward"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.3-dev1)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -repeat 500 -start
+
+syslog Slg1 -level info {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c1 HTTP/1.1\""
+} -repeat 50 -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe_1}"
+ log 127.0.0.1:1514 local0
+# log ${Slg1_addr}:${Slg1_port} local0
+ default_backend be
+
+ backend be
+ server app1 ${s1_addr}:${s1_port}
+
+ ring myring
+ description "My local buffer"
+ format rfc5424
+ maxlen 1200
+ size 32764
+ timeout connect 5s
+ timeout server 10s
+ # syslog tcp server
+ server mysyslogsrv 127.0.0.1:2514
+
+ log-forward syslog2tcp
+ dgram-bind 127.0.0.1:1514
+ log ring@myring local0 # To TCP log
+
+ log-forward syslog2local
+ bind 127.0.0.1:2514
+ log ${Slg1_addr}:${Slg1_port} local0 # To VTest syslog
+} -start
+
+client c1 -connect ${h1_fe_1_sock} {
+ txreq -url "/client_c1"
+ rxresp
+ expect resp.status == 200
+} -repeat 50 -start
+
+syslog Slg1 -wait
diff --git a/reg-tests/log/log_uri.vtc b/reg-tests/log/log_uri.vtc
new file mode 100644
index 0000000..6993b7c
--- /dev/null
+++ b/reg-tests/log/log_uri.vtc
@@ -0,0 +1,61 @@
+varnishtest "Verify logging of relative/absolute URI path"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+
+server s1 {
+ rxreq
+ txresp
+} -repeat 4 -start
+
+syslog Slg_1 -level info {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* hpo=/r/1 hp=/r/1 hu=/r/1 hq="
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* hpo=/r/2 hp=/r/2 hu=/r/2\\?q=2 hq=\\?q=2"
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* hpo=/r/3 hp=http://localhost/r/3 hu=http://localhost/r/3 hq="
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* hpo=/r/4 hp=http://localhost/r/4 hu=http://localhost/r/4\\?q=4 hq=\\?q=4"
+} -start
+
+haproxy h1 -conf {
+ global
+ nbthread 1
+
+ defaults
+ mode http
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe_1}"
+ log ${Slg_1_addr}:${Slg_1_port} local0
+ log-format "ci:%cp [%tr] hpo=%HPO hp=%HP hu=%HU hq=%HQ"
+ default_backend be
+
+ backend be
+ server app1 ${s1_addr}:${s1_port}
+} -start
+
+# The following client are started in background and synchronized
+client c1 -connect ${h1_fe_1_sock} {
+ txreq -url "/r/1"
+ rxresp
+ expect resp.status == 200
+ txreq -url "/r/2?q=2"
+ rxresp
+ expect resp.status == 200
+ txreq -url "http://localhost/r/3" -hdr "host: localhost"
+ rxresp
+ expect resp.status == 200
+ txreq -url "http://localhost/r/4?q=4" -hdr "host: localhost"
+ rxresp
+ expect resp.status == 200
+} -start
+
+syslog Slg_1 -wait
+
+client c1 -wait
diff --git a/reg-tests/log/wrong_ip_port_logging.vtc b/reg-tests/log/wrong_ip_port_logging.vtc
new file mode 100644
index 0000000..af8ca84
--- /dev/null
+++ b/reg-tests/log/wrong_ip_port_logging.vtc
@@ -0,0 +1,62 @@
+# commit d02286d
+# BUG/MINOR: log: pin the front connection when front ip/ports are logged
+#
+# Mathias Weiersmueller reported an interesting issue with logs which Lukas
+# diagnosed as dating back from commit 9b061e332 (1.5-dev9). When front
+# connection information (ip, port) are logged in TCP mode and the log is
+# emitted at the end of the connection (eg: because %B or any log tag
+# requiring LW_BYTES is set), the log is emitted after the connection is
+# closed, so the address and ports cannot be retrieved anymore.
+#
+# It could be argued that we'd make a special case of these to immediately
+# retrieve the source and destination addresses from the connection, but it
+# seems cleaner to simply pin the front connection, marking it "tracked" by
+# adding the LW_XPRT flag to mention that we'll need some of these elements
+# at the last moment. Only LW_FRTIP and LW_CLIP are affected. Note that after
+# this change, LW_FRTIP could simply be removed as it's not used anywhere.
+#
+# Note that the problem doesn't happen when using %[src] or %[dst] since
+# all sample expressions set LW_XPRT.
+
+#REGTEST_TYPE=bug
+
+varnishtest "Wrong ip/port logging"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ delay 0.02
+} -start
+
+syslog Slg_1 -level notice {
+ recv info
+ expect ~ \"dip\":\"${h1_fe_1_addr}\",\"dport\":\"${h1_fe_1_port}.*\"ts\":\"[cC]D\",\"
+} -start
+
+haproxy h1 -conf {
+ global
+ log ${Slg_1_addr}:${Slg_1_port} local0
+
+defaults
+ log global
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client 1
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+frontend fe1
+ bind "fd@${fe_1}"
+ mode tcp
+ log-format {\"dip\":\"%fi\",\"dport\":\"%fp\",\"c_ip\":\"%ci\",\"c_port\":\"%cp\",\"fe_name\":\"%ft\",\"be_name\":\"%b\",\"s_name\":\"%s\",\"ts\":\"%ts\",\"bytes_read\":\"%B\"}
+ default_backend be_app
+
+backend be_app
+ server app1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_1_sock} {
+ txreq -url "/"
+ expect_close
+} -run
+
+syslog Slg_1 -wait
+
diff --git a/reg-tests/lua/bad_http_clt_req_duration.lua b/reg-tests/lua/bad_http_clt_req_duration.lua
new file mode 100644
index 0000000..2c2ab1d
--- /dev/null
+++ b/reg-tests/lua/bad_http_clt_req_duration.lua
@@ -0,0 +1,8 @@
+core.register_service("foo.http", "http", function(applet)
+ core.msleep(10)
+ applet:start_response()
+end)
+
+core.register_service("foo.tcp", "tcp", function(applet)
+ applet:send("HTTP/1.1 200 OK\r\nTransfer-encoding: chunked\r\n\r\n0\r\n\r\n")
+end)
diff --git a/reg-tests/lua/bad_http_clt_req_duration.vtc b/reg-tests/lua/bad_http_clt_req_duration.vtc
new file mode 100644
index 0000000..5cfdf1a
--- /dev/null
+++ b/reg-tests/lua/bad_http_clt_req_duration.vtc
@@ -0,0 +1,76 @@
+# commit 7b6cc52784526c32efda44b873a4258d3ae0b8c7
+# BUG/MINOR: lua: Bad HTTP client request duration.
+#
+# HTTP LUA applet callback should not update the date on which the HTTP client requests
+# arrive. This was done just after the LUA applet has completed its job.
+#
+# This patch simply removes the affected statement. The same fix has been applied
+# to TCP LUA applet callback.
+#
+# To reproduce this issue, as reported by Patrick Hemmer, implement an HTTP LUA applet
+# which sleeps a bit before replying:
+#
+# core.register_service("foo", "http", function(applet)
+# core.msleep(100)
+# applet:set_status(200)
+# applet:start_response()
+# end)
+#
+# This had as a consequence to log %TR field with approximately the same value as
+# the LUA sleep time.
+
+varnishtest "LUA bug"
+#REQUIRE_OPTIONS=LUA
+#REGTEST_TYPE=bug
+
+feature ignore_unknown_macro
+
+syslog Slog {
+ recv info
+ expect ~ "[^:\\[ ]\\[[0-9]*\\]: Ta=[0-9]* Tc=[0-9]* Td=[0-9]* Th=[0-9]* Ti=[0-9]* Tq=[0-9]* TR=[0-9]* Tr=[0-9]* Tt=[0-9]* Tw=[0-9]*$"
+
+ recv info
+ expect ~ "[^:\\[ ]\\[[0-9]*\\]: Tc=[0-9]* Td=[0-9]* Th=[0-9]* Tt=[0-9]* Tw=[0-9]*$"
+} -start
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/bad_http_clt_req_duration.lua
+
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend f1
+ mode http
+ bind "fd@${f1}"
+ log ${Slog_addr}:${Slog_port} daemon
+ log-format Ta=%Ta\ Tc=%Tc\ Td=%Td\ Th=%Th\ Ti=%Ti\ Tq=%Tq\ TR=%TR\ Tr=%Tr\ Tt=%Tt\ Tw=%Tw
+ default_backend b1
+
+ backend b1
+ mode http
+ http-request use-service lua.foo.http
+
+ frontend f2
+ mode tcp
+ bind "fd@${f2}"
+ log ${Slog_addr}:${Slog_port} daemon
+ log-format Tc=%Tc\ Td=%Td\ Th=%Th\ Tt=%Tt\ Tw=%Tw
+
+ tcp-request inspect-delay 1s
+ tcp-request content use-service lua.foo.tcp
+} -start
+
+client c1 -connect "${h1_f1_sock}" {
+ txreq
+ rxresp
+} -run
+
+client c2 -connect "${h1_f2_sock}" {
+ txreq
+ rxresp
+} -run
+
+syslog Slog -wait
diff --git a/reg-tests/lua/close_wait_lf.lua b/reg-tests/lua/close_wait_lf.lua
new file mode 100644
index 0000000..cc897e7
--- /dev/null
+++ b/reg-tests/lua/close_wait_lf.lua
@@ -0,0 +1 @@
+core.register_service("donothing", "http", function(applet) end)
diff --git a/reg-tests/lua/close_wait_lf.vtc b/reg-tests/lua/close_wait_lf.vtc
new file mode 100644
index 0000000..7bed3fd
--- /dev/null
+++ b/reg-tests/lua/close_wait_lf.vtc
@@ -0,0 +1,53 @@
+# commit 70d318c
+# BUG/MEDIUM: lua: possible CLOSE-WAIT state with '\n' headers
+#
+# The Lua parser doesn't takes in account end-of-headers containing
+# only '\n'. It expects always '\r\n'. If a '\n' is processes the Lua
+# parser considers it miss 1 byte, and wait indefinitely for new data.
+#
+# When the client reaches their timeout, it closes the connection.
+# This close is not detected and the connection keep in CLOSE-WAIT
+# state.
+#
+# I guess that this patch fix only a visible part of the problem.
+# If the Lua HTTP parser wait for data, the timeout server or the
+# connectio closed by the client may stop the applet.
+
+varnishtest "possible CLOSE-WAIT with '\n' headers"
+#REQUIRE_OPTIONS=LUA
+#REGTEST_TYPE=bug
+
+feature ignore_unknown_macro
+
+syslog Slog -level info -repeat 100 {
+ recv info
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Ta=[0-9]* Tc=[0-9]* Td=[0-9]* Th=[0-9]* Ti=[0-9]* Tq=[0-9]* TR=[0-9]* Tr=[0-9]* Tt=[0-9]* Tw=[0-9]*"
+} -start
+
+haproxy h1 -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ global
+ lua-load ${testdir}/close_wait_lf.lua
+
+ frontend frt
+ log ${Slog_addr}:${Slog_port} local0 debug err
+ log-format Ta=%Ta\ Tc=%Tc\ Td=%Td\ Th=%Th\ Ti=%Ti\ Tq=%Tq\ TR=%TR\ Tr=%Tr\ Tt=%Tt\ Tw=%Tw
+ mode http
+ bind "fd@${frt}"
+ http-request use-service lua.donothing
+} -start
+
+
+client c1 -connect ${h1_frt_sock} -repeat 100 {
+ send "GET / HTTP/1.1\n\n"
+} -run
+
+syslog Slog -wait
+
+shell {
+ ss -pt | grep CLOSE-WAIT.*haproxy.*pid=${h1_pid}
+ exit $((!$?))
+}
diff --git a/reg-tests/lua/common.pem b/reg-tests/lua/common.pem
new file mode 120000
index 0000000..a4433d5
--- /dev/null
+++ b/reg-tests/lua/common.pem
@@ -0,0 +1 @@
+../ssl/common.pem \ No newline at end of file
diff --git a/reg-tests/lua/h_txn_get_priv.lua b/reg-tests/lua/h_txn_get_priv.lua
new file mode 100644
index 0000000..999ea88
--- /dev/null
+++ b/reg-tests/lua/h_txn_get_priv.lua
@@ -0,0 +1,15 @@
+core.register_action("bug", { "http-res" }, function(txn)
+ data = txn:get_priv()
+ if not data then
+ data = 0
+ end
+ data = data + 1
+ print(string.format("set to %d", data))
+ txn.http:res_set_status(200 + data)
+ txn:set_priv(data)
+end)
+
+core.register_service("fakeserv", "http", function(applet)
+ applet:set_status(200)
+ applet:start_response()
+end)
diff --git a/reg-tests/lua/h_txn_get_priv.vtc b/reg-tests/lua/h_txn_get_priv.vtc
new file mode 100644
index 0000000..bd8c069
--- /dev/null
+++ b/reg-tests/lua/h_txn_get_priv.vtc
@@ -0,0 +1,33 @@
+varnishtest "Lua: txn:get_priv() scope"
+#REQUIRE_OPTIONS=LUA
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/h_txn_get_priv.lua
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ default_backend b1
+
+ http-response lua.bug
+
+ backend b1
+ mode http
+ http-request use-service lua.fakeserv
+} -start
+
+client c0 -connect ${h1_fe1_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 201
+ txreq -url "/"
+ rxresp
+ expect resp.status == 201
+}
+
+client c0 -start
+
+client c0 -wait
diff --git a/reg-tests/lua/httpclient_action.lua b/reg-tests/lua/httpclient_action.lua
new file mode 100644
index 0000000..9a7209c
--- /dev/null
+++ b/reg-tests/lua/httpclient_action.lua
@@ -0,0 +1,8 @@
+function test()
+ local httpclient = core.httpclient()
+ local response = httpclient:get{url="http://127.0.0.1", headers={ [ "Host" ] = { "localhost" } }}
+
+end
+
+
+core.register_action("test", {"tcp-req"}, test, 0)
diff --git a/reg-tests/lua/httpclient_action.vtc b/reg-tests/lua/httpclient_action.vtc
new file mode 100644
index 0000000..11c7d62
--- /dev/null
+++ b/reg-tests/lua/httpclient_action.vtc
@@ -0,0 +1,39 @@
+varnishtest "Lua: test the httpclient when the lua action timeout"
+#
+# Start an httpclient from "lua.test" whose lua task will expire before the
+# httpclient is ended.
+
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev7)'"
+feature ignore_unknown_macro
+
+#REQUIRE_OPTIONS=LUA
+
+haproxy h1 -conf {
+
+ global
+ lua-load ${testdir}/httpclient_action.lua
+ defaults
+ mode tcp
+ timeout http-request 10s
+ timeout queue 1m
+ timeout connect 10s
+ timeout client 1m
+ timeout server 1m
+ timeout check 10s
+
+ listen li1
+ mode http
+ bind "fd@${fe1}"
+ tcp-request inspect-delay 10ms
+ tcp-request content lua.test
+ http-request return status 503
+
+} -start
+
+client c0 -connect ${h1_fe1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
diff --git a/reg-tests/lua/lua_httpclient.lua b/reg-tests/lua/lua_httpclient.lua
new file mode 100644
index 0000000..b5a5180
--- /dev/null
+++ b/reg-tests/lua/lua_httpclient.lua
@@ -0,0 +1,49 @@
+
+local vtc_port = 0
+local vtc_port2 = 0
+local vtc_port3 = 0
+
+core.register_service("fakeserv", "http", function(applet)
+ vtc_port = applet.headers["vtcport"][0]
+ vtc_port2 = applet.headers["vtcport2"][0]
+ vtc_port3 = applet.headers["vtcport3"][0]
+ core.Info("APPLET START")
+ local response = "OK"
+ applet:add_header("Server", "haproxy/webstats")
+ applet:add_header("Content-Length", string.len(response))
+ applet:add_header("Content-Type", "text/html")
+ applet:start_response()
+ applet:send(response)
+ core.Info("APPLET DONE")
+end)
+
+local function cron()
+ -- wait for until the correct port is set through the c0 request..
+ while vtc_port == 0 do
+ core.msleep(1)
+ end
+ core.Debug('CRON port:' .. vtc_port)
+
+ local body = ""
+
+ for i = 0, 2000 do
+ body = body .. i .. ' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n'
+ end
+ core.Info("First httpclient request")
+ local httpclient = core.httpclient()
+ local response = httpclient:post{url="http://127.0.0.1:" .. vtc_port, body=body}
+ core.Info("Received: " .. response.body)
+
+ body = response.body
+
+ core.Info("Second httpclient request")
+ local httpclient2 = core.httpclient()
+ local response2 = httpclient2:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
+
+ core.Info("Third httpclient request")
+ local httpclient3 = core.httpclient()
+ local response3 = httpclient3:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
+
+end
+
+core.register_task(cron)
diff --git a/reg-tests/lua/lua_httpclient.vtc b/reg-tests/lua/lua_httpclient.vtc
new file mode 100644
index 0000000..0a27493
--- /dev/null
+++ b/reg-tests/lua/lua_httpclient.vtc
@@ -0,0 +1,68 @@
+varnishtest "Lua: check httpclient functionality from a lua-task"
+
+# A request if first made with c0 with the port of s1 and s2 so the httpclient
+# can generate its URI with it.
+#
+# This reg-test sends a payload with the httpclient to s1, s1 returns another
+# payload. The 2nd lua httpclient sends back the payload from s1 to s2.
+#
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev7)'"
+feature ignore_unknown_macro
+
+#REQUIRE_OPTIONS=LUA
+
+server s1 {
+ rxreq
+ txresp -bodylen 54000
+ expect req.body ~ ".*0 ABCDEFGHIJKLMNOPQRSTUVWXYZ.*"
+ expect req.body ~ ".*500 ABCDEFGHIJKLMNOPQRSTUVWXYZ.*"
+ expect req.body ~ ".*1000 ABCDEFGHIJKLMNOPQRSTUVWXYZ.*"
+ expect req.body ~ ".*1500 ABCDEFGHIJKLMNOPQRSTUVWXYZ.*"
+ expect req.body ~ ".*2000 ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+} -start
+
+server s2 {
+ rxreq
+ txresp
+ expect req.bodylen == 54000
+} -start
+
+server s3 {
+ rxreq
+ txresp -bodylen 54000
+ expect req.method == "GET"
+ expect req.http.host == "foobar.haproxy.local"
+} -start
+
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/lua_httpclient.lua
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ default_backend b1
+
+ backend b1
+ mode http
+ http-request use-service lua.fakeserv
+
+ listen li1
+ mode http
+ bind unix@${tmpdir}/srv3
+ server srv3 ${s3_addr}:${s3_port}
+
+} -start
+
+client c0 -connect ${h1_fe1_sock} {
+ txreq -url "/" -hdr "vtcport: ${s1_port}" -hdr "vtcport2: ${s2_port}" -hdr "vtcport3: unix@${tmpdir}/srv3"
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+server s1 -wait
+server s2 -wait
+server s3 -wait
diff --git a/reg-tests/lua/lua_socket.lua b/reg-tests/lua/lua_socket.lua
new file mode 100644
index 0000000..3ad14fe
--- /dev/null
+++ b/reg-tests/lua/lua_socket.lua
@@ -0,0 +1,44 @@
+
+local vtc_port = 0
+
+core.register_service("fakeserv", "http", function(applet)
+ vtc_port = applet.headers["vtcport"][0]
+ core.Info("APPLET START")
+ local response = "OK"
+ applet:add_header("Server", "haproxy/webstats")
+ applet:add_header("Content-Length", string.len(response))
+ applet:add_header("Content-Type", "text/html")
+ applet:start_response()
+ applet:send(response)
+ core.Info("APPLET DONE")
+end)
+
+local function cron()
+ -- wait for until the correct port is set through the c0 request..
+ while vtc_port == 0 do
+ core.msleep(1)
+ end
+ core.Debug('CRON port:' .. vtc_port)
+
+ local socket = core.tcp()
+ local success = socket:connect("127.0.0.1", vtc_port)
+ core.Info("SOCKET MADE ".. (success or "??"))
+ if success ~= 1 then
+ core.Info("CONNECT SOCKET FAILED?")
+ return
+ end
+ local request = "GET / HTTP/1.1\r\n\r\n"
+ core.Info("SENDING REQUEST")
+ socket:send(request)
+ local result = ""
+ repeat
+ core.Info("4")
+ local d = socket:receive("*a")
+ if d ~= nil then
+ result = result .. d
+ end
+ until d == nil or d == 0
+ core.Info("Received: "..result)
+end
+
+core.register_task(cron) \ No newline at end of file
diff --git a/reg-tests/lua/lua_socket.vtc b/reg-tests/lua/lua_socket.vtc
new file mode 100644
index 0000000..83e06a6
--- /dev/null
+++ b/reg-tests/lua/lua_socket.vtc
@@ -0,0 +1,33 @@
+varnishtest "Lua: check socket functionality from a lua-task"
+feature ignore_unknown_macro
+
+#REQUIRE_OPTIONS=LUA
+
+server s1 {
+ rxreq
+ txresp -bodylen 20
+} -start
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/lua_socket.lua
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ default_backend b1
+
+ backend b1
+ mode http
+ http-request use-service lua.fakeserv
+
+} -start
+
+client c0 -connect ${h1_fe1_sock} {
+ txreq -url "/" -hdr "vtcport: ${s1_port}"
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+server s1 -wait
diff --git a/reg-tests/lua/set_var.lua b/reg-tests/lua/set_var.lua
new file mode 100644
index 0000000..f4d5e7a
--- /dev/null
+++ b/reg-tests/lua/set_var.lua
@@ -0,0 +1,25 @@
+core.register_service("set_var", "http", function(applet)
+ local var_name = applet.headers["var"][0]
+ local result = applet:set_var(var_name, "value")
+ if result then
+ applet:set_status(202)
+ else
+ applet:set_status(400)
+ end
+ applet:add_header("echo", applet:get_var(var_name) or "(nil)")
+ applet:start_response()
+ applet:send("")
+end)
+
+core.register_service("set_var_ifexist", "http", function(applet)
+ local var_name = applet.headers["var"][0]
+ local result = applet:set_var(var_name, "value", true)
+ if result then
+ applet:set_status(202)
+ else
+ applet:set_status(400)
+ end
+ applet:add_header("echo", applet:get_var(var_name) or "(nil)")
+ applet:start_response()
+ applet:send("")
+end)
diff --git a/reg-tests/lua/set_var.vtc b/reg-tests/lua/set_var.vtc
new file mode 100644
index 0000000..0c8a4b1
--- /dev/null
+++ b/reg-tests/lua/set_var.vtc
@@ -0,0 +1,83 @@
+varnishtest "Lua: set_var"
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=LUA
+
+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
+
+ global
+ lua-load ${testdir}/set_var.lua
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+
+ http-request use-service lua.set_var
+
+ frontend fe2
+ mode http
+ bind "fd@${fe2}"
+ # just make sure the variable exists
+ http-request set-header Dummy %[var(proc.fe2_foo)]
+
+ http-request use-service lua.set_var_ifexist
+} -start
+
+client c0 -connect ${h1_fe1_sock} {
+ # create var
+ txreq -url "/" \
+ -hdr "Var: txn.fe1_foo"
+ rxresp
+ expect resp.status == 202
+ expect resp.http.echo == "value"
+
+ # rewrite var
+ txreq -url "/" \
+ -hdr "Var: txn.fe1_foo"
+ rxresp
+ expect resp.status == 202
+ expect resp.http.echo == "value"
+
+ # create var under scope "proc"
+ txreq -url "/" \
+ -hdr "Var: proc.fe1_foo"
+ rxresp
+ expect resp.status == 202
+ expect resp.http.echo == "value"
+
+ # fail to create bad scope
+ txreq -url "/" \
+ -hdr "Var: invalid.var"
+ rxresp
+ expect resp.status == 400
+ expect resp.http.echo == "(nil)"
+} -run
+
+client c1 -connect ${h1_fe2_sock} {
+ # this one exists in the conf, it must succeed
+ txreq -url "/" \
+ -hdr "Var: proc.fe2_foo"
+ rxresp
+ expect resp.status == 202
+ expect resp.http.echo == "value"
+
+ # this one does not exist in the conf, it must fail
+ txreq -url "/" \
+ -hdr "Var: proc.fe2_bar"
+ rxresp
+ expect resp.status == 400
+ expect resp.http.echo == "(nil)"
+
+ # this one is under txn, it must succeed
+ txreq -url "/" \
+ -hdr "Var: txn.fe2_foo"
+ rxresp
+ expect resp.status == 202
+ expect resp.http.echo == "value"
+} -run
diff --git a/reg-tests/lua/txn_get_priv-print_r.lua b/reg-tests/lua/txn_get_priv-print_r.lua
new file mode 100644
index 0000000..185614f
--- /dev/null
+++ b/reg-tests/lua/txn_get_priv-print_r.lua
@@ -0,0 +1,96 @@
+-- Copyright 2016 Thierry Fournier
+
+function color(index, str)
+ return "\x1b[" .. index .. "m" .. str .. "\x1b[00m"
+end
+
+function nocolor(index, str)
+ return str
+end
+
+function sp(count)
+ local spaces = ""
+ while count > 0 do
+ spaces = spaces .. " "
+ count = count - 1
+ end
+ return spaces
+end
+
+function escape(str)
+ local s = ""
+ for i = 1, #str do
+ local c = str:sub(i,i)
+ local ascii = string.byte(c, 1)
+ if ascii > 126 or ascii < 20 then
+ s = s .. string.format("\\x%02x", ascii)
+ else
+ s = s .. c
+ end
+ end
+ return s
+end
+
+function print_rr(p, indent, c, wr, hist)
+ local i = 0
+ local nl = ""
+
+ if type(p) == "table" then
+ wr(c("33", "(table)") .. " " .. c("36", tostring(p)) .. " [")
+
+ for idx, value in ipairs(hist) do
+ if value == p then
+ wr(" " .. c("35", "/* recursion */") .. " ]")
+ return
+ end
+ end
+ hist[indent + 1] = p
+
+ mt = getmetatable(p)
+ if mt ~= nil then
+ wr("\n" .. sp(indent+1) .. c("31", "METATABLE") .. ": ")
+ print_rr(mt, indent+1, c, wr, hist)
+ end
+
+ for k,v in pairs(p) do
+ if i > 0 then
+ nl = "\n"
+ else
+ wr("\n")
+ end
+ wr(nl .. sp(indent+1))
+ if type(k) == "number" then
+ wr(c("32", tostring(k)))
+ else
+ wr("\"" .. c("32", escape(tostring(k))) .. "\"")
+ end
+ wr(": ")
+ print_rr(v, indent+1, c, wr, hist)
+ i = i + 1
+ end
+ if i == 0 then
+ wr(" " .. c("35", "/* empty */") .. " ]")
+ else
+ wr("\n" .. sp(indent) .. "]")
+ end
+
+ hist[indent + 1] = nil
+
+ elseif type(p) == "string" then
+ wr(c("33", "(string)") .. " \"" .. c("36", escape(p)) .. "\"")
+ else
+ wr(c("33", "(" .. type(p) .. ")") .. " " .. c("36", tostring(p)))
+ end
+end
+
+function print_r(p, col, wr)
+ if col == nil then col = true end
+ if wr == nil then wr = function(msg) io.stdout:write(msg) end end
+ local hist = {}
+ if col == true then
+ print_rr(p, 0, color, wr, hist)
+ else
+ print_rr(p, 0, nocolor, wr, hist)
+ end
+ wr("\n")
+end
diff --git a/reg-tests/lua/txn_get_priv-thread.vtc b/reg-tests/lua/txn_get_priv-thread.vtc
new file mode 100644
index 0000000..9538363
--- /dev/null
+++ b/reg-tests/lua/txn_get_priv-thread.vtc
@@ -0,0 +1,69 @@
+varnishtest "Lua: txn:get_priv() scope"
+#REQUIRE_OPTIONS=LUA,OPENSSL
+#REQUIRE_VERSION=2.4
+#REGTEST_TYPE=bug
+
+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
+
+ lua-load-per-thread ${testdir}/txn_get_priv.lua
+ lua-load-per-thread ${testdir}/txn_get_priv-print_r.lua
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ default_backend b1
+
+ frontend fe2
+ mode http
+ bind ":8443" ssl crt ${testdir}/common.pem
+ stats enable
+ stats uri /
+
+ backend b1
+ mode http
+ http-request use-service lua.fakeserv
+} -start
+
+client c0 -repeat 4 -connect ${h1_fe1_sock} {
+ txreq -url "/0"
+ rxresp
+ expect resp.status == 200
+ txreq -url "/0"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c1 -repeat 4 -connect ${h1_fe1_sock} {
+ txreq -url "/1"
+ rxresp
+ expect resp.status == 200
+ txreq -url "/1"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -repeat 4 -connect ${h1_fe1_sock} {
+ txreq -url "/2"
+ rxresp
+ expect resp.status == 200
+ txreq -url "/2"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c3 -repeat 4 -connect ${h1_fe1_sock} {
+ txreq -url "/3"
+ rxresp
+ expect resp.status == 200
+ txreq -url "/3"
+ rxresp
+ expect resp.status == 200
+} -run
+
diff --git a/reg-tests/lua/txn_get_priv.lua b/reg-tests/lua/txn_get_priv.lua
new file mode 100644
index 0000000..dd5623c
--- /dev/null
+++ b/reg-tests/lua/txn_get_priv.lua
@@ -0,0 +1,180 @@
+Luacurl = {}
+Luacurl.__index = Luacurl
+setmetatable(Luacurl, {
+ __call = function (cls, ...)
+ return cls.new(...)
+ end,
+})
+function Luacurl.new(server, port, ssl)
+ local self = setmetatable({}, Luacurl)
+ self.sockconnected = false
+ self.server = server
+ self.port = port
+ self.ssl = ssl
+ self.cookies = {}
+ return self
+end
+
+function Luacurl:get(method,url,headers,data)
+ core.Info("MAKING SOCKET")
+ if self.sockconnected == false then
+ self.sock = core.tcp()
+ if self.ssl then
+ local r = self.sock:connect_ssl(self.server,self.port)
+ else
+ local r = self.sock:connect(self.server,self.port)
+ end
+ self.sockconnected = true
+ end
+ core.Info("SOCKET MADE")
+ local request = method.." "..url.." HTTP/1.1"
+ if data ~= nil then
+ request = request .. "\r\nContent-Length: "..string.len(data)
+ end
+ if headers ~= null then
+ for h,v in pairs(headers) do
+ request = request .. "\r\n"..h..": "..v
+ end
+ end
+ cookstring = ""
+ for cook,cookval in pairs(self.cookies) do
+ cookstring = cookstring .. cook.."="..cookval.."; "
+ end
+ if string.len(cookstring) > 0 then
+ request = request .. "\r\nCookie: "..cookstring
+ end
+
+ request = request .. "\r\n\r\n"
+ if data and string.len(data) > 0 then
+ request = request .. data
+ end
+--print(request)
+ core.Info("SENDING REQUEST")
+ self.sock:send(request)
+
+-- core.Info("PROCESSING RESPONSE")
+ return processhttpresponse(self.sock)
+end
+
+function processhttpresponse(socket)
+ local res = {}
+core.Info("1")
+ res.status = socket:receive("*l")
+core.Info("2")
+
+ if res.status == nil then
+ core.Info(" processhttpresponse RECEIVING status: NIL")
+ return res
+ end
+ core.Info(" processhttpresponse RECEIVING status:"..res.status)
+ res.headers = {}
+ res.headerslist = {}
+ repeat
+core.Info("3")
+ local header = socket:receive("*l")
+ if header == nil then
+ return "error"
+ end
+ local valuestart = header:find(":")
+ if valuestart ~= nil then
+ local head = header:sub(1,valuestart-1)
+ local value = header:sub(valuestart+2)
+ table.insert(res.headerslist, {head,value})
+ res.headers[head] = value
+ end
+ until header == ""
+ local bodydone = false
+ if res.headers["Connection"] ~= nil and res.headers["Connection"] == "close" then
+-- core.Info("luacurl processresponse with connection:close")
+ res.body = ""
+ repeat
+core.Info("4")
+ local d = socket:receive("*a")
+ if d ~= nil then
+ res.body = res.body .. d
+ end
+ until d == nil or d == 0
+ bodydone = true
+ end
+ if bodydone == false and res.headers["Content-Length"] ~= nil then
+ res.contentlength = tonumber(res.headers["Content-Length"])
+ if res.contentlength == nil then
+ core.Warning("res.contentlength ~NIL = "..res.headers["Content-Length"])
+ end
+-- core.Info("luacur, contentlength="..res.contentlength)
+ res.body = ""
+ repeat
+ local d = socket:receive(res.contentlength)
+ if d == nil then
+-- core.Info("luacurl, ERROR?: received NIL, expecting "..res.contentlength.." bytes only got "..string.len(res.body).." sofar")
+ return
+ else
+ res.body = res.body..d
+-- core.Info("luacurl, COMPLETE?: expecting "..res.contentlength.." bytes, got "..string.len(res.body))
+ if string.len(res.body) >= res.contentlength then
+-- core.Info("luacurl, COMPLETE?: expecting "..res.contentlength.." bytes, got "..string.len(res.body))
+ break
+ end
+ end
+-- core.Info("processhttpresponse, Loopy, get more body data! to receive complete contentlenght")
+ until false
+ end
+ if res.headers["Transfer-Encoding"] ~= nil and res.headers["Transfer-Encoding"] == "chunked" then
+ local chunksize = 0
+ res.contentlength = 0
+ res.body = ""
+ repeat
+core.Info("5")
+ local chunksizestr = socket:receive("*l")
+ if chunksizestr == nil then
+ break
+ end
+ chunksize = tonumber("0x"..chunksizestr)
+ if chunksize ~= nil then
+ res.contentlength = res.contentlength + chunksize
+ if chunksize ~= 0 then
+ local chunk = socket:receive(chunksize)
+ res.body = res.body .. chunk
+ chunksizestr = socket:receive("*l")
+ if chunksizestr ~= "" then
+ return "ERROR Chunk-end expected."
+ end
+ end
+ else
+ break
+ end
+ until false
+ end
+core.Info("6")
+ return res
+end
+
+function Luacurl:close()
+ if self.sockconnected == true then
+ self.sock:close()
+ self.sockconnected = false
+ end
+end
+
+function print_r_string(object)
+ local res = ""
+ print_r(object,false,function(x) res = res .. x end)
+ return res
+end
+
+core.register_service("fakeserv", "http", function(applet)
+ core.Info("APPLET START")
+ local mc = Luacurl("127.0.0.1",8443, true)
+ local headers = {}
+ local body = ""
+ core.Info("APPLET GET")
+ local res = mc:get("GET", "/", headers, body)
+ core.Info("APPLET GET done")
+ local response = print_r_string(res)
+ applet:add_header("Server", "haproxy/webstats")
+ applet:add_header("Content-Length", string.len(response))
+ applet:add_header("Content-Type", "text/html")
+ applet:start_response()
+ applet:send(response)
+ core.Info("APPLET DONE")
+end)
diff --git a/reg-tests/lua/txn_get_priv.vtc b/reg-tests/lua/txn_get_priv.vtc
new file mode 100644
index 0000000..71e7bb5
--- /dev/null
+++ b/reg-tests/lua/txn_get_priv.vtc
@@ -0,0 +1,35 @@
+varnishtest "Lua: txn:get_priv() scope"
+#REQUIRE_OPTIONS=LUA,OPENSSL
+#REGTEST_TYPE=bug
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/txn_get_priv.lua
+ lua-load ${testdir}/txn_get_priv-print_r.lua
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ default_backend b1
+
+ frontend fe2
+ mode http
+ bind ":8443" ssl crt ${testdir}/common.pem
+ stats enable
+ stats uri /
+
+ backend b1
+ mode http
+ http-request use-service lua.fakeserv
+} -start
+
+client c0 -connect ${h1_fe1_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/lua/wrong_types_usage.lua b/reg-tests/lua/wrong_types_usage.lua
new file mode 100644
index 0000000..d2401fa
--- /dev/null
+++ b/reg-tests/lua/wrong_types_usage.lua
@@ -0,0 +1,3 @@
+core.register_action("foo", { "http-req" }, function(txn)
+ txn.sc:ipmask(txn.f:src(), 24, 112)
+end)
diff --git a/reg-tests/lua/wrong_types_usage.vtc b/reg-tests/lua/wrong_types_usage.vtc
new file mode 100644
index 0000000..ed76579
--- /dev/null
+++ b/reg-tests/lua/wrong_types_usage.vtc
@@ -0,0 +1,77 @@
+# commit f874a83
+# BUG/MINOR: lua: Segfaults with wrong usage of types.
+#
+# Patrick reported that this simple configuration made haproxy segfaults:
+#
+# global
+# lua-load /tmp/haproxy.lua
+#
+# frontend f1
+# mode http
+# bind :8000
+# default_backend b1
+#
+# http-request lua.foo
+#
+# backend b1
+# mode http
+# server s1 127.0.0.1:8080
+#
+# with this '/tmp/haproxy.lua' script:
+#
+# core.register_action("foo", { "http-req" }, function(txn)
+# txn.sc:ipmask(txn.f:src(), 24, 112)
+# end)
+#
+# This is due to missing initialization of the array of arguments
+# passed to hlua_lua2arg_check() which makes it enter code with
+# corrupted arguments.
+#
+# Thanks a lot to Patrick Hemmer for having reported this issue.
+
+
+varnishtest "Basic LUA test h00000"
+#REQUIRE_OPTIONS=LUA
+#REGTEST_TYPE=bug
+
+feature ignore_unknown_macro
+
+server s1 -repeat 2 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/wrong_types_usage.lua
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ default_backend b1
+
+ http-request lua.foo
+
+ backend b1
+ mode http
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c0 -connect ${h1_fe1_sock} {
+ txreq -url "/foo"
+ rxresp
+ expect resp.status == 200
+}
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -url "/foo"
+ rxresp
+ expect resp.status == 200
+}
+
+client c0 -start
+client c1 -start
+
+client c0 -wait
+client c1 -wait
diff --git a/reg-tests/mailers/healthcheckmail.lua b/reg-tests/mailers/healthcheckmail.lua
new file mode 100644
index 0000000..4cb0e9d
--- /dev/null
+++ b/reg-tests/mailers/healthcheckmail.lua
@@ -0,0 +1,70 @@
+
+local vtc_port1 = 0
+local mailsreceived = 0
+local mailconnectionsmade = 0
+local healthcheckcounter = 0
+
+function RecieveAndCheck(applet, expect)
+ data = applet:getline()
+ if data:sub(1,expect:len()) ~= expect then
+ core.Info("Expected: "..expect.." but got:"..data:sub(1,expect:len()))
+ applet:send("Expected: "..expect.." but got:"..data.."\r\n")
+ return false
+ end
+ return true
+end
+
+core.register_service("mailservice", "tcp", function(applet)
+ core.Info("############# Mailservice Called #############")
+ mailconnectionsmade = mailconnectionsmade + 1
+ applet:send("220 Welcome\r\n")
+ local data
+
+ if RecieveAndCheck(applet, "HELO") == false then
+ applet:set_var("txn.result", "ERROR (step: HELO)")
+ return
+ end
+ applet:send("250 OK\r\n")
+ if RecieveAndCheck(applet, "MAIL FROM:") == false then
+ applet:set_var("txn.result", "ERROR (step: MAIL FROM)")
+ return
+ end
+ applet:send("250 OK\r\n")
+ if RecieveAndCheck(applet, "RCPT TO:") == false then
+ applet:set_var("txn.result", "ERROR (step: RCPT TO)")
+ return
+ end
+ applet:send("250 OK\r\n")
+ if RecieveAndCheck(applet, "DATA") == false then
+ applet:set_var("txn.result", "ERROR (step: DATA)")
+ return
+ end
+ applet:send("354 OK\r\n")
+ core.Info("#### Send your mailbody")
+ local endofmail = false
+ local subject = ""
+ while endofmail ~= true do
+ data = applet:getline() -- BODY CONTENT
+ --core.Info(data)
+ if data:sub(1, 9) == "Subject: " then
+ subject = data
+ end
+ if (data == "\r\n") then
+ data = applet:getline() -- BODY CONTENT
+ core.Info(data)
+ if (data == ".\r\n") then
+ endofmail = true
+ end
+ end
+ end
+ core.Info("#### Body received OK")
+ applet:send("250 OK\r\n")
+
+ if RecieveAndCheck(applet, "QUIT") == false then
+ applet:set_var("txn.result", "ERROR (step: QUIT)")
+ return
+ end
+ applet:send("221 Mail queued for delivery to /dev/null \r\n")
+ core.Info("Mail queued for delivery to /dev/null subject: "..subject)
+ applet:set_var("txn.result", "SUCCESS")
+end)
diff --git a/reg-tests/mailers/healthcheckmail.vtc b/reg-tests/mailers/healthcheckmail.vtc
new file mode 100644
index 0000000..821250f
--- /dev/null
+++ b/reg-tests/mailers/healthcheckmail.vtc
@@ -0,0 +1,59 @@
+varnishtest "Check health-check email alerts"
+#REQUIRE_OPTIONS=LUA
+
+feature ignore_unknown_macro
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 failed.+check duration: [[:digit:]]+ms.+status: 0/1 DOWN."
+ recv info
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Result=SUCCESS Bytes=[[:digit:]]+"
+} -start
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/healthcheckmail.lua
+
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen lisrv
+ mode tcp
+ bind "fd@${lisrv}"
+ tcp-request connection reject
+
+ listen lismtp
+ mode tcp
+ bind "fd@${lismtp}"
+ log ${S1_addr}:${S1_port} daemon
+ log-format "Result=%[var(txn.result)] Bytes=%B"
+ tcp-request content use-service lua.mailservice
+
+ frontend fe1
+ mode http
+ bind "fd@${fe1}"
+ default_backend be1
+
+ backend be1
+ mode http
+ log ${S1_addr}:${S1_port} daemon
+ option httpchk
+ option log-health-checks
+
+ default-server inter 200ms downinter 100ms rise 1 fall 1
+
+ email-alert mailers mymailers
+ email-alert level info
+ email-alert from from@domain.tld
+ email-alert to to@domain.tld
+
+ server srv1 ${h1_lisrv_addr}:${h1_lisrv_port} check
+
+ mailers mymailers
+ mailer smtp1 ${h1_lismtp_addr}:${h1_lismtp_port}
+
+} -start
+
+syslog S1 -wait
diff --git a/reg-tests/mcli/mcli_show_info.vtc b/reg-tests/mcli/mcli_show_info.vtc
new file mode 100644
index 0000000..ae533da
--- /dev/null
+++ b/reg-tests/mcli/mcli_show_info.vtc
@@ -0,0 +1,29 @@
+varnishtest "Show info of process 1"
+
+#REQUIRE_VERSION=1.9
+
+feature ignore_unknown_macro
+
+# Do nothing. Is there only to create s1_* macros
+server s1 {
+} -start
+
+haproxy h1 -W -S -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend myfrontend
+ bind "fd@${my_fe}"
+ default_backend test
+
+ backend test
+ server www1 ${s1_addr}:${s1_port}
+} -start
+
+haproxy h1 -mcli {
+ send "@1 show info"
+ expect ~ ".*\nProcess_num: 1\n.*"
+} -wait
diff --git a/reg-tests/mcli/mcli_start_progs.vtc b/reg-tests/mcli/mcli_start_progs.vtc
new file mode 100644
index 0000000..a2e0f75
--- /dev/null
+++ b/reg-tests/mcli/mcli_start_progs.vtc
@@ -0,0 +1,37 @@
+varnishtest "Try to start a master CLI with 2 programs"
+#REGTEST_TYPE=bug
+#REQUIRE_VERSION=2.0
+feature cmd "command -v sleep"
+
+feature ignore_unknown_macro
+
+# Do nothing. Is there only to create s1_* macros
+server s1 {
+} -start
+
+haproxy h1 -W -S -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend myfrontend
+ bind "fd@${my_fe}"
+ default_backend test
+
+ backend test
+ server www1 ${s1_addr}:${s1_port}
+
+ program foo
+ command sleep 10
+
+ program bar
+ command sleep 10
+
+} -start
+
+haproxy h1 -mcli {
+ send "show proc"
+ expect ~ ".*foo.*\n.*bar.*\n"
+} -wait
diff --git a/reg-tests/peers/basic_sync.vtc b/reg-tests/peers/basic_sync.vtc
new file mode 100644
index 0000000..02449e2
--- /dev/null
+++ b/reg-tests/peers/basic_sync.vtc
@@ -0,0 +1,121 @@
+vtest "Basic test for peers protocol"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.0
+#REGTEST_TYPE=slow
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+ stick-table type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000) peers peers
+
+ peers peers
+ bind "fd@${A}"
+ server A
+ server B ${h2_B_addr}:${h2_B_port}
+ server C ${h3_C_addr}:${h3_C_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ tcp-request inspect-delay 100ms
+ tcp-request content track-sc0 url table stkt
+ tcp-request content sc-inc-gpc0(0)
+}
+
+haproxy h2 -arg "-L B" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+ stick-table type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000) peers peers
+
+ peers peers
+ bind "fd@${B}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B
+ server C ${h3_C_addr}:${h3_C_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table stkt
+ http-request sc-inc-gpc0(0)
+}
+
+haproxy h3 -arg "-L C" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+ stick-table type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000) peers peers
+
+ peers peers
+ bind "fd@${C}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B ${h2_B_addr}:${h2_B_port}
+ server C
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table stkt
+ http-request sc-inc-gpc0(0)
+}
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/c1_client"
+ expect_close
+} -start
+
+client c2 -connect ${h1_fe_sock} {
+ txreq -url "/c2_client"
+ expect_close
+} -start
+
+client c3 -connect ${h1_fe_sock} {
+ txreq -url "/c3_client"
+ expect_close
+} -start
+
+client c4 -connect ${h1_fe_sock} {
+ txreq -url "/c4_client"
+ expect_close
+} -start
+
+haproxy h1 -start
+delay 0.2
+haproxy h2 -start
+delay 0.2
+haproxy h3 -start
+delay 0.2
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
+client c4 -wait
+
+delay 2
+
+haproxy h1 -cli {
+ send "show table stkt"
+ expect ~ "# table: stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h2 -cli {
+ send "show table stkt"
+ expect ~ "# table: stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h3 -cli {
+ send "show table stkt"
+ expect ~ "# table: stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
diff --git a/reg-tests/peers/basic_sync_wo_stkt_backend.vtc b/reg-tests/peers/basic_sync_wo_stkt_backend.vtc
new file mode 100644
index 0000000..e4f59e1
--- /dev/null
+++ b/reg-tests/peers/basic_sync_wo_stkt_backend.vtc
@@ -0,0 +1,116 @@
+vtest "Basic test for peers protocol stick-table declared in peers sections"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.0
+#REGTEST_TYPE=slow
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ peers peers
+ bind "fd@${A}"
+ server A
+ server B ${h2_B_addr}:${h2_B_port}
+ server C ${h3_C_addr}:${h3_C_port}
+ table stkt type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000)
+
+ frontend fe
+ bind "fd@${fe}"
+ tcp-request inspect-delay 100ms
+ tcp-request content track-sc0 url table peers/stkt
+ tcp-request content sc-inc-gpc0(0)
+}
+
+haproxy h2 -arg "-L B" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ peers peers
+ bind "fd@${B}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B
+ server C ${h3_C_addr}:${h3_C_port}
+ table stkt type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000)
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table peers/stkt
+ http-request sc-inc-gpc0(0)
+}
+
+haproxy h3 -arg "-L C" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ peers peers
+ bind "fd@${C}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B ${h2_B_addr}:${h2_B_port}
+ server C
+ table stkt type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000)
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table peers/stkt
+ http-request sc-inc-gpc0(0)
+}
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/c1_client"
+ expect_close
+} -start
+
+client c2 -connect ${h1_fe_sock} {
+ txreq -url "/c2_client"
+ expect_close
+} -start
+
+client c3 -connect ${h1_fe_sock} {
+ txreq -url "/c3_client"
+ expect_close
+} -start
+
+client c4 -connect ${h1_fe_sock} {
+ txreq -url "/c4_client"
+ expect_close
+} -start
+
+haproxy h1 -start
+delay 0.2
+haproxy h2 -start
+delay 0.2
+haproxy h3 -start
+delay 0.2
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
+client c4 -wait
+
+delay 2
+
+haproxy h1 -cli {
+ send "show table peers/stkt"
+ expect ~ "# table: peers/stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h2 -cli {
+ send "show table peers/stkt"
+ expect ~ "# table: peers/stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h3 -cli {
+ send "show table peers/stkt"
+ expect ~ "# table: peers/stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
diff --git a/reg-tests/peers/common.pem b/reg-tests/peers/common.pem
new file mode 120000
index 0000000..a4433d5
--- /dev/null
+++ b/reg-tests/peers/common.pem
@@ -0,0 +1 @@
+../ssl/common.pem \ No newline at end of file
diff --git a/reg-tests/peers/tls_basic_sync.vtc b/reg-tests/peers/tls_basic_sync.vtc
new file mode 100644
index 0000000..7b6e9b2
--- /dev/null
+++ b/reg-tests/peers/tls_basic_sync.vtc
@@ -0,0 +1,158 @@
+vtest "Basic test for peers protocol over SSL/TLS"
+#REQUIRE_OPTIONS=OPENSSL
+#REQUIRE_VERSION=2.0
+feature ignore_unknown_macro
+
+#REGTEST_TYPE=slow
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+ stick-table type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000) peers peers
+
+ peers peers
+ default-server ssl crt ${testdir}/common.pem verify none
+ bind "fd@${A}" ssl crt ${testdir}/common.pem
+ server A
+ server B ${h2_B_addr}:${h2_B_port}
+ server C ${h3_C_addr}:${h3_C_port}
+ server D ${h4_D_addr}:${h4_D_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ tcp-request inspect-delay 100ms
+ tcp-request content track-sc0 url table stkt
+ tcp-request content sc-inc-gpc0(0)
+}
+
+haproxy h2 -arg "-L B" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+ stick-table type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000) peers peers
+
+ peers peers
+ default-server ssl crt ${testdir}/common.pem verify none
+ bind "fd@${B}" ssl crt ${testdir}/common.pem
+ server A ${h1_A_addr}:${h1_A_port}
+ server B
+ server C ${h3_C_addr}:${h3_C_port}
+ server D ${h4_D_addr}:${h4_D_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table stkt
+ http-request sc-inc-gpc0(0)
+}
+
+haproxy h3 -arg "-L C" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+ stick-table type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000) peers peers
+
+ peers peers
+ default-server ssl crt ${testdir}/common.pem verify none
+ bind "fd@${C}" ssl crt ${testdir}/common.pem
+ server A ${h1_A_addr}:${h1_A_port}
+ server B ${h2_B_addr}:${h2_B_port}
+ server C
+ server D ${h4_D_addr}:${h4_D_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table stkt
+ http-request sc-inc-gpc0(0)
+}
+
+haproxy h4 -arg "-L D" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+ stick-table type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000) peers peers
+
+ peers peers
+ bind "fd@${D}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B ${h2_B_addr}:${h2_B_port}
+ server C ${h3_C_addr}:${h3_C_port}
+ server D
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table stkt
+ http-request sc-inc-gpc0(0)
+}
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/c1_client"
+ expect_close
+} -start
+
+client c2 -connect ${h1_fe_sock} {
+ txreq -url "/c2_client"
+ expect_close
+} -start
+
+client c3 -connect ${h1_fe_sock} {
+ txreq -url "/c3_client"
+ expect_close
+} -start
+
+client c4 -connect ${h1_fe_sock} {
+ txreq -url "/c4_client"
+ expect_close
+} -start
+
+haproxy h1 -start
+delay 0.2
+haproxy h2 -start
+delay 0.2
+haproxy h3 -start
+delay 0.2
+haproxy h4 -start
+delay 0.2
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
+client c4 -wait
+
+delay 2
+
+haproxy h1 -cli {
+ send "show table stkt"
+ expect ~ "# table: stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h2 -cli {
+ send "show table stkt"
+ expect ~ "# table: stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h3 -cli {
+ send "show table stkt"
+ expect ~ "# table: stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h4 -cli {
+ send "show table stkt"
+ expect ~ "# table: stkt, type: string, size:1048[0-9]{4}, used:0\n"
+}
+
diff --git a/reg-tests/peers/tls_basic_sync_wo_stkt_backend.vtc b/reg-tests/peers/tls_basic_sync_wo_stkt_backend.vtc
new file mode 100644
index 0000000..7e1bfd3
--- /dev/null
+++ b/reg-tests/peers/tls_basic_sync_wo_stkt_backend.vtc
@@ -0,0 +1,152 @@
+vtest "Basic test for peers protocol over SSL/TLS with stick-table declared in peers sections"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.0
+#REQUIRE_OPTIONS=OPENSSL
+#REGTEST_TYPE=slow
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ peers peers
+ table stkt type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000)
+ default-server ssl crt ${testdir}/common.pem verify none
+ bind "fd@${A}" ssl crt ${testdir}/common.pem
+ server A
+ server B ${h2_B_addr}:${h2_B_port}
+ server C ${h3_C_addr}:${h3_C_port}
+ server D ${h4_D_addr}:${h4_D_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ tcp-request inspect-delay 100ms
+ tcp-request content track-sc0 url table peers/stkt
+ tcp-request content sc-inc-gpc0(0)
+}
+
+haproxy h2 -arg "-L B" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ peers peers
+ table stkt type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000)
+ default-server ssl crt ${testdir}/common.pem verify none
+ bind "fd@${B}" ssl crt ${testdir}/common.pem
+ server A ${h1_A_addr}:${h1_A_port}
+ server B
+ server C ${h3_C_addr}:${h3_C_port}
+ server D ${h4_D_addr}:${h4_D_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table peers/stkt
+ http-request sc-inc-gpc0(0)
+}
+
+haproxy h3 -arg "-L C" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ peers peers
+ table stkt type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000)
+ default-server ssl crt ${testdir}/common.pem verify none
+ bind "fd@${C}" ssl crt ${testdir}/common.pem
+ server A ${h1_A_addr}:${h1_A_port}
+ server B ${h2_B_addr}:${h2_B_port}
+ server C
+ server D ${h4_D_addr}:${h4_D_port}
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table peers/stkt
+ http-request sc-inc-gpc0(0)
+}
+
+haproxy h4 -arg "-L D" -conf {
+ defaults
+ mode http
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ backend stkt
+
+ peers peers
+ table stkt type string size 10m store server_id,gpc0,conn_cur,conn_rate(50000)
+ bind "fd@${D}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B ${h2_B_addr}:${h2_B_port}
+ server C ${h3_C_addr}:${h3_C_port}
+ server D
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request track-sc0 url table peers/stkt
+ http-request sc-inc-gpc0(0)
+}
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/c1_client"
+ expect_close
+} -start
+
+client c2 -connect ${h1_fe_sock} {
+ txreq -url "/c2_client"
+ expect_close
+} -start
+
+client c3 -connect ${h1_fe_sock} {
+ txreq -url "/c3_client"
+ expect_close
+} -start
+
+client c4 -connect ${h1_fe_sock} {
+ txreq -url "/c4_client"
+ expect_close
+} -start
+
+haproxy h1 -start
+delay 0.02
+haproxy h2 -start
+delay 0.02
+haproxy h3 -start
+delay 0.02
+haproxy h4 -start
+delay 0.02
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
+client c4 -wait
+
+delay 3
+
+haproxy h1 -cli {
+ send "show table peers/stkt"
+ expect ~ "# table: peers/stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h2 -cli {
+ send "show table peers/stkt"
+ expect ~ "# table: peers/stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h3 -cli {
+ send "show table peers/stkt"
+ expect ~ "# table: peers/stkt, type: string, size:1048[0-9]{4}, used:4(\n0x[0-9a-f]*: key=/c[1-4]_client use=0 exp=0 server_id=0 gpc0=1 conn_rate\\(50000\\)=1 conn_cur=0){4}"
+}
+
+haproxy h4 -cli {
+ send "show table peers/stkt"
+ expect ~ "# table: peers/stkt, type: string, size:1048[0-9]{4}, used:0\n"
+}
+
diff --git a/reg-tests/sample_fetches/cond_set_var.vtc b/reg-tests/sample_fetches/cond_set_var.vtc
new file mode 100644
index 0000000..67786ae
--- /dev/null
+++ b/reg-tests/sample_fetches/cond_set_var.vtc
@@ -0,0 +1,362 @@
+#REGTEST_TYPE=devel
+
+# This regtest checks the multiple conditions that can be specified to a
+# set-var call (be it a converter or HTTP or TCP action). It mainly uses the
+# actions but since the "var_set" function is used for the converter and for
+# the actions, it should be enough to focus on one type of set-var.
+# Among the variables that can be defined and the multiple scopes they can
+# have, the proc scope is the only one having a specific behaviour. Proc scoped
+# variables are created during init when a variable of any other scope is
+# created during the first successful set-var.
+# Since this test uses variables of different scopes, the validation cannot be
+# based on the "show var" command of the CLI because it only displays process
+# variables. It then always follows the same logic, for every sub test case :
+# an HTTP header is added to the response in which we add a concatenation of
+# all the tested variables (which exist in the request/response scope). These
+# HTTP headers are then tested upon an expected result (which changes for every
+# test case).
+#
+
+varnishtest "Test the conditional set-var converter and action"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 10 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ set-var proc.int12 int(12)
+ set-var proc.int5 var(proc.str60,60),div(proc.int12)
+
+ defaults
+ mode http
+ timeout connect 100ms
+ timeout client 1s
+ timeout server 1s
+
+ listen main-fe
+ bind "fd@${mainfe}"
+
+ use_backend ifexists_be if { path_beg /ifexists }
+ use_backend ifnotexists_be if { path_beg /ifnotexists }
+ use_backend ifempty_be if { path_beg /ifempty }
+ use_backend ifnotempty_be if { path_beg /ifnotempty }
+ use_backend ifset_be if { path_beg /ifset }
+ use_backend ifnotset_be if { path_beg /ifnotset }
+ use_backend ifgt_be if { path_beg /ifgt }
+ use_backend iflt_be if { path_beg /iflt }
+ use_backend combined_be if { path_beg /combined }
+ use_backend converter_be if { path_beg /converter }
+
+
+ backend ifexists_be
+ server s1 ${s1_addr}:${s1_port}
+
+ # proc scope variables are created during configuration parsing so the
+ # ifexists condition will always be true for those variables
+ http-response set-var(proc.ifexists_proc,ifexists) var(proc.int12)
+ http-response set-var(sess.ifexists_sess,ifexists) var(proc.int5)
+ http-response set-var(res.ifexists_res,ifexists) str(toto)
+
+ http-response set-header x-var "proc.ifexists=%[var(proc.ifexists_proc)] sess.ifexists=%[var(sess.ifexists_sess)] res.ifexists=%[var(res.ifexists_res)]"
+
+
+ backend ifnotexists_be
+ server s1 ${s1_addr}:${s1_port}
+
+ http-response set-header x-var-init "proc.ifnotexists=%[var(proc.ifnotexists_proc)] sess.ifnotexists=%[var(sess.ifnotexists_sess)] res.ifnotexists=%[var(res.ifnotexists_res)]"
+
+ http-response set-var(proc.ifnotexists_proc,ifnotexists) var(proc.int12)
+ http-response set-var(sess.ifnotexists_sess,ifnotexists) var(proc.int5)
+ http-response set-var(res.ifnotexists_res,ifnotexists) str(toto)
+
+ http-response set-header x-var "proc.ifnotexists=%[var(proc.ifnotexists_proc)] sess.ifnotexists=%[var(sess.ifnotexists_sess)] res.ifnotexists=%[var(res.ifnotexists_res)]"
+
+
+ backend ifempty_be
+ server s1 ${s1_addr}:${s1_port}
+ # init
+ http-response set-var(proc.ifempty_proc) str(ifempty_proc)
+ http-response set-var(sess.ifempty_sess) bin(6966656d7074795f73657373) #ifempty_sess
+ http-response set-var(res.ifempty_res) str(ifempty_res)
+ http-response set-var(res.ifempty_res_int) int(5)
+
+ http-response set-header x-var-init "proc.ifempty=%[var(proc.ifempty_proc)] sess.ifempty=%[var(sess.ifempty_sess)] res.ifempty=%[var(res.ifempty_res)] res.ifempty_res_int=%[var(res.ifempty_res_int)]"
+
+ # None of those set-var calls should actually change their respective variables
+ # since none of the samples is empty
+ http-response set-var(proc.ifempty_proc,ifempty) int(12)
+ http-response set-var(sess.ifempty_sess,ifempty) bool(false)
+ http-response set-var(res.ifempty_res,ifempty) bin(746F746F) # "toto"
+ http-response set-var(res.ifempty_res_int,ifempty) str(toto)
+
+ http-response set-header x-var1 "proc.ifempty=%[var(proc.ifempty_proc)] sess.ifempty=%[var(sess.ifempty_sess)] res.ifempty=%[var(res.ifempty_res)] res.ifempty_res_int=%[var(res.ifempty_res_int)]"
+
+ http-response set-var(proc.ifempty_proc,ifempty) str()
+ http-response set-var(sess.ifempty_sess,ifempty) str()
+ http-response set-var(res.ifempty_res,ifempty) str()
+ http-response set-var(res.ifempty_res_int,ifempty) int(7) # should not work, scalar types are never empty
+
+ http-response set-header x-var2 "proc.ifempty=%[var(proc.ifempty_proc)] sess.ifempty=%[var(sess.ifempty_sess)] res.ifempty=%[var(res.ifempty_res)] res.ifempty_res_int=%[var(res.ifempty_res_int)]"
+
+
+ backend ifnotempty_be
+ server s1 ${s1_addr}:${s1_port}
+ # init
+ http-response set-var(proc.ifnotempty_proc) str(ifnotempty_proc)
+ http-response set-var(sess.ifnotempty_sess) bin(69666e6f74656d7074795f73657373) # "ifnotempty_sess"
+ http-response set-var(res.ifnotempty_res) str(ifnotempty_res)
+ http-response set-var(res.ifnotempty_res_int) int(5)
+
+ http-response set-header x-var-init "proc.ifnotempty=%[var(proc.ifnotempty_proc)] sess.ifnotempty=%[var(sess.ifnotempty_sess)] res.ifnotempty=%[var(res.ifnotempty_res)] res.ifnotempty_res_int=%[var(res.ifnotempty_res_int)]"
+
+ # None of those set-var calls should actually change their respective variables
+ # since none of the samples is not empty
+ http-response set-var(proc.ifnotempty_proc,ifnotempty) str(toto)
+ http-response set-var(sess.ifnotempty_sess,ifnotempty) bin(746F746F) # "toto"
+ http-response set-var(res.ifnotempty_res,ifnotempty) str(tata)
+ http-response set-var(res.ifnotempty_res_int,ifnotempty) int(6)
+
+ http-response set-header x-var1 "proc.ifnotempty=%[var(proc.ifnotempty_proc)] sess.ifnotempty=%[var(sess.ifnotempty_sess)] res.ifnotempty=%[var(res.ifnotempty_res)] res.ifnotempty_res_int=%[var(res.ifnotempty_res_int)]"
+
+ # The first three variables should remain unchanged.
+ http-response set-var(proc.ifnotempty_proc,ifnotempty) str()
+ http-response set-var(sess.ifnotempty_sess,ifnotempty) str()
+ http-response set-var(res.ifnotempty_res,ifnotempty) str()
+ http-response set-var(res.ifnotempty_res_int,ifnotempty) int(7) # should not work
+
+ http-response set-header x-var2 "proc.ifnotempty=%[var(proc.ifnotempty_proc)] sess.ifnotempty=%[var(sess.ifnotempty_sess)] res.ifnotempty=%[var(res.ifnotempty_res)] res.ifnotempty_res_int=%[var(res.ifnotempty_res_int)]"
+
+
+ backend ifset_be
+ server s1 ${s1_addr}:${s1_port}
+ # init
+ http-response set-var(proc.ifset_proc) str(ifset_proc)
+ http-response set-var(sess.ifset_sess) bin(69667365745f73657373) # "ifset_sess"
+ http-response set-var(res.ifset_res) str(ifset_res)
+ http-response set-var(res.ifset_res_int) int(5)
+
+ http-response set-header x-var-init "proc.ifset=%[var(proc.ifset_proc)] sess.ifset=%[var(sess.ifset_sess)] res.ifset=%[var(res.ifset_res)] res.ifset_res_int=%[var(res.ifset_res_int)]"
+
+ # All those set-var calls should succeed
+ http-response set-var(proc.ifset_proc,ifset) str(toto)
+ http-response set-var(sess.ifset_sess,ifset) bin(746F746F) # "toto"
+ http-response set-var(res.ifset_res,ifset) int(123)
+ http-response set-var(res.ifset_res_int,ifset) str(azerty)
+
+ http-response set-header x-var1 "proc.ifset=%[var(proc.ifset_proc)] sess.ifset=%[var(sess.ifset_sess)] res.ifset=%[var(res.ifset_res)] res.ifset_res_int=%[var(res.ifset_res_int)]"
+
+ http-response unset-var(proc.ifset_proc)
+ http-response unset-var(sess.ifset_sess)
+ http-response unset-var(res.ifset_res)
+ http-response unset-var(res.ifset_res_int)
+
+ http-response set-header x-var2 "proc.ifset=%[var(proc.ifset_proc)] sess.ifset=%[var(sess.ifset_sess)] res.ifset=%[var(res.ifset_res)] res.ifset_res_int=%[var(res.ifset_res_int)]"
+
+ # None of those set-var calls should succeed
+ http-response set-var(proc.ifset_proc,ifset) str(toto)
+ http-response set-var(sess.ifset_sess,ifset) bin(746F746F) # "toto"
+ http-response set-var(res.ifset_res,ifset) int(123)
+ http-response set-var(res.ifset_res_int,ifset) str(azerty)
+
+ http-response set-header x-var3 "proc.ifset=%[var(proc.ifset_proc)] sess.ifset=%[var(sess.ifset_sess)] res.ifset=%[var(res.ifset_res)] res.ifset_res_int=%[var(res.ifset_res_int)]"
+
+
+ backend ifnotset_be
+ server s1 ${s1_addr}:${s1_port}
+ # init
+ http-response set-var(proc.ifnotset_proc) str(ifnotset_proc)
+ http-response set-var(sess.ifnotset_sess) bin(69666e6f747365745f73657373) # "ifnotset_sess"
+ http-response set-var(res.ifnotset_res) str(ifnotset_res)
+ http-response set-var(res.ifnotset_res_int) int(5)
+
+ http-response set-header x-var-init "proc.ifnotset=%[var(proc.ifnotset_proc)] sess.ifnotset=%[var(sess.ifnotset_sess)] res.ifnotset=%[var(res.ifnotset_res)] res.ifnotset_res_int=%[var(res.ifnotset_res_int)]"
+
+ # None of those set-var calls should succeed
+ http-response set-var(proc.ifnotset_proc,ifnotset) str(toto)
+ http-response set-var(sess.ifnotset_sess,ifnotset) bin(746F746F) # "toto"
+ http-response set-var(res.ifnotset_res,ifnotset) int(123)
+ http-response set-var(res.ifnotset_res_int,ifnotset) str(azerty)
+
+ http-response set-header x-var1 "proc.ifnotset=%[var(proc.ifnotset_proc)] sess.ifnotset=%[var(sess.ifnotset_sess)] res.ifnotset=%[var(res.ifnotset_res)] res.ifnotset_res_int=%[var(res.ifnotset_res_int)]"
+
+ http-response unset-var(proc.ifnotset_proc)
+ http-response unset-var(sess.ifnotset_sess)
+ http-response unset-var(res.ifnotset_res)
+ http-response unset-var(res.ifnotset_res_int)
+
+ http-response set-header x-var2 "proc.ifnotset=%[var(proc.ifnotset_proc)] sess.ifnotset=%[var(sess.ifnotset_sess)] res.ifnotset=%[var(res.ifnotset_res)] res.ifnotset_res_int=%[var(res.ifnotset_res_int)]"
+
+ # All of those set-var calls should succeed
+ http-response set-var(proc.ifnotset_proc,ifnotset) str(toto)
+ http-response set-var(sess.ifnotset_sess,ifnotset) bin(746F746F) # "toto"
+ http-response set-var(res.ifnotset_res,ifnotset) int(123)
+ http-response set-var(res.ifnotset_res_int,ifnotset) str(azerty)
+
+ http-response set-header x-var3 "proc.ifnotset=%[var(proc.ifnotset_proc)] sess.ifnotset=%[var(sess.ifnotset_sess)] res.ifnotset=%[var(res.ifnotset_res)] res.ifnotset_res_int=%[var(res.ifnotset_res_int)]"
+
+ backend ifgt_be
+ server s1 ${s1_addr}:${s1_port}
+ # init
+ http-response set-var(proc.ifgt_proc) str(ifgt_proc)
+ http-response set-var(sess.ifgt_sess) bin(696667745f73657373) # "ifgt_sess"
+ http-response set-var(res.ifgt_res1) str(ifgt_res)
+ http-response set-var(res.ifgt_res2) int(5)
+ http-response set-var(res.ifgt_res_int1) int(5)
+ http-response set-var(res.ifgt_res_int2) int(5)
+
+ http-response set-header x-var-init "proc.ifgt=%[var(proc.ifgt_proc)] sess.ifgt=%[var(sess.ifgt_sess)] res.ifgt1=%[var(res.ifgt_res1)] res.ifgt2=%[var(res.ifgt_res2)] res.ifgt_res_int1=%[var(res.ifgt_res_int1)] res.ifgt_res_int2=%[var(res.ifgt_res_int2)]"
+
+ # ifgt does not apply on non scalar type so the two following set-var will ignore the condition
+ http-response set-var(proc.ifgt_proc,ifgt) str(toto)
+ http-response set-var(sess.ifgt_sess,ifgt) bin(746F746F) # "toto"
+ # ifgt can only apply when the variable and the sample are both scalar. In this case, the variable was a string so the condition is ignored
+ http-response set-var(res.ifgt_res1,ifgt) int(55)
+ # ifgt can only apply when the variable and the sample are both scalar. In this case, the sample is a string so the condition is ignored
+ http-response set-var(res.ifgt_res2,ifgt) str(text)
+ http-response set-var(res.ifgt_res_int1,ifgt) int(55) # should not work
+ http-response set-var(res.ifgt_res_int2,ifgt) int(2) # should work
+
+ http-response set-header x-var1 "proc.ifgt=%[var(proc.ifgt_proc)] sess.ifgt=%[var(sess.ifgt_sess)] res.ifgt1=%[var(res.ifgt_res1)] res.ifgt2=%[var(res.ifgt_res2)] res.ifgt_res_int1=%[var(res.ifgt_res_int1)] res.ifgt_res_int2=%[var(res.ifgt_res_int2)]"
+
+
+ backend iflt_be
+ server s1 ${s1_addr}:${s1_port}
+ # init
+ http-response set-var(proc.iflt_proc) str(iflt_proc)
+ http-response set-var(sess.iflt_sess) bin(69666c745f73657373) # "iflt_sess"
+ http-response set-var(res.iflt_res1) str(iflt_res)
+ http-response set-var(res.iflt_res2) int(5)
+ http-response set-var(res.iflt_res_int1) int(5)
+ http-response set-var(res.iflt_res_int2) int(5)
+
+ http-response set-header x-var-init "proc.iflt=%[var(proc.iflt_proc)] sess.iflt=%[var(sess.iflt_sess)] res.iflt1=%[var(res.iflt_res1)] res.iflt2=%[var(res.iflt_res2)] res.iflt_res_int1=%[var(res.iflt_res_int1)] res.iflt_res_int2=%[var(res.iflt_res_int2)]"
+
+ # iflt does not apply on non scalar type so the two following set-var will ignore the condition
+ http-response set-var(proc.iflt_proc,iflt) str(toto)
+ http-response set-var(sess.iflt_sess,iflt) bin(746F746F) # "toto"
+ # iflt can only apply when the variable and the sample are both scalar. In this case, the variable was a string so the condition is ignored
+ http-response set-var(res.iflt_res1,iflt) int(55)
+ # iflt can only apply when the variable and the sample are both scalar. In this case, the sample is a string so the condition is ignored
+ http-response set-var(res.iflt_res2,iflt) str(text)
+ http-response set-var(res.iflt_res_int1,iflt) int(55) # should work
+ http-response set-var(res.iflt_res_int2,iflt) int(2) # should not work
+
+ http-response set-header x-var1 "proc.iflt=%[var(proc.iflt_proc)] sess.iflt=%[var(sess.iflt_sess)] res.iflt1=%[var(res.iflt_res1)] res.iflt2=%[var(res.iflt_res2)] res.iflt_res_int1=%[var(res.iflt_res_int1)] res.iflt_res_int2=%[var(res.iflt_res_int2)]"
+
+
+ # Test multiple conditions at once
+ backend combined_be
+ server s1 ${s1_addr}:${s1_port}
+ # init
+ http-response set-var(proc.combined_proc) str(combined_proc)
+ http-response set-var(res.combined_res) int(5)
+ http-response unset-var(proc.combined_proc)
+
+ http-response set-header x-var-init "proc.combined=%[var(proc.combined_proc)] res.combined=%[var(res.combined_res)]"
+
+ http-response set-var(proc.combined_proc,ifnotset,ifnotempty) str(toto)
+ http-response set-var(res.combined_res,ifset,iflt) int(55)
+
+ http-response set-header x-var1 "proc.combined=%[var(proc.combined_proc)] res.combined=%[var(res.combined_res)]"
+
+
+ # Test the set-var converter
+ backend converter_be
+ server s1 ${s1_addr}:${s1_port}
+
+ http-request deny if { req.len,set-var(proc.req_len,ifexists) -m int 0 }
+ http-request deny if { req.hdr("X-Cust"),set-var(sess.x_cust,ifnotempty,ifnotset),length -m int 0 }
+
+ http-response set-header x-var "proc.req_len=%[var(proc.req_len)] sess.x_cust=%[var(sess.x_cust)]"
+
+} -start
+
+
+client c1 -connect ${h1_mainfe_sock} {
+ txreq -url "/ifexists"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var == "proc.ifexists=12 sess.ifexists= res.ifexists="
+} -run
+
+client c2 -connect ${h1_mainfe_sock} {
+ txreq -url "/ifnotexists"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.ifnotexists= sess.ifnotexists= res.ifnotexists="
+ expect resp.http.x-var == "proc.ifnotexists= sess.ifnotexists=5 res.ifnotexists=toto"
+} -run
+
+client c3 -connect ${h1_mainfe_sock} {
+ txreq -url "/ifempty"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.ifempty=ifempty_proc sess.ifempty=ifempty_sess res.ifempty=ifempty_res res.ifempty_res_int=5"
+ expect resp.http.x-var1 == "proc.ifempty=ifempty_proc sess.ifempty=ifempty_sess res.ifempty=ifempty_res res.ifempty_res_int=5"
+ expect resp.http.x-var2 == "proc.ifempty= sess.ifempty= res.ifempty= res.ifempty_res_int=5"
+} -run
+
+client c4 -connect ${h1_mainfe_sock} {
+ txreq -url "/ifnotempty"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.ifnotempty=ifnotempty_proc sess.ifnotempty=ifnotempty_sess res.ifnotempty=ifnotempty_res res.ifnotempty_res_int=5"
+ expect resp.http.x-var1 == "proc.ifnotempty=toto sess.ifnotempty=toto res.ifnotempty=tata res.ifnotempty_res_int=6"
+ expect resp.http.x-var2 == "proc.ifnotempty=toto sess.ifnotempty=toto res.ifnotempty=tata res.ifnotempty_res_int=7"
+} -run
+
+client c5 -connect ${h1_mainfe_sock} {
+ txreq -url "/ifset"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.ifset=ifset_proc sess.ifset=ifset_sess res.ifset=ifset_res res.ifset_res_int=5"
+ expect resp.http.x-var1 == "proc.ifset=toto sess.ifset=toto res.ifset=123 res.ifset_res_int=azerty"
+ expect resp.http.x-var2 == "proc.ifset= sess.ifset= res.ifset= res.ifset_res_int="
+ expect resp.http.x-var3 == "proc.ifset= sess.ifset= res.ifset= res.ifset_res_int="
+} -run
+
+client c6 -connect ${h1_mainfe_sock} {
+ txreq -url "/ifnotset"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.ifnotset=ifnotset_proc sess.ifnotset=ifnotset_sess res.ifnotset=ifnotset_res res.ifnotset_res_int=5"
+ expect resp.http.x-var1 == "proc.ifnotset=ifnotset_proc sess.ifnotset=ifnotset_sess res.ifnotset=ifnotset_res res.ifnotset_res_int=5"
+ expect resp.http.x-var2 == "proc.ifnotset= sess.ifnotset= res.ifnotset= res.ifnotset_res_int="
+ expect resp.http.x-var3 == "proc.ifnotset=toto sess.ifnotset=toto res.ifnotset=123 res.ifnotset_res_int=azerty"
+} -run
+
+client c7 -connect ${h1_mainfe_sock} {
+ txreq -url "/ifgt"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.ifgt=ifgt_proc sess.ifgt=ifgt_sess res.ifgt1=ifgt_res res.ifgt2=5 res.ifgt_res_int1=5 res.ifgt_res_int2=5"
+ expect resp.http.x-var1 == "proc.ifgt=toto sess.ifgt=toto res.ifgt1=55 res.ifgt2=text res.ifgt_res_int1=5 res.ifgt_res_int2=2"
+} -run
+
+client c8 -connect ${h1_mainfe_sock} {
+ txreq -url "/iflt"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.iflt=iflt_proc sess.iflt=iflt_sess res.iflt1=iflt_res res.iflt2=5 res.iflt_res_int1=5 res.iflt_res_int2=5"
+ expect resp.http.x-var1 == "proc.iflt=toto sess.iflt=toto res.iflt1=55 res.iflt2=text res.iflt_res_int1=55 res.iflt_res_int2=5"
+} -run
+
+client c9 -connect ${h1_mainfe_sock} {
+ txreq -url "/combined"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var-init == "proc.combined= res.combined=5"
+ expect resp.http.x-var1 == "proc.combined=toto res.combined=55"
+} -run
+
+client c10 -connect ${h1_mainfe_sock} {
+ txreq -url "/converter" -hdr "X-Cust: foobar"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var == "proc.req_len=67 sess.x_cust=foobar"
+} -run
diff --git a/reg-tests/sample_fetches/cook.vtc b/reg-tests/sample_fetches/cook.vtc
new file mode 100644
index 0000000..e2c1284
--- /dev/null
+++ b/reg-tests/sample_fetches/cook.vtc
@@ -0,0 +1,37 @@
+varnishtest "cook sample fetch Test"
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ frontend fe
+ bind "fd@${fe}"
+ http-request set-var(txn.count) req.cook_cnt()
+ http-request set-var(txn.val) req.cook_val()
+ http-request set-var(txn.val_cook2) req.cook_val(cook2)
+ http-response set-header count %[var(txn.count)]
+ http-response set-header val %[var(txn.val)]
+ http-response set-header val_cook2 %[var(txn.val_cook2)]
+
+ default_backend be
+
+ backend be
+ server srv1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/" \
+ -hdr "cookie: cook1=0; cook2=123; cook3=22"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.count == "3"
+ expect resp.http.val == "0"
+ expect resp.http.val_cook2 == "123"
+} -run
diff --git a/reg-tests/sample_fetches/hashes.vtc b/reg-tests/sample_fetches/hashes.vtc
new file mode 100644
index 0000000..2c2f60d
--- /dev/null
+++ b/reg-tests/sample_fetches/hashes.vtc
@@ -0,0 +1,101 @@
+varnishtest "Hash validity test"
+
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ 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}"
+
+ # base64 encoding of \x00\x01\x02...\xFF
+ http-response set-var(res.key) "str(AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==),b64dec"
+
+ # length (start:0, next:255)
+ http-response set-header x-len0 "%[var(res.key),length]"
+ http-response set-header x-len1 "%[var(res.key),bytes(1),length]"
+
+ # text-based encoding
+ http-response set-header x-hex "%[var(res.key),hex]"
+ http-response set-header x-b64 "%[var(res.key),base64]"
+
+ # SHA family
+ http-response set-header x-sha1 "%[var(res.key),sha1,hex]"
+ #http-response set-header x-sha2 "%[var(res.key),sha2,hex]"
+ #http-response set-header x-sha2-224 "%[var(res.key),sha2(224),hex]"
+ #http-response set-header x-sha2-256 "%[var(res.key),sha2(256),hex]"
+ #http-response set-header x-sha2-384 "%[var(res.key),sha2(384),hex]"
+ #http-response set-header x-sha2-512 "%[var(res.key),sha2(512),hex]"
+
+ # 32-bit hashes, and their avalanche variants
+ http-response set-header x-crc32 "%[var(res.key),crc32]"
+ http-response set-header x-crc32-1 "%[var(res.key),crc32(1)]"
+
+ http-response set-header x-crc32c "%[var(res.key),crc32c]"
+ http-response set-header x-crc32c-1 "%[var(res.key),crc32c(1)]"
+
+ http-response set-header x-djb2 "%[var(res.key),djb2]"
+ http-response set-header x-djb2-1 "%[var(res.key),djb2(1)]"
+
+ http-response set-header x-sdbm "%[var(res.key),sdbm]"
+ http-response set-header x-sdbm-1 "%[var(res.key),sdbm(1)]"
+
+ http-response set-header x-wt6 "%[var(res.key),wt6]"
+ http-response set-header x-wt6-1 "%[var(res.key),wt6(1)]"
+
+ # 32/64-bit hashes, with seed variant
+ http-response set-header x-xxh3 "%[var(res.key),xxh3]"
+ http-response set-header x-xxh3-1 "%[var(res.key),xxh3(1)]"
+ http-response set-header x-xxh32 "%[var(res.key),xxh32]"
+ http-response set-header x-xxh32-1 "%[var(res.key),xxh32(1)]"
+ http-response set-header x-xxh64 "%[var(res.key),xxh64]"
+ http-response set-header x-xxh64-1 "%[var(res.key),xxh64(1)]"
+ default_backend be
+
+ backend be
+ server srv1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-len0 == "0"
+ expect resp.http.x-len1 == "255"
+ expect resp.http.x-hex == "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
+ expect resp.http.x-b64 == "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
+
+ expect resp.http.x-sha1 == "4916D6BDB7F78E6803698CAB32D1586EA457DFC8"
+ #expect resp.http.x-sha2 == "40AFF2E9D2D8922E47AFD4648E6967497158785FBD1DA870E7110266BF944880"
+ #expect resp.http.x-sha2-224 == "88702E63237824C4EB0D0FCFE41469A462493E8BEB2A75BBE5981734"
+ #expect resp.http.x-sha2-256 == "40AFF2E9D2D8922E47AFD4648E6967497158785FBD1DA870E7110266BF944880"
+ #expect resp.http.x-sha2-384 == "FFDAEBFF65ED05CF400F0221C4CCFB4B2104FB6A51F87E40BE6C4309386BFDEC2892E9179B34632331A59592737DB5C5"
+ #expect resp.http.x-sha2-512 == "1E7B80BC8EDC552C8FEEB2780E111477E5BC70465FAC1A77B29B35980C3F0CE4A036A6C9462036824BD56801E62AF7E9FEBA5C22ED8A5AF877BF7DE117DCAC6D"
+ expect resp.http.x-crc32 == "688229491"
+ expect resp.http.x-crc32-1 == "4230317029"
+ expect resp.http.x-crc32c == "2621708363"
+ expect resp.http.x-crc32c-1 == "2242979626"
+ expect resp.http.x-djb2 == "2589693061"
+ expect resp.http.x-djb2-1 == "600622701"
+ expect resp.http.x-sdbm == "905707648"
+ expect resp.http.x-sdbm-1 == "3103804144"
+ expect resp.http.x-wt6 == "4090277559"
+ expect resp.http.x-wt6-1 == "1192658767"
+ expect resp.http.x-xxh3 == "-7779787747613135503"
+ expect resp.http.x-xxh3-1 == "5930632130106562027"
+ expect resp.http.x-xxh32 == "1497633363"
+ expect resp.http.x-xxh32-1 == "1070421674"
+ expect resp.http.x-xxh64 == "2282408585429094475"
+ expect resp.http.x-xxh64-1 == "-4689339368900765961"
+} -run
diff --git a/reg-tests/sample_fetches/so_name.vtc b/reg-tests/sample_fetches/so_name.vtc
new file mode 100644
index 0000000..c6211fa
--- /dev/null
+++ b/reg-tests/sample_fetches/so_name.vtc
@@ -0,0 +1,22 @@
+varnishtest "so_name sample fetche Test"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+
+ frontend fe
+ bind "fd@${fe}" name foo
+ http-request return status 200 hdr so-name %[so_name]
+
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.so-name == "foo"
+} -run
diff --git a/reg-tests/sample_fetches/srv_name.vtc b/reg-tests/sample_fetches/srv_name.vtc
new file mode 100644
index 0000000..900957e
--- /dev/null
+++ b/reg-tests/sample_fetches/srv_name.vtc
@@ -0,0 +1,46 @@
+varnishtest "srv_name sample fetche Test"
+
+#REQUIRE_VERSION=2.1
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+server s2 {
+ rxreq
+ 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-response set-header srv-id "%[srv_id]"
+ http-response set-header srv-name "%[srv_name]"
+ default_backend be
+
+ backend be
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s2_addr}:${s2_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.srv-id == "1"
+ expect resp.http.srv-name == "srv1"
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.srv-id == "2"
+ expect resp.http.srv-name == "srv2"
+} -run
diff --git a/reg-tests/sample_fetches/ubase64.vtc b/reg-tests/sample_fetches/ubase64.vtc
new file mode 100644
index 0000000..8e47d86
--- /dev/null
+++ b/reg-tests/sample_fetches/ubase64.vtc
@@ -0,0 +1,57 @@
+varnishtest "ub64dec sample fetche Test"
+
+#REQUIRE_VERSION=2.4
+
+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 fe
+ bind "fd@${fe}"
+ acl input hdr(encode) -m found
+ http-request return content-type text/plain hdr encode %[hdr(encode),ub64enc] hdr decode %[hdr(decode),ub64dec] if input
+ http-request return content-type text/plain hdr encode %[bin(14fb9c03d97f12d97e),ub64enc] hdr decode %[str(FPucA9l_Etl-),ub64dec,hex,lower] if !input
+
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -hdr "encode: f" -hdr "decode: Zg"
+ rxresp
+ expect resp.http.encode == "Zg"
+ expect resp.http.decode == "f"
+ txreq -hdr "encode: fo" -hdr "decode: Zm8"
+ rxresp
+ expect resp.http.encode == "Zm8"
+ expect resp.http.decode == "fo"
+ txreq -hdr "encode: foo" -hdr "decode: Zm9v"
+ rxresp
+ expect resp.http.encode == "Zm9v"
+ expect resp.http.decode == "foo"
+ txreq -hdr "encode: foob" -hdr "decode: Zm9vYg"
+ rxresp
+ expect resp.http.encode == "Zm9vYg"
+ expect resp.http.decode == "foob"
+ txreq -hdr "encode: fooba" -hdr "decode: Zm9vYmE"
+ rxresp
+ expect resp.http.encode == "Zm9vYmE"
+ expect resp.http.decode == "fooba"
+ txreq -hdr "encode: foobar" -hdr "decode: Zm9vYmFy"
+ rxresp
+ expect resp.http.encode == "Zm9vYmFy"
+ expect resp.http.decode == "foobar"
+ txreq
+ rxresp
+ expect resp.http.encode == "FPucA9l_Etl-"
+ expect resp.http.decode == "14fb9c03d97f12d97e"
+} -run
diff --git a/reg-tests/sample_fetches/vars.vtc b/reg-tests/sample_fetches/vars.vtc
new file mode 100644
index 0000000..5144e9f
--- /dev/null
+++ b/reg-tests/sample_fetches/vars.vtc
@@ -0,0 +1,84 @@
+varnishtest "Test a few set-var() in global, tcp and http rule sets, at different scopes"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev5)'"
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ # note below, str60 is purposely not defined so that the default is used
+ set-var proc.int12 int(12)
+ set-var proc.int5 var(proc.str60,60),div(proc.int12)
+ set-var proc.str1 str("this is")
+ set-var proc.str2 str("a string")
+ set-var proc.str var(proc.str1)
+ set-var-fmt proc.str "%[var(proc.str)] a string"
+ set-var proc.uuid uuid()
+
+ 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}"
+ tcp-request session set-var-fmt(sess.str3) "%[var(proc.str1)] %[var(proc.str2)]"
+ tcp-request session set-var(sess.int5) var(proc.int5)
+ tcp-request session set-var(proc.int5) var(proc.int5),add(sess.int5) ## proc. becomes 10
+ tcp-request content set-var-fmt(req.str4) "%[var(sess.str3),regsub(is a,is also a)]"
+ http-request set-var-fmt(txn.str5) "%[var(req.str4)]"
+ http-request set-var(req.int5) var(sess.int5)
+ http-request set-var(sess.int5) var(sess.int5),add(req.int5) ## sess. becomes 10 first time, then 15...
+ http-request return status 200 hdr x-var "proc=%[var(proc.int5)] sess=%[var(sess.int5)] req=%[var(req.int5)] str=%[var(proc.str)] str5=%[var(txn.str5)] uuid=%[var(proc.uuid)]"
+} -start
+
+haproxy h1 -cli {
+ send "get var proc.int5"
+ expect ~ "^proc.int5: type=sint value=<5>$"
+}
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /req1_1
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var ~ "proc=10 sess=10 req=5 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+
+ txreq -req GET -url /req1_2
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var ~ "proc=10 sess=20 req=10 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+} -run
+
+haproxy h1 -cli {
+ send "get var proc.int5"
+ expect ~ "^proc.int5: type=sint value=<10>$"
+}
+
+client c2 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /req2_1
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var ~ "proc=20 sess=20 req=10 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+
+ txreq -req GET -url /req2_2
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var ~ "proc=20 sess=40 req=20 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+} -run
+
+haproxy h1 -cli {
+ send "get var proc.int5"
+ expect ~ "^proc.int5: type=sint value=<20>$"
+}
+
+haproxy h1 -cli {
+ send "experimental-mode on; set var proc.str str(updating); set var proc.str fmt %[var(proc.str),regsub(ing,ed)]"
+ expect ~ .*
+}
+
+client c3 -connect ${h1_fe1_sock} {
+ txreq -req GET -url /req3_1
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-var ~ "proc=40 sess=40 req=20 str=updated str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+} -run
diff --git a/reg-tests/seamless-reload/abns_socket.vtc b/reg-tests/seamless-reload/abns_socket.vtc
new file mode 100644
index 0000000..ce1b156
--- /dev/null
+++ b/reg-tests/seamless-reload/abns_socket.vtc
@@ -0,0 +1,53 @@
+# commit b4dd15b
+# BUG/MINOR: unix: Make sure we can transfer abns sockets on seamless reload.
+#
+# When checking if a socket we got from the parent is suitable for a listener,
+# we just checked that the path matched sockname.tmp, however this is
+# unsuitable for abns sockets, where we don't have to create a temporary
+# file and rename it later.
+# To detect that, check that the first character of the sun_path is 0 for
+# both, and if so, that &sun_path[1] is the same too.
+#
+# Note: there are some tricks here. One of them is that we must not bind the
+# same abns address to multiple processes that may run in parallel. Since
+# vtest cannot provide abns sockets, we're instead concatenating the number
+# of the listening port that vtest allocated for another frontend to the abns
+# path, which guarantees to make them unique in the system.
+
+varnishtest "Seamless reload issue with abns sockets"
+feature ignore_unknown_macro
+
+# abns@ sockets are not available on freebsd
+#EXCLUDE_TARGETS=freebsd,osx,generic
+#REGTEST_TYPE=broken
+
+haproxy h1 -W -conf {
+ global
+ stats socket "${tmpdir}/h1/stats" level admin expose-fd listeners
+
+ 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}"
+
+ listen testme
+ bind "fd@${testme}"
+ server test_abns_server abns@wpproc1_${h1_testme_port} send-proxy-v2
+
+ frontend test_abns
+ bind abns@wpproc1_${h1_testme_port} accept-proxy
+ http-request deny deny_status 200
+} -start
+
+shell {
+ kill -USR2 $(cat "${tmpdir}/h1/pid")
+}
+
+client c1 -connect ${h1_testme_sock} {
+ txreq -url "/"
+ rxresp
+} -repeat 50 -run
+
diff --git a/reg-tests/server/cli_add_check_server.vtc b/reg-tests/server/cli_add_check_server.vtc
new file mode 100644
index 0000000..c63710c
--- /dev/null
+++ b/reg-tests/server/cli_add_check_server.vtc
@@ -0,0 +1,161 @@
+varnishtest "Add/Delete server via cli with check support"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev3)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature ignore_unknown_macro
+
+barrier b1 cond 2 -cyclic
+barrier b2 cond 2 -cyclic
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+server s2 {
+} -start
+
+# used for agent checks
+server s3 {
+ recv 5
+ send "ready up\n"
+ barrier b2 sync
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ ".*Server be1/s1 is UP/READY \\(leaving forced maintenance\\)."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/s1 succeeded.+reason: Layer7 check passed, code: 200, check duration: [[:digit:]]+ms.+status: 1/1 UP"
+
+ barrier b1 sync
+
+ recv
+ expect ~ ".*Server be1/s2 is UP/READY \\(leaving forced maintenance\\)."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/s2 failed.+reason: Layer7 timeout, check duration: [[:digit:]]+ms.+status: 0/1 DOWN"
+
+ barrier b1 sync
+
+ recv
+ expect ~ ".*Server be1/s2 was DOWN and now enters maintenance."
+
+ recv
+ expect ~ ".*Server be1/s3 is UP/READY \\(leaving forced maintenance\\)."
+
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Agent check for server be1/s3 succeeded.+reason: Layer7 check passed, code: 0, info: \"via agent : up\", check duration: [[:digit:]]+ms.+status: 1/1 UP"
+
+ barrier b1 sync
+ barrier b2 sync
+
+ recv
+ expect ~ ".*Server be1/s4 is UP/READY \\(leaving forced maintenance\\)."
+ recv
+ expect ~ "Health check for server be1/s4 failed"
+
+ barrier b1 sync
+
+ recv
+ expect ~ ".*Server be1/s5 is UP/READY \\(leaving forced maintenance\\)."
+ recv
+ expect ~ "Health check for server be1/s5 succeeded."
+} -start
+
+haproxy h1 -conf {
+ global
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ backend be1
+ option log-health-checks
+ option httpchk GET /
+ log ${S1_addr}:${S1_port} daemon
+
+ frontend fe-proxy
+ mode http
+ bind "fd@${hapsrv}" accept-proxy
+ http-request return status 200
+} -start
+
+# check on a functional server
+haproxy h1 -cli {
+ send "add server be1/s1 ${s1_addr}:${s1_port} check inter 200ms rise 1 fall 1"
+ expect ~ "New server registered."
+
+ send "enable server be1/s1"
+ expect ~ ".*"
+ send "enable health be1/s1"
+ expect ~ ".*"
+
+ barrier b1 sync
+
+ send "disable server be1/s1"
+ expect ~ ".*"
+
+ send "del server be1/s1"
+ expect ~ "Server deleted."
+}
+
+server s2 -break
+
+# check on a disabled server
+haproxy h1 -cli {
+ send "add server be1/s2 ${s2_addr}:${s2_port} check inter 200ms rise 1 fall 1"
+ expect ~ "New server registered."
+
+ send "enable server be1/s2"
+ expect ~ ".*"
+ send "enable health be1/s2"
+ expect ~ ".*"
+
+ barrier b1 sync
+
+ send "disable server be1/s2"
+ expect ~ ".*"
+
+ send "del server be1/s2"
+ expect ~ "Server deleted."
+}
+
+# agent check
+haproxy h1 -cli {
+ send "add server be1/s3 ${s1_addr}:${s1_port} agent-check agent-addr ${s3_addr} agent-port ${s3_port} agent-send 'hello' agent-inter 200ms rise 1 fall 1"
+ expect ~ "New server registered."
+
+ send "enable agent be1/s3"
+ expect ~ ".*"
+
+ barrier b1 sync
+
+ send "disable agent be1/s3; disable server be1/s3"
+ expect ~ ".*"
+
+ send "del server be1/s3"
+ expect ~ "Server deleted."
+}
+
+# check PROXY protocol interaction with checks
+haproxy h1 -cli {
+ # no explicit check-send-proxy
+ # The health check should failed.
+ send "add server be1/s4 ${h1_hapsrv_addr}:${h1_hapsrv_port} send-proxy check rise 1 fall 1"
+ expect ~ "New server registered."
+
+ send "enable server be1/s4"
+ expect ~ ".*"
+ send "enable health be1/s4"
+ expect ~ ".*"
+
+ barrier b1 sync
+
+ # explicit check-send-proxy : health check should succeeded
+ send "add server be1/s5 ${h1_hapsrv_addr}:${h1_hapsrv_port} send-proxy check rise 1 fall 1 check-send-proxy"
+ expect ~ "New server registered."
+
+ send "enable server be1/s5"
+ expect ~ ".*"
+ send "enable health be1/s5"
+ expect ~ ".*"
+}
+
+syslog S1 -wait
diff --git a/reg-tests/server/cli_add_server.vtc b/reg-tests/server/cli_add_server.vtc
new file mode 100644
index 0000000..8c29305
--- /dev/null
+++ b/reg-tests/server/cli_add_server.vtc
@@ -0,0 +1,87 @@
+varnishtest "Add server via cli"
+
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+
+server s1 {
+ rxreq
+ 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@${feS}"
+ default_backend test
+
+ backend test
+ balance random
+
+ backend other
+ balance static-rr
+
+ backend other2
+ balance random
+ mode tcp
+} -start
+
+client c1 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+haproxy h1 -cli {
+ # non existent backend
+ send "add server foo/s1 ${s1_addr}:${s1_port}"
+ expect ~ "No such backend."
+
+ # missing address
+ send "add server test/s1"
+ expect ~ "'server' expects <name> and <addr>\\[:<port>\\] as arguments."
+
+ # invalid load-balancing algo
+ send "add server other/s1 ${s1_addr}:${s1_port}"
+ expect ~ "Backend must use a dynamic load balancing to support dynamic servers."
+
+ # invalid mux proto
+ send "add server other2/s1 ${s1_addr}:${s1_port} proto h2"
+ expect ~ "MUX protocol is not usable for server."
+
+ # valid command
+ send "add server test/s1 ${s1_addr}:${s1_port}"
+ expect ~ "New server registered."
+
+ # duplicate server
+ send "add server test/s1 ${s1_addr}:${s1_port}"
+ expect ~ "Already exists a server with the same name in backend."
+
+ # valid command
+ # specify the proto, it should be accepted for this backend
+ send "add server test/s2 ${s1_addr}:${s1_port} proto h2"
+ expect ~ "New server registered."
+}
+
+# dynamic servers are created on MAINT mode and should not be available at first
+client c2 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+haproxy h1 -cli {
+ send "enable server test/s1"
+ expect ~ ".*"
+}
+
+client c3 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/server/cli_add_ssl_server.vtc b/reg-tests/server/cli_add_ssl_server.vtc
new file mode 100644
index 0000000..95caf3f
--- /dev/null
+++ b/reg-tests/server/cli_add_ssl_server.vtc
@@ -0,0 +1,110 @@
+varnishtest "Add server via cli with SSL activated"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+barrier b1 cond 2 -cyclic
+
+syslog S1 -level notice {
+ recv
+ expect ~ ".*Server li-ssl/s1 is UP/READY \\(leaving forced maintenance\\)."
+ recv
+ expect ~ ".*Server li-ssl/s2 is UP/READY \\(leaving forced maintenance\\)."
+ recv
+ expect ~ "Health check for server li-ssl/s2 failed"
+
+ barrier b1 sync
+
+ recv
+ expect ~ ".*Server li-ssl/s3 is UP/READY \\(leaving forced maintenance\\)."
+ recv
+ expect ~ "Health check for server li-ssl/s3 succeeded."
+} -start
+
+haproxy h1 -conf {
+ global
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ option log-health-checks
+ option httpchk GET /
+
+ # proxy to attach a ssl server
+ listen li-ssl
+ bind "fd@${feSsl}"
+ balance random
+ log ${S1_addr}:${S1_port} daemon
+
+ # frontend used to respond to ssl connection
+ frontend fe-ssl-term
+ bind "fd@${feSslTerm}" ssl crt ${testdir}/common.pem
+ http-request return status 200
+} -start
+
+### SSL SUPPORT
+# 1. first create a ca-file using CLI
+# 2. create an SSL server and use it
+
+client c1 -connect ${h1_feSsl_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+shell {
+ echo "new ssl ca-file common.pem" | socat "${tmpdir}/h1/stats" -
+ printf "set ssl ca-file common.pem <<\n$(cat ${testdir}/common.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file common.pem" | socat "${tmpdir}/h1/stats" -
+} -run
+
+haproxy h1 -cli {
+ send "show ssl ca-file common.pem"
+ expect ~ ".*SHA1 FingerPrint: 9A6418E498C43EDBCF5DD3C4C6FCD1EE0D7A946D"
+}
+
+haproxy h1 -cli {
+ # non existent backend
+ send "add server li-ssl/s1 ${h1_feSslTerm_addr}:${h1_feSslTerm_port} ssl ca-file common.pem verify none"
+ expect ~ "New server registered."
+
+ send "enable server li-ssl/s1"
+ expect ~ ".*"
+}
+
+client c2 -connect ${h1_feSsl_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+# test interaction between SSL and checks for dynamic servers
+haproxy h1 -cli {
+ # no explicit check-ssl
+ # The health check should failed.
+ send "add server li-ssl/s2 ${h1_feSslTerm_addr}:${h1_feSslTerm_port} ssl verify none check"
+ expect ~ "New server registered."
+
+ send "enable server li-ssl/s2"
+ expect ~ ".*"
+ send "enable health li-ssl/s2"
+ expect ~ ".*"
+
+ barrier b1 sync
+
+ # explicit check-ssl : health check should succeeded
+ send "add server li-ssl/s3 ${h1_feSslTerm_addr}:${h1_feSslTerm_port} ssl verify none check check-ssl"
+ expect ~ "New server registered."
+
+ send "enable server li-ssl/s3"
+ expect ~ ".*"
+ send "enable health li-ssl/s3"
+ expect ~ ".*"
+}
+
+syslog S1 -wait
diff --git a/reg-tests/server/cli_add_track_server.vtc b/reg-tests/server/cli_add_track_server.vtc
new file mode 100644
index 0000000..318f236
--- /dev/null
+++ b/reg-tests/server/cli_add_track_server.vtc
@@ -0,0 +1,242 @@
+varnishtest "Add/Delete server via cli with track support"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature ignore_unknown_macro
+
+
+# just use to provide s1_{addr,port} macros
+server s1 {
+}
+
+
+# scenario 1
+# -> 3 dynamic servers, delete the first one
+syslog S1 {
+ recv notice
+ expect ~ "Server be1/s1 is UP/READY"
+ recv notice
+ expect ~ "Server be1/s2 is UP/READY"
+ recv notice
+ expect ~ "Server be1/s3 is UP/READY"
+
+ recv alert
+ expect ~ "Server be1/srv is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be1/s3 is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be1/s2 is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be1/s1 is going DOWN for maintenance."
+
+ recv notice
+ expect ~ "Server be1/srv is UP/READY"
+ recv notice
+ expect ~ "Server be1/s3 is UP/READY"
+ recv notice
+ expect ~ "Server be1/s2 is UP/READY"
+} -start
+
+# scenario 2
+# -> 3 dynamic servers, delete the middle one
+syslog S2 {
+ recv notice
+ expect ~ "Server be2/s1 is UP/READY"
+ recv notice
+ expect ~ "Server be2/s2 is UP/READY"
+ recv notice
+ expect ~ "Server be2/s3 is UP/READY"
+
+ recv alert
+ expect ~ "Server be2/srv is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be2/s3 is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be2/s2 is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be2/s1 is going DOWN for maintenance."
+
+ recv notice
+ expect ~ "Server be2/srv is UP/READY"
+ recv notice
+ expect ~ "Server be2/s3 is UP/READY"
+ recv notice
+ expect ~ "Server be2/s1 is UP/READY"
+} -start
+
+# scenario 3
+# -> 3 dynamic servers, delete all of them
+syslog S3 {
+ recv notice
+ expect ~ "Server be3/s1 is UP/READY"
+ recv notice
+ expect ~ "Server be3/s2 is UP/READY"
+ recv notice
+ expect ~ "Server be3/s3 is UP/READY"
+
+ recv alert
+ expect ~ "Server be3/s1 is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be3/s3 is going DOWN for maintenance."
+ recv alert
+ expect ~ "Server be3/s2 is going DOWN for maintenance."
+
+ recv alert
+ expect ~ "Server be3/srv is going DOWN for maintenance."
+
+ recv notice
+ expect ~ "Server be3/srv is UP/READY"
+} -start
+
+
+haproxy h1 -conf {
+ global
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ backend be_check
+ server srv_check ${s1_addr}:${s1_port} check
+ server srv_no_check ${s1_addr}:${s1_port}
+
+ backend be1
+ log ${S1_addr}:${S1_port} daemon
+ server srv ${s1_addr}:${s1_port} check
+
+ backend be2
+ log ${S2_addr}:${S2_port} daemon
+ server srv ${s1_addr}:${s1_port} check
+
+ backend be3
+ log ${S3_addr}:${S3_port} daemon
+ server srv ${s1_addr}:${s1_port} check
+} -start
+
+
+###
+# check the support of the 'track' keyword on 'add server' CLI command.
+# rejection must happen if track on a non-checked or a dynamic server
+###
+haproxy h1 -cli {
+ # invalid command: track on a non-checked server
+ send "add server be_check/s1 ${s1_addr}:${s1_port} track be_check/srv_no_check"
+ expect ~ "unable to use be_check/srv_no_check for tracking as it does not have any check nor agent enabled."
+
+ # valid track usage
+ send "add server be_check/s1 ${s1_addr}:${s1_port} track be_check/srv_check"
+ expect ~ "New server registered."
+
+ # invalid command: track on a dynamic server
+ send "add server be_check/s3 ${s1_addr}:${s1_port} track be_check/s1"
+ expect ~ "unable to use be_check/s1 for tracking as it is a dynamic server."
+}
+
+###
+# scenario 1
+#
+# Add 3 dynamic servers with tracking on be1/srv
+# Disable be1/srv, S1 should report all servers DOWN
+# Delete the first dynamic server, enable be1/srv, S1 should report s1 and s3 UP
+####
+haproxy h1 -cli {
+ send "add server be1/s1 ${s1_addr}:${s1_port} track be1/srv"
+ expect ~ "New server registered."
+ send "enable server be1/s1"
+ expect ~ ".*"
+
+ send "add server be1/s2 ${s1_addr}:${s1_port} track be1/srv"
+ expect ~ "New server registered."
+ send "enable server be1/s2"
+ expect ~ ".*"
+
+ send "add server be1/s3 ${s1_addr}:${s1_port} track be1/srv"
+ expect ~ "New server registered."
+ send "enable server be1/s3"
+ expect ~ ".*"
+
+ send "disable server be1/srv"
+ expect ~ ".*"
+
+ send "del server be1/s1"
+ expect ~ "Server deleted."
+
+ send "enable server be1/srv"
+ expect ~ ".*"
+}
+
+###
+# scenario 2
+#
+# Add 3 dynamic servers with tracking on be2/srv
+# Disable be2/srv, S3 should report all servers DOWN
+# Delete the second dynamic server, enable be2/srv, S2 should report s2 and s3 UP
+####
+haproxy h1 -cli {
+ send "add server be2/s1 ${s1_addr}:${s1_port} track be2/srv"
+ expect ~ "New server registered."
+ send "enable server be2/s1"
+ expect ~ ".*"
+
+ send "add server be2/s2 ${s1_addr}:${s1_port} track be2/srv"
+ expect ~ "New server registered."
+ send "enable server be2/s2"
+ expect ~ ".*"
+
+ send "add server be2/s3 ${s1_addr}:${s1_port} track be2/srv"
+ expect ~ "New server registered."
+ send "enable server be2/s3"
+ expect ~ ".*"
+
+ send "disable server be2/srv"
+ expect ~ ".*"
+
+ send "del server be2/s2"
+ expect ~ "Server deleted."
+
+ send "enable server be2/srv"
+ expect ~ ".*"
+}
+
+###
+# scenario 3
+#
+# Add 3 dynamic servers with tracking on be3/srv
+# Delete all of them, disable/enable be3/srv, only be3/srv should be reported
+# as DOWN/UP.
+####
+haproxy h1 -cli {
+ # create server 1, track on be3/srv
+ send "add server be3/s1 ${s1_addr}:${s1_port} track be3/srv"
+ expect ~ "New server registered."
+ send "enable server be3/s1"
+ expect ~ ".*"
+
+ # create server 2, track on be3/srv
+ send "add server be3/s2 ${s1_addr}:${s1_port} track be3/srv"
+ expect ~ "New server registered."
+ send "enable server be3/s2"
+ expect ~ ".*"
+
+ # create server 3, track on be3/srv
+ send "add server be3/s3 ${s1_addr}:${s1_port} track be3/srv"
+ expect ~ "New server registered."
+ send "enable server be3/s3"
+ expect ~ ".*"
+
+ # delete all dynamic servers
+ send "disable server be3/s1; del server be3/s1"
+ expect ~ "Server deleted."
+ send "disable server be3/s3; del server be3/s3"
+ expect ~ "Server deleted."
+ send "disable server be3/s2; del server be3/s2"
+ expect ~ "Server deleted."
+
+ # disable / enable the static server
+ send "disable server be3/srv"
+ expect ~ ".*"
+ send "enable server be3/srv"
+ expect ~ ".*"
+}
+
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
diff --git a/reg-tests/server/cli_delete_dynamic_server.vtc b/reg-tests/server/cli_delete_dynamic_server.vtc
new file mode 100644
index 0000000..e667641
--- /dev/null
+++ b/reg-tests/server/cli_delete_dynamic_server.vtc
@@ -0,0 +1,94 @@
+# This script is to test the proper behavior with dynamic servers insertion and
+# deletion, in particular with the load-balancing of requests.
+#
+varnishtest "Delete server via cli"
+
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+
+# static server
+server s1 -repeat 3 {
+ rxreq
+ txresp \
+ -body "resp from s1"
+} -start
+
+# use as a dynamic server, added then deleted via CLI
+server s2 -repeat 3 {
+ rxreq
+ txresp \
+ -body "resp from s2"
+} -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@${feS}"
+ default_backend test
+
+ backend test
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+# add a new dynamic server to be able to delete it then
+haproxy h1 -cli {
+ # add a dynamic server and enable it
+ send "add server test/s2 ${s2_addr}:${s2_port}"
+ expect ~ "New server registered."
+
+ send "enable server test/s2"
+ expect ~ ".*"
+}
+
+haproxy h1 -cli {
+ # non existent backend
+ send "del server foo/s1"
+ expect ~ "No such backend."
+
+ # non existent server
+ send "del server test/other"
+ expect ~ "No such server."
+}
+
+# first check that both servers are active
+client c1 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.body == "resp from s1"
+
+ txreq
+ rxresp
+ expect resp.body == "resp from s2"
+} -run
+
+# delete the dynamic server
+haproxy h1 -cli {
+ # server not in maintenance mode
+ send "del server test/s2"
+ expect ~ "Only servers in maintenance mode can be deleted."
+
+ send "disable server test/s2"
+ expect ~ ".*"
+
+ # valid command
+ send "del server test/s2"
+ expect ~ "Server deleted."
+}
+
+# now check that only the first server is used
+client c2 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.body == "resp from s1"
+
+ txreq
+ rxresp
+ expect resp.body == "resp from s1"
+} -run
+
diff --git a/reg-tests/server/cli_delete_server.vtc b/reg-tests/server/cli_delete_server.vtc
new file mode 100644
index 0000000..61d241c
--- /dev/null
+++ b/reg-tests/server/cli_delete_server.vtc
@@ -0,0 +1,60 @@
+# This script is to test the ability to remove servers, unless they are
+# referenced by some elements from the configuration.
+#
+varnishtest "Delete server via cli"
+
+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 fe
+ bind "fd@${feS}"
+ acl s1_full srv_sess_rate(test/s1) gt 50
+ default_backend test
+
+ backend test
+ use-server s3 unless { always_false }
+ server s1 ${s1_addr}:${s1_port} # referenced in ACL
+ server s2 ${s1_addr}:${s1_port} check # referenced in track
+ server s3 ${s1_addr}:${s1_port} track s2 # referenced in use-server
+ server s4 ${s1_addr}:${s1_port} # removable server
+} -start
+
+haproxy h1 -cli {
+ # non existent backend
+ send "del server foo/s1"
+ expect ~ "No such backend."
+
+ # non existent server
+ send "del server test/other"
+ expect ~ "No such server."
+
+ # server referenced in ACL
+ send "del server test/s1"
+ expect ~ "This server cannot be removed at runtime due to other configuration elements pointing to it."
+
+ # tracked server
+ send "del server test/s2"
+ expect ~ "This server cannot be removed at runtime due to other configuration elements pointing to it."
+
+ # tracked server
+ send "del server test/s3"
+ expect ~ "This server cannot be removed at runtime due to other configuration elements pointing to it."
+
+ # server in running mode
+ send "del server test/s4"
+ expect ~ "Only servers in maintenance mode can be deleted."
+
+ send "disable server test/s4"
+ expect ~ ".*"
+
+ # valid command
+ send "del server test/s4"
+ expect ~ "Server deleted."
+}
diff --git a/reg-tests/server/cli_delete_server_lua.vtc b/reg-tests/server/cli_delete_server_lua.vtc
new file mode 100644
index 0000000..396cd21
--- /dev/null
+++ b/reg-tests/server/cli_delete_server_lua.vtc
@@ -0,0 +1,43 @@
+# This script is to check that servers that are referenced by a lua script
+# cannot be removed at runtime.
+varnishtest "Delete lua server via cli"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(LUA)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ lua-load ${testdir}/get_srv_stats.lua
+
+ 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@${feS}"
+ default_backend test
+
+ backend test
+ http-request add-header s1-stats %[lua.get_srv_stats(s1)]
+ server s1 ${s1_addr}:${s1_port} # referenced in lua script
+} -start
+
+# make a request to force the execution of the lua script which references a
+# server
+client c1 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+} -run
+
+haproxy h1 -cli {
+ send "experimental-mode on; del server test/s1"
+ expect ~ "This server cannot be removed at runtime due to other configuration elements pointing to it."
+}
diff --git a/reg-tests/server/cli_set_fdqn.vtc b/reg-tests/server/cli_set_fdqn.vtc
new file mode 100644
index 0000000..86f32b6
--- /dev/null
+++ b/reg-tests/server/cli_set_fdqn.vtc
@@ -0,0 +1,32 @@
+varnishtest "Set server FQDN via CLI crash"
+
+feature ignore_unknown_macro
+
+# for "set server <srv> fqdn"
+#REGTEST_TYPE=bug
+
+# Do nothing. Is there only to create s1_* macros
+server s1 {
+} -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 myfrontend
+ bind "fd@${my_fe}"
+ default_backend test
+
+ backend test
+ server www1 ${s1_addr}:${s1_port}
+} -start
+
+haproxy h1 -cli {
+ send "set server test/www1 fqdn foo.fqdn"
+ expect ~ "set server <b>/<s> fqdn failed because no resolution is configured."
+ send "show servers state test"
+ expect ~ "test 1 www1 ${s1_addr} .* - ${s1_port}"
+} -wait
diff --git a/reg-tests/server/cli_set_ssl.vtc b/reg-tests/server/cli_set_ssl.vtc
new file mode 100644
index 0000000..fa6fe68
--- /dev/null
+++ b/reg-tests/server/cli_set_ssl.vtc
@@ -0,0 +1,60 @@
+varnishtest "Set server ssl via CLI"
+
+feature ignore_unknown_macro
+
+# for "set server <srv> ssl"
+#REQUIRE_VERSION=2.4
+#REGTEST_TYPE=devel
+#REQUIRE_OPTIONS=OPENSSL
+
+# Do nothing. Is there only to create s1_* macros
+server s1 {
+} -start
+
+haproxy h1 -conf {
+ global
+ ssl-server-verify none
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend myfrontend
+ bind "fd@${my_fe}"
+ default_backend test0
+
+ backend test0
+ server www0 ${s1_addr}:${s1_port} no-ssl
+ default-server ssl
+ server www1 ${s1_addr}:${s1_port} no-ssl
+
+ backend test1
+ server www0 ${s1_addr}:${s1_port} no-ssl
+} -start
+
+haproxy h1 -cli {
+ # supported case
+ send "show servers state test0"
+ expect ~ "test0 2 www1 ${s1_addr} .* - ${s1_port} - -1"
+ send "set server test0/www1 ssl on"
+ expect ~ "server ssl setting updated"
+ send "show servers state test0"
+ expect ~ "test0 2 www1 ${s1_addr} .* - ${s1_port} - 1"
+ send "set server test0/www1 ssl off"
+ expect ~ "server ssl setting updated"
+ send "show servers state test0"
+ expect ~ "test0 2 www1 ${s1_addr} .* - ${s1_port} - 0"
+
+ # unsupported cases
+ send "show servers state test0"
+ expect ~ "test0 1 www0 ${s1_addr} .* - ${s1_port} - -1"
+ send "set server test0/www0 ssl on"
+ expect ~ "'set server <srv> ssl' cannot be set"
+
+ send "show servers state test1"
+ expect ~ "test1 1 www0 ${s1_addr} .* - ${s1_port} - -1"
+ send "set server test1/www0 ssl on"
+ expect ~ "'set server <srv> ssl' cannot be set"
+} -wait
diff --git a/reg-tests/server/common.pem b/reg-tests/server/common.pem
new file mode 120000
index 0000000..a4433d5
--- /dev/null
+++ b/reg-tests/server/common.pem
@@ -0,0 +1 @@
+../ssl/common.pem \ No newline at end of file
diff --git a/reg-tests/server/get_srv_stats.lua b/reg-tests/server/get_srv_stats.lua
new file mode 100644
index 0000000..105b954
--- /dev/null
+++ b/reg-tests/server/get_srv_stats.lua
@@ -0,0 +1,11 @@
+local function lua_get_srv_stats(txn, name)
+ for _, backend in pairs(core.backends) do
+ for _, server in pairs(backend.servers) do
+ if server.name == name then
+ return server:get_stats()
+ end
+ end
+ end
+end
+
+core.register_fetches('get_srv_stats', lua_get_srv_stats)
diff --git a/reg-tests/spoe/wrong_init.vtc b/reg-tests/spoe/wrong_init.vtc
new file mode 100644
index 0000000..152622c
--- /dev/null
+++ b/reg-tests/spoe/wrong_init.vtc
@@ -0,0 +1,22 @@
+# commit 84c844eb12b250aa86f2aadaff77c42dfc3cb619
+# BUG/MINOR: spoe: Initialize variables used during conf parsing before any check
+#
+# Some initializations must be done at the beginning of parse_spoe_flt to avoid
+# segmentation fault when first errors are caught, when the "filter spoe" line is
+# parsed.
+
+#REGTEST_TYPE=bug
+
+varnishtest "SPOE bug: missing configuration file"
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf-BAD {} {
+ defaults
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend my-front
+ filter spoe
+}
diff --git a/reg-tests/ssl/README b/reg-tests/ssl/README
new file mode 100644
index 0000000..f2fc534
--- /dev/null
+++ b/reg-tests/ssl/README
@@ -0,0 +1,2 @@
+File list:
+ - common.pem: PEM file which may be used by most of the VTC files.
diff --git a/reg-tests/ssl/add_ssl_crt-list.vtc b/reg-tests/ssl/add_ssl_crt-list.vtc
new file mode 100644
index 0000000..e62ac48
--- /dev/null
+++ b/reg-tests/ssl/add_ssl_crt-list.vtc
@@ -0,0 +1,114 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "add ssl crt-list" command to add a certificate over the CLI.
+# It requires socat to upload the certificate
+
+# this check does 2 requests, the first one will use "www.test1.com" as SNI, and
+# the second one will use "localhost". Since vtest can't do SSL, we use haproxy
+# as an SSL client with 2 chained listen section.
+
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'add ssl crt-list' feature of the CLI"
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 2 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ crt-base ${testdir}
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(www.test1.com)
+ server s2 "${tmpdir}/ssl.sock" ssl verify none sni str(localhost)
+
+
+ listen ssl-lst
+ mode http
+ bind "${tmpdir}/ssl.sock" ssl strict-sni crt-list ${testdir}/localhost.crt-list
+
+ server s1 ${s1_addr}:${s1_port}
+ server s2 ${s1_addr}:${s1_port} ssl crt "${testdir}/common.pem" weight 0 verify none
+} -start
+
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/common.pem"
+ expect ~ ".*SHA1 FingerPrint: 2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+shell {
+ echo "new ssl cert ${testdir}/ecdsa.pem" | socat "${tmpdir}/h1/stats" -
+ printf "set ssl cert ${testdir}/ecdsa.pem <<\n$(cat ${testdir}/ecdsa.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/ecdsa.pem" | socat "${tmpdir}/h1/stats" -
+ printf "add ssl crt-list ${testdir}/localhost.crt-list/ <<\n${testdir}/common.pem [ssl-min-ver SSLv3 verify none allow-0rtt] !*\n\n" | socat "${tmpdir}/h1/stats" -
+ printf "add ssl crt-list ${testdir}/localhost.crt-list/ <<\n${testdir}/ecdsa.pem [ssl-min-ver SSLv3 verify none allow-0rtt] localhost !www.test1.com\n\n" | socat "${tmpdir}/h1/stats" -
+ printf "add ssl crt-list ${testdir}/localhost.crt-list <<\n${testdir}/ecdsa.pem [verify none allow-0rtt]\n\n" | socat "${tmpdir}/h1/stats" -
+ printf "add ssl crt-list ${testdir}/localhost.crt-list/// <<\n${testdir}/ecdsa.pem localhost !www.test1.com\n\n" | socat "${tmpdir}/h1/stats" -
+ printf "add ssl crt-list ${testdir}/localhost.crt-list///// <<\n${testdir}/ecdsa.pem\n\n" | socat "${tmpdir}/h1/stats" -
+ printf "add ssl crt-list ${testdir}/localhost.crt-list// ${testdir}/ecdsa.pem\n" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/ecdsa.pem"
+ expect ~ ".*SHA1 FingerPrint: A490D069DBAFBEE66DE434BEC34030ADE8BCCBF1"
+}
+
+haproxy h1 -cli {
+ send "show ssl crt-list ${testdir}/localhost.crt-list//"
+ # check the options and the filters in any order
+ expect ~ ".*${testdir}/ecdsa.pem \\[(?=.*verify none)(?=.*allow-0rtt)(?=.*ssl-min-ver SSLv3).*\\](?=.*!www.test1.com)(?=.*localhost).*"
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+# Try to add a new line that mentions an "unknown" CA file (not loaded yet).
+# It should fail since no disk access are allowed during runtime.
+shell {
+ printf "add ssl crt-list ${testdir}/localhost.crt-list/ <<\n${testdir}/ecdsa.pem [ca-file ${testdir}/ca-auth.crt] localhost\n\n" | socat "${tmpdir}/h1/stats" - | grep "unable to load ${testdir}/ca-auth.crt"
+}
+shell {
+ printf "add ssl crt-list ${testdir}/localhost.crt-list/ <<\n${testdir}/ecdsa.pem [ca-verify-file ${testdir}/ca-auth.crt] localhost\n\n" | socat "${tmpdir}/h1/stats" - | grep "unable to load ${testdir}/ca-auth.crt"
+}
+shell {
+ printf "add ssl crt-list ${testdir}/localhost.crt-list/ <<\n${testdir}/ecdsa.pem [crl-file ${testdir}/ca-auth.crt] localhost\n\n" | socat "${tmpdir}/h1/stats" - | grep "unable to load ${testdir}/ca-auth.crt"
+}
+
+# Check that the new line was not added to the crt-list.
+haproxy h1 -cli {
+ send "show ssl crt-list ${testdir}/localhost.crt-list//"
+ expect !~ ".*ca-file ${testdir}/ca-auth.crt"
+}
diff --git a/reg-tests/ssl/ca-auth.crt b/reg-tests/ssl/ca-auth.crt
new file mode 100644
index 0000000..1695af5
--- /dev/null
+++ b/reg-tests/ssl/ca-auth.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFyzCCA7OgAwIBAgIURpSju/jEN7LJUV4vEibyeuJwd5kwDQYJKoZIhvcNAQEL
+BQAwdDELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxHTAbBgNVBAoM
+FEhBUHJveHkgVGVjaG5vbG9naWVzMTEwLwYDVQQDDChIQVByb3h5IFRlY2hub2xv
+Z2llcyBDQSBUZXN0IENsaWVudCBBdXRoMCAXDTIwMDQyODE4NTIwMloYDzIwNTAw
+NDIxMTg1MjAyWjB0MQswCQYDVQQGEwJGUjETMBEGA1UECAwKU29tZS1TdGF0ZTEd
+MBsGA1UECgwUSEFQcm94eSBUZWNobm9sb2dpZXMxMTAvBgNVBAMMKEhBUHJveHkg
+VGVjaG5vbG9naWVzIENBIFRlc3QgQ2xpZW50IEF1dGgwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDGlgKEhw5RLOBgaTsPJJYglozt3SVv94084RoA19kw
+udemrMaJdXV517MsR9qoHoxFVFdYP//W6vx7c5RadPqMZrWT9QXhJSR0Kr5KdHUs
++t8H8pmlDicxIx0cuRtmKmRuAMoDI1E+5EsRspemyq1ExcBm42zM9Oj9QysKF0wc
+FXq56eHgXSmKQGAiGuGB5v6CeVu3DVhZHuGyv3XVeOsf35s2M757hi+N6mqqvtw8
+JiQyw53YxBaB80CRtiIXzLd07S4GZnRCNOWgXLwo6+8K1gId3KRk4DhUIEIMrchy
+aqeZmJVToF+8fbjZ97pREJyQo4EXsgPrLE3Ck5Y+TfYJli3NJNhEWhucu4RQ6XXg
+lTruatM9uj9ZZEvtJreu5KRvAOfRLBj+C3f+VRoDrE9RSEn/XSGek+/D7+n3U0GO
+h2KcrUn7R+Yy6DdwxhGImqDnYaKaZds+vEjtvP4ViOC982eVl5/lFAw3JBHR57iL
+/K0zTRwjSasUvlJFQNUNAG9HktCYTdEj0U3C/xBDPayY04BFvn8piZeMpoCN9dre
+UxuctmMrz1pIvYAdZSseraf4W0psx6oeU/CcFZnkc5lbUDvn7u6Ozk4gnfyo1fxJ
+8a2X7dl3joqUABwaS/FkP/CPoEKBMFf4pcZUhuWbPkhiVNXZIkQYQISY6JOr5qDn
+TwIDAQABo1MwUTAdBgNVHQ4EFgQUW4t2W4MUuBG0EyFdHObYYZbtjEowHwYDVR0j
+BBgwFoAUW4t2W4MUuBG0EyFdHObYYZbtjEowDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAIqQJu2nX0Rn9EUPuVDhCrirQFDwMFb7Ifoqr6rMoD9OT
+pgyQb198TkW550Rhg36LnnmBzifOoPBmHVJQWvMAVnH/BQrkRqXFRk2M3PRoEv44
+twMlUPU/NMLVKnXE+neBlXhBWeyY/bCmVftk/TdLwom0Mer4Nw+rt1JQAXKKNRdj
+3b8EnJHGy7Es4fv/traZQ6ZSHoD0GsxydweCjZEO2hLw9/VVrjdM6rNDZlz7cST5
+rhyUeG3mlwWweGY6ahlMx//Z11m/1JLGyDcwMgunRoBiDep7I3ZMcWH1PjM3CyrL
+ZrDoUvwtMSEAuT/be5SfU/CzS/DTyBtfSpEUbm1dg9cqm1vG7/GFdzJqafv8ppwh
+fZhtxKXcyH4C1BeWlDqA06aNM3ClxWNyrAjdcyI45tosxgDuWyRyUC5IhyK6s81O
+6AP7xQH6s+i0k3mzgOxieV/QRo4E67y31XZHJz6uFKSaHOIdpV7li7mAiswFfhMl
++C3ud2rU79X2vTYLzELR05djzAXHJT9sc5NjbODw3RRKRkcB78IoNM7D0Mcctz+3
+1DHcmk6crsxPRDmvKj9zQTjbG1UpjTogdsbh1afuqJ1atxBgav+/YhefAziXazAy
+P1CHU/OYq/vjfGobIz6RVUjkg50RwkD58TR3LzQPOpSNoM55U/jGd3x4X3kh8tU=
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/cert1-example.com.pem.ecdsa b/reg-tests/ssl/cert1-example.com.pem.ecdsa
new file mode 100644
index 0000000..060d92b
--- /dev/null
+++ b/reg-tests/ssl/cert1-example.com.pem.ecdsa
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIBhzCCAQ2gAwIBAgIUWnUgbYQBOPUC1tc9NFqD2gjVBawwCgYIKoZIzj0EAwIw
+FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wIBcNMjEwNDAyMTI0NzAyWhgPMjA1MTAz
+MjYxMjQ3MDJaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMHYwEAYHKoZIzj0CAQYF
+K4EEACIDYgAEWuf05jTK9E7VNfDVknHTdp7FHB+LNOMVPB/XBRiLmU/+/EzF0D+5
+t4APkwa4vSw3UckWUMoAxOrJ1dUk8T8Y5AxWGBomcuAQGtfmUlDBXvhUjsJ1s9Zz
+iy6WyRkU/fcsoxowGDAWBgNVHREEDzANggtleGFtcGxlLmNvbTAKBggqhkjOPQQD
+AgNoADBlAjEAwDVLrc9jL2zx9byM1qGyHKnuk8xsEvZEkUPMor1hrTyqkLGIEu3h
+1vyRvboYvGh6AjB45GdtABrNeRHI7QeA1ZX0j34dj7lYP0NvYjSVSyvRhpe/nzl7
+CzU2IkkQ4fmxosI=
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCSlVR2c8kUsBYDAqrH
+M60zwqNVVB0FGafWXBJBn4kgTKRQPCqmwkAJp+yd62Z05iKhZANiAARa5/TmNMr0
+TtU18NWScdN2nsUcH4s04xU8H9cFGIuZT/78TMXQP7m3gA+TBri9LDdRyRZQygDE
+6snV1STxPxjkDFYYGiZy4BAa1+ZSUMFe+FSOwnWz1nOLLpbJGRT99yw=
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/cert1-example.com.pem.rsa b/reg-tests/ssl/cert1-example.com.pem.rsa
new file mode 100644
index 0000000..4639b75
--- /dev/null
+++ b/reg-tests/ssl/cert1-example.com.pem.rsa
@@ -0,0 +1,80 @@
+-----BEGIN CERTIFICATE-----
+MIIE1jCCAr6gAwIBAgIUJUqgFv3XQuBU7FxDOYZDO/DZFPowDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wIBcNMjEwNDAyMTI0NzAzWhgPMjA1
+MTAzMjYxMjQ3MDNaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEA1Qyp+JCxptby6yTjlF6GKoSiYXMuTC15GqkQ
+9cA5wExRvRj444ZDeltt4qFh50MQGaPL1Uq5pk2LxVhIMApn3aFv0vVXXLOpkcWL
+tknYhcL7y1wZCGrYff0jJsi/en2YKbzdJ+avFlkrae7uhTmEwLcDRVhJpJYj0nj7
+7NIRZEzzvYxdNVVDkdNacZtJrtanTagse15OV7w6dniIjzyr7P5backq8EyQTWvg
+hf56gx8r/JVoMZdxSd3EXcIXBnyDOU6KTiHu970DJmcz4oEaAlKFCehquNfGyVw5
++jzUPyMP/IzvJZY68s3TjKYnJhoyu2GRf+SH2DBjYVL/I9ULK5G68Oqrjl3lZMM9
+NCjvLykBVAeQ2wYscCUChmLU9Vor1N5Z0EqZx9Wx/SBSPmlpTR4p1eoEmcrrZjUW
+TjDBVk4F3cBrFrMEq0rr+aUSluPzpfYEv/tn1h0WTW/8PbSoQluf85i/BXnzmW1L
+JplcembL1cbm0idJjzRvQx8/WGoSSIYHzWFgRhagvQ7xGf88pGGh0+n/K/xPXZ+Z
+I1b89rLqs5pdBJtAgj7wd2oTxiKDILkpvwRBq9q2p7+yEnaIhWVQr3UudiSRcB8O
+lEk8YHpa8wiKMksezCqs4zfdk3Wh1JEwgy1zYk+penzfvQGaySv5Q20P8V2ZK8i1
+HHnTRLUCAwEAAaMaMBgwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20wDQYJKoZIhvcN
+AQELBQADggIBAD6LkOmRDupXUyDmvA1PsZoNAnN6/ikZOzgcLoPbmPto2MAG16VD
+VJF+2i4FddUJmuPSYkGoo+eEcIJ6pyifWm0f673dvHSn/Epgkyp+uOQcLnVGE5QK
+cYk7ETlw9BQ/uRYi70hXLk8yi/XbBXIZdtICuxzEJrB+uE3tBK33Zy+KoDweifAV
+vGNLDdhK2Slq0/ExaifeO2Agkz0Cb5nihsMnNlSiJPh+Qqhcyn0+o5hW80AozD3A
+MZYVhiPtCfOoHYO02GpsPkYq1mfez79O+t5d3akLLPXEMO8iK4HUtlkYj84wP220
+fRct1E1apRCCfHORqnlPEYcinoEvlsl+c0olH6L2L3t4sDzWGHQoAzNQMSMAwdPr
+NShvuWmKdYoPrTfdp73neP4jkzNMi2FR1SL7M/Mr272njrBrYLayVbb5Aogp9Myp
+PrWohhrYaMCeCVLdtX0C8Ijjo+WhQjMJ5I7J2CCsRifhCnloD3nP3Cfd+obmGxTV
+spGxTfQxn8BH/rqEkTKZgqz8McpMXJChzSe7JduGnv5E8nZH1UQBqbtgDP+JndI3
+5Ncs7GsU0JLfju4w3IaAjslOmu4TLS0MDSDJo5heo1U/OB/kqocbKcoP39mCiWPy
+juW/VTheRaszG8tuPhXYovg9LXZX5HW7eWjgwm9kn9c4fu/3NY7PJbmO
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDVDKn4kLGm1vLr
+JOOUXoYqhKJhcy5MLXkaqRD1wDnATFG9GPjjhkN6W23ioWHnQxAZo8vVSrmmTYvF
+WEgwCmfdoW/S9Vdcs6mRxYu2SdiFwvvLXBkIath9/SMmyL96fZgpvN0n5q8WWStp
+7u6FOYTAtwNFWEmkliPSePvs0hFkTPO9jF01VUOR01pxm0mu1qdNqCx7Xk5XvDp2
+eIiPPKvs/ltpySrwTJBNa+CF/nqDHyv8lWgxl3FJ3cRdwhcGfIM5TopOIe73vQMm
+ZzPigRoCUoUJ6Gq418bJXDn6PNQ/Iw/8jO8lljryzdOMpicmGjK7YZF/5IfYMGNh
+Uv8j1Qsrkbrw6quOXeVkwz00KO8vKQFUB5DbBixwJQKGYtT1WivU3lnQSpnH1bH9
+IFI+aWlNHinV6gSZyutmNRZOMMFWTgXdwGsWswSrSuv5pRKW4/Ol9gS/+2fWHRZN
+b/w9tKhCW5/zmL8FefOZbUsmmVx6ZsvVxubSJ0mPNG9DHz9YahJIhgfNYWBGFqC9
+DvEZ/zykYaHT6f8r/E9dn5kjVvz2suqzml0Em0CCPvB3ahPGIoMguSm/BEGr2ran
+v7ISdoiFZVCvdS52JJFwHw6USTxgelrzCIoySx7MKqzjN92TdaHUkTCDLXNiT6l6
+fN+9AZrJK/lDbQ/xXZkryLUcedNEtQIDAQABAoICAAfQoxt/E0UvdVGy1LZIkVtV
+6i7w7q3UrTCRKxIYrwWixwzMsbSG5ErEt88sZE77YsfN/lggmZbEGXBvwJYii5TR
+qyxt23qHDJ1QRcO2Cb8+W8Yl5rUsViyo8HUnv/5aRQ6i4unnyFxlgPYt0YoJhhkb
+nX8ZsfnbmAzMa1FQk1q+h+JYF8MxEX1z50lrjNRhA1oR5S/RUcZeHTbjTP8UFqpm
+2iuTOYP/CvwMDPxdTVkp948YW+4VxA4VmHJoADg4sQeVHfWnwQBNaqQp/Pk+Cxoy
+tLacU+3b3GreezH2sUJvotJ8yPjz/c2SR0RNg/od0+aTuaabV3BSthKH3NwPoI0z
+bfLkwrR5KyJobB399UN3aqg2s4toKNy+6l9x2dh+QimwDOivptvynEd9BIXd0ZCn
+ohdE9b9j9eq0l36WX+u30JMyevjjumnZjKCh80Pf7MnTcqzggcWvoPYtjPqBj0ig
+WvKwPCmV0TG8wN441mjObUXLa1mFlb8b+NM8k8gy5odkyRGm8ZOOxYlOWmtu/sNM
+VBdjG3U6yONDf+TO+v7OVsOVs/IHFOX3RtpCt8wnFZfTxkxjqrk3E8O7RTXcrIny
+Tgzmi0h0bSTahsKm/0roQNPK6XNw6S6CW9B2kPz2gBEIpjrEl+C8hmsiYEzNJ9kM
+oLWlKEuwcMaXS1oazTqBAoIBAQD3S7icGxwTVypEKq7ZT4859UOtsdrqTKEFIVtf
+z4IIwmlo65mfNA7/w2TSV8p/o3NH4yznkEnVzvYYNXKt316oZM2CqCoA3XjeFlO8
+hUoScVn1VV/66E6wTIbRUCMdBfyPVNQ12bTZ/rPpmSlatXfUGarVRlJ15DDS0TpV
+s+ohxpT1IUnCx7N0z8cPbTFy2qguSbID6UydajXtM/h8up4866wg8nzT4PBssiqf
+NzWgAA+XP7oigfncgqSuQ2zk8Bedbm+tE6bKgK3O6VfTDRIV2Kw89Kvt0OWQYpOD
+F/CTarNdlp0kYmos/rC57AVSpdTNQm3944WFi1ts+aL74+b9AoIBAQDcjF0TnKr0
++uSAFNHDIxf7LHnX+uOZ7cTs284hIHZJ4z/GgwHKimWeG4XZsOGPh9Lk5GGMyDBB
+N9daaGYskoQ9qh0e3IyRbbzdcwUMV9xzulYzUg5OKoezpBlp8Ydd8Gp3/9SBQtTi
+9jjLZ45Qea7/F/Kk1TebUvqGQa+c7HdeJ60/6121QPw7eFqJIOVqf47Tkaq3Wmpr
+csfQulNwN4Gi+v2gp3iMR5q/agKCOtI56daheYyNgPxX+chjiqOqC5WElTxPihde
+lKtYtKh3rnboKGUQ4fJOVFoV/wrfo5wfcYkPDB32Ct1B2hsI3oHbnPkBPgvCB0Xa
+/HPrEqWP5W4ZAoIBACQgVbnIZBOXOj93FM/+RWgsIlTvlJGB3EwJkXWvtMlezVNc
+h7awPjiy7LmlxZlb4W1xDJBPjdnEQENNG5G2/fcPss4RjwFNWWjoThdOSYHkOUYT
+0M+wvD4ZD+DoGhkVVM4DkHTFdxwZj2Li0x3DQNwlW8WIXmeGjHNfyWvXuq5wejZN
+RJ9F2TuJVwUz6HNk6gjJD05u+JhOec5LN1PRV2iC7URq6D1zsOvQI1XbFORo3d40
+mxaLclr6YuBqTTAsuuZuybW5FzaiEcIWaJQWZrv2SUMmYy98wuyS2gXeq3B9t/JG
+HHLCRcyI8HxYtHZcb3gE6liasljOAO8skNjHdGkCggEBANF9dm/Jkc2vf1p17CWJ
+8R6BSZ8wzf6JjlNaGjr3JcTbWdnK2Om1ef6rsAFudWKrplQK5uodwVBBpYpXvi26
+YmhcbNrCrbb54LsMpQ/raRh4N6b522K+HTYyun0akfVWBxvC4uyBOcv4C0ySKekh
+HGtsKOwPJ4mfUR4zyIarSlsiHvunKtSfTLeEg6Lbn28AiP9HzzvoY0t6tHf8dIMU
+Bkx0UnPGf8fnwALvxEBFdSjTiC7LUQmcKpW6SnDa4MkFxdkxFB+NUNNjLjrNJ3S/
+QG0W6aEWrd1fXE6meoKhWwu3AXRMky0Bdtc1QBa1m+2p9hALCoob9Guk/sqcZK0B
+RgkCggEAHjEa/4q05VPbMm7TOgF2m5QTdap47LyTBti9TRurGtB/9nWvIHpM9sAy
+0xVvGcoZOqVHYvRZGpZ8IX4B+9FGMNUDBMc8shj3oA514tCZVPCEolnHcuwERiZD
+c5zh2PccktAmT5EXGch0+eRuxJ1ROKgR0coeo8KMOxtrm0hRFTznsJ0nzNjAoCA4
+zW6DVY7qIb9ksI44rWlgGSwXG1OuUpqH8+tBAvR3uNa/j59psBb7Pu5zmg/qhx1m
+Ljd/0JTxE8A00l0bC8S1F15wGn8GQD63pjq8nr/biI0Y39g3TEAffkI33FfCjBxQ
+gO96WUZwPEimQAnu4Jw+RlpLtWjOBg==
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/cert2-example.com.pem.ecdsa b/reg-tests/ssl/cert2-example.com.pem.ecdsa
new file mode 100644
index 0000000..9dbf25f
--- /dev/null
+++ b/reg-tests/ssl/cert2-example.com.pem.ecdsa
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIBhzCCAQ2gAwIBAgIUJ2zhyUgHjXsPzANqN5ZSHX0RVHYwCgYIKoZIzj0EAwIw
+FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wIBcNMjEwNDAyMTI0ODMxWhgPMjA1MTAz
+MjYxMjQ4MzFaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMHYwEAYHKoZIzj0CAQYF
+K4EEACIDYgAEx1lz/PGGGGI9EG5L7qx8JGwt15HNuJI9SsdwzkRV/N8sBFzAEVbS
+UWVthQ8tIAdW1y7d9fwkHrzkPulDVwZGGr3qrnZSAgb7NCxBICxgdDI7ku3oPdNd
+bsSASmhJrQO4oxowGDAWBgNVHREEDzANggtleGFtcGxlLmNvbTAKBggqhkjOPQQD
+AgNoADBlAjEAnHx8jSzldb5z4FR3oZ3twWCzRR98n1IBuBi5fe6hhBlQF5u0iAyb
+oDcZ2Tx9UfWhAjB/DKDFrlXAkow4rQxHU602c9SI6hJTCKxIfWWoBYP7zqZXEUjj
+2QK7BQb3sHNpsqY=
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDsiqzn+NewEL5bc3CA
+sY4ADwk42yQJCPZalIct5i4e5u660YCUMHqbVAUQe2R6YFyhZANiAATHWXP88YYY
+Yj0QbkvurHwkbC3Xkc24kj1Kx3DORFX83ywEXMARVtJRZW2FDy0gB1bXLt31/CQe
+vOQ+6UNXBkYavequdlICBvs0LEEgLGB0MjuS7eg9011uxIBKaEmtA7g=
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/cert2-example.com.pem.rsa b/reg-tests/ssl/cert2-example.com.pem.rsa
new file mode 100644
index 0000000..7a6678f
--- /dev/null
+++ b/reg-tests/ssl/cert2-example.com.pem.rsa
@@ -0,0 +1,80 @@
+-----BEGIN CERTIFICATE-----
+MIIE1jCCAr6gAwIBAgIUCMeB9uw+PcBIqW8cDI21s7SxWVYwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wIBcNMjEwNDAyMTI0ODMyWhgPMjA1
+MTAzMjYxMjQ4MzJaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAzt3oEBc1jWk2PaN/tJA/PTTdwfi6ZXqXCrCA
+ZScmo1jvM3CcoOM1BUhiMcoeK4uHRryYUO/eL/ZM5OA11GAIaMevhK65rtBYIh2Q
+klRH+IojmRL91U9tXno+oMBS8WwF7K6eCCj4XUTAKuolQ4yiFHTvdwOsqSrVY3m/
+m2Pp4VTqjDSsljmv8GJ0lQpxan5bZt6WWQiCIbdS7ExgJIALDemg+JOIz/bDmCr/
+3tihmHOK94lCcV/CFOs2XctVnkS6W8x/S4U41Y/eciUbLWr5CxAvfZLOQBuriWiU
+SMHPJI63VPijGKStnBn/zRMvDJhaadkRqAqXlJUZ7nkcZ5WlPuIMgAOc2uCZioW8
+DvyJmplBjBBQdGqRFaeX2lDvJwDECDxSHglfQgVVz3II3ZMSlDsystu4MCgeFa0e
+S0UCvl+5mK1/QVzkzxYj1o9iXhtq5VSLmbaAssDcn20ashJMxmruagsOR4MhaKA0
+RsMosrAiCbcBiY/Q8W6NoOwxNUC8agsqDRNSoJfQgYhTJXqxbnteyy3TXtF4zW+S
+7D0ZsRXM+u2z6V7lP8rvS8ZwzI7nDA/hH34IIw4H875IESLA/8ZiMA3luzMNxwWr
+xCn58JCJM0lJmgkO+NvKctGAGxgtdKzgHemzczx6GuA3V5mOOD01KUbMpZITN4lP
+vAt++qkCAwEAAaMaMBgwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20wDQYJKoZIhvcN
+AQELBQADggIBAMc0Z6hDp5VuihQ1LpmfisQtrs0F5SpfxlbCshg9MOrgRGwViRBM
+bCw1UhDZPT7sQ47JucUkw4RguJTsNQO6Iacq04EKSfHmbxznlZ9eBpAMdK8vWLQH
+jrpmNVE6At3kuyFJrXEc4BOrvzwDqcbG8cFFwT+l9C5BGSZCG/muLPuW3S36IY7i
+uVGc4MqrOQLRghyZbjkXrReGzBZVbuCiz9O+zsjorEzt58gdwIhrl8WyHTJ/Nqy7
+ibfFDh+tJxdNkipa0PZEqovMUcMG1N1E+n4nl6QooUsIx8JmeL5OD4J15ZuvrK3A
+emggxAMs+rkooocc8SL8i0C7l1m74qRKCP/dhIw8R8XiSKaSU5PQxlmY62qHJNkh
+RIkwvv+VcGdUzC74eEPUagKABzYARXBC2410E8vekxVYAZ3U31ypB+/3nWBJOqH0
+P//I1ZKwYLQCuC02O2Uy44kwZsZ1Syh2BYJxjdIeg5oMVnrDhi9kYnMtDmtzLsnC
+kP/cMKX7NZ7d/qbF6Aa9vVE/Ta/OrLxETF8CrjSa/nDLdLpm9sDC26/aqZv5L554
+xeSKVxvZyRFtObSKW1qzK40RMkWUarh72urtd9aM1t5PHOnwY77jO/yemjxfhgvp
+jUKM0pxIe7EmNqoEay+zdN58x8VPDtLFNehorGUnUGkaS57BFBjpEUvY
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDO3egQFzWNaTY9
+o3+0kD89NN3B+LplepcKsIBlJyajWO8zcJyg4zUFSGIxyh4ri4dGvJhQ794v9kzk
+4DXUYAhox6+Errmu0FgiHZCSVEf4iiOZEv3VT21eej6gwFLxbAXsrp4IKPhdRMAq
+6iVDjKIUdO93A6ypKtVjeb+bY+nhVOqMNKyWOa/wYnSVCnFqfltm3pZZCIIht1Ls
+TGAkgAsN6aD4k4jP9sOYKv/e2KGYc4r3iUJxX8IU6zZdy1WeRLpbzH9LhTjVj95y
+JRstavkLEC99ks5AG6uJaJRIwc8kjrdU+KMYpK2cGf/NEy8MmFpp2RGoCpeUlRnu
+eRxnlaU+4gyAA5za4JmKhbwO/ImamUGMEFB0apEVp5faUO8nAMQIPFIeCV9CBVXP
+cgjdkxKUOzKy27gwKB4VrR5LRQK+X7mYrX9BXOTPFiPWj2JeG2rlVIuZtoCywNyf
+bRqyEkzGau5qCw5HgyFooDRGwyiysCIJtwGJj9Dxbo2g7DE1QLxqCyoNE1Kgl9CB
+iFMlerFue17LLdNe0XjNb5LsPRmxFcz67bPpXuU/yu9LxnDMjucMD+EffggjDgfz
+vkgRIsD/xmIwDeW7Mw3HBavEKfnwkIkzSUmaCQ7428py0YAbGC10rOAd6bNzPHoa
+4DdXmY44PTUpRsylkhM3iU+8C376qQIDAQABAoICAEQ6PiKodPop3EDiHul/tcvL
+FuS100xK7WwSIJa8Hes8FtCBcLdDmKYgZHqFbgPwpfI3m4j+Q+rPsja+mCJudfeQ
+/JunQQieIKNH2vnYIFChxvHiqKNk6e6CJQvBwtlrRlz0jpykXp3sYfEFfrrTtFVI
+5/350UWOIgkIC6EFiArQhfcuHEoDxrpizo6lfhigiibYfP/qZXkXTJsw6XjAXmT9
+TCEQD8x/V61laTSngEyWtxvDQo3ABnP9y9WNjbSAeHJ0dPuEeeU96SD+igMlx/PV
+J8Sj2bCdL6tHObjxaw9knqTAyJIFJllY3dxWWmsuCIvmkwM4UxwnPQFBIpQrb+9A
+rguNl+t31zljmToDIEF97G/QcbFqMQEKeNCkwIdtD/8tND7RrchcqQPc96rdHbB7
+Hfb/ZXqCSsYNahurEmeAUZJkLO9U6/0GbWHcxkHBTkrmUs2qV4LrhWP71tKpbNY7
+mGXK6Ok6ZfkAD4uau1oQkndqdlKg/rBOjcT+HGPtxWL9gPtG7om+O9mu++BngrGr
+oyNgujkVRN0fpJhKLhsT6OiZF+7CVQo4ZIw9dBQ2hzLNw5tKgW36GAVTfFxNRTje
+SerlyEog/P3s1tnDn7BngdVOdnDfiOi1O4TEb4btwqP3BSs2p0wJKaJGoClFFuwN
+n5dtHMABtSOKPbmWurbtAoIBAQDqPmZjSLfEwSXph33m7Ms2/AbQJltzU0ftRJU9
+TQGVHBajouupVcyrZ+WiWcltLov+JNlseXG/PsIWEmqSiLodIZJyjWSDUiC5iFEM
+fn2d9X4NLz0A508pFR5FQnULFEDMDryLn+4ta8Bf5NeL2p/ZavKh9rxX/8LAanse
+6Lst59RiiRMkazkjC4DHDmqUAZBt+uQVaHVFpTBJLa1k1nIc82GjsJwWsbADL3+o
+PKiggSir/Uf3nOOPhXsegVTZBiq9DNFciCa+kqT4eluUopjWxIuOKnp5mVh2DnTr
+NXyZ6jDb2JwjcJpy6HLk9EsqY1YuMpT+OCNnLM3l2Gxp/KovAoIBAQDiFJEh/LHl
+++7Z4TE0whMdjkFdSCuPyEnU4WFRKLMTPQRCdS+5GxHDy4lzpArde+51C6UkAjxe
+jaAGzQvabKBl4Al6eFpYvv0d8CQMWIrOffzVMRXuHWgm/SBg7um6ok0rM4/BOdUr
+CN2nWvBF02ZTSsGzzBmzTo4vMkcAQOiGes0Haefxm0DiVvoElL20Fv/iuEzbf60p
+W/0TzeiOBar8WxpTTcnHc6QWQ2t/Zon3/5E1LIOEU2/GQiS6zqNBRGr+kfWtz2wB
+d1IFLXITiqAQb+F3EjKqGS8ln0JYLSLRk3ALbb0EtN59lYwrabUYq9WzA1MlprLp
+GFqzAHNPc+qnAoIBAFg4DAOUXXGCdK7Q0n/n6ljY7g/ygjqawNoBHFur5s6rd3NF
+Zo+tuplLVdahDhVKlHqwkhoiWs516k65vN1XFRDnleoCijpS8fQt/KhB8zlMPZ7l
+jYoLk2qbg3z+HGqBxC2V1ziWkPMWQ6tZ2jvXqKAPgTWyYRibQFOLRrdLW0NcrkY1
+7bmnkCs8p9FQAp+fPy/Mb54IazJBlj/ZLhZuFSgGGV22o/KAFRP+DYvk3HUmb5Tm
+nPYKZkGlOcsxVi0t/2aXrzm0JTNcszjJjDgcTIeGGjD+moW1VPWRWENFL5of8yq5
+F4TZYbGpDaxgvPZH1ysq7aYYqmyvGRRZP+titw0CggEAaPoB1hU/Cbps0xDEx2mi
+dKPcaBMd3xqyZb3tcUEDvdgkRTOi4EHYguDcxyyRuvxT4ldw7AJ5w7Hhb6cAbQDp
+jcR0wkBmOzUb1S3vnyfx9CX+I4QyWamf9hKtWTU2pGm+iWPcyW0wNVZdjdslHFcn
++V8KCJGqEV9VmEaxP0XkcqVM1LdxcveTLkYSu9PRLnFLihvn3Dgx0LWdEvgWlvO8
+zIcE9090dT+WHpxZqwOS5uvtohI0U1pm2VlXMsLGfYTmZaSivn1E+n1MQYkzoi1J
+W7iHqcFycxdUlBSaOtViiIv8h+IB1dCiSxAI0RO5emY3yXKuxhnck22yl9GKuYbq
+mwKCAQA25h2vjVD9x1Yci/qWnKnchjVlTkKWb0D404fhibJpSXHtFOYiE8YXsBBS
+zLYDeDXFagl+AorvG45SoodJGl1/uqGbZMPBs0Yh211nBVtR5W+8vHLPEbw/Qvl/
+AXSmwnVT+K3oeJRxUBIlOLQcDtXcFGBhF3CbbjKU7+9gRdj0oq+O4DZXZVnJPeI4
+Rf42bfQYXub1bB+kH4WwkuLYItrzv4vLgS7kO6Z1GXz7mIBZi7zlUI7Wl5pWg1fq
+H5X6u6V6N2LKS7Sqwa7ihL1ScUMhfmcPE362FyxqwkSMWOx3F/L812MKCgwVoil6
+yupxw0d9CircRDDG93pWn3WxCHpV
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/client.ecdsa.pem b/reg-tests/ssl/client.ecdsa.pem
new file mode 100644
index 0000000..b9940f6
--- /dev/null
+++ b/reg-tests/ssl/client.ecdsa.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIDPzCCAScCAQQwDQYJKoZIhvcNAQELBQAwPjELMAkGA1UEBhMCRlIxHTAbBgNV
+BAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRAwDgYDVQQDDAdSb290IENBMB4XDTIx
+MTEzMDExMDIwN1oXDTQ5MDQxNzExMDIwN1owWDELMAkGA1UEBhMCRlIxEzARBgNV
+BAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRUw
+EwYDVQQDDAxFQ0RTQSBjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQd
+LnaDhFB0RmWq5WQMdPuFXqjBNZutLQRZ5t+AJ1afpujuwr4G79Q0eZKeYyFHldq5
+PMQDBL69D285AsUbMT42MA0GCSqGSIb3DQEBCwUAA4ICAQACnqZefAYCf51Pxhwp
+NElVCzLFcrNfMnCcOxHkuaWjdWbKZi+G4PNIT0ghWgX4k5Gzjx6cVjNmWVkLnJxg
+r6fL31u+Edl9BLr6KKrh830EOK7jN62zySFVjd9sqqBPiEBnT+3OCI9sXWXWg5nB
+B00E6Ll2axwEVrQFIVYnTPC8CJyDvF1t1Jmw/caaiWWVVoUu7Zoq1kVzMCuj7aCO
+BmhvDh237+Cjkly829/Q41aKVBSQ6yDsds4uNceOpAcXOQ8A5ZXa2yearIttvvAz
+LHvXcJZD3h/23mnLQZWo3YisQb3u7O9iIpIXdxpbVHtJ6JvshmiFHTCNB9KG+q2W
+CltrL8lYu2bWzNT8CPJRa5CsFyolIi5fEOfVOWLHKYkOgb9h2hiI9hT9Ujg5H1vM
+d7AeqE+frF5dzxslcQ/wLQoUc+v4bfhh3ffeAdNul8bydoSu3Lq1nXWchNkE6rcg
+pN2uD0eLC1hAXvxi6kQPlutmFJ8yXHySEA5uCek2Kf7dxudqIRKShT7aDVw6pd6R
+ShX4dXTGEO4eBxTcooK4mYQhf+ivyTxfkACnML85C84hXPSoIffMk+Y+PMfNgW/2
+9OH6IwEq7+dSSsAlYweGnqznPCyVfpesMaQCoG3l+5Ciznt5/WA4Mh5HzLE2PE15
+VTEdimSkNohOKYdqQHA3mcH3Dg==
+-----END CERTIFICATE-----
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIAe2GvrgpqaNk1wzyawK9CJYQz4lTVLDyf5MtbDDMYrcoAoGCCqGSM49
+AwEHoUQDQgAEHS52g4RQdEZlquVkDHT7hV6owTWbrS0EWebfgCdWn6bo7sK+Bu/U
+NHmSnmMhR5XauTzEAwS+vQ9vOQLFGzE+Ng==
+-----END EC PRIVATE KEY-----
diff --git a/reg-tests/ssl/client1.pem b/reg-tests/ssl/client1.pem
new file mode 100644
index 0000000..d830a42
--- /dev/null
+++ b/reg-tests/ssl/client1.pem
@@ -0,0 +1,187 @@
+-----BEGIN CERTIFICATE-----
+MIIFLTCCAxUCAQIwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCRlIxEzARBgNV
+BAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMTEw
+LwYDVQQDDChIQVByb3h5IFRlY2hub2xvZ2llcyBDQSBUZXN0IENsaWVudCBBdXRo
+MCAXDTIwMDQyODE4NTk0MloYDzIwNTAwNDIxMTg1OTQyWjBDMQswCQYDVQQGEwJG
+UjEiMCAGA1UECgwZSEFQcm94eSBUZWNobm9sb2dpZXMgVGVzdDEQMA4GA1UEAwwH
+Y2xpZW50MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMCUnq4y/rYG
+n2BPYutNd/dQX3KV1qVKKXBsXXqBzE84qjBg6SeQQVwcVN0UYK+l2Rqnkt9m+sV+
+CxAwDAVHts/QmD/4tbjuP8tMQiZcUsl7hxRzLRK2lXGwoX3B7GJgXxjDckCllert
+FgUMylb4ACt8oA1e4c75fY8ppmOcjaNcPBghk09uKVUOKy+UZ/HWkNncF6cO9N82
+Y+bPdL1hg8mr6n7U+jv0bdyBjjN+b/3ggInY8NGyPHpl6ezvmgaI5+stA77YolCY
+NoG7ZexMpBbtv/2M7+PHlx5c5lzd4HbuC5fOtfkMvoosIZJaI8/mM5J6aeu6JpPB
+XvGRRE1Opmmhk1M3aQvU4q9LPYLkXIivuH+sHZnVZHZ32hhpZ4GhTpgayF22n/hI
+fMOzSIMhpao/1YuLVbLgXdWJZx9uOIT//a/3Bd4I/c1/Pt11oNSIhiEAS7beWj0c
+QtsSabeQwEIOOlbxWFA1aRogFNNE3iW4gps4p/4oHmT9Warb5AadE6nzh7N1nCiD
+oO7JoHUzOj8VunLn2RZ8vWuBJI/2fh1TJVjOBmQBl6YGHD8BaRWlzv/VOiq2z8at
+90rXGUb58KYvcfOTOZmYjKK16r/112pEgJuivXXr+N6qJKYxw46m+MAD2eDQ0Bc6
+gFZMlcyBAyJwuxIejUTvWwoddfRnaFajAgMBAAEwDQYJKoZIhvcNAQELBQADggIB
+AFk00NuZDresZ9voh2E9J2GvUbG9x+NSjZR6pQ3MiPPXpLYskV2xAvxFSayGcQhG
+mIfHshsnEhE35WYU80R5Ag1Mxh+XPbZUiNj/oOEFdBj45c0HGorChaVkZtnLilMo
+B0yW+0pnkqKaRkgmVsSrNCgimBtZX1hsZRLDxa2vldJ9lTIg3OuveqBv/uwbMOUC
+eT+il/sdl68K6oNHvAFdY1U34oJnvj4yF6ZZM1jRERK38wY0+2C+mlcXNL648k+2
+lOMeBddaDUKhscWdw1+Ui8Sn6lc6H+iPpGo3xdj9awc0568SCH+D5cpuTMNTREuM
+p3paOMGpLWuQQisltdmz8Ms8lAcJUDeyDmrgE9CPx9DiydB6Z1uP9y9sozqB2SIN
++QqfQLv+lAaUB6cu5xIWfZIFeTxxziABZ2jDF4vVvK+NN2IdBahbI63HQpfeK4tG
+Bkmuny4vlCnHBnzVaAboaQk2xzI9Yp79IN6yhmuO8AjCvd0XlH/nYF6b7WjVy0gU
+LpmkYVHWhADLY4q06PUz8gFGsfDHnx9RQIV01SXbcFxhmAjJBequBbTpucW4UAK4
+sTmg59wlYEeNdomLBPW8f0zkY3Nm/IbyJ8kEofUa4kbwdD/osS5fgWgARiVQXEMW
+W4oMGWpplJan6qe+hInvd+5syZXtO+K/uSOj63H6BwAu
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIJazCCBVOgAwIBAgIUWHoc5e2FUECgyCvyVf8wCtt8gTYwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA4MDQxODU4MTZaFw0yMDA5
+MDMxODU4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggQiMA0GCSqGSIb3DQEB
+AQUAA4IEDwAwggQKAoIEAQDARiuHkhrnf38Md1nxGDSneJfwv/QksdNNMNTJBdjg
+OVmaRCIAyz43oefTWDQ/TebbSwB+Lg9pud1zadGWhlZRhCgBPP8JDMhIKH4eXIRk
+5IIa8WD08EwvSlqJL0r4gsMtVsxy7BZHAkka/2Ket9pyGt4kG5n75RFdc6BI80/8
+RwJt/MDxPrcVBAT7LnCluxQpyya9mZCabj7l+9a2yU2hgWS6QqfZJ133krkP/MMh
+AEQkSoA4mmBwWk9yPqXmUqiOi7v6iLkIUEh5SgYVPRk9BtU/kDaUdSwuqRrpCZo4
+SsWZWFLxBmLHkSh+G+BWjCVYMQr2ye7e+VMT/20+5xAfq4fj9n5BsPcx3QcVuTof
+RAc/Oygnt4MYnIcUb7zRFvCAvgpUHL7BnEn6nhyXjHJGqGDchsg8m9t3v/Y3ohq+
+qmrSzdeuylE1n3W5aWJlbFmyXegNP45MJ0xicesVrXEWF7YD/ir9mGJ8bQYr4blf
+77PrbF02komC6AzVPKOJa0jR+eW1wErzYlkYgez6ylBWCiHJd1dhEHlK3h2rXdYa
+Gnb45ILCLpEDjNEUrHifLLNXwqJpgZQsJU6BgMgk7ZgBfAKrCfTeg0rkCqCAPeVb
+8eSLf7FBF7YBRJ5P6u8qXc4RtgEu607GaWV0gIMfyVBY52oV+OaNsEdFetrJnp3c
+friG8vJ+7jdq6zjUCGgnfUIHoViJPh3JuFfhA3jT0gQDKW5PeI7dxhrNvlqdYfHI
+fxX7Y1/J6cTQkqJ1cai2f0bwJIJiTAThNbG+zrtjJ7fZ3wJ4udyU/IKrwShqtmTb
+1Ofj0tJDdwOH8i84vIySLUvR9aAb7ClFlnsx6rzwOxG90W7C0LA2M0EHm4FezJm/
+FfujnZwEWr1T9Wki6qE0MHCbdN/TTDws//EKkkE44FC+amL96w0IQl70vpE37j2A
+zlDWvFFID95SIxfmpkwWDvXDKv6gr1GMLeysCl2fgpY05Xidw5cEo9/tEkuWn/dG
+x/D9hnLBGeroA0251ES12jemqDjI2U0tfaeHakjwSsoWElf94Qmuh2iPZ+1zIxQs
+7o6nAWN8X9hfsmrDTTHlww0TEfrjlbzG5Yh+0ZRxmejgiUyOCXck+eh/ZXMXvfWh
+y3CorIIuWgkRjm80PYkdaRDJdZuyP6R7tXfTXNVzAiSQf0Qx9ru2KB2Fs/XZPamH
+KjItAU5Q6msIVvaRMS0muQgV+b6hqSEBzqXqJfAlpVLHXr5FqK+U7EB9y02B6piB
+tAmxqXP8OOCoQql6/vgIcrDFUOo6KtGBW36ef74XE3KCUVaIzVJZSIt6i/Vi0bZj
+bAjsJUQ3qDlHdorv9TRVOhnC1GUz7SuYnpEOyiXmyx3LAgMBAAGjUzBRMB0GA1Ud
+DgQWBBQ62csZcH/meQcENHhNbqz9LMzwjjAfBgNVHSMEGDAWgBQ62csZcH/meQcE
+NHhNbqz9LMzwjjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IEAQBA
+wLsGf3R1+/I2zQE+lsj7RasZtA/Cos92iEGDAPvFbx9e+roG8Gg8KBsEJu/HN0JH
+lMMiQ8dDRHSBMvRBENL5/57oOOhmqc+1u5sazLuANhzAYPZG17Klib7YpEwWoXar
+FDDiJYtCyLW0oNLpCswYopWK9GC0RJNucB0NFvOxehJ2sP2/fxGBQMB09L6mjKjd
+4KsOzyd3dNf0VYS6jB+/1pcKSHKQUo9HRHB5FK04PsYHoh4AtmEHvmYQKcWWidgU
+v26ftlH00ERzuW2juqBbz9mghlNRqXi0IyZ9b4tSj29dxW+WWFzo7j2zEPaD6z2W
+DEHq7zvON+g+q6qLgWeszqMgJzjvWjMj00E/t06PoHPiz/cAnDKEqp+ZzxCIFrxj
+/qneChpogDWyLbawhyyzbZvbirx5znOSbWjPZgydqaNEFViqbxwinBx4Xxabo6XN
+TU020FuMWmgfbIcvtgjKgyKqc97l7JMNNm7LQV9+9W0U5zdIqQKLZ9MMrd2w3xh4
+MAB8NKnwzHReK0TWwUU9HSgFAGdEX6HnyZ3bQ13ijg+sNBRMEi0gBHaqZKDdyoft
+B2u2uasSwioV48dbSIcHl+rTBKxiMh5XQ7ENnaGOJkjsIqTVzizqnPHU8eMBnSbb
+dsXlamROYII44+j3Ku6OGt51w86eGk4VxI3tmaECcJKqTkwUFD8AcNDrkjtmLuxK
+12yjnoM+u1cclfqQ5NOtRc6MJZ27jCobfBBhVdKVDp4X1WNyqGlbsU5adDAzknuI
+GT7MJO7lGjkZX2n54BNPSfrSknYMOVYcZqL0Dbcrhx5IyEmg+iOlOu1HO1tdnZop
+ej4vT+1V2w9Sa4Wo3UCo84jcm5v/4z7jCYh4BRQ60CFb7GLxZoqXIslcGSPool3n
+jl8JWoaLXrJUPfZGXo1iAlayJ5EiMyZl4eB/TBUf6TMm8vLvsPiUT+CEsjLppOdS
+eYppZAZ6H1JrJGs5kKBdOJHGn6Pkp5QsHIswOBd1HqHrBbYbZmDaDLRHduILWLrM
+e0/IfDdeXB/bKfmZoEpT8xRiauw15p0AHLumiK7KISAehfgBqUnxx+YmgGoZ7EWX
+KnMYAfCuC6oJ1DL0gp4Z9yMK1eu+GV1sLxPq9ZruEHW1R+H+4sGyiA5Gso2tgB6/
+XW//wxKclNp5LZR7hqfs/kGuh5asrJrnEbMwWn2+tr/LqfYtYh1D6nHfIXpT0o1d
+rNy/HrsKnRDMWxjm03r4hCViuNVD3Zb9anAF/NSPDVu8ATM5JbJNrCYX4eipz6ZE
+aQBkwIBkTPgtgP4r8v2G+uMYDw8nq7xh72FK107aeTTwc6MgU5jfeFNMr2XJisJd
+lSem1ngKYQSEzjVsTE4c
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIJazCCBVOgAwIBAgIUJ67hHFw8DWW8omAyqE92SPRxENcwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA4MDQxODU4NTRaFw0yMDA5
+MDMxODU4NTRaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggQiMA0GCSqGSIb3DQEB
+AQUAA4IEDwAwggQKAoIEAQDARiuHkhrnf38Md1nxGDSneJfwv/QksdNNMNTJBdjg
+OVmaRCIAyz43oefTWDQ/TebbSwB+Lg9pud1zadGWhlZRhCgBPP8JDMhIKH4eXIRk
+5IIa8WD08EwvSlqJL0r4gsMtVsxy7BZHAkka/2Ket9pyGt4kG5n75RFdc6BI80/8
+RwJt/MDxPrcVBAT7LnCluxQpyya9mZCabj7l+9a2yU2hgWS6QqfZJ133krkP/MMh
+AEQkSoA4mmBwWk9yPqXmUqiOi7v6iLkIUEh5SgYVPRk9BtU/kDaUdSwuqRrpCZo4
+SsWZWFLxBmLHkSh+G+BWjCVYMQr2ye7e+VMT/20+5xAfq4fj9n5BsPcx3QcVuTof
+RAc/Oygnt4MYnIcUb7zRFvCAvgpUHL7BnEn6nhyXjHJGqGDchsg8m9t3v/Y3ohq+
+qmrSzdeuylE1n3W5aWJlbFmyXegNP45MJ0xicesVrXEWF7YD/ir9mGJ8bQYr4blf
+77PrbF02komC6AzVPKOJa0jR+eW1wErzYlkYgez6ylBWCiHJd1dhEHlK3h2rXdYa
+Gnb45ILCLpEDjNEUrHifLLNXwqJpgZQsJU6BgMgk7ZgBfAKrCfTeg0rkCqCAPeVb
+8eSLf7FBF7YBRJ5P6u8qXc4RtgEu607GaWV0gIMfyVBY52oV+OaNsEdFetrJnp3c
+friG8vJ+7jdq6zjUCGgnfUIHoViJPh3JuFfhA3jT0gQDKW5PeI7dxhrNvlqdYfHI
+fxX7Y1/J6cTQkqJ1cai2f0bwJIJiTAThNbG+zrtjJ7fZ3wJ4udyU/IKrwShqtmTb
+1Ofj0tJDdwOH8i84vIySLUvR9aAb7ClFlnsx6rzwOxG90W7C0LA2M0EHm4FezJm/
+FfujnZwEWr1T9Wki6qE0MHCbdN/TTDws//EKkkE44FC+amL96w0IQl70vpE37j2A
+zlDWvFFID95SIxfmpkwWDvXDKv6gr1GMLeysCl2fgpY05Xidw5cEo9/tEkuWn/dG
+x/D9hnLBGeroA0251ES12jemqDjI2U0tfaeHakjwSsoWElf94Qmuh2iPZ+1zIxQs
+7o6nAWN8X9hfsmrDTTHlww0TEfrjlbzG5Yh+0ZRxmejgiUyOCXck+eh/ZXMXvfWh
+y3CorIIuWgkRjm80PYkdaRDJdZuyP6R7tXfTXNVzAiSQf0Qx9ru2KB2Fs/XZPamH
+KjItAU5Q6msIVvaRMS0muQgV+b6hqSEBzqXqJfAlpVLHXr5FqK+U7EB9y02B6piB
+tAmxqXP8OOCoQql6/vgIcrDFUOo6KtGBW36ef74XE3KCUVaIzVJZSIt6i/Vi0bZj
+bAjsJUQ3qDlHdorv9TRVOhnC1GUz7SuYnpEOyiXmyx3LAgMBAAGjUzBRMB0GA1Ud
+DgQWBBQ62csZcH/meQcENHhNbqz9LMzwjjAfBgNVHSMEGDAWgBQ62csZcH/meQcE
+NHhNbqz9LMzwjjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IEAQCa
+SXUWwou6JG/0ubilpl4nBPIhK5sp/7jKBSsOEwn4jROz656Qf5M+mSgbQAjp/4I1
+qwCBktTF14bUDrxKpAga4M1KYilal8kiExd3WSGxbeNpdazbjLGdoDKKIBa6++df
+lCrxs/2Mg4RvTN4GOaj9A9LanWLj+rhIi27WD039dzHpZwYwgKpLwpvGHz+7bJzE
+93BqLqoG2q7/Gj+Y/uVfy9Vn1ikxHGJlS5pggH38F0iGy1QhmVHDp7umNUTHBG3p
+Q9a+wcNycrEkHQ/sniXiEaWzn1CFmVt6VcP2AAlioyfv9Q0hF6DRFeQrgNFYixj8
+kpomkqEtFO5Yj+J2FQZFq8UE7Boqv1oSdVnON+7Hy5gb4x5flKvTx5Sok1dg7W9B
+bYfICLwKCEi4mr1toQLT7e7PicGJXKh0nyHWHhpn9SeSElQniIlZbVrkDHx7zwOq
+fcYbjMha3uyqJbd10Rs0ytlL3uiQcHVs+bc9apSW9QPPFW1r5PC05Wn/9+iwU5Vx
+2s9WNgncvvdete/UjGBSbpXROe0fSuJf4+VYNK1SF9DJFaDim1zrOJWiT5bSxJGi
+MGKnQjEZZEs304dfunuH/I16l+UzTecd7QHgHgCfRN+pJnGyYbpT2lt9CCBD4YZX
+qBSQm1iR/7OjgFuLniOF4GLmatuNgVQdKQd6IcllPVK/E0khUwZ3LNV1RRrkvb0c
+9mNsnvhW81rBoD6+KHVgaiA9v9fSqeH8KDNbaqKImt9f9/hZJE1joy2hJIkkc4vz
+KNQy4aWmRUU37xlvF2yTWt8MuSf6UcM1IC5pfl+cEXNM3kyUs6dps2D66AfAsz7w
+C82xUPJ5blKhEWcskmiGXDL64NnD465WoMHPGVorRlRvdHy2mXQWaePF0OpmGtJh
+7LqRuV5ou9M4/fmPHrfLJ81ZDoGoBvKpibr4V/3wxdWYjIaQ97MePssVnBFtBKxI
+lcPsvunxL6dyxL16FfQ2WPqWe6Fq3UT39Lz+3y6SjtrIcASKJAE77HIPypITSoRI
+7Od5OT7ZxB1hxtvqHz45Wyer/aDMq2YDBDDs45s8qEMSPYozvs7LNprU42SJC/LG
+GjVFiIXjeBzwTUIjZOAjQ8lLFN2eBOPljdDLmqNjuVV7CgWrlIQ9PafPRXLsfC11
+71Xx4Kmb+I3v/upagQXKikNQZ3IFuXmCofRoOZEnpIvIj9+Dp3TgvK1Fpe9ldFhN
+h4Q09rb/zCMvB/yRMkp/JP6+9qySBCHl9kl5W9/bsgLgvdZKR0nDKSwxu/doyPQg
+/lgbeYbaZes520gwORtgSYJzuCt0n1nuYxbxINzo9Dw1hH0xgWEhDNL3gjZonmf5
+gCN9CPQlyEFKI9Q2QVUC
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAwJSerjL+tgafYE9i601391BfcpXWpUopcGxdeoHMTziqMGDp
+J5BBXBxU3RRgr6XZGqeS32b6xX4LEDAMBUe2z9CYP/i1uO4/y0xCJlxSyXuHFHMt
+EraVcbChfcHsYmBfGMNyQKWV6u0WBQzKVvgAK3ygDV7hzvl9jymmY5yNo1w8GCGT
+T24pVQ4rL5Rn8daQ2dwXpw703zZj5s90vWGDyavqftT6O/Rt3IGOM35v/eCAidjw
+0bI8emXp7O+aBojn6y0DvtiiUJg2gbtl7EykFu2//Yzv48eXHlzmXN3gdu4Ll861
++Qy+iiwhklojz+Yzknpp67omk8Fe8ZFETU6maaGTUzdpC9Tir0s9guRciK+4f6wd
+mdVkdnfaGGlngaFOmBrIXbaf+Eh8w7NIgyGlqj/Vi4tVsuBd1YlnH244hP/9r/cF
+3gj9zX8+3XWg1IiGIQBLtt5aPRxC2xJpt5DAQg46VvFYUDVpGiAU00TeJbiCmzin
+/igeZP1ZqtvkBp0TqfOHs3WcKIOg7smgdTM6PxW6cufZFny9a4Ekj/Z+HVMlWM4G
+ZAGXpgYcPwFpFaXO/9U6KrbPxq33StcZRvnwpi9x85M5mZiMorXqv/XXakSAm6K9
+dev43qokpjHDjqb4wAPZ4NDQFzqAVkyVzIEDInC7Eh6NRO9bCh119GdoVqMCAwEA
+AQKCAgEAuDVxE2/z9GmhSZ6mIC2Z8xcONazeBH1L5h3BzM0bgSvSnzQT0aRK4LC4
+/D/hvCIH6VchRlBaz04hhvpuhR5z35TIDWj5akt6+huXqtnk1pUyQH1rP9smV/l8
+f65fTjqgvC83ul6paG4gAfSaF1Zh0zcCYcfAdxpu3+IXJnE5imlPkkWLgw78uj8z
+T+/E/a8gH0RH26SS1nBQXxdRs1TzmpS3WVqfgXntHF9QhjELLuzwButcbzjuYKXb
+fKgzzMxoCqykSIkvuaffe3ilpcIps0T3wLBvRpJEGucB8xLJAvnwXOV9axylpcVQ
+140hdFveON6fMrx86hitmKQ7kTcKNZXEnaduehhQgDn6bqeJoAfHs4EX1JJqPZyw
+ibON1LYeUoFw9yWRy35Wr/XMkBoPwAykCWUwmOm2QEbmwhC7ORdjUVVuzrFdn/c8
+beoBfJTJ19GQjqSNcUv0cevfwYMxvimTh6oC0yPn3prRXCzL5Xd6ssSW9ISlIpu1
+etbhkvP1GNDKiAbH5uTZNIYMANbdOybfFHDUDWXHg0ObvXVLOhjH3OzdAORHKugS
+PPygnW4eXKt5R/uDRW/B0aUWLDtuB2Uj/+YQoA6Bm2AD75e4BkW9tRTqNBXOCCtk
+onvyAVJC4NoBZZQBRaOMBa0FIIxrjPLS9zmlyLehLjg5vjwjbAECggEBAPKmWUhO
+0HmwQ4/167CMwn+lIW/v9U1gQ1fvpBClFeJF/Px1AUQP+foajGxuXoMMnwH0fTrE
++ya2PA5ShZCkV+ajlBd1B8ymCRu1lp4ilCzEgjNU5U7nhJESCXBNRFEGFSjmO26a
+66sny4FiV7d6DeiJ36vPSn2BV0GezHedhbZBuaE+vVWahCXESsAqhAgejRB0A1uf
+sSyxXDaJl365J17jdO3YbS7p8LsovsK/Gfn6tTqxMNDnSJWgfFUPLOBznK64L1bJ
+RhW9HVrOeIrgV/l7mWEN/LFmWNMEcvi1E1oF08ZamjcwCmtBdGADaPcx07xQyJnQ
+6czKf52RMMcQK1ECggEBAMstD3JinyrNb71kb3fL0qlx3kO2HRsqbFVvuItd5SF8
+3/y6GuKmAikhmiZTx/DtGDGUIHD/ioiWkgswyoNKbuKcuDHklc9v3zk2A2Kac35c
+KXg//3keNouPLX/NEsu8haVas6GJhlvA+FxgtuNYQeCRMWzrzyCDBNf9KVnV8uV6
+/DBs7W306Q07im1MGMCc3P9Jl1cA0auyxOPD3mSnoDZfdzJmwNjrELSunM2BfzRn
+6h9Yi6adFkIgcEKg1a6/R6CyCTGGYs3QQ7+a+UoiNCXj99td5KWW+WMpSns6I7pw
+0bmiCb87lXrit1nn7Zgkk7W46jO6H/KyGgMQDs7b/bMCggEAC4S4AkWzgcNLQb8z
+w/q6lOKa1rx2UYj7SWZXzG55vncCDl3jhH6ZqDSwa8lFdUUZGzem5i5JmcnWyftC
+2d3jSlCDjCWDDETpc8ZH8xPDIujlIVirVfaJhXVsu0b/sjTutjVPpu11uHc4Itkv
+Psdtd5dr5bT+XTzRjoziOd9hZIh0LmJTDIg8M6rAvaSHBfelTJ2lNyk6eNume/RG
+G77gTpHPkCbQ8AQs6EWD4Ky/p+0twy58Gb6Q5IFsxYDl6XWzf1vA64a8a/XBOflJ
+IZaKto4WdtP6JdWs679vUb0OwRw7tFPCtFH0fKjrxE8FIY7c9TiEfUC8iHsoWat1
+vE1ZMQKCAQBBLl6TQsJvd+LOLsd39kLKK4az0Fv8GBsTOblJtMDKgoZVTNtNNRbi
+XS5X927ggx/M4AmcVs75zNxjjK6beiiiuMZ68yuAlhJWB1cErio5MpX3RwjNsXys
+Py3In2DVTdDOYI/aVgVtsDW9ZSWnP+w6gDoMCIa1lnLaXBSFBpdbOZ5oZrmxGe4e
+WaiqMcyLFofruo870T5yx/JUY8UYI5LJfsz9tWtO6/K7FH2njFDj3iaFEeITfLfk
+VQXOykxjOGhhTFyYr9VI0/S4Jp6tQtXaBg3BKZkt6oZtYpTLfbZynLkbxbk8yX/G
+Ia/Svw5BThK5LO6t05tmP+8KZn9pq7fzAoIBAQC/JwXRuUelycI7+tIXvcDX1ydu
+xASH6fyYcB1378KWNzWddcEb4NscfdRK4ekMO+oPyd40DNFl9ESXSUrxoILUIERV
+DywvQPMh+2sEblzDXvKO70BmSBSwq0tgfLSXpnu6nv+EHMRARA/qTk9R+Gl/REF/
+mH4ojpv2jHE50ysWFWvxK6whSG1/bMXBsT7YocR1TLBxZpaB1mVxUJ11ESKDMy+A
+lf79rIhGfU41mjzr4fkuYbERQy0yM3+lfG5qShAFAl52Fa2eFVBFso090+1TMhlR
+1ZmG9ZnE31uXoKU6OGcAGyFmvwhBIkjczH0z74CIYkD9gZJ1lW4RohgiZUja
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/client2_expired.pem b/reg-tests/ssl/client2_expired.pem
new file mode 100644
index 0000000..9d0d2e5
--- /dev/null
+++ b/reg-tests/ssl/client2_expired.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIIFKzCCAxMCAQMwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCRlIxEzARBgNV
+BAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMTEw
+LwYDVQQDDChIQVByb3h5IFRlY2hub2xvZ2llcyBDQSBUZXN0IENsaWVudCBBdXRo
+MB4XDTE4MDQyOTE5MDEwMVoXDTE5MDQyOTE5MDEwMVowQzELMAkGA1UEBhMCRlIx
+IjAgBgNVBAoMGUhBUHJveHkgVGVjaG5vbG9naWVzIFRlc3QxEDAOBgNVBAMMB2Ns
+aWVudDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD8JQiW54JVNyFa
+lPvw6skL0W5790n5R5kx10H4RUT3sIErV0K6Hvz/KwVG/jYb8yA7YSHZKYaklNis
+Zjpxj7tnKop7QwyWViXlbW1hRC+imsyO8PLrrc6YkLujKBmB1/z4523/opgNE0+m
+ROEjLIEB/nPHSPy57qdS3RdbCkQoBT/1fG8yyKbhcyHbL1Aj3Hk/553ZSgOo/Xl7
+HJ8wM+MzgkoSvPFGHn4WGckBEtiz9Fvt7v8RQJhMePjOXmDLdoiaRmeyhu0a8drq
+fg55s4LFbM58vW/pXAPyb6KzPFC1htFY+yBk2l5s4JpggNuvXEJIiP+9COY4D/oy
+79mMxZXWY/6VY5NQu54LN6vt24q9pBtaF6OjsaXUz4ZW5pj8Qpej1uXS8N69jgy1
+3CR4kFDb7pa1roe9zXq14h64kpoLA86Y17B3rRAIkIDGf/LdwL1il92Jdcl+K4g4
+YycbWCzgNb4whgokfYGfwsVV01SG1+19h+Nsme5hYROQmYbCbC94lAWJD/U/7EUN
+6KN4A7WgCxTt7Vvz2GSEE+HU/WVO+tfgxOPs40M5R3D2LKC0owEyXqkFxAANstd3
+ky6KZfkVQP0U+iz8m54o5HKvoF6EAzEHR/l2kPNCBj/hhyYGi44SwjXEOdzOcJVM
+buA7Hp2U4eOhoAJ/VoWJhY2gntcQJQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCl
+h7ITBQcBe/Rhc7Q7YE/1Sr9duVrUAUgS5bO1xHzqlBeUxPXqQhBMYBYLnvoVdJUz
+Hk/7JgvuTgQWUHHabSmKiQ5ug/8sRJSJpOavWelJW+gKaBbMUDZ2xiVYsVXJSmCk
+RpvZV+Gb4Q3JRPxkz7+KddB8FnvPYg16LyfoRKk5aVPD4vjT3ePgFZRRLY2w6BH3
+tQFB/xjCTLyX6Bhu+fC37S2N/+a+i7/vEpcOcjKpqkE/Kvb9W5Usjz9kIy5ceq6h
+i0t6FfYVcpwO6ZCSB6DT9OnzbdzPbYILdYhpCua5i64YS4cSaW9ltFvsTMDy1Nvm
+VbRh3kEtrkywXa5XmYbQE1Zm56jc7MIiyQRLBS60/SA5IzFQFZQh/NDzysLlvDMf
+vdExBQ5HJGKje+GN9deYoN3WXKpK+Qik6YZ3cVKMhBD6hYTM/da/4A6XGJEKvARy
+21rreRz/D3YMwac1/b5DPViU/6pXMGKud9/ZtlkEi7NHdzVJHMl/htcVQGlTnZdK
+Q6Yeug24JbnBZxIbhcp5aaJ+rzQeswL2SkWpF4td87Ku0gFEBShxG4tQNtfvewML
+XFybPNAkKOhR84K2rdMKwjva7vxm3tI22wll6LTVP8YUd1SS3/t9yL4jWxHX4Ds8
+gxdxuGWjJe6zm9R6OX63sv0meKNUsesmKQTpdu/gLw==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEA/CUIlueCVTchWpT78OrJC9Fue/dJ+UeZMddB+EVE97CBK1dC
+uh78/ysFRv42G/MgO2Eh2SmGpJTYrGY6cY+7ZyqKe0MMllYl5W1tYUQvoprMjvDy
+663OmJC7oygZgdf8+Odt/6KYDRNPpkThIyyBAf5zx0j8ue6nUt0XWwpEKAU/9Xxv
+Msim4XMh2y9QI9x5P+ed2UoDqP15exyfMDPjM4JKErzxRh5+FhnJARLYs/Rb7e7/
+EUCYTHj4zl5gy3aImkZnsobtGvHa6n4OebOCxWzOfL1v6VwD8m+iszxQtYbRWPsg
+ZNpebOCaYIDbr1xCSIj/vQjmOA/6Mu/ZjMWV1mP+lWOTULueCzer7duKvaQbWhej
+o7Gl1M+GVuaY/EKXo9bl0vDevY4MtdwkeJBQ2+6Wta6Hvc16teIeuJKaCwPOmNew
+d60QCJCAxn/y3cC9YpfdiXXJfiuIOGMnG1gs4DW+MIYKJH2Bn8LFVdNUhtftfYfj
+bJnuYWETkJmGwmwveJQFiQ/1P+xFDeijeAO1oAsU7e1b89hkhBPh1P1lTvrX4MTj
+7ONDOUdw9iygtKMBMl6pBcQADbLXd5MuimX5FUD9FPos/JueKORyr6BehAMxB0f5
+dpDzQgY/4YcmBouOEsI1xDncznCVTG7gOx6dlOHjoaACf1aFiYWNoJ7XECUCAwEA
+AQKCAgEApmQJ9wtvhqyK5ivK1oEZiyL5tfTKbCZDghB7CEst6AYiN2euMQSiEAFj
+yiWXr3oRmx3OKHCu2Y0gLySHDMm88aexGwZ0GAFhoLVFqRpGFRfyRaHbrItV+ngI
+WvLrYjQWTGrsu/WgQYCs3xw1NfD4cUhpPul7XXeQE66y6vEraP2N54HmH60p8zz2
+6p2eVQv5N6KxF+Mv5yTeNc/9fOHA3QzttM/aqFsW+Z6qdnrpZlerEqjUyZ3G4zAx
+gH3ngl0GaEhtxfIkJdPUk0n8Y3OCqKXU3Zxlbam7MRFaXM1AtYnyPLX7+pHgHhlZ
+xrVCQ8auNw+xNB3bTsO8aEC/X5ZD+ZdO/NCbhzEXPdx5XF6LDlB9uthC/i5G79DB
+5DK3GsrPjFjmeY3gmvKm5ikiLNiAvMqghIrKKdLhMJe/AfUTkwVh0Hh5St1o9zPT
+ZZP0sNIw+da5/qW2iB1uBdP8h6sdrZVOsfkY/fynny+wEkkP40FAVRHH42p/evRY
+qLu0/4MVUjHEgkC0G2ZLFw3n2Eq5omwH2/4u2xzN8W83+kMdBj8gB3qNFjFXLba0
+Z9izOc8xcFsvnmjWIIZ1RZsby0DqefVSfYuc1ON4qOA4hiZZNywS8Uk52i/+7MDi
+Q8eGOdUzFPmM6nTPwMLFspzzLTiflTvGDeEITJO5/DFa2ZWf3AECggEBAP/QNzTx
+vxOw3nuEvoa/4yp7TBSwvw1V8KC9298IQ3wIqNs699wNqxADGz3P3Vb37psPyBmk
+oOclX7we8hUpP0F7OIQo5oENcRu20fMY9Lvbygr8T9j6rxUj5CCqO/HqiD07J59w
+4/DE3kuzG6wLV+Tbuo+kV1ywNT3NnjzjFgaZYVjp1P71uUQfsg9ccX8N6jaF5LTn
+UTxMAxxvLJ+7qk+4OmFDLZ5y3LiCyezdsCUQeEcHf3VQKNdafkGSmMlpyIpa7D1b
+CLfJcR/UOYMezNzuHlLc18pxATOzbCLLZFmGfhdgI4SnNFpdqGk/tUAyiQrN8tTv
+JeSDi/usSQaZXlECggEBAPxUIfMCaSPpG8KM8gHgp7yz+KqY+cErD4Oh/8j9do/x
+0JkO2sV8EMSuD/alKzbN9O40KCPPw8unnlvkKE0C4of0ugzRg/e1L8SpOYzKWX5J
+zMjO6g7m+QU2kKwrcrjosmaWH32ANPY16fUiUOyl1Md7U0trYFRt9+4eqNdlej9j
+xMql9KCii1SrGrrh1sTzluvkIedqKYB7S9nv/z5diTm4F+IiBXU536YfJXJ16LpM
+aJE8+yECnV8x5Hr9LEGayjRtxIvLObBrCyeVDCQSXT0fB9sAL+gr5baFO6fq3kS0
+pk2hCkx+r0lqwIOOd8guKSIHIpTFOmd0x2RtoxNOu5UCggEBAKL76yCdYYtPJgD2
+i1lZCof3dHq+PYtmlOAk9uA76Jsu/T5obKDUSIf0IrgYJsKRRoGe5XOJE0cR5dP6
+t2xKElZLBrAVSv8wLD9nFI7Y+Jx0JV8ocEsjNMe4TVGOhJDWR6UTemQ4TdIJ7EO4
+wsmzlHVolY2NPGf+kH1m8wmB+XeM45v9p0omDrrbotvsnxc/K1k/p36m3ngXSegk
+4P6IV7NhAjkTzw3jysL3+WUjvWVv/+HpYgjBYLQMoOJwX038StvzoA5bYMuP2bZY
+xafHyOh+Ae3zbL07kHN7PktQ4Qe1C8Mi6p5K1a05fOJJx9Y2HGA45R1LnQ3hzh80
+HnbI4nECggEAPGu0+WixXnz6PbrcVGDEKaZ6u/cHjx7NhzqqcilnU46W4Z+x+Sn9
+Jet8PRZN48CrjsKEfhbJDqIjhGN81vwC3IVYa6tby1vihVf0ROdLSLdJRyhs2Yar
+SHlJaUC6JtbpqTD3d2jUxcQhMqa19AS9j8rTJjMfDPiMLsO+sF1HSZiNTe0xR6nE
+bVDPhMKBWAXwNKobCDveljpv7k7OstNZAa44Ydi9r9Vc3X2FzQO456tWOrj8dWoX
+3uymhmDLUSZMlwNV1heix8DKGf9Rue1/0Bv3GJTR4+lnBy6eG1ZdRNxxGhOe0LRh
+KtZaJOZfflq3VMOanz8e/hjzifPK4duvhQKCAQB8Mo5dWvs5fCpWAQrNqj+ua8gY
+a8ftp7R+idGGgOLSCUArjY7sS1RvZzCB28I3/5QpAuEEhaLFTABNonhbD5MdB5SL
+xVxfXqcW/WfXkGF+QqB1AMXpE4zLeGSRERWpWJSaD7B2I8UdS/Leo3lVchvA66qx
+SG+Pojcp5DsoZP3hrh54fsPdGorzezoTIwfQtsy3P8DnzPohjzbqDKmloglDbo4Q
+rBuJVs/Gc7UwZGvka+roi6VUaVdRa5PAluCE4GS9HSwf31k74jw3TfYVIlQgL5Yi
+kIHsC0yXfJ0FPXiw62CMEr51ssX3QNGTIKfos24smCjK09eInNZpIZm+p+SL
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/client3_revoked.pem b/reg-tests/ssl/client3_revoked.pem
new file mode 100644
index 0000000..0aba2ce
--- /dev/null
+++ b/reg-tests/ssl/client3_revoked.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIIFLTCCAxUCAQMwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCRlIxEzARBgNV
+BAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMTEw
+LwYDVQQDDChIQVByb3h5IFRlY2hub2xvZ2llcyBDQSBUZXN0IENsaWVudCBBdXRo
+MCAXDTIwMDQyODE5MjkxN1oYDzIwNTAwNDIxMTkyOTE3WjBDMQswCQYDVQQGEwJG
+UjEiMCAGA1UECgwZSEFQcm94eSBUZWNobm9sb2dpZXMgVGVzdDEQMA4GA1UEAwwH
+Y2xpZW50MzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM+8CxcAKMMh
+BILdtSx27Zjlri9ygpI55eW94t9rYb967glYbF+ZGZ2LiqXHzIpabasquXD/oH9l
+fQpaeb498ZUblbVb0OPyVkSSBpt4y0wPBYYiUGU7T94drdMwEySIyMEIRNlfXePB
+EQJLbdksdFBu4QCQEzzdL3aMBCogFfN85zJ6WJhDHnkbtKdUpKJ5irBB/3Hs+pMq
+I3Y4cdeWmFkJ+xQpu9oh0igAhkbSPYXu+asSCzExO4G4ttBnQQh4RYUUe+IqO8z0
+QQ/La+m+OBXWR1ti+/3ImeZWdRlA7xpTNYTOxAg0eO1FuUhwvw6Fpvo5KV7wre4W
+Qkmsjc5vpxubWWrdfSK/YB8jZJsdx2zgk8thdhj31Zhv0PUP64fhX03DKFSF+qNG
+0POpjPthu+f96umHfIFNNKiLPyWBpl0+ppI1FB8uW9xXRZw00iXl89bXNa1lbQqr
+c3cj893HUnEpx3H0Q3piEsKu0mchGXiVVJsoZgbLn6yOXDnkWBQhAFvvRcfrAzki
+w3f/gU+BiT05csRCXtmbL28RaK70fBxD6fDhGRHyMt/0MFhYnJRxmIB3GniQAgC0
+lyqyMOplkHVeHO0LhjrLTZwbbD083A/KRzpsrVLHd8sjCEjojae0tPDj65u0xg1R
+JrszrjO8ZNLQoXr2rl6hjxeLC2Yn08W1AgMBAAEwDQYJKoZIhvcNAQELBQADggIB
+AIzgQBfDxEdowxYsdZ4cb0wySg+xB11XRLeR9k6c1kExDkpTKRyAy+6CNS0X2mAz
+3v/jVoh3G4crlBkL7UJn6ycunuJ2SdiUexsJAOveVgPPml7YnRRfPW9ddM+gn0y/
+TtTB0D52XaXczeIqQKFD67OtjbVvObbrO1cITkh9q+mMtTO8T/V1gBRd1VH1YFdi
+nPqTYYA9QqJ2zAaufhZVCkpJJn5onpT5t+GBpe9O0lKlkQrduLzjr2rrfJCg2Uuw
+xBXwpvFdOK4BY5tDqVLb7BOLkEUUltWKTYd4IFjonRE6OSxtY+1L/RnEYMfSSVIf
+GsTkKugTuVSmmyFmh5H10YjwMMD3j36hjxJcGJDzZIuOQMY+2UKI61eF7StqZTXE
+wRj+JMHHRHIEw0181lHxGSCArWyLEoSn57NSAqJEzdhq7wb6eZwqZzRo8EJUSYIK
+3fLnfjSLHS/XaH9mCbx7VpYfC310UGzQ1QXSOIp1LtKtxUbT1YL7RTwa7GVfvQ0e
+9nsY9/qd9Oo2VJtxKQRsfro6Z/MdP97lUpTaigQEUpB7KICl1ks56oQrunRLXkO7
+EoDNlnDGkp8DghO+tPqx44OogbXBFCRTO7ncYxSE83WcG0UMUvVfGoMJKqF8V0n3
+LmKLNCvzLQ2Gt21Cp/zNiwHSjMNIIqybjAe+nVT4+sSI
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAz7wLFwAowyEEgt21LHbtmOWuL3KCkjnl5b3i32thv3ruCVhs
+X5kZnYuKpcfMilptqyq5cP+gf2V9Clp5vj3xlRuVtVvQ4/JWRJIGm3jLTA8FhiJQ
+ZTtP3h2t0zATJIjIwQhE2V9d48ERAktt2Sx0UG7hAJATPN0vdowEKiAV83znMnpY
+mEMeeRu0p1SkonmKsEH/cez6kyojdjhx15aYWQn7FCm72iHSKACGRtI9he75qxIL
+MTE7gbi20GdBCHhFhRR74io7zPRBD8tr6b44FdZHW2L7/ciZ5lZ1GUDvGlM1hM7E
+CDR47UW5SHC/DoWm+jkpXvCt7hZCSayNzm+nG5tZat19Ir9gHyNkmx3HbOCTy2F2
+GPfVmG/Q9Q/rh+FfTcMoVIX6o0bQ86mM+2G75/3q6Yd8gU00qIs/JYGmXT6mkjUU
+Hy5b3FdFnDTSJeXz1tc1rWVtCqtzdyPz3cdScSnHcfRDemISwq7SZyEZeJVUmyhm
+BsufrI5cOeRYFCEAW+9Fx+sDOSLDd/+BT4GJPTlyxEJe2ZsvbxForvR8HEPp8OEZ
+EfIy3/QwWFiclHGYgHcaeJACALSXKrIw6mWQdV4c7QuGOstNnBtsPTzcD8pHOmyt
+Usd3yyMISOiNp7S08OPrm7TGDVEmuzOuM7xk0tChevauXqGPF4sLZifTxbUCAwEA
+AQKCAgEAr48B6ExQJqhOwbJRHOTdY6woCx1BUAbyTbiudZawozxm0ysRW7FtvoFh
+iT1TlVFbAZ833VGL+F5y0D8qSCbddeA8I2sXHK1/TrACOX5agUropjV1sDfyBYsb
+jjFA3E1lLA2q8fHwzTwq/b91dGZnXlp2eR1JxNRA+nTWSCuZoY9bbIONQBDpPfy2
+LfwQrub82HzOPx/BnIGuOoj1XPd+hTE0KhQjF/QhQYE/+bZQHFKcWYEESGyNF9Jd
+Xb8FbP0H87IeCEMdCtcZ7RlDd+U0TPAsmgULZa0Us1850z/XUm4j+rsrXKvLzupv
+7dKrBMDbHvkUB5Jry5ywJMdZiK8/j0QW5bw9Hw8tEyXxT2gzXFze4DrEHC1cPLod
+3bcMOnp8axtdfm23tlFQuq4fGsERABFWByylF2Pu9KQ1AgH8/53IcVnNjd7Z4ZlA
+eBrZynEDg67sggFNRa6EnjAYFS0Zqgmfo/160awcGSLPLSkE5FhtByQOEzyAumXn
+UmDO4zlP3dc54WzCnxdS8GpbbmjniXGSDe9D16D0uWQeo+LCoDDTExT3wDjY3tDt
+R4VjIBVs1vWXH2oG/oC/ulgXwcSKSSvtqftcGnPj9EE6exNzanTQwCFHosYWl5Tn
+MkRsxNRFITKksNH5sXAkzwogWlyG4PK4ink0IixpYh2N0WgncDkCggEBAPqZ7z45
+F/YusR+eJdMrloECYhP1BId7zUzZglAUof/KJH86TVnQ/wGBcl6x9SXGzy5Mix8S
+q0qUILJ3nXkyRqFjlch9c8NlMR5P/IPyQWupSGNFPTIBvNefCfqosEFD9635P4ND
+sN19gwrx9IqMYgyPzw05G2CJPafemjF5NLIHKyhUnjGUij+D/WZYCKvHIrq085ac
+0dLncRvlnzloa8PCGXDXcTuFacVGcj6QZJvn7ZrprMOwpwh7RT02U9cqlLYKfjEg
+9xnTSmxbb8esRtWDlJPoj3+P732Cj7BynSpvLFSsvHAAFS4j/g3XIjdY/yocd0xG
+UfYDEcilgPiaEnMCggEBANQ1st6aaf/7oH4hJ/mgOc2RhGYxpjHgXs+ux77llHks
+o4VlAILV9CLQyQ9/3PU/4vidw/rseZwPmONDlBeU4319MQzjZeihaqfaTQXnQRBj
+xX2sJ/7EeBoq6Xlkvc+lVbiWcA7i97dYEumV/q7ozYRyAhP/D/VAdmT9ZWK3qJ+l
+/L7h43ch9PWGDOjiUIfWx/xAodyDUEM1iforv/S7D4+3j9BriRnhpZRLZT5bG24h
+vVN344ETxaoVWwjw9yzmJHF/4ooqJgdTsjJ8ujPPLvJKXAD7ZVerhcUpZWmyMACL
+Dj8IuNZeB7IN9LOCr1xPenR0jdltz7+LU6WTK0F4NTcCggEBALvxvOL+sMDaTc63
+rgiM1ShWIDZ1ePsfV15+dmQWxVRwRyUAFcj5nHaFnb/1WTUGwJUppOEeAEdDhq4y
+VXDyytP5OvmNVMfDWa4xMOHIS1YyNG73G6kocneH+FT8NIwOLHBW0VJh7wB+RExu
+IAfUtyhSpmd1X9nrs8j1gtD95Q5rn+t0YtwuWey+0cny1jX2eE5srY9Ud2zkVQkm
+El9cuA2twaTGf06zhRsF9WKEql/e9m1LOV3eW7dZtBjvaLujXLqWbgPshEXjGBri
+DJhE1S8GquSu8wgpa+TGiXs8yjBsBmRO1FhvR3M+XSgGI8w8u8naZYJX7tpBfRHt
+RiesbY8CggEBAKMcm14rBmryOIx6Y8Wl+Igf38rFQt93fKjZyULcKGFzhEUWO2xV
+lA/mt4SoXWhNMOK0MV1/woHII1YcLYpqsOlV/wvPBhfnapmWXDm7ZPF6HuTYHO3g
+ighjD451dshSZy84wu1OW+WbVv4gguBipQW2tA42sUdrwxUhCHr2fDAqX7lA25xI
+h4bpAKdIvWmMF6+25jMe4+SlFGcslaFA31cyWuJypbE1FhaEVU+2q8DdQi8UKdSf
+JAH15EFdJkBmrYBoMfLNLunW1VOlN2J3T7iAm3NNyLm4Z1wC06aIhgkE2XBt/dUX
+9YZQ39PTEYM8u/0jUZzcoSCzsRnFoyvxf8UCggEAblaNsi8/Nx8FId/aW3klrIxY
+UfSVXL3InIkrr2hJs7GYMpdWRwQZpo+Nv45cBnGoRwWWIsYkcWEbrs1vxvkNg0Ro
+pTa0Pt5gb1u7BfvpSqC/VyFBd66BcTQeJqTUHzWPKhMyCrP/eRYfFFQtpy5EZ+/O
+cjEVO1Tv5VhqM1PtANHdsS6o0jKMWFQ/Ofu4sOp6hQl4E1oOAzjLdtyBgJzSk1Jg
+M1lKPzSpYgRWcMB9CFTE2JO/4b+iMhxQjvGtD5nkeA6ZD7DSDItH6lhAQsho1pMi
+uoFlxDSFYHt0KcFp9zMrB2810mmNvjiEaqVXkA7QRH3XCA0BTkIXxzQe5QgTNg==
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/common.4096.dh b/reg-tests/ssl/common.4096.dh
new file mode 100644
index 0000000..8db27ac
--- /dev/null
+++ b/reg-tests/ssl/common.4096.dh
@@ -0,0 +1,13 @@
+-----BEGIN DH PARAMETERS-----
+MIICCAKCAgEAvpZPDLMGhdop4RgoSzRJfr681WeWplvSvBsIqyKB8D3uNPZchSg7
+Aci6+yupRDtVeaLMmJgqjiTb9wYXhJNxyuVMPfnFrYwGSm32OUcMhECD6N2elOj5
+WS8fvoYIMvnENyDsutmBObXISKLxeaY+PJEbwyEeJmdzEV6oM0qM/2bEJcgQ00p2
+V1Nge6OZpjahlRCpKHsQAIgtUpchZVTKZCrO9WbYUPVYcUIAmyNLmTlPmM08EcsN
+dJqkhse0xZN2isnGJybe1ABIW8D31aWqfWhjmuNqe9JTqz8BS00WOeKGYiEENIIF
+lHmU1uKYm+9ii1stT7WyrtAMRjbQSVsye9CEkne5jsQuhF2gzLMFhsEwE5svDBn9
+CeJC7V0WHef0kHNUSm9yzRQWFp4Y9sJI7Uq3Po1xOBBCDUQnDJTFsNiJSF84gYGo
+fvjMsvf3mLNkDE12g3trHUMjrns4MLpla21bA3FKEqyfUuR/yYQRtLOkR7sxF4+J
+lporo7jHhgPK57euhG8YLOgSEa0LIYXsNSHI7yDpkXFmwtPBQRE5ZOaN4mw1fsHp
+/+adsUAh30KDeoXXyZg9dpZFnq/GZlAHdiO48oVsYnloNNYwrPH9bU53u5oj73bo
+CTCZOb7V2BvfvnfwNmzwuofXMFXBvNqDSKcM3rkMSi3OomuHBZ/QQwsCAQI=
+-----END DH PARAMETERS-----
diff --git a/reg-tests/ssl/common.crt b/reg-tests/ssl/common.crt
new file mode 100644
index 0000000..1f0c275
--- /dev/null
+++ b/reg-tests/ssl/common.crt
@@ -0,0 +1,90 @@
+-----BEGIN CERTIFICATE-----
+MIIGeTCCBGGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJGUjEW
+MBQGA1UECBMNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxEDAOBgNVBAoT
+B296b24uaW8xFTATBgNVBAMTDE96b24gVGVzdCBDQTEeMBwGCSqGSIb3DQEJARYP
+c3VwcG9ydEBvem9uLmlvMB4XDTE2MDExNzIzMDIzOFoXDTE4MDExNjIzMDIzOFow
+gb4xCzAJBgNVBAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJhbmNlMRowGAYDVQQH
+ExFOZXVpbGx5LXN1ci1TZWluZTEYMBYGA1UEChMPVE9BRCBDb25zdWx0aW5nMRcw
+FQYDVQQLEw5lUGFyYXBoZXIgVGVhbTEWMBQGA1UEAxMNd3d3LnRlc3QxLmNvbTEw
+MC4GCSqGSIb3DQEJARYhYXJuYXVsdC5taWNoZWxAdG9hZC1jb25zdWx0aW5nLmZy
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnb0BDF7FsqzslakNg7u/
+n/JQkq6nheuKwvyTqECfpc9y7uSBe/vrEFqBaDSLQagJxuZdL5geFeVtRbdAoB97
+N1/LZa6vecjjgGSP0Aag/gS/ocnMRIyvlVWWT9MrD46OG3qZY1ORU1ltrVL0NKtt
+JP8xME7j3bTwIDElx/hNI0n7L+ySkAe2xb/7CbZRfoOhjTVAcGv4aSLVc/Hi8k6V
+kIzdOEtH6TcghXmuGcuqvLNH9BuosyngKTcQ8zg6J+e64aVvC+e7vi94uil9Qu+J
+Hm0pkDzAZ2WluNsuXlrJToPirWyj6/YdN6xgSI1hbZkBmUPAebgYuxBt6huvfyQd
+3wIDAQABo4IBvzCCAbswCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUFBwMB
+MB0GA1UdDgQWBBTIihFNVNgOseQnsWEcAQxAbIKE4TCBsgYDVR0jBIGqMIGngBRv
+G9At9gzk2MW5Z7JVey1LtPIZ8KGBg6SBgDB+MQswCQYDVQQGEwJGUjEWMBQGA1UE
+CBMNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxEDAOBgNVBAoTB296b24u
+aW8xFTATBgNVBAMTDE96b24gVGVzdCBDQTEeMBwGCSqGSIb3DQEJARYPc3VwcG9y
+dEBvem9uLmlvggkA15FtIaGcrk8wDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg9j
+b21tb25OYW1lOmNvcHkwCQYDVR0SBAIwADBIBgNVHR8EQTA/MD2gO6A5hjdodHRw
+Oi8vb3BlbnNzbGNhLnRvYWQtY29uc3VsdGluZy5jb20vb3BlbnZwbi9MYXRlc3Qu
+Y3JsMBEGCWCGSAGG+EIBAQQEAwIGQDAxBglghkgBhvhCAQ0EJBYiVE9BRC1Db25z
+dWx0aW5nIHNlcnZlciBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAgEAewDa
+9BukGNJMex8gsXmmdaczTr8yh9Uvw4NJcZS38I+26o//2g+d6i7wxcQg8hIm62Hj
+0TblGU3+RsJo4uzcWxxA5YUYlVszbHNBRpQengEE5pjwHvoXVMNES6Bt8xP04+Vj
+0qVnA8gUaDMk9lN5anK7tF/mbHOIJwHJZYCa2t3y95dIOVEXFwOIzzbSbaprjkLN
+w0BgR5paJz7NZWNqo4sZHUUz94uH2bPEd01SqHO0dJwEVxadgxuPnD05I9gqGpGX
+Zf3Rn7EQylvUtX9mpPaulQPXc3emefewLUSSAdnZrVikZK2J/B4lSi9FpUwl4iQH
+pZoE0QLQHtB1SBKacnOAddGSTLSdFvpzjErjjWSpMukF0vutmrP86GG3xtshWVhI
+u+yLfDJVm/pXfaeDtWMXpxIT/U1i0avpk5MZtFMRC0MTaxEWBTnnJm+/yiaAXQYg
+E1ZIP0mkZkiUojIawTR7JTjHGhIraP9UVPNceVy0DLfETHEou3vhwBn7PFOz7piJ
+wjp3A47DStJD4fapaX6B1fqM+n34CMD9ZAiJFgQEIQfObAWC9hyr4m+pqkp1Qfuw
+vsAP/ZoS1CBirJfm3i+Gshh+VeH+TAmO/NBBYCfzBdgkNz4tJCkOc7CUT/NQTR/L
+N2OskR/Fkge149RJi7hHvE3gk/mtGtNmHJPuQ+s=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIJazCCBVOgAwIBAgIUWHoc5e2FUECgyCvyVf8wCtt8gTYwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA4MDQxODU4MTZaFw0yMDA5
+MDMxODU4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggQiMA0GCSqGSIb3DQEB
+AQUAA4IEDwAwggQKAoIEAQDARiuHkhrnf38Md1nxGDSneJfwv/QksdNNMNTJBdjg
+OVmaRCIAyz43oefTWDQ/TebbSwB+Lg9pud1zadGWhlZRhCgBPP8JDMhIKH4eXIRk
+5IIa8WD08EwvSlqJL0r4gsMtVsxy7BZHAkka/2Ket9pyGt4kG5n75RFdc6BI80/8
+RwJt/MDxPrcVBAT7LnCluxQpyya9mZCabj7l+9a2yU2hgWS6QqfZJ133krkP/MMh
+AEQkSoA4mmBwWk9yPqXmUqiOi7v6iLkIUEh5SgYVPRk9BtU/kDaUdSwuqRrpCZo4
+SsWZWFLxBmLHkSh+G+BWjCVYMQr2ye7e+VMT/20+5xAfq4fj9n5BsPcx3QcVuTof
+RAc/Oygnt4MYnIcUb7zRFvCAvgpUHL7BnEn6nhyXjHJGqGDchsg8m9t3v/Y3ohq+
+qmrSzdeuylE1n3W5aWJlbFmyXegNP45MJ0xicesVrXEWF7YD/ir9mGJ8bQYr4blf
+77PrbF02komC6AzVPKOJa0jR+eW1wErzYlkYgez6ylBWCiHJd1dhEHlK3h2rXdYa
+Gnb45ILCLpEDjNEUrHifLLNXwqJpgZQsJU6BgMgk7ZgBfAKrCfTeg0rkCqCAPeVb
+8eSLf7FBF7YBRJ5P6u8qXc4RtgEu607GaWV0gIMfyVBY52oV+OaNsEdFetrJnp3c
+friG8vJ+7jdq6zjUCGgnfUIHoViJPh3JuFfhA3jT0gQDKW5PeI7dxhrNvlqdYfHI
+fxX7Y1/J6cTQkqJ1cai2f0bwJIJiTAThNbG+zrtjJ7fZ3wJ4udyU/IKrwShqtmTb
+1Ofj0tJDdwOH8i84vIySLUvR9aAb7ClFlnsx6rzwOxG90W7C0LA2M0EHm4FezJm/
+FfujnZwEWr1T9Wki6qE0MHCbdN/TTDws//EKkkE44FC+amL96w0IQl70vpE37j2A
+zlDWvFFID95SIxfmpkwWDvXDKv6gr1GMLeysCl2fgpY05Xidw5cEo9/tEkuWn/dG
+x/D9hnLBGeroA0251ES12jemqDjI2U0tfaeHakjwSsoWElf94Qmuh2iPZ+1zIxQs
+7o6nAWN8X9hfsmrDTTHlww0TEfrjlbzG5Yh+0ZRxmejgiUyOCXck+eh/ZXMXvfWh
+y3CorIIuWgkRjm80PYkdaRDJdZuyP6R7tXfTXNVzAiSQf0Qx9ru2KB2Fs/XZPamH
+KjItAU5Q6msIVvaRMS0muQgV+b6hqSEBzqXqJfAlpVLHXr5FqK+U7EB9y02B6piB
+tAmxqXP8OOCoQql6/vgIcrDFUOo6KtGBW36ef74XE3KCUVaIzVJZSIt6i/Vi0bZj
+bAjsJUQ3qDlHdorv9TRVOhnC1GUz7SuYnpEOyiXmyx3LAgMBAAGjUzBRMB0GA1Ud
+DgQWBBQ62csZcH/meQcENHhNbqz9LMzwjjAfBgNVHSMEGDAWgBQ62csZcH/meQcE
+NHhNbqz9LMzwjjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IEAQBA
+wLsGf3R1+/I2zQE+lsj7RasZtA/Cos92iEGDAPvFbx9e+roG8Gg8KBsEJu/HN0JH
+lMMiQ8dDRHSBMvRBENL5/57oOOhmqc+1u5sazLuANhzAYPZG17Klib7YpEwWoXar
+FDDiJYtCyLW0oNLpCswYopWK9GC0RJNucB0NFvOxehJ2sP2/fxGBQMB09L6mjKjd
+4KsOzyd3dNf0VYS6jB+/1pcKSHKQUo9HRHB5FK04PsYHoh4AtmEHvmYQKcWWidgU
+v26ftlH00ERzuW2juqBbz9mghlNRqXi0IyZ9b4tSj29dxW+WWFzo7j2zEPaD6z2W
+DEHq7zvON+g+q6qLgWeszqMgJzjvWjMj00E/t06PoHPiz/cAnDKEqp+ZzxCIFrxj
+/qneChpogDWyLbawhyyzbZvbirx5znOSbWjPZgydqaNEFViqbxwinBx4Xxabo6XN
+TU020FuMWmgfbIcvtgjKgyKqc97l7JMNNm7LQV9+9W0U5zdIqQKLZ9MMrd2w3xh4
+MAB8NKnwzHReK0TWwUU9HSgFAGdEX6HnyZ3bQ13ijg+sNBRMEi0gBHaqZKDdyoft
+B2u2uasSwioV48dbSIcHl+rTBKxiMh5XQ7ENnaGOJkjsIqTVzizqnPHU8eMBnSbb
+dsXlamROYII44+j3Ku6OGt51w86eGk4VxI3tmaECcJKqTkwUFD8AcNDrkjtmLuxK
+12yjnoM+u1cclfqQ5NOtRc6MJZ27jCobfBBhVdKVDp4X1WNyqGlbsU5adDAzknuI
+GT7MJO7lGjkZX2n54BNPSfrSknYMOVYcZqL0Dbcrhx5IyEmg+iOlOu1HO1tdnZop
+ej4vT+1V2w9Sa4Wo3UCo84jcm5v/4z7jCYh4BRQ60CFb7GLxZoqXIslcGSPool3n
+jl8JWoaLXrJUPfZGXo1iAlayJ5EiMyZl4eB/TBUf6TMm8vLvsPiUT+CEsjLppOdS
+eYppZAZ6H1JrJGs5kKBdOJHGn6Pkp5QsHIswOBd1HqHrBbYbZmDaDLRHduILWLrM
+e0/IfDdeXB/bKfmZoEpT8xRiauw15p0AHLumiK7KISAehfgBqUnxx+YmgGoZ7EWX
+KnMYAfCuC6oJ1DL0gp4Z9yMK1eu+GV1sLxPq9ZruEHW1R+H+4sGyiA5Gso2tgB6/
+XW//wxKclNp5LZR7hqfs/kGuh5asrJrnEbMwWn2+tr/LqfYtYh1D6nHfIXpT0o1d
+rNy/HrsKnRDMWxjm03r4hCViuNVD3Zb9anAF/NSPDVu8ATM5JbJNrCYX4eipz6ZE
+aQBkwIBkTPgtgP4r8v2G+uMYDw8nq7xh72FK107aeTTwc6MgU5jfeFNMr2XJisJd
+lSem1ngKYQSEzjVsTE4c
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/common.key b/reg-tests/ssl/common.key
new file mode 100644
index 0000000..4b06553
--- /dev/null
+++ b/reg-tests/ssl/common.key
@@ -0,0 +1,28 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAnb0BDF7FsqzslakNg7u/n/JQkq6nheuKwvyTqECfpc9y7uSB
+e/vrEFqBaDSLQagJxuZdL5geFeVtRbdAoB97N1/LZa6vecjjgGSP0Aag/gS/ocnM
+RIyvlVWWT9MrD46OG3qZY1ORU1ltrVL0NKttJP8xME7j3bTwIDElx/hNI0n7L+yS
+kAe2xb/7CbZRfoOhjTVAcGv4aSLVc/Hi8k6VkIzdOEtH6TcghXmuGcuqvLNH9Buo
+syngKTcQ8zg6J+e64aVvC+e7vi94uil9Qu+JHm0pkDzAZ2WluNsuXlrJToPirWyj
+6/YdN6xgSI1hbZkBmUPAebgYuxBt6huvfyQd3wIDAQABAoIBABojc8UE/2W4WgwC
+04Z82ig7Ezb7Ui9S9M+S4zUCYHItijIkE4DkIfO3y7Hk4x6iJdyb191HK9UdC5p9
+32upS9XFPgM/izx3GZvxDhO+xXbSep7ovbyuQ3pPkHTx3TTavpm3GyvmcTKKoy4R
+jP4dWhzDXPdQW1ol3ZS4EDau4rlyClY6oi1mq9aBEX3MqVjB/nO7s2AbdgclAgP2
+OZMhTzWYR1k5tYySHCXh3ggGMCikyvHU0+SsGyrstYzP1VYi/n3f0VgqW/5ZjG8x
+6SHpe04unErPF3HuSun2ZMCFdBxaTFZ8FENb8evrSXe3nQOc9W21RQdRRrNNUbjl
+JYI4veECgYEA0ATYKMS1VCUYRZoQ49b5GTg7avUYqfW4bEo4fSfBue8NrnKR3Wu8
+PPBiCTuIYq1vSF+60B7Vu+hW0A8OuQ2UuMxLpYcQ7lKfNad/+yAfoWWafIqCqNU9
+at0QMdbW6A69d6jZt7OrXtleBsphCnN58jTz4ch4PIa2Oyq46NUXCvUCgYEAwh8t
+G6BOHOs3yRNI2s9Y9EEfwoil2uIKrZhqiL3AwdIpu5uNIMuPnbaEpXvRX6jv/qtL
+321i8vZLc31aM7zfxQ6B4ReQFJfYC80FJsWvcLwT9hB9mTJpLS4sIu5tzQc87O6w
+RtjFMom+5ns5hfPB4Eccy0EtbQWVY4nCzUeO6QMCgYBSvqqRRPXwG7VU8lznlHqP
+upuABzChYrnScY+Y0TixUlL54l79Wb6N6vzEOWceAWkzu8iewrU4QspNhr/PgoR3
+IeSxWlG0yy7Dc/ZnmTabx8O06I/iwrfkizzG5nOj6UEamRLJjPGNEB/jyZriQl7u
+pnugg1K4mMliLbNSAnlhBQKBgQCmYepbv260Qrex1KGhSg9Ia3k5V74weYYFfJnz
+UhChD+1NK+ourcsOtp3C6PlwMHBjq5aAjlU9QfUxq8NgjQaO8/xGXdfUjsFSfAtq
+TA4vZkUFpuTAJgEYBHc4CXx7OzTxLzRPxQRgaMgC7KNFOMR34vu/CsJQq3R7uFwL
+bsYC2QKBgQCtEmg1uDZVdByX9zyUMuRxz5Tq/vDcp+A5lJj2mha1+bUMaKX2+lxQ
+vPxY55Vaw/ukWkJirRrpGv6IytBn0dLAFSlKZworZGBaxsm8OGTFJ5Oe9+kZTjI9
+hvjpClOA1otbmj2F2uZAbuIjxQGDNUkLoifN5yDYCC8JPujHuHmULw==
+-----END RSA PRIVATE KEY-----
+
diff --git a/reg-tests/ssl/common.pem b/reg-tests/ssl/common.pem
new file mode 100644
index 0000000..206e417
--- /dev/null
+++ b/reg-tests/ssl/common.pem
@@ -0,0 +1,117 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAnb0BDF7FsqzslakNg7u/n/JQkq6nheuKwvyTqECfpc9y7uSB
+e/vrEFqBaDSLQagJxuZdL5geFeVtRbdAoB97N1/LZa6vecjjgGSP0Aag/gS/ocnM
+RIyvlVWWT9MrD46OG3qZY1ORU1ltrVL0NKttJP8xME7j3bTwIDElx/hNI0n7L+yS
+kAe2xb/7CbZRfoOhjTVAcGv4aSLVc/Hi8k6VkIzdOEtH6TcghXmuGcuqvLNH9Buo
+syngKTcQ8zg6J+e64aVvC+e7vi94uil9Qu+JHm0pkDzAZ2WluNsuXlrJToPirWyj
+6/YdN6xgSI1hbZkBmUPAebgYuxBt6huvfyQd3wIDAQABAoIBABojc8UE/2W4WgwC
+04Z82ig7Ezb7Ui9S9M+S4zUCYHItijIkE4DkIfO3y7Hk4x6iJdyb191HK9UdC5p9
+32upS9XFPgM/izx3GZvxDhO+xXbSep7ovbyuQ3pPkHTx3TTavpm3GyvmcTKKoy4R
+jP4dWhzDXPdQW1ol3ZS4EDau4rlyClY6oi1mq9aBEX3MqVjB/nO7s2AbdgclAgP2
+OZMhTzWYR1k5tYySHCXh3ggGMCikyvHU0+SsGyrstYzP1VYi/n3f0VgqW/5ZjG8x
+6SHpe04unErPF3HuSun2ZMCFdBxaTFZ8FENb8evrSXe3nQOc9W21RQdRRrNNUbjl
+JYI4veECgYEA0ATYKMS1VCUYRZoQ49b5GTg7avUYqfW4bEo4fSfBue8NrnKR3Wu8
+PPBiCTuIYq1vSF+60B7Vu+hW0A8OuQ2UuMxLpYcQ7lKfNad/+yAfoWWafIqCqNU9
+at0QMdbW6A69d6jZt7OrXtleBsphCnN58jTz4ch4PIa2Oyq46NUXCvUCgYEAwh8t
+G6BOHOs3yRNI2s9Y9EEfwoil2uIKrZhqiL3AwdIpu5uNIMuPnbaEpXvRX6jv/qtL
+321i8vZLc31aM7zfxQ6B4ReQFJfYC80FJsWvcLwT9hB9mTJpLS4sIu5tzQc87O6w
+RtjFMom+5ns5hfPB4Eccy0EtbQWVY4nCzUeO6QMCgYBSvqqRRPXwG7VU8lznlHqP
+upuABzChYrnScY+Y0TixUlL54l79Wb6N6vzEOWceAWkzu8iewrU4QspNhr/PgoR3
+IeSxWlG0yy7Dc/ZnmTabx8O06I/iwrfkizzG5nOj6UEamRLJjPGNEB/jyZriQl7u
+pnugg1K4mMliLbNSAnlhBQKBgQCmYepbv260Qrex1KGhSg9Ia3k5V74weYYFfJnz
+UhChD+1NK+ourcsOtp3C6PlwMHBjq5aAjlU9QfUxq8NgjQaO8/xGXdfUjsFSfAtq
+TA4vZkUFpuTAJgEYBHc4CXx7OzTxLzRPxQRgaMgC7KNFOMR34vu/CsJQq3R7uFwL
+bsYC2QKBgQCtEmg1uDZVdByX9zyUMuRxz5Tq/vDcp+A5lJj2mha1+bUMaKX2+lxQ
+vPxY55Vaw/ukWkJirRrpGv6IytBn0dLAFSlKZworZGBaxsm8OGTFJ5Oe9+kZTjI9
+hvjpClOA1otbmj2F2uZAbuIjxQGDNUkLoifN5yDYCC8JPujHuHmULw==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIGeTCCBGGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJGUjEW
+MBQGA1UECBMNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxEDAOBgNVBAoT
+B296b24uaW8xFTATBgNVBAMTDE96b24gVGVzdCBDQTEeMBwGCSqGSIb3DQEJARYP
+c3VwcG9ydEBvem9uLmlvMB4XDTE2MDExNzIzMDIzOFoXDTE4MDExNjIzMDIzOFow
+gb4xCzAJBgNVBAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJhbmNlMRowGAYDVQQH
+ExFOZXVpbGx5LXN1ci1TZWluZTEYMBYGA1UEChMPVE9BRCBDb25zdWx0aW5nMRcw
+FQYDVQQLEw5lUGFyYXBoZXIgVGVhbTEWMBQGA1UEAxMNd3d3LnRlc3QxLmNvbTEw
+MC4GCSqGSIb3DQEJARYhYXJuYXVsdC5taWNoZWxAdG9hZC1jb25zdWx0aW5nLmZy
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnb0BDF7FsqzslakNg7u/
+n/JQkq6nheuKwvyTqECfpc9y7uSBe/vrEFqBaDSLQagJxuZdL5geFeVtRbdAoB97
+N1/LZa6vecjjgGSP0Aag/gS/ocnMRIyvlVWWT9MrD46OG3qZY1ORU1ltrVL0NKtt
+JP8xME7j3bTwIDElx/hNI0n7L+ySkAe2xb/7CbZRfoOhjTVAcGv4aSLVc/Hi8k6V
+kIzdOEtH6TcghXmuGcuqvLNH9BuosyngKTcQ8zg6J+e64aVvC+e7vi94uil9Qu+J
+Hm0pkDzAZ2WluNsuXlrJToPirWyj6/YdN6xgSI1hbZkBmUPAebgYuxBt6huvfyQd
+3wIDAQABo4IBvzCCAbswCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUFBwMB
+MB0GA1UdDgQWBBTIihFNVNgOseQnsWEcAQxAbIKE4TCBsgYDVR0jBIGqMIGngBRv
+G9At9gzk2MW5Z7JVey1LtPIZ8KGBg6SBgDB+MQswCQYDVQQGEwJGUjEWMBQGA1UE
+CBMNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxEDAOBgNVBAoTB296b24u
+aW8xFTATBgNVBAMTDE96b24gVGVzdCBDQTEeMBwGCSqGSIb3DQEJARYPc3VwcG9y
+dEBvem9uLmlvggkA15FtIaGcrk8wDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg9j
+b21tb25OYW1lOmNvcHkwCQYDVR0SBAIwADBIBgNVHR8EQTA/MD2gO6A5hjdodHRw
+Oi8vb3BlbnNzbGNhLnRvYWQtY29uc3VsdGluZy5jb20vb3BlbnZwbi9MYXRlc3Qu
+Y3JsMBEGCWCGSAGG+EIBAQQEAwIGQDAxBglghkgBhvhCAQ0EJBYiVE9BRC1Db25z
+dWx0aW5nIHNlcnZlciBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAgEAewDa
+9BukGNJMex8gsXmmdaczTr8yh9Uvw4NJcZS38I+26o//2g+d6i7wxcQg8hIm62Hj
+0TblGU3+RsJo4uzcWxxA5YUYlVszbHNBRpQengEE5pjwHvoXVMNES6Bt8xP04+Vj
+0qVnA8gUaDMk9lN5anK7tF/mbHOIJwHJZYCa2t3y95dIOVEXFwOIzzbSbaprjkLN
+w0BgR5paJz7NZWNqo4sZHUUz94uH2bPEd01SqHO0dJwEVxadgxuPnD05I9gqGpGX
+Zf3Rn7EQylvUtX9mpPaulQPXc3emefewLUSSAdnZrVikZK2J/B4lSi9FpUwl4iQH
+pZoE0QLQHtB1SBKacnOAddGSTLSdFvpzjErjjWSpMukF0vutmrP86GG3xtshWVhI
+u+yLfDJVm/pXfaeDtWMXpxIT/U1i0avpk5MZtFMRC0MTaxEWBTnnJm+/yiaAXQYg
+E1ZIP0mkZkiUojIawTR7JTjHGhIraP9UVPNceVy0DLfETHEou3vhwBn7PFOz7piJ
+wjp3A47DStJD4fapaX6B1fqM+n34CMD9ZAiJFgQEIQfObAWC9hyr4m+pqkp1Qfuw
+vsAP/ZoS1CBirJfm3i+Gshh+VeH+TAmO/NBBYCfzBdgkNz4tJCkOc7CUT/NQTR/L
+N2OskR/Fkge149RJi7hHvE3gk/mtGtNmHJPuQ+s=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIJazCCBVOgAwIBAgIUWHoc5e2FUECgyCvyVf8wCtt8gTYwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA4MDQxODU4MTZaFw0yMDA5
+MDMxODU4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggQiMA0GCSqGSIb3DQEB
+AQUAA4IEDwAwggQKAoIEAQDARiuHkhrnf38Md1nxGDSneJfwv/QksdNNMNTJBdjg
+OVmaRCIAyz43oefTWDQ/TebbSwB+Lg9pud1zadGWhlZRhCgBPP8JDMhIKH4eXIRk
+5IIa8WD08EwvSlqJL0r4gsMtVsxy7BZHAkka/2Ket9pyGt4kG5n75RFdc6BI80/8
+RwJt/MDxPrcVBAT7LnCluxQpyya9mZCabj7l+9a2yU2hgWS6QqfZJ133krkP/MMh
+AEQkSoA4mmBwWk9yPqXmUqiOi7v6iLkIUEh5SgYVPRk9BtU/kDaUdSwuqRrpCZo4
+SsWZWFLxBmLHkSh+G+BWjCVYMQr2ye7e+VMT/20+5xAfq4fj9n5BsPcx3QcVuTof
+RAc/Oygnt4MYnIcUb7zRFvCAvgpUHL7BnEn6nhyXjHJGqGDchsg8m9t3v/Y3ohq+
+qmrSzdeuylE1n3W5aWJlbFmyXegNP45MJ0xicesVrXEWF7YD/ir9mGJ8bQYr4blf
+77PrbF02komC6AzVPKOJa0jR+eW1wErzYlkYgez6ylBWCiHJd1dhEHlK3h2rXdYa
+Gnb45ILCLpEDjNEUrHifLLNXwqJpgZQsJU6BgMgk7ZgBfAKrCfTeg0rkCqCAPeVb
+8eSLf7FBF7YBRJ5P6u8qXc4RtgEu607GaWV0gIMfyVBY52oV+OaNsEdFetrJnp3c
+friG8vJ+7jdq6zjUCGgnfUIHoViJPh3JuFfhA3jT0gQDKW5PeI7dxhrNvlqdYfHI
+fxX7Y1/J6cTQkqJ1cai2f0bwJIJiTAThNbG+zrtjJ7fZ3wJ4udyU/IKrwShqtmTb
+1Ofj0tJDdwOH8i84vIySLUvR9aAb7ClFlnsx6rzwOxG90W7C0LA2M0EHm4FezJm/
+FfujnZwEWr1T9Wki6qE0MHCbdN/TTDws//EKkkE44FC+amL96w0IQl70vpE37j2A
+zlDWvFFID95SIxfmpkwWDvXDKv6gr1GMLeysCl2fgpY05Xidw5cEo9/tEkuWn/dG
+x/D9hnLBGeroA0251ES12jemqDjI2U0tfaeHakjwSsoWElf94Qmuh2iPZ+1zIxQs
+7o6nAWN8X9hfsmrDTTHlww0TEfrjlbzG5Yh+0ZRxmejgiUyOCXck+eh/ZXMXvfWh
+y3CorIIuWgkRjm80PYkdaRDJdZuyP6R7tXfTXNVzAiSQf0Qx9ru2KB2Fs/XZPamH
+KjItAU5Q6msIVvaRMS0muQgV+b6hqSEBzqXqJfAlpVLHXr5FqK+U7EB9y02B6piB
+tAmxqXP8OOCoQql6/vgIcrDFUOo6KtGBW36ef74XE3KCUVaIzVJZSIt6i/Vi0bZj
+bAjsJUQ3qDlHdorv9TRVOhnC1GUz7SuYnpEOyiXmyx3LAgMBAAGjUzBRMB0GA1Ud
+DgQWBBQ62csZcH/meQcENHhNbqz9LMzwjjAfBgNVHSMEGDAWgBQ62csZcH/meQcE
+NHhNbqz9LMzwjjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IEAQBA
+wLsGf3R1+/I2zQE+lsj7RasZtA/Cos92iEGDAPvFbx9e+roG8Gg8KBsEJu/HN0JH
+lMMiQ8dDRHSBMvRBENL5/57oOOhmqc+1u5sazLuANhzAYPZG17Klib7YpEwWoXar
+FDDiJYtCyLW0oNLpCswYopWK9GC0RJNucB0NFvOxehJ2sP2/fxGBQMB09L6mjKjd
+4KsOzyd3dNf0VYS6jB+/1pcKSHKQUo9HRHB5FK04PsYHoh4AtmEHvmYQKcWWidgU
+v26ftlH00ERzuW2juqBbz9mghlNRqXi0IyZ9b4tSj29dxW+WWFzo7j2zEPaD6z2W
+DEHq7zvON+g+q6qLgWeszqMgJzjvWjMj00E/t06PoHPiz/cAnDKEqp+ZzxCIFrxj
+/qneChpogDWyLbawhyyzbZvbirx5znOSbWjPZgydqaNEFViqbxwinBx4Xxabo6XN
+TU020FuMWmgfbIcvtgjKgyKqc97l7JMNNm7LQV9+9W0U5zdIqQKLZ9MMrd2w3xh4
+MAB8NKnwzHReK0TWwUU9HSgFAGdEX6HnyZ3bQ13ijg+sNBRMEi0gBHaqZKDdyoft
+B2u2uasSwioV48dbSIcHl+rTBKxiMh5XQ7ENnaGOJkjsIqTVzizqnPHU8eMBnSbb
+dsXlamROYII44+j3Ku6OGt51w86eGk4VxI3tmaECcJKqTkwUFD8AcNDrkjtmLuxK
+12yjnoM+u1cclfqQ5NOtRc6MJZ27jCobfBBhVdKVDp4X1WNyqGlbsU5adDAzknuI
+GT7MJO7lGjkZX2n54BNPSfrSknYMOVYcZqL0Dbcrhx5IyEmg+iOlOu1HO1tdnZop
+ej4vT+1V2w9Sa4Wo3UCo84jcm5v/4z7jCYh4BRQ60CFb7GLxZoqXIslcGSPool3n
+jl8JWoaLXrJUPfZGXo1iAlayJ5EiMyZl4eB/TBUf6TMm8vLvsPiUT+CEsjLppOdS
+eYppZAZ6H1JrJGs5kKBdOJHGn6Pkp5QsHIswOBd1HqHrBbYbZmDaDLRHduILWLrM
+e0/IfDdeXB/bKfmZoEpT8xRiauw15p0AHLumiK7KISAehfgBqUnxx+YmgGoZ7EWX
+KnMYAfCuC6oJ1DL0gp4Z9yMK1eu+GV1sLxPq9ZruEHW1R+H+4sGyiA5Gso2tgB6/
+XW//wxKclNp5LZR7hqfs/kGuh5asrJrnEbMwWn2+tr/LqfYtYh1D6nHfIXpT0o1d
+rNy/HrsKnRDMWxjm03r4hCViuNVD3Zb9anAF/NSPDVu8ATM5JbJNrCYX4eipz6ZE
+aQBkwIBkTPgtgP4r8v2G+uMYDw8nq7xh72FK107aeTTwc6MgU5jfeFNMr2XJisJd
+lSem1ngKYQSEzjVsTE4c
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/crl-auth.pem b/reg-tests/ssl/crl-auth.pem
new file mode 100644
index 0000000..af59d1d
--- /dev/null
+++ b/reg-tests/ssl/crl-auth.pem
@@ -0,0 +1,18 @@
+-----BEGIN X509 CRL-----
+MIIC0jCBuzANBgkqhkiG9w0BAQUFADB0MQswCQYDVQQGEwJGUjETMBEGA1UECAwK
+U29tZS1TdGF0ZTEdMBsGA1UECgwUSEFQcm94eSBUZWNobm9sb2dpZXMxMTAvBgNV
+BAMMKEhBUHJveHkgVGVjaG5vbG9naWVzIENBIFRlc3QgQ2xpZW50IEF1dGgXDTIw
+MDQyODE5MjkyNloYDzIwNTAwNDIxMTkyOTI2WjAUMBICAQMXDTIwMDQyODE5MDE1
+MVowDQYJKoZIhvcNAQEFBQADggIBAMPJgdU6bsFMFKBop0dngtAG1DXSrHo1XlYY
+J1uWEuVcNnimH1EHQXMmL5C26ALrHlQILLzq3RVcNZIT0tVF6jvcf8tzcaGeybS1
+tpDloE2A2jPz3Pf/uS4MB7eTPiMvY7cUl7fk4Oif/PjGPxdu+E5SP6HWVdjCvBHb
+2yye/KjN/vj3g5uI6z2l1Hxh2yzYmMVS8cTRG5SfUXgH+IXJOS8zE7CsMB/IRctQ
+TXD0q0iZLn7Q0liA/wxxJHYg2m3RdFa82THdWaqsIM4ao2KLz324ycQpWT0eRWpv
+6gyVXbEU/sX8HdZdNpfgQADiU8eK4XlnEmXehSE3TwyM1ysnoFRtOqDvaQrHbAMh
+Av0/9JLOPGDqCjof4lLfAW6JDtU55J4SxCYlaRj152939eXwDkb70WefZMssfqcw
+ZPDK6afY358kb7Yb0U2pE73+Z3VDcczBF085nc6q/2m5lvA+XwZYr4xBkVzHbdP3
+USEFd06FHlh2i2rpaiihR7sQx9KJ75ko3TjDbeg/QryMBKsS2CeJoHPDcFjjzFZF
+RW1HYReV1MZT8UEuskMvl+w57OYbfqf/pwhQcJTL8XE9PRtzntmLMofmiN/X5PQV
+YS6JvGVAIC7HFDiZ8Wn8B+WT93ecCNQL1FpIpo1JxuRfx6jTtGqGg65R3CzwbqUH
+dBkieO8E
+-----END X509 CRL-----
diff --git a/reg-tests/ssl/del_ssl_crt-list.vtc b/reg-tests/ssl/del_ssl_crt-list.vtc
new file mode 100644
index 0000000..70dbbb5
--- /dev/null
+++ b/reg-tests/ssl/del_ssl_crt-list.vtc
@@ -0,0 +1,102 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "del ssl crt-list" command to remove a line from a crt-list.
+
+# It performs three requests towards a frontend that uses simple.crt-list.
+# Between the second and third requests, a line is deleted from the crt-list,
+# which makes the third request fail since it would have used the deleted line
+# and the strict-sni option is enabled on the frontend.
+# Another test is performed as well. A line corresponding to the default instance
+# of a frontend that does not have the strict-sni option enabled cannot be deleted.
+
+varnishtest "Test the 'del ssl crt-list' feature of the CLI"
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 2 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ crt-base ${testdir}
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ http-response set-header X-SSL-Server-SHA1 %[ssl_s_sha1,hex]
+ server s1 "${tmpdir}/first-ssl.sock" ssl verify none sni str(record2.bug940.domain.tld)
+ server s2 "${tmpdir}/first-ssl.sock" ssl verify none sni str(record3.bug940.domain.tld)
+ server s3 "${tmpdir}/first-ssl.sock" ssl verify none sni str(record2.bug940.domain.tld)
+
+ listen first-ssl-fe
+ mode http
+ bind "${tmpdir}/first-ssl.sock" ssl strict-sni crt-list ${testdir}/simple.crt-list
+ server s1 ${s1_addr}:${s1_port}
+
+ listen second-ssl-fe
+ mode http
+ bind "${tmpdir}/second-ssl.sock" ssl crt-list ${testdir}/localhost.crt-list
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+ expect resp.status == 200
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "A490D069DBAFBEE66DE434BEC34030ADE8BCCBF1"
+ expect resp.status == 200
+} -run
+
+haproxy h1 -cli {
+ send "del ssl crt-list ${testdir}/simple.crt-list ${testdir}/common.pem:2"
+ expect ~ "Entry '${testdir}/common.pem' deleted in crtlist '${testdir}/simple.crt-list'!"
+}
+
+haproxy h1 -cli {
+ send "show ssl crt-list -n ${testdir}/simple.crt-list"
+ expect !~ "common.pem:2"
+}
+
+# This connection should fail since the corresponding line was deleted from the crt-list
+# and the strict-sni option is enabled.
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+# We should not be able to delete the crt-list's first line since it is the
+# default certificate of this bind line and the strict-sni option is not enabled.
+haproxy h1 -cli {
+ send "del ssl crt-list ${testdir}/localhost.crt-list ${testdir}/common.pem:1"
+ expect ~ "Can't delete the entry: certificate '${testdir}/common.pem' cannot be deleted, it is used as default certificate by the following frontends:"
+}
+
+# We should be able to delete any line of the crt-list since the strict-sni option is enabled.
+haproxy h1 -cli {
+ send "del ssl crt-list ${testdir}/simple.crt-list ${testdir}/common.pem:1"
+ expect ~ "Entry '${testdir}/common.pem' deleted in crtlist '${testdir}/simple.crt-list'!"
+}
diff --git a/reg-tests/ssl/dynamic_server_ssl.vtc b/reg-tests/ssl/dynamic_server_ssl.vtc
new file mode 100644
index 0000000..b7730f5
--- /dev/null
+++ b/reg-tests/ssl/dynamic_server_ssl.vtc
@@ -0,0 +1,113 @@
+#REGTEST_TYPE=bug
+# Test if a certificate can be dynamically updated once a server which used it
+# was removed.
+#
+varnishtest "Delete server via cli and update certificates"
+
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.4
+#REQUIRE_OPTIONS=OPENSSL
+feature cmd "command -v socat"
+
+# static server
+server s1 -repeat 3 {
+ rxreq
+ txresp \
+ -body "resp from s1"
+} -start
+
+haproxy h1 -conf {
+ global
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httpclose
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe
+ bind "fd@${feS}"
+ default_backend test
+
+ backend test
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt "${testdir}/client1.pem"
+ server s2 "${tmpdir}/ssl.sock" ssl verify none crt "${testdir}/client1.pem"
+ server s3 "${tmpdir}/ssl.sock" ssl verify none crt "${testdir}/client1.pem"
+
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl crt "${testdir}/common.pem"
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*SHA1 FingerPrint: D9C3BAE37EA5A7EDB7B3C9BDD4DCB2FE58A412E4"
+}
+client c1 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.body == "resp from s1"
+} -run
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*SHA1 FingerPrint: D9C3BAE37EA5A7EDB7B3C9BDD4DCB2FE58A412E4"
+}
+
+## delete the servers
+haproxy h1 -cli {
+ send "disable server test/s1"
+ expect ~ ".*"
+ send "disable server test/s2"
+ expect ~ ".*"
+ send "disable server test/s3"
+ expect ~ ".*"
+
+ # valid command
+ send "del server test/s1"
+ expect ~ "Server deleted."
+ send "del server test/s2"
+ expect ~ "Server deleted."
+ send "del server test/s3"
+ expect ~ "Server deleted."
+}
+
+# Replace certificate with an expired one
+shell {
+ printf "set ssl cert ${testdir}/client1.pem <<\n$(cat ${testdir}/client2_expired.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/client1.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*SHA1 FingerPrint: C625EB01A0A660294B9D7F44C5CEEE5AFC495BE4"
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*Status: Unused"
+}
+
+haproxy h1 -cli {
+ send "add server test/s1 ${tmpdir}/ssl.sock ssl verify none crt ${testdir}/client1.pem"
+ expect ~ "New server registered."
+ send "enable server test/s1"
+ expect ~ ".*"
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*Status: Used"
+}
+
+
+# check that servers are active
+client c1 -connect ${h1_feS_sock} {
+ txreq
+ rxresp
+ expect resp.body == "resp from s1"
+} -run
+
diff --git a/reg-tests/ssl/ecdsa.crt b/reg-tests/ssl/ecdsa.crt
new file mode 100644
index 0000000..27b5f5d
--- /dev/null
+++ b/reg-tests/ssl/ecdsa.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBfzCCAQWgAwIBAgIUYDgleyiLJSKbSWzlU3PTCB/PPYIwCgYIKoZIzj0EAwIw
+FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTIxOTA5MzExMloXDTIwMDExODA5
+MzExMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MHYwEAYHKoZIzj0CAQYFK4EEACID
+YgAEHNNG/ZSuS7CXvL03ye/Y+LpWnX818mnYkxqUQdFO2N1CO0p6kSIMHrzMQIRe
+v3+j2g6drKehMGjBmeZJwsbD6nYyUO1z+0MatW5UiTMWFmPq4v08TDDtd8sNcWgs
+SWrToxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDaAAwZQIw
+N2BdTJOH3BZlJ7HRIJNRC7jjByI9+QYAHiBoXmJVi9aoKd7OIz1Nb2DPe3QS1sDw
+AjEA9KzI8BVIZJEmsVA6rs+vRjX0tUfBhD7BCHKas0roOny9Smj/TkBFxVTNnjzM
+8iLn
+-----END CERTIFICATE-----
+
diff --git a/reg-tests/ssl/ecdsa.key b/reg-tests/ssl/ecdsa.key
new file mode 100644
index 0000000..6eec0ec
--- /dev/null
+++ b/reg-tests/ssl/ecdsa.key
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDZMkuztqaUgCAC9/7P
+CsmlC2ac7rWerq5+NKbP0Cz1+mao6+F5Hc8DKNXHgi5GPr2hZANiAAQc00b9lK5L
+sJe8vTfJ79j4uladfzXyadiTGpRB0U7Y3UI7SnqRIgwevMxAhF6/f6PaDp2sp6Ew
+aMGZ5knCxsPqdjJQ7XP7Qxq1blSJMxYWY+ri/TxMMO13yw1xaCxJatM=
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/ecdsa.pem b/reg-tests/ssl/ecdsa.pem
new file mode 100644
index 0000000..e737689
--- /dev/null
+++ b/reg-tests/ssl/ecdsa.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIBfzCCAQWgAwIBAgIUYDgleyiLJSKbSWzlU3PTCB/PPYIwCgYIKoZIzj0EAwIw
+FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTIxOTA5MzExMloXDTIwMDExODA5
+MzExMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MHYwEAYHKoZIzj0CAQYFK4EEACID
+YgAEHNNG/ZSuS7CXvL03ye/Y+LpWnX818mnYkxqUQdFO2N1CO0p6kSIMHrzMQIRe
+v3+j2g6drKehMGjBmeZJwsbD6nYyUO1z+0MatW5UiTMWFmPq4v08TDDtd8sNcWgs
+SWrToxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDaAAwZQIw
+N2BdTJOH3BZlJ7HRIJNRC7jjByI9+QYAHiBoXmJVi9aoKd7OIz1Nb2DPe3QS1sDw
+AjEA9KzI8BVIZJEmsVA6rs+vRjX0tUfBhD7BCHKas0roOny9Smj/TkBFxVTNnjzM
+8iLn
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDZMkuztqaUgCAC9/7P
+CsmlC2ac7rWerq5+NKbP0Cz1+mao6+F5Hc8DKNXHgi5GPr2hZANiAAQc00b9lK5L
+sJe8vTfJ79j4uladfzXyadiTGpRB0U7Y3UI7SnqRIgwevMxAhF6/f6PaDp2sp6Ew
+aMGZ5knCxsPqdjJQ7XP7Qxq1blSJMxYWY+ri/TxMMO13yw1xaCxJatM=
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/filters.crt-list b/reg-tests/ssl/filters.crt-list
new file mode 100644
index 0000000..e72ee0b
--- /dev/null
+++ b/reg-tests/ssl/filters.crt-list
@@ -0,0 +1,2 @@
+common.pem *.bug810.domain.tld record.bug810.domain.tld *.bug818.domain.tld !another-record.bug818.domain.tld
+ecdsa.pem record.bug810.domain.tld another-record.bug810.domain.tld *.bug818.domain.tld
diff --git a/reg-tests/ssl/generate_certificates/gen_cert_ca.pem b/reg-tests/ssl/generate_certificates/gen_cert_ca.pem
new file mode 100644
index 0000000..1aae9a7
--- /dev/null
+++ b/reg-tests/ssl/generate_certificates/gen_cert_ca.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIICOjCCAcCgAwIBAgIUf+VQOeilN1b1jiOroaMItFRozf8wCgYIKoZIzj0EAwIw
+VDELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhB
+UHJveHkgVGVjaG5vbG9naWVzMREwDwYDVQQDDAhFQ0RTQSBDQTAeFw0yMjAxMTIx
+NDAzNTlaFw00OTA1MzAxNDAzNTlaMFQxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApT
+b21lLVN0YXRlMR0wGwYDVQQKDBRIQVByb3h5IFRlY2hub2xvZ2llczERMA8GA1UE
+AwwIRUNEU0EgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARyx1wAgb1/fuAflF73
+j3Z1intP7+11kGtVZ1EAKd//xqtxFuJ+98/gc5cpiOBMWcn6FyEZ+GShTpQeqsFs
+2C4k0LTtKadXwuQaIs05QMpahTN2vmc6LPgzOrEJxFafjdejUzBRMB0GA1UdDgQW
+BBTX2Q6ojJB88kEKjdnoufDv8TGphzAfBgNVHSMEGDAWgBTX2Q6ojJB88kEKjdno
+ufDv8TGphzAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA2gAMGUCMQCLVP3+
+dvfS2k6GYplmmkyC7YVlmNre5gZwIE9zYDDvKDxsS95oqXLT5dTVm9W0MhACMAgB
+D9uOlqoGaHbRGBE8wlV33bVdpzD6JEqVyGCdEtdCW4T5Vsg3pAsUiG2tPWQ2LA==
+-----END CERTIFICATE-----
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDDosJpJuqxVdp/wuJYM1k2OTK8Pri+ChDRVlDySnHYP92aFT0GXX8A5
+X5rLNDtbaCGgBwYFK4EEACKhZANiAARyx1wAgb1/fuAflF73j3Z1intP7+11kGtV
+Z1EAKd//xqtxFuJ+98/gc5cpiOBMWcn6FyEZ+GShTpQeqsFs2C4k0LTtKadXwuQa
+Is05QMpahTN2vmc6LPgzOrEJxFafjdc=
+-----END EC PRIVATE KEY-----
diff --git a/reg-tests/ssl/generate_certificates/gen_cert_server.pem b/reg-tests/ssl/generate_certificates/gen_cert_server.pem
new file mode 100644
index 0000000..ce2f621
--- /dev/null
+++ b/reg-tests/ssl/generate_certificates/gen_cert_server.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIBujCCAV8CAQEwCgYIKoZIzj0EAwIwWDELMAkGA1UEBhMCRlIxEzARBgNVBAgM
+ClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDER
+MA8GA1UEAwwIRUNEU0EgQ0EwHhcNMjIwMjA4MTU0MjMxWhcNNDkwNjI2MTU0MjMx
+WjBcMQswCQYDVQQGEwJGUjETMBEGA1UECAwKU29tZS1TdGF0ZTEdMBsGA1UECgwU
+SEFQcm94eSBUZWNobm9sb2dpZXMxGTAXBgNVBAMMEHNlcnZlci5lY2RzYS5jb20w
+djAQBgcqhkjOPQIBBgUrgQQAIgNiAARXlODrnr208aoToRb8MqTp4GYgnk9V4LJ5
+XE8HyM7EWbqx46PdUpLUseFOtF/Yr9nyzMcdd6GNZrHkgM2NaQ/13tTbLJ84wXRQ
+jS9FSqFmDmmgbEARiyEf0K8D9lxI0bgwCgYIKoZIzj0EAwIDSQAwRgIhAJlwV5oJ
+Uz4nYUEWIrgFd7de5GZseFBIbW+UWr17Ip6gAiEAhrVEpmd4Tl5JPTwQznPa6ZlJ
+Zc8S6ipcwXPCJzsSOnQ=
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDD6ONh7kiRD6TxwQGIa
+bY5kUclHcPXiWO1QNscmeVtObmTKYiVcRR+Mj4tNRXWH6lyhZANiAARXlODrnr20
+8aoToRb8MqTp4GYgnk9V4LJ5XE8HyM7EWbqx46PdUpLUseFOtF/Yr9nyzMcdd6GN
+ZrHkgM2NaQ/13tTbLJ84wXRQjS9FSqFmDmmgbEARiyEf0K8D9lxI0bg=
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/interCA1_crl.pem b/reg-tests/ssl/interCA1_crl.pem
new file mode 100644
index 0000000..b4b8b03
--- /dev/null
+++ b/reg-tests/ssl/interCA1_crl.pem
@@ -0,0 +1,27 @@
+-----BEGIN X509 CRL-----
+MIIBpDCBjTANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJGUjEdMBsGA1UECgwU
+SEFQcm94eSBUZWNobm9sb2dpZXMxGTAXBgNVBAMMEEludGVybWVkaWF0ZSBDQTEX
+DTIxMDQyMzE0MzYyNloXDTQ4MDkwODE0MzYyNlowFTATAgIQBxcNMjEwNDIzMTQz
+NjE1WjANBgkqhkiG9w0BAQsFAAOCAQEAi9NKPoPVgYo68ZvTJP2STnFLk71bLoB/
+PbQsM7gpJvgStmLs2lVpSxL2y5CUEG8Ok73yNkQIcOZq9DAXVL/49QHXQOZ0pPMD
+XbUn5py3mEQfuuM46n3wPP8lDgbfbDMPxs2yDf7FZKQQpxBVBq9H3m+nc3RIPP9B
+3kDvYuo3PeRlqkzIdP9ceEfBGY8+cOfvPHFzLl+BEeUI2dhhdRxtWUrfPXfLXSks
+TWp0hgu9vFHguuV3mZGcxzxvdRTsq/vu0rxg2aqGOVJyNhshRn14Tt7z9uQty4Qr
+vrkvpoKVzq93bI6ITRzXlgKKzXK70wGm8tAfbioNostRJ6/gcjaWAg==
+-----END X509 CRL-----
+-----BEGIN X509 CRL-----
+MIICgzBtMA0GCSqGSIb3DQEBCwUAMD4xCzAJBgNVBAYTAkZSMR0wGwYDVQQKDBRI
+QVByb3h5IFRlY2hub2xvZ2llczEQMA4GA1UEAwwHUm9vdCBDQRcNMjEwNDIzMTQz
+MDQ0WhcNNDgwOTA4MTQzMDQ0WjANBgkqhkiG9w0BAQsFAAOCAgEAa39JkwPzmyPc
+1SY8HfJjrkvvaIO4qV/lMUzYjg6yxuTw6g7hoH0fyxK+2+RCoplXwFS7NTDG+jS1
+H3sZWvTg/aY3g4SRudJGSxeqT2a43+U4QjmTm8uClXAA7tuOcz+wSXP7sDGQ0kyg
+PCQGGmiOL5Q2lpziVRuWTHVmUkH48Na7Lyeq5cCry2AwAPjUQtcUiivuASjhUGXh
+Gya8gPV5MXNVq5T2WcZWJnkMGbWLvSFAm5POX1i8c3o2rlIjoYws/VAwOi6wqe9K
+NGNNUUXbOhyqocbzhZvWreyNUx63Pk4TxAAHwSn7H7fJe0yzfqjbZRF3KHCBPRbB
+NBOCYr5YKH6i1xQbrEGzj8+jrXWVvpYF0FXkjzO78I5c366HXPwBCPEsoIYlgjBN
++AqgiyB7xGWiRa2SZVPn+j6wHWdohar6zw4UIBLS7EIjvb/RAM6SduScIbc9l+0I
+VrciCgPScQXbkxLoh0sVBPdfR0cU08JNG+eZ8gUNce8PKdWO2mnFSNiaEz6ESgRn
+0j7q+s4V15LX/vkKyA6u2JTYZCJi23DDBzZp57sgXsQwzp50lkoFmNNASXdURJL3
+60PvFGxzBDgOUhq4yTRbz945SF3jwF9CEl9kFOffOHigwrcbKS0Cib2ac+IpXYrE
+BCm2lSjFCK0p/mAgK8yiNQFKnCAgsXU=
+-----END X509 CRL-----
diff --git a/reg-tests/ssl/interCA1_crl_empty.pem b/reg-tests/ssl/interCA1_crl_empty.pem
new file mode 100644
index 0000000..ce9e0f4
--- /dev/null
+++ b/reg-tests/ssl/interCA1_crl_empty.pem
@@ -0,0 +1,27 @@
+-----BEGIN X509 CRL-----
+MIIBjDB2MA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNVBAYTAkZSMR0wGwYDVQQKDBRI
+QVByb3h5IFRlY2hub2xvZ2llczEZMBcGA1UEAwwQSW50ZXJtZWRpYXRlIENBMRcN
+MjEwNDIzMTQzNDI3WhcNNDgwOTA4MTQzNDI3WjANBgkqhkiG9w0BAQsFAAOCAQEA
+As2A6ys84+wpG7vpjDnxD1Pri45M3sxn9Wb0v7kLBV1AsRADE6bquKYH73pUwsQB
+FmfpgE6KfagM5d/1Pap9rV0PuMc3n8Uc0Q1c30AhHELlzObEzVVeT2WTHBm5XPWn
++jY5eijsWp9NCivDwzCXP/dEXxjXPtO0l4Mctq1vv5c1Ipq9FczpfDiJJMFh7Pxy
+uVDhsnfx6uel851NxA5h2US0tQLbL/50t6vtdAgWaQdUprQwFc9oLStePbLEzT43
+zOQh7DIx0hZltd0p+OqQnfZFR0P/TlLLMGE/HZSzNi0L7WOwuG3MtrFzJwV/wqT+
+AH08jXjU5Pc3XThatlTvGA==
+-----END X509 CRL-----
+-----BEGIN X509 CRL-----
+MIICgzBtMA0GCSqGSIb3DQEBCwUAMD4xCzAJBgNVBAYTAkZSMR0wGwYDVQQKDBRI
+QVByb3h5IFRlY2hub2xvZ2llczEQMA4GA1UEAwwHUm9vdCBDQRcNMjEwNDIzMTQz
+MDQ0WhcNNDgwOTA4MTQzMDQ0WjANBgkqhkiG9w0BAQsFAAOCAgEAa39JkwPzmyPc
+1SY8HfJjrkvvaIO4qV/lMUzYjg6yxuTw6g7hoH0fyxK+2+RCoplXwFS7NTDG+jS1
+H3sZWvTg/aY3g4SRudJGSxeqT2a43+U4QjmTm8uClXAA7tuOcz+wSXP7sDGQ0kyg
+PCQGGmiOL5Q2lpziVRuWTHVmUkH48Na7Lyeq5cCry2AwAPjUQtcUiivuASjhUGXh
+Gya8gPV5MXNVq5T2WcZWJnkMGbWLvSFAm5POX1i8c3o2rlIjoYws/VAwOi6wqe9K
+NGNNUUXbOhyqocbzhZvWreyNUx63Pk4TxAAHwSn7H7fJe0yzfqjbZRF3KHCBPRbB
+NBOCYr5YKH6i1xQbrEGzj8+jrXWVvpYF0FXkjzO78I5c366HXPwBCPEsoIYlgjBN
++AqgiyB7xGWiRa2SZVPn+j6wHWdohar6zw4UIBLS7EIjvb/RAM6SduScIbc9l+0I
+VrciCgPScQXbkxLoh0sVBPdfR0cU08JNG+eZ8gUNce8PKdWO2mnFSNiaEz6ESgRn
+0j7q+s4V15LX/vkKyA6u2JTYZCJi23DDBzZp57sgXsQwzp50lkoFmNNASXdURJL3
+60PvFGxzBDgOUhq4yTRbz945SF3jwF9CEl9kFOffOHigwrcbKS0Cib2ac+IpXYrE
+BCm2lSjFCK0p/mAgK8yiNQFKnCAgsXU=
+-----END X509 CRL-----
diff --git a/reg-tests/ssl/interCA2_crl.pem b/reg-tests/ssl/interCA2_crl.pem
new file mode 100644
index 0000000..798096c
--- /dev/null
+++ b/reg-tests/ssl/interCA2_crl.pem
@@ -0,0 +1,27 @@
+-----BEGIN X509 CRL-----
+MIIBpDCBjTANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJGUjEdMBsGA1UECgwU
+SEFQcm94eSBUZWNobm9sb2dpZXMxGTAXBgNVBAMMEEludGVybWVkaWF0ZSBDQTIX
+DTIxMDQyMzE0NDUzOVoXDTQ4MDkwODE0NDUzOVowFTATAgIQCBcNMjEwNDIzMTQ0
+NTM2WjANBgkqhkiG9w0BAQsFAAOCAQEAdD35Sf47YUxG6GXiMsT4jFY0hXWgc8QS
+vR6gx6MQkWFV973ALVe1bfIXBGLZ2bTU/IppFUEJxVtyXyMCJIIpdYHirF1Y7kTi
+DLVuWE4I0ZnDSF4LI5g73dYciKeVCq+ZvKx2dZ7Y37pKqNYvhVwp+HwtB4536XvQ
+m7WjFYJFFR71gAscGky621XiRflQoGvpCOVRiJxFQFYRWRA+eR+vjQ4NTYvotDKe
+O9ejZNEpfTeil+wxi5h38GVIBa2aocMVLIu5o0EQGg8d0SEU46rJKowaUz7kESuf
+Al4jnmsb1W8LSD9Agp4GQE8pV2d42kXwpWk/JrUovHRPV2vy5PQuGA==
+-----END X509 CRL-----
+-----BEGIN X509 CRL-----
+MIICgzBtMA0GCSqGSIb3DQEBCwUAMD4xCzAJBgNVBAYTAkZSMR0wGwYDVQQKDBRI
+QVByb3h5IFRlY2hub2xvZ2llczEQMA4GA1UEAwwHUm9vdCBDQRcNMjEwNDIzMTQz
+MDQ0WhcNNDgwOTA4MTQzMDQ0WjANBgkqhkiG9w0BAQsFAAOCAgEAa39JkwPzmyPc
+1SY8HfJjrkvvaIO4qV/lMUzYjg6yxuTw6g7hoH0fyxK+2+RCoplXwFS7NTDG+jS1
+H3sZWvTg/aY3g4SRudJGSxeqT2a43+U4QjmTm8uClXAA7tuOcz+wSXP7sDGQ0kyg
+PCQGGmiOL5Q2lpziVRuWTHVmUkH48Na7Lyeq5cCry2AwAPjUQtcUiivuASjhUGXh
+Gya8gPV5MXNVq5T2WcZWJnkMGbWLvSFAm5POX1i8c3o2rlIjoYws/VAwOi6wqe9K
+NGNNUUXbOhyqocbzhZvWreyNUx63Pk4TxAAHwSn7H7fJe0yzfqjbZRF3KHCBPRbB
+NBOCYr5YKH6i1xQbrEGzj8+jrXWVvpYF0FXkjzO78I5c366HXPwBCPEsoIYlgjBN
++AqgiyB7xGWiRa2SZVPn+j6wHWdohar6zw4UIBLS7EIjvb/RAM6SduScIbc9l+0I
+VrciCgPScQXbkxLoh0sVBPdfR0cU08JNG+eZ8gUNce8PKdWO2mnFSNiaEz6ESgRn
+0j7q+s4V15LX/vkKyA6u2JTYZCJi23DDBzZp57sgXsQwzp50lkoFmNNASXdURJL3
+60PvFGxzBDgOUhq4yTRbz945SF3jwF9CEl9kFOffOHigwrcbKS0Cib2ac+IpXYrE
+BCm2lSjFCK0p/mAgK8yiNQFKnCAgsXU=
+-----END X509 CRL-----
diff --git a/reg-tests/ssl/interCA2_crl_empty.pem b/reg-tests/ssl/interCA2_crl_empty.pem
new file mode 100644
index 0000000..175528b
--- /dev/null
+++ b/reg-tests/ssl/interCA2_crl_empty.pem
@@ -0,0 +1,27 @@
+-----BEGIN X509 CRL-----
+MIIBjDB2MA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNVBAYTAkZSMR0wGwYDVQQKDBRI
+QVByb3h5IFRlY2hub2xvZ2llczEZMBcGA1UEAwwQSW50ZXJtZWRpYXRlIENBMhcN
+MjEwNDIzMTQ0NTE2WhcNNDgwOTA4MTQ0NTE2WjANBgkqhkiG9w0BAQsFAAOCAQEA
+IriCgDMzPowZl99/LoDW42xKFL5Db9mdPPNMY1Xk/6BowIhugz2vP5z38Ryfxy8B
+f1IFaGSf6Twl+F1RHv8twHMi4Vf8hbzPG4PRoEhy0gvzbD8YBtaV/GPyJY8iQt2o
+nuecskDhRp/D2YU5GXy90BMwBfH89yGPW4fUpFn3/83fZ1hhvkewTQedcLihxWGC
+KPuuWyrIN8qw/VKLARlXoFPIqyEdqttJliR1/GHej5iY1msMCftUQpC5sowse3B7
+F2oNySIPxm4jZ+QBrtMNbY7E1EHDBjcLInAfY17fKs6P0HytInBOhpeqz3Jcft2i
+b3qzM/7Ac5k6KPXs/UplMg==
+-----END X509 CRL-----
+-----BEGIN X509 CRL-----
+MIICgzBtMA0GCSqGSIb3DQEBCwUAMD4xCzAJBgNVBAYTAkZSMR0wGwYDVQQKDBRI
+QVByb3h5IFRlY2hub2xvZ2llczEQMA4GA1UEAwwHUm9vdCBDQRcNMjEwNDIzMTQz
+MDQ0WhcNNDgwOTA4MTQzMDQ0WjANBgkqhkiG9w0BAQsFAAOCAgEAa39JkwPzmyPc
+1SY8HfJjrkvvaIO4qV/lMUzYjg6yxuTw6g7hoH0fyxK+2+RCoplXwFS7NTDG+jS1
+H3sZWvTg/aY3g4SRudJGSxeqT2a43+U4QjmTm8uClXAA7tuOcz+wSXP7sDGQ0kyg
+PCQGGmiOL5Q2lpziVRuWTHVmUkH48Na7Lyeq5cCry2AwAPjUQtcUiivuASjhUGXh
+Gya8gPV5MXNVq5T2WcZWJnkMGbWLvSFAm5POX1i8c3o2rlIjoYws/VAwOi6wqe9K
+NGNNUUXbOhyqocbzhZvWreyNUx63Pk4TxAAHwSn7H7fJe0yzfqjbZRF3KHCBPRbB
+NBOCYr5YKH6i1xQbrEGzj8+jrXWVvpYF0FXkjzO78I5c366HXPwBCPEsoIYlgjBN
++AqgiyB7xGWiRa2SZVPn+j6wHWdohar6zw4UIBLS7EIjvb/RAM6SduScIbc9l+0I
+VrciCgPScQXbkxLoh0sVBPdfR0cU08JNG+eZ8gUNce8PKdWO2mnFSNiaEz6ESgRn
+0j7q+s4V15LX/vkKyA6u2JTYZCJi23DDBzZp57sgXsQwzp50lkoFmNNASXdURJL3
+60PvFGxzBDgOUhq4yTRbz945SF3jwF9CEl9kFOffOHigwrcbKS0Cib2ac+IpXYrE
+BCm2lSjFCK0p/mAgK8yiNQFKnCAgsXU=
+-----END X509 CRL-----
diff --git a/reg-tests/ssl/localhost.crt-list b/reg-tests/ssl/localhost.crt-list
new file mode 100644
index 0000000..a0d9756
--- /dev/null
+++ b/reg-tests/ssl/localhost.crt-list
@@ -0,0 +1,5 @@
+common.pem !not.test1.com *.test1.com !localhost # comment
+
+
+ common.pem !not.test1.com *.test1.com !localhost
+# comment
diff --git a/reg-tests/ssl/log_forward_ssl.vtc b/reg-tests/ssl/log_forward_ssl.vtc
new file mode 100644
index 0000000..6b7515b
--- /dev/null
+++ b/reg-tests/ssl/log_forward_ssl.vtc
@@ -0,0 +1,60 @@
+varnishtest "Test the TCP+SSL load-forward"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.3-dev1)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -repeat 500 -start
+
+syslog Slg1 -level info {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* \"GET /client_c1 HTTP/1.1\""
+} -repeat 50 -start
+
+haproxy h1 -conf {
+ global
+ insecure-fork-wanted
+ defaults
+ mode http
+ option httplog
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe_1}"
+ log 127.0.0.1:1514 local0
+# log ${Slg1_addr}:${Slg1_port} local0
+ default_backend be
+
+ backend be
+ server app1 ${s1_addr}:${s1_port}
+
+ ring myring
+ description "My local buffer"
+ format rfc5424
+ maxlen 1200
+ size 32764
+ timeout connect 5s
+ timeout server 10s
+ # syslog tcp server
+ server mysyslogsrv 127.0.0.1:2514 ssl verify none
+
+ log-forward syslog2tcp
+ dgram-bind 127.0.0.1:1514
+ log ring@myring local0 # To TCP log
+
+ log-forward syslog2local
+ bind 127.0.0.1:2514 ssl crt ${testdir}/common.pem
+ log ${Slg1_addr}:${Slg1_port} local0 # To VTest syslog
+} -start
+
+client c1 -connect ${h1_fe_1_sock} {
+ txreq -url "/client_c1"
+ rxresp
+ expect resp.status == 200
+} -repeat 50 -start
+
+syslog Slg1 -wait
diff --git a/reg-tests/ssl/new_del_ssl_cafile.vtc b/reg-tests/ssl/new_del_ssl_cafile.vtc
new file mode 100644
index 0000000..4b04571
--- /dev/null
+++ b/reg-tests/ssl/new_del_ssl_cafile.vtc
@@ -0,0 +1,137 @@
+#REGTEST_TYPE=devel
+
+# This test uses the "new ssl ca-file" and "del ssl ca-file" commands to create
+# a new CA file or delete an unused CA file.
+#
+# It requires socat to upload the CA file.
+#
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'new ssl ca-file' and 'del ssl ca-file' commands of the CLI"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 2 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+ crt-base ${testdir}
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ use_backend with_ca_be if { path /with-ca }
+ default_backend default_be
+
+ backend default_be
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/set_cafile_client.pem sni str(www.test1.com)
+
+ backend with_ca_be
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/set_cafile_client.pem sni str(with-ca.com)
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl strict-sni crt-list ${testdir}/localhost.crt-list ca-verify-file ${testdir}/set_cafile_rootCA.crt ca-file ${testdir}/set_cafile_interCA2.crt verify required crt-ignore-err all
+ http-response add-header X-SSL-Client-Verify %[ssl_c_verify]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+# Request using the default backend and the www.test1.com sni
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ # The CA file known by the frontend does not allow to verify the client's certificate
+ expect resp.http.X-SSL-Client-Verify ~ "20|21"
+} -run
+
+# This connection should fail because the with-ca.com sni is not mentioned in the crt-list yet.
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/with-ca"
+ rxresp
+ expect resp.status == 503
+} -run
+
+# Create a new unlinked CA file
+haproxy h1 -cli {
+ send "new ssl ca-file new_cafile.crt"
+ expect ~ "New CA file created 'new_cafile.crt'!"
+}
+
+shell {
+ printf "set ssl ca-file new_cafile.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file new_cafile.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl ca-file"
+ expect ~ ".*new_cafile.crt - 1 certificate.*"
+
+ send "show ssl ca-file new_cafile.crt"
+ expect ~ ".*SHA1 FingerPrint: 4FFF535278883264693CEA72C4FAD13F995D0098"
+}
+
+# The new CA file is still not linked anywhere so the request should fail.
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/with-ca"
+ rxresp
+ expect resp.status == 503
+} -run
+
+# Add a new certificate that will use the new CA file
+shell {
+ echo "new ssl cert ${testdir}/set_cafile_server.pem" | socat "${tmpdir}/h1/stats" -
+ printf "set ssl cert ${testdir}/set_cafile_server.pem <<\n$(cat ${testdir}/set_cafile_server.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/set_cafile_server.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+# Create a new crt-list line that will use the new CA file
+shell {
+ printf "add ssl crt-list ${testdir}/localhost.crt-list <<\n${testdir}/set_cafile_server.pem [ca-file new_cafile.crt] with-ca.com\n\n" | socat "${tmpdir}/h1/stats" -
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/with-ca"
+ rxresp
+ expect resp.status == 200
+ # Thanks to the newly added CA file, the client's certificate can be verified
+ expect resp.http.X-SSL-Client-Verify == 0
+} -run
+
+# Delete the newly added crt-list line and CA file
+haproxy h1 -cli {
+ send "del ssl crt-list ${testdir}/localhost.crt-list ${testdir}/set_cafile_server.pem"
+ expect ~ "Entry '${testdir}/set_cafile_server.pem' deleted in crtlist '${testdir}/localhost.crt-list'!"
+
+ send "del ssl ca-file new_cafile.crt"
+ expect ~ "CA file 'new_cafile.crt' deleted!"
+
+ send "show ssl ca-file"
+ expect !~ "new_cafile.crt"
+}
+
+# The connection should now fail since the crt-list line was deleted
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/with-ca"
+ rxresp
+ expect resp.status == 503
+} -run
+
diff --git a/reg-tests/ssl/new_del_ssl_crlfile.vtc b/reg-tests/ssl/new_del_ssl_crlfile.vtc
new file mode 100644
index 0000000..8658a1a
--- /dev/null
+++ b/reg-tests/ssl/new_del_ssl_crlfile.vtc
@@ -0,0 +1,139 @@
+#REGTEST_TYPE=devel
+
+# This test uses the "new ssl crl-file" and "del ssl crl-file" commands to create
+# a new CRL file or delete an unused CRL file.
+#
+# It requires socat to upload the CRL file.
+#
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'new ssl crl-file' and 'del ssl crl-file' commands of the CLI"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+ crt-base ${testdir}
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ use_backend with_crl_be if { path /with-crl }
+ default_backend default_be
+
+ backend default_be
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client3_revoked.pem sni str(www.test1.com)
+
+ backend with_crl_be
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client3_revoked.pem sni str(with-crl.com)
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl strict-sni crt-list ${testdir}/localhost.crt-list ca-file ${testdir}/ca-auth.crt verify required crt-ignore-err all
+ http-response add-header X-SSL-Client-Verify %[ssl_c_verify]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+# Request using the default backend and the www.test1.com sni
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ # The backend has no CRL so the connection should succeed
+ expect resp.http.X-SSL-Client-Verify == 0
+} -run
+
+# This connection should fail because the with-crl.com sni is not mentioned in the crt-list yet.
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/with-crl"
+ rxresp
+ expect resp.status == 503
+} -run
+
+# Create a new unlinked CRL file
+haproxy h1 -cli {
+ send "new ssl crl-file new_crlfile.crt"
+ expect ~ "New CRL file created 'new_crlfile.crt'!"
+}
+
+shell {
+ printf "set ssl crl-file new_crlfile.crt <<\n$(cat ${testdir}/crl-auth.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl crl-file new_crlfile.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl crl-file"
+ expect ~ ".*new_crlfile.crt"
+
+ send "show ssl crl-file new_crlfile.crt"
+ expect ~ ".*Issuer:.*/CN=HAProxy Technologies CA Test Client Auth"
+}
+
+# Add a new certificate that will use the new CA file
+shell {
+ echo "new ssl cert ${testdir}/set_cafile_server.pem" | socat "${tmpdir}/h1/stats" -
+ printf "set ssl cert ${testdir}/set_cafile_server.pem <<\n$(cat ${testdir}/set_cafile_server.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/set_cafile_server.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+# Create a new crt-list line that will use the new CA file
+shell {
+ printf "add ssl crt-list ${testdir}/localhost.crt-list <<\n${testdir}/set_cafile_server.pem [crl-file new_crlfile.crt] with-crl.com\n\n" | socat "${tmpdir}/h1/stats" -
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/with-crl"
+ rxresp
+ expect resp.status == 200
+ # The frontend's certificate is revoked in the newly added CRL, connection should fail
+ expect resp.http.X-SSL-Client-Verify == 23
+} -run
+
+# Request using the default backend and the www.test1.com sni
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ # The backend has no CRL for this SNI so the connection should still succeed
+ expect resp.http.X-SSL-Client-Verify == 0
+} -run
+
+# Delete the newly added crt-list line and CRL file
+haproxy h1 -cli {
+ send "del ssl crt-list ${testdir}/localhost.crt-list ${testdir}/set_cafile_server.pem"
+ expect ~ "Entry '${testdir}/set_cafile_server.pem' deleted in crtlist '${testdir}/localhost.crt-list'!"
+
+ send "del ssl crl-file new_crlfile.crt"
+ expect ~ "CRL file 'new_crlfile.crt' deleted!"
+
+ send "show ssl crl-file"
+ expect !~ "new_crlfile.crt"
+}
+
+# The connection should now fail since the crt-list line was deleted
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/with-crl"
+ rxresp
+ expect resp.status == 503
+} -run
+
diff --git a/reg-tests/ssl/rootCA_crl.pem b/reg-tests/ssl/rootCA_crl.pem
new file mode 100644
index 0000000..cee411e
--- /dev/null
+++ b/reg-tests/ssl/rootCA_crl.pem
@@ -0,0 +1,16 @@
+-----BEGIN X509 CRL-----
+MIICmzCBhDANBgkqhkiG9w0BAQsFADA+MQswCQYDVQQGEwJGUjEdMBsGA1UECgwU
+SEFQcm94eSBUZWNobm9sb2dpZXMxEDAOBgNVBAMMB1Jvb3QgQ0EXDTIxMDQyMzA4
+MjM0NVoXDTQ4MDkwODA4MjM0NVowFTATAgIQBxcNMjEwNDIzMDgyMDM5WjANBgkq
+hkiG9w0BAQsFAAOCAgEAgECfAAcCu1yojdIa3BxpfXgnUoi/Kgp796w67fAOZ9ZS
+0r68n754rWNC6QXsolrMVB4xIHe9PWWY5aCFcdmrZOts3JWaP8/UD/CeUSK30+jR
+jPhDaZJHarHfocPAOvhR2faFmFMrT2NWC9swX1UMPXKAeWg8YubxT7ACx/Yrja3F
+3p/UAAHpGmfPpRPGC6G2zN2zmpycpsH7vDQ7vS/pImyjuOYjMY9qKJeyHhwBIZXK
+C0fuK/40HkFpcWBq6rFoiWRX8gfuKwo0i6BUDyHoFXrptvkXW/ufk+H3uM82/g4I
+ZxLaCSoST+S2aoJOzF8JtjOEjCokP0I4Qs/4uVhbd5PNofgAZhdZY/CREErlVgIa
+OT4hGgyjom7T8+QWApSWRdAkkSDpITSFnXJYXScmxfeT1nRjG9HBX3NHCgQWL8a/
+VwCrzBkCsLfXxFoCuMIKQ2JwhHMTl+gm1YaO6p9BrGMVfxgXvCPWKH0D52pM0z4L
+6F1pKV3OA/LhQMW2tfZpvoWYtlSEy9RnaThS8OdEDI2pxlnI2F4Z6BAMVHUtlBHA
+raklj6ZnD8NkzpVlU7+0OK1rSasP/UEFBXhAOHxDEGXWA8nJCVQiOUjMbitEvQRS
++L+aSMfdpXQcIA3mTJQGXMgsnR75YXllWeHv9EYMHRkoBBUuDi4QX3MvTwa97DM=
+-----END X509 CRL-----
diff --git a/reg-tests/ssl/set_cafile_client.pem b/reg-tests/ssl/set_cafile_client.pem
new file mode 100644
index 0000000..f2fe6f3
--- /dev/null
+++ b/reg-tests/ssl/set_cafile_client.pem
@@ -0,0 +1,95 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 4103 (0x1007)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FR, O=HAProxy Technologies, CN=Intermediate CA1
+ Validity
+ Not Before: Apr 22 15:16:27 2021 GMT
+ Not After : Sep 7 15:16:27 2048 GMT
+ Subject: C=FR, O=HAProxy Technologies, CN=Client
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:bb:d6:23:03:04:ae:d2:41:19:3c:6a:91:f1:41:
+ 07:2f:db:87:fa:ac:d5:c3:ad:db:cd:b3:fd:fa:55:
+ 78:3d:eb:b1:50:98:ce:de:f7:1d:44:42:56:15:e6:
+ cf:f3:75:d5:54:90:39:07:54:70:d1:d2:71:f9:26:
+ 96:79:14:8b:20:e0:7c:dd:8f:d0:13:f1:16:a5:85:
+ 52:5f:ff:16:bf:3d:f3:5b:78:e8:00:6e:0b:79:6a:
+ b7:c3:17:49:df:96:1a:7f:c7:e0:cf:c6:01:03:55:
+ af:36:03:95:aa:95:93:08:75:e4:46:86:9f:af:23:
+ 69:ac:fa:65:f0:5d:5a:97:f6:36:78:b2:a7:11:a7:
+ 93:8b:6b:4b:c4:54:67:b1:82:23:91:72:0f:d9:8b:
+ d8:1a:b4:d4:99:9e:cd:3f:3c:34:73:48:ba:cd:f4:
+ 7d:c8:9f:b2:17:a8:90:9c:e6:c2:f3:46:39:8b:06:
+ af:d0:df:e7:7d:05:92:33:4d:08:80:17:e7:a7:39:
+ 49:eb:f1:35:06:ac:07:d6:0b:1a:4d:55:ae:26:1a:
+ 49:4a:a1:b2:5f:c5:8b:39:98:2c:0c:63:41:2e:be:
+ 0e:3f:5c:c3:3a:39:25:2a:23:a3:a2:d3:51:03:cb:
+ 81:5e:76:04:76:a8:fb:80:a8:5b:19:9f:6c:e3:d7:
+ 31:ff
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha256WithRSAEncryption
+ 71:76:f0:82:f0:06:c8:ed:5a:dd:92:37:16:82:c5:9c:dd:7d:
+ 65:b7:47:ee:d5:3c:cc:cd:69:d2:57:83:6a:c1:20:ef:28:a8:
+ b9:c2:db:1b:2b:e1:36:95:e5:e7:03:84:67:64:20:ff:ed:a4:
+ 3c:8b:d5:35:32:fe:7e:c8:c5:fc:04:15:ef:be:de:56:dc:f6:
+ d8:cc:1c:fb:03:02:01:66:fa:e4:2b:3f:2c:3e:9b:46:ec:29:
+ f3:02:1b:d4:c2:be:fe:fa:4d:0c:48:e0:d7:af:30:ca:6c:b3:
+ ea:0b:60:5b:a5:17:17:6f:f6:2f:0b:25:db:7c:ce:65:a5:94:
+ 94:09:84:10:39:1e:69:16:e5:0e:bc:1e:96:68:88:54:39:83:
+ b6:0f:74:61:6a:1c:d3:b6:65:36:bc:4f:75:30:9a:84:8f:98:
+ 68:ab:61:ab:57:88:8b:7c:64:7b:7f:39:a7:56:8a:e0:88:e3:
+ 66:7d:2c:0a:eb:f3:aa:9c:a6:f4:88:e1:0b:58:66:69:06:6b:
+ 93:e8:78:52:56:fc:7f:96:69:1d:76:40:30:fa:d6:4a:c7:2a:
+ 47:24:e0:cd:14:32:74:70:ba:b7:b4:0f:33:ca:3a:3c:75:49:
+ ff:65:2e:4f:65:e3:79:14:1f:76:5e:3f:44:39:60:42:df:97:
+ 0e:f3:a2:2e
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAeECAhAHMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNVBAYTAkZSMR0wGwYD
+VQQKDBRIQVByb3h5IFRlY2hub2xvZ2llczEZMBcGA1UEAwwQSW50ZXJtZWRpYXRl
+IENBMTAeFw0yMTA0MjIxNTE2MjdaFw00ODA5MDcxNTE2MjdaMD0xCzAJBgNVBAYT
+AkZSMR0wGwYDVQQKDBRIQVByb3h5IFRlY2hub2xvZ2llczEPMA0GA1UEAwwGQ2xp
+ZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu9YjAwSu0kEZPGqR
+8UEHL9uH+qzVw63bzbP9+lV4PeuxUJjO3vcdREJWFebP83XVVJA5B1Rw0dJx+SaW
+eRSLIOB83Y/QE/EWpYVSX/8Wvz3zW3joAG4LeWq3wxdJ35Yaf8fgz8YBA1WvNgOV
+qpWTCHXkRoafryNprPpl8F1al/Y2eLKnEaeTi2tLxFRnsYIjkXIP2YvYGrTUmZ7N
+Pzw0c0i6zfR9yJ+yF6iQnObC80Y5iwav0N/nfQWSM00IgBfnpzlJ6/E1BqwH1gsa
+TVWuJhpJSqGyX8WLOZgsDGNBLr4OP1zDOjklKiOjotNRA8uBXnYEdqj7gKhbGZ9s
+49cx/wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBxdvCC8AbI7VrdkjcWgsWc3X1l
+t0fu1TzMzWnSV4NqwSDvKKi5wtsbK+E2leXnA4RnZCD/7aQ8i9U1Mv5+yMX8BBXv
+vt5W3PbYzBz7AwIBZvrkKz8sPptG7CnzAhvUwr7++k0MSODXrzDKbLPqC2BbpRcX
+b/YvCyXbfM5lpZSUCYQQOR5pFuUOvB6WaIhUOYO2D3RhahzTtmU2vE91MJqEj5ho
+q2GrV4iLfGR7fzmnVorgiONmfSwK6/OqnKb0iOELWGZpBmuT6HhSVvx/lmkddkAw
++tZKxypHJODNFDJ0cLq3tA8zyjo8dUn/ZS5PZeN5FB92Xj9EOWBC35cO86Iu
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC71iMDBK7SQRk8
+apHxQQcv24f6rNXDrdvNs/36VXg967FQmM7e9x1EQlYV5s/zddVUkDkHVHDR0nH5
+JpZ5FIsg4Hzdj9AT8RalhVJf/xa/PfNbeOgAbgt5arfDF0nflhp/x+DPxgEDVa82
+A5WqlZMIdeRGhp+vI2ms+mXwXVqX9jZ4sqcRp5OLa0vEVGexgiORcg/Zi9gatNSZ
+ns0/PDRzSLrN9H3In7IXqJCc5sLzRjmLBq/Q3+d9BZIzTQiAF+enOUnr8TUGrAfW
+CxpNVa4mGklKobJfxYs5mCwMY0Euvg4/XMM6OSUqI6Oi01EDy4FedgR2qPuAqFsZ
+n2zj1zH/AgMBAAECggEAE60Fs948tdeN3i9HpF7scs3rO435Qmsm2DBfjWaAlvPm
+egvXt7FpBmpwfBDWfak9NIN7BdKJkuEZgUDSiFJnlIUPb2IOKNibR7FkhJvC9Tt3
+D4DlxI3Cc/CC2VPKMDqYAgSc/wa9umyyUtUjS2Apq7w1slGNzpnGCxGbtgcBY2OA
+ILjPffpVYJv87LijTIozScjx/Xdub5fWgcLtByWEDk8SxAb47qAAIAjbilpARWmf
+CHOeF+BG7ku2PT9+tLeMDabwRctNs88pef4+Dbe9+2Ess+2bdsG8As1/fw49QCnm
+ODNV1wPXdpS5wHEEdLxnQNXOQEVSRrVOhz5KWG3F0QKBgQDo/LXfjzcrNoVRG29/
+4l9aInk0+5tE4MCsM828LBmGxbYsQqt2g3ZGQCNW2IfnwQOYlujIm+F7ZYivT5Dq
+j3QvuuHjo/EGz4JuJef1oSkWeYVLm+gSzlmt5EzYximtfnEBBeJJh1Zl0R8nQfWh
+RjRMTboVC22dcBSVJdCM+lg8uQKBgQDOY8A2HxDuK3RyVgz9/YtIEqtMg2tzh0jE
+NnqPcy0AGMc1V1lmhn9ZHuUwspc3ZCi7gHRSjFoW+SWIIDFcPqRcZ5ZPxIejhwtF
+vbi20OAx+mbSdXjyYH0Z/CaVOIMHKaOWv6EbYLWIjVRGfLsMHl1xzYjE8SiNdcMf
+naLjF564dwKBgQDStSmuw5D7TdWIIq3WFF5z39WKazpjMnhNxJP96Ew1rL0yjiEP
+j5j5s6vCMRXILLEZ4PEp7IAh3xOcqPLAj3heaj88ZtnmdOjawQFlDZlhMAmy9Y8O
+4vwL3fr52U18EGwWpsGeCf3DGzt3f5mrfxhxIaJ2wd0ik2ip8ocH2KjQUQKBgQCS
+D23730hwBTjHobZYGZL0UqH/6BGnFNqeYZ+i3XO/WcnBKiwOrqh5PbAdIoZ5oNxi
+tamcsc8f6vpwt7e2/G39JyHtGbyUMgH8PSP33SKMvBUAZDpP7ZEbTqNPf0rbStCG
+4t71LR/Ln5lAuQz2qpae70IXfkOguPJ58WlRJWoiSQKBgHwgYsjep0Ms/ZJkkhAp
+59vwahpyWmnq/Wm6I9eyM84H+VQNqJf9/pQ3q3afPl4hRQydpenPBQF3GJ1m/9Nb
+BenesNrmJabCBYsaZEnwOnb7xlai5xjBEQxAJ5ROxdmTYmA0xWzuGwuu9fHwdCvW
+2ZXtTzEEzU7mLUhbiQWAF+H4
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/set_cafile_interCA1.crt b/reg-tests/ssl/set_cafile_interCA1.crt
new file mode 100644
index 0000000..840af61
--- /dev/null
+++ b/reg-tests/ssl/set_cafile_interCA1.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEETCCAfmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPjELMAkGA1UEBhMCRlIx
+HTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRAwDgYDVQQDDAdSb290IENB
+MB4XDTIxMDQyMjE0MDEyMFoXDTQ4MDkwNzE0MDEyMFowRzELMAkGA1UEBhMCRlIx
+HTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRkwFwYDVQQDDBBJbnRlcm1l
+ZGlhdGUgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAteNSI+ds
+/DWtxa69I3qQ2dn6nKTCHx3IxS5/UprcO/4t0z/gOgsAn+jDVnMRh57SNQGm5/7o
+DkiYnhV04qfdR8amKVKqAhFHUdpKXRZhP6XpqmpKLwvJH/kQmz1oIg27LPlvrAyV
+UqV1Y0vKkMCVEPuap5sJYQasYfYaavpATOAEAC10dlnpYjQQYt4fHetVi10Jmtzr
+Yea5BDsdeajbq8jWgSWGxA3BrbCubCwhCZfih5ct1KTjotj1hsfVyjPwtp8xVpMs
++amWSL/OI6pxdkVBH/dQa4M67rkQdCezLq3UAryQbdbJJLeJKqgGyIpYpKcS0GQA
+JF3UeWxWgur1IQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4ICAQCiVX9SJFgXpoHHRw42A6AZDyhdv+gagQRhcjtRzdhJM+syot5WL+jxGU7U
+26v3MGDRpn0zDRJ8Hdy+IYefB3NO/D5OPxTiziQcx15qf2fj8VtCygbC77u8QHHR
+08b2uvEDgj9K3b5skfwwez4HabD0Ol1QRZOyykG/wl2g1Bz2lgS6Jfz1wYpmT3Ju
+omufTFfB1a07DkkokGe7qAAsnfUidBScIJbLoD7xMr/zY9iMTVo+MI+Pb/4fivxq
++T75ybJwxlCpv74Zk2ATI/VEfHgPpidYtTkScRe8fsnFrE9z5TNKgNwXRhiDZe8U
+F2oggdnA0fBux5H4AmjbXHgAB8x4NjRZ9yrurjdP7AIOdOn5vvXfDehFKN+OP0Pk
++5ZoUEtd876UrupaeEPd7XZIRrGItmytuntA1pW3RCU2yGsA9ep9Ur84ogzBxXpp
+5/8eGnV/TLfaB56f9cYqL9rcTCI+VLhaTFpBpEjCae5EtS50gsAoaideb45F0wCt
+P266qAc69MU7hPqc6z2rzumcQiAKM6krELhTOVSfRo+Gzuei6bFbTK0RqwGYn+Oc
+shQN++eqrT6YAVudGUJWrKp6JEj3Y24fVhW1x631NRW5JinV2jWVWPgObDrReyya
+4KlIwKMR8vPGOa3qlIRP/QPozktkoonRYg+DsLqAD6sQhJllww==
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/set_cafile_interCA2.crt b/reg-tests/ssl/set_cafile_interCA2.crt
new file mode 100644
index 0000000..dab7bc0
--- /dev/null
+++ b/reg-tests/ssl/set_cafile_interCA2.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEETCCAfmgAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwPjELMAkGA1UEBhMCRlIx
+HTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRAwDgYDVQQDDAdSb290IENB
+MB4XDTIxMDQyMjE0MDEyMVoXDTQ4MDkwNzE0MDEyMVowRzELMAkGA1UEBhMCRlIx
+HTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRkwFwYDVQQDDBBJbnRlcm1l
+ZGlhdGUgQ0EyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0jJiCfXy
+yzBDFTW3kaznyYZOZ6+IK1xnN6HhrB0nHwpNtC5nNtFKqbN2uNhOs9qvsX6Lx+oI
+4+811OEdn269EGdlsdni/fLo+nofoD/bkcnvUGTnnmJNp4SGL6npHbHhJaAB0ETe
+3F+blo9N6i+g41c+/8gD2VTgjoSiB6/Cm1sJw5jMxlmZ6dkk+HiHqg2B2o4pkOQm
+0DmRuT3c6mN8I1sMWvZeMq7WaAzwbGmERK88p79QhPKr2kl5gfOOOphhPYqyN8dk
+c1xooWoeUo8ZI+uI8LFVljR7+VMKmkG59wtYW6vVoUrGMH8tKPZVEmKVG57wjoqP
+72n7IodhBFWnBwIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4ICAQBHTi+Wzg6g5N5/R88Zt9MFiNfQQJwLm5arVjmw+y8uSIsi5e8l2ZrqSi8i
+AtbduX5PID0kzaDAAzH37YpbJScdXBymh2kX6qJOs+bRFl/GkCD7nD9VjV23h72R
+F67bNpNsT3crcxZTD4QytahdKY43XQwicyrmLl9NahxOOfWR6RD2RriBe1Wj5v/R
+SEu5pVcON5qIgo5mgO0GM7X/IW7hZaUyCdboqd29zgYWsiqIGB23V6RTwAZ2WN/0
+xz3IPgwBwzif9L0RNZaGbg+jlmgMJTv+m+/VYoZsFwxgWIaHkR6dYxEZ2ak/djeb
+DOc764Obg+5XlxIgK+hZQbvK2zRkysUTMdzuPZtIgQU4+V4NzeEdsU2Y3IcsUzvG
+29n9CCZxyeG2hTQ1eJrvLqolPUZAn/u+EbH59h01nrycd3k7AJtr05UCrTKk+6EJ
+YwSNvnNEkmw0MX+aYNE0JYtHp8IrFgnO1vbAT6YCxR69LKWAWMy9eDVxK0bTEnrW
+7lRTTgbUCaM31g2peNoiQdSS5xVwO5bcWmi3CHJtrLavMOV7OVi9f+ggTju4CZqK
+v5U6stVyrLSUkdLZP3uMLvDVSPzPt6kGbeyHxqHfE2ywOwFtF3uxKskTNwdxYwuJ
+T8kCxcSLTyefVwkCn7P6r+LdRpJhcbDRdt9cmyfiePElj2uq2w==
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/set_cafile_rootCA.crt b/reg-tests/ssl/set_cafile_rootCA.crt
new file mode 100644
index 0000000..bed2061
--- /dev/null
+++ b/reg-tests/ssl/set_cafile_rootCA.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFGjCCAwKgAwIBAgIUHgviUJMgCZlOPOhVc09pZ4NhfxcwDQYJKoZIhvcNAQEL
+BQAwPjELMAkGA1UEBhMCRlIxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVz
+MRAwDgYDVQQDDAdSb290IENBMB4XDTIxMDQyMjE0MDEyMFoXDTQ4MDkwNzE0MDEy
+MFowPjELMAkGA1UEBhMCRlIxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVz
+MRAwDgYDVQQDDAdSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
+AgEAti+5onUeFJNyF5s6xlnBxDnFhw7Q5VbBestHeQttjBWN31zq5yaf/+CYXdu+
+lY6gNZj6JBiFJ5P7VXX3DqUIJBX6byXWfIUWM+auBAMKlTz0+hWrF/UxI/3uG67N
++Z6NVffEPYbA4Emqozr0DIicWorRyHnrhEQQP87xBCUboUr3QEkNngfiJ0fPm3fj
+7HfQemGL2OnTA8qdy0q1l4aUhVr9bgedP2Klvs0XhbszCGLI0Gq5lyNadlH1MEiw
+SXa9rklE6NCNcyamO7Wt8LVrg6pxopa7oGnkLbnjzSuE+xsN0isOLaHH5LfYg6gT
+aAHpnBHiWuDZQIyzKc+Z37gNksd46/y9B+oBZoCTcYMOsn7PK+gPzTbu3ic4L9hO
+WCsTV0tn+qUGj6/J98gRgvuvZGA7NPDKNZU5p34oyApBPBUOgpn6pCuT5NlkPYAe
+Rp/ypiy5NCHp0JW3JWkJ4+wEasZM34TZUYrOsicA0GV4ZVkoQ3WYyAjmLvRXmo/w
+Z3sSlmHvCg9MrQ9pk24+OtvCbii0bb/Zmlx0Y4lU5TogcuJffJDVbj7oxTc2gRmI
+SIZsnYLv2qVoeBoMY5otj+ef0Y8v98mKCbiWe2MzBkC2h5wmwyWedez8RysTaFHS
+Z4yOYoCsEAtCxnib9d5fXf0+6aOuFtKMknkuWbYj6En647ECAwEAAaMQMA4wDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAjVzxHzq/87uj24It5hYj4mq4
+ero0zix4fA4tJNuTpZ/5r7GUYaf/uT4xfDilBX2fGMsxVTxJC25KzhdFeTzg1Tde
+/N0LAeLWHfe6jR/P5XDATD0ZA73DQALOxRM5uRMeWJDVaUeco/aXsdQaCz2STDI3
+h7VVFoaOlmxQW3BBEvg2VUp9DS2UjqqdwsUDtzwKfrmj/FqyBvGrvNeIMv28HCu7
+r1WE1Z0UEJhpc1BPbu7F/vl60gRF3bQjh2tL8pWThxTJe6Qy+pLoSShyi85AM9XK
+scCmUtQWjy7KQDL8XVFvuCWvMzknZQjJcncbKddPaaSIDkKUpz9FDv+wSJj/LKf7
+bGSFPM6sblioLbLNJByRYI8G7VHvKDbUnYHbHp75NTGA2eDeNqx5bC2G/EJUTwLM
+bfcZr9hv+z1QpvSLEpar30kJjc1QMQcf60ToGYIC93rsVAKou2GPGry4h/nzwro0
+jjFWNgORTXllfcQDbDNOPkV1kFFibPbAU4faZMgC+xwIwDBsndvcvXjLaRUa4fmw
+1xNkOO5Lj9AuvTXdCc9yUXRzmPZhU6Q4YB2daWvs3vbMTtvkAXGyQL4b2HD+NYZs
+cMUtbteGgQzwM1gpMBn4GX53vhlCXq28r3cH1/1tLDweglSrxyvZbB7pZU7BAmLk
+TEj2fXcvdcX+TtYhC10=
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/set_cafile_server.pem b/reg-tests/ssl/set_cafile_server.pem
new file mode 100644
index 0000000..04e2c22
--- /dev/null
+++ b/reg-tests/ssl/set_cafile_server.pem
@@ -0,0 +1,95 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 4104 (0x1008)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FR, O=HAProxy Technologies, CN=Intermediate CA2
+ Validity
+ Not Before: Apr 22 15:18:37 2021 GMT
+ Not After : Sep 7 15:18:37 2048 GMT
+ Subject: C=FR, O=HAProxy Technologies, CN=Server
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:9f:14:1d:de:57:84:a9:8e:17:2a:75:92:be:
+ 70:0e:6d:95:82:36:7a:2d:b2:57:e3:82:fd:20:be:
+ 83:e5:71:0a:ae:3e:58:cc:31:bc:42:12:e7:42:50:
+ 9d:47:c6:f4:02:01:1f:6e:a1:74:38:12:27:df:45:
+ 23:56:9a:b7:74:cb:f1:5a:1a:35:60:0d:6d:59:5e:
+ 71:87:19:9e:84:16:3a:69:ff:8d:ea:b2:77:dd:40:
+ d1:8f:c8:5d:35:c1:53:a4:0b:3f:73:c4:c2:03:52:
+ 2a:f1:bf:dc:2f:32:75:d3:2b:d2:e7:3a:de:ac:ac:
+ 43:59:f1:be:52:a7:30:51:54:ff:3d:a4:5f:97:e7:
+ f8:aa:65:86:b4:7d:a6:9d:c4:2d:94:68:2d:71:dc:
+ 5c:d2:2f:bf:d4:9c:ca:7e:2e:97:a7:10:ad:d9:ad:
+ 8b:74:c9:dd:91:54:71:83:1c:51:17:7e:1b:10:fc:
+ 00:c3:f7:5b:43:76:2d:a3:1e:93:a5:c4:cb:c1:eb:
+ 8e:df:a3:6c:6e:31:1e:27:fc:40:54:ac:8e:a2:ba:
+ 6d:d3:26:0d:ef:8b:e6:20:18:55:fd:11:37:61:90:
+ 40:48:d9:86:fc:34:0b:9c:65:1b:d5:02:02:28:16:
+ 08:1f:df:d8:91:8b:be:89:63:1a:09:27:00:4c:a4:
+ f4:59
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha256WithRSAEncryption
+ 01:be:4e:27:fe:cd:03:c9:df:30:5f:a8:e5:b7:33:21:a1:9e:
+ d3:1f:cb:4c:00:64:a0:47:c6:73:c8:f8:f5:a5:f3:ee:8d:b4:
+ 2c:b9:7a:47:71:fb:4a:bd:a4:df:c9:b2:2b:06:f5:77:69:ec:
+ c6:90:8d:16:d0:3d:fa:c0:fb:30:50:39:56:0f:2b:78:15:0a:
+ c2:62:6a:98:59:70:aa:6b:61:55:58:ee:50:b7:cf:d3:7c:0a:
+ 24:04:3d:db:ab:bc:c4:ba:82:52:0c:62:4b:aa:48:47:f4:4f:
+ 05:d8:4f:b2:88:f1:d6:1a:10:e1:bc:98:0b:b9:7f:f9:47:21:
+ 89:7a:37:61:f0:1a:e3:1d:c1:23:ba:71:8d:c8:de:cc:b0:da:
+ 6a:21:5c:41:02:a1:8a:6a:d4:02:32:de:a9:84:97:38:27:de:
+ 2d:8c:bc:c4:fa:a9:fc:3a:7c:58:92:62:20:4b:be:60:25:f6:
+ f4:4e:49:a1:b2:f3:e3:97:7c:84:cd:6c:f5:42:e6:3f:ca:34:
+ a3:26:c7:91:e4:0c:8c:df:36:5e:6b:68:e6:45:2d:c0:af:56:
+ 3c:1e:85:46:79:db:85:6e:98:49:69:ea:4f:fc:00:fc:23:8c:
+ dc:b8:fe:b9:fd:f9:fb:ec:28:f0:1a:f7:3c:b4:74:38:5e:71:
+ bc:1e:39:90
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAeECAhAIMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNVBAYTAkZSMR0wGwYD
+VQQKDBRIQVByb3h5IFRlY2hub2xvZ2llczEZMBcGA1UEAwwQSW50ZXJtZWRpYXRl
+IENBMjAeFw0yMTA0MjIxNTE4MzdaFw00ODA5MDcxNTE4MzdaMD0xCzAJBgNVBAYT
+AkZSMR0wGwYDVQQKDBRIQVByb3h5IFRlY2hub2xvZ2llczEPMA0GA1UEAwwGU2Vy
+dmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo58UHd5XhKmOFyp1
+kr5wDm2VgjZ6LbJX44L9IL6D5XEKrj5YzDG8QhLnQlCdR8b0AgEfbqF0OBIn30Uj
+Vpq3dMvxWho1YA1tWV5xhxmehBY6af+N6rJ33UDRj8hdNcFTpAs/c8TCA1Iq8b/c
+LzJ10yvS5zrerKxDWfG+UqcwUVT/PaRfl+f4qmWGtH2mncQtlGgtcdxc0i+/1JzK
+fi6XpxCt2a2LdMndkVRxgxxRF34bEPwAw/dbQ3Ytox6TpcTLweuO36NsbjEeJ/xA
+VKyOorpt0yYN74vmIBhV/RE3YZBASNmG/DQLnGUb1QICKBYIH9/YkYu+iWMaCScA
+TKT0WQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQABvk4n/s0Dyd8wX6jltzMhoZ7T
+H8tMAGSgR8ZzyPj1pfPujbQsuXpHcftKvaTfybIrBvV3aezGkI0W0D36wPswUDlW
+Dyt4FQrCYmqYWXCqa2FVWO5Qt8/TfAokBD3bq7zEuoJSDGJLqkhH9E8F2E+yiPHW
+GhDhvJgLuX/5RyGJejdh8BrjHcEjunGNyN7MsNpqIVxBAqGKatQCMt6phJc4J94t
+jLzE+qn8OnxYkmIgS75gJfb0TkmhsvPjl3yEzWz1QuY/yjSjJseR5AyM3zZea2jm
+RS3Ar1Y8HoVGeduFbphJaepP/AD8I4zcuP65/fn77CjwGvc8tHQ4XnG8HjmQ
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjnxQd3leEqY4X
+KnWSvnAObZWCNnotslfjgv0gvoPlcQquPljMMbxCEudCUJ1HxvQCAR9uoXQ4Eiff
+RSNWmrd0y/FaGjVgDW1ZXnGHGZ6EFjpp/43qsnfdQNGPyF01wVOkCz9zxMIDUirx
+v9wvMnXTK9LnOt6srENZ8b5SpzBRVP89pF+X5/iqZYa0faadxC2UaC1x3FzSL7/U
+nMp+LpenEK3ZrYt0yd2RVHGDHFEXfhsQ/ADD91tDdi2jHpOlxMvB647fo2xuMR4n
+/EBUrI6ium3TJg3vi+YgGFX9ETdhkEBI2Yb8NAucZRvVAgIoFggf39iRi76JYxoJ
+JwBMpPRZAgMBAAECggEAAj1OKC5/4ifz8us42r5SiFAFqNeYCoITY+DKGCWjZoOK
+kuH1ky3nFrxtf+HclTvq4RAk3v3EunO9KfgnSKsrcaTM89/B9UOZyIxbX28BVWt8
+dzDxP1IcA+I8PAyRAghYXbltr+b0hNkeD1sB5394T2CdLV8H5zMgZN3DLhxts99B
+V6fp77eSxKvjCByKzTvlECYwxt6GnkAfZulmYGtThBoTyIz9clzl2lcpoPwp9VpF
+IwYn6qig4Gfkrwj/2iMg3b6KOQIGcbH493cGmU+ujK1l4ZbkG6VIYHqLnbD+myui
+LpUjUeZPigvgvhkdakGyrwqBxqNFDMOFTdunKuZ65QKBgQDVKKuAf5NeBWAPUiaN
+AI82+4RTIecw5svrgk/9qSNCVnUwm9qJHyDpJZLvYUXcfB1CW6iYUQ9oo/+RvK5O
+YhouwQotKMI7moIyUiRhvOSFC/7QFYLSf8uMOPlYOxofq1OAqzAsGTHItrydu709
+sdox1alxroScpRfZm8I2fm9l4wKBgQDEgaKDTY3UgpY/KWH4SWMc0+UD9ordf96m
+E9rYTLW7pleZB/L5YvmpAiewUvwk1YipiLh0fQZVEx1BKirzmiWeLm2FO4SX7z9t
+kMeVb3XiGgeoTdPV98YNfB6tx3+2WEYQ5FkvyABsdoUp6e8AkwbFPZnFmM/a0ZSU
+Ob/Sfq8xkwKBgCfzTmlv/7PAeCeG8xi8QRtB+qQGF6mPqCqEqu9U0vns8Fvi6guH
+HQj1dNuOtKRFUsqMGUYq8yNekVjELzsboeKfZYPfPsAjDkHWKWF0ILRa8jAXyAQh
+1Yl7aChEM3o6BxV3gDjTpAQFU8aQWECG4+kxLWfUGKCvRJARZE4IVmKXAoGAU2Hy
+tKaW9ULIQFruAG4biWL8fbcC68RTlMM+DKRYRRzrdLsjxeDSsX2Bm9dKuNKHH/Es
+2/klU7o9oqYi/aU+KyXmQS+lLtdNYc+acPWP3vZOo4MKzXNK7fPqDLFnptdEO+y5
+T4Ydb+jGzqc+TE8XA2EFPAyAvohJ9K+gjtBExNMCgYEAhQSFwr8FRE4TVJT9zTxG
+PUsKzCMin5ewrYSVReBBKSEymrEC2MhsDgikfJHbDF4N3o8gbhXJKf3LcLJH0761
+y6Wt+0tyfUWk4Zv8oliiZi9vcFeNmArLW5+NHQLBh5SX2UXGRmtguZUAs1gkAe5E
+S3GzLHPhcWNEOE/PxejIRKI=
+-----END PRIVATE KEY-----
diff --git a/reg-tests/ssl/set_default_cert.crt-list b/reg-tests/ssl/set_default_cert.crt-list
new file mode 100644
index 0000000..a0d2caa
--- /dev/null
+++ b/reg-tests/ssl/set_default_cert.crt-list
@@ -0,0 +1,2 @@
+set_default_cert.pem !*
+set_default_cert.pem www.test1.com
diff --git a/reg-tests/ssl/set_default_cert.pem b/reg-tests/ssl/set_default_cert.pem
new file mode 100644
index 0000000..550208a
--- /dev/null
+++ b/reg-tests/ssl/set_default_cert.pem
@@ -0,0 +1,52 @@
+-----BEGIN CERTIFICATE-----
+MIIENjCCAh4CAQEwDQYJKoZIhvcNAQELBQAwWzELMAkGA1UEBhMCRlIxDjAMBgNV
+BAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEVMBMGA1UECgwMSEFQcm94eSBUZWNo
+MRUwEwYDVQQDDAxIQVByb3h5IFRlY2gwHhcNMjEwMzAyMTcxODUwWhcNMjIwMzAy
+MTcxODUwWjBnMQswCQYDVQQGEwJGUjETMBEGA1UECAwKU29tZS1TdGF0ZTEOMAwG
+A1UEBwwFUGFyaXMxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRQwEgYD
+VQQDDAsqLnRlc3QxLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+APjmyWLJ1olKg/EOarln7oQB7pdUrF6kS1YG+Nz0sgFzxnU0PHn/IeARCprHyEZ4
+eBOrQ0CHhM5hdEFDX8iq32rektcQqwfH83iwD9zXxFBJ7ItoWG6YAN6WLXjEDYEI
+hxLJMlW3kfYODKhNMvoqXyZi2wTyAJI+aLJI7pbeD+YNb0AwOnSH5ag5ohZIr3QU
+99UD/VUhndv4OP8JZwBiV6Qy79jVDVbPFGaOc70VkMQSCHytyudQicUZrYQdIw1E
+981JF/UpbnECLWyB3V+4t1KtWOW90vkUoBEj8Nxe6kYnMaNSjQhfKSF6zGmUOXYp
+oHPCgms8v4JaovQygo02Qi8CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAAz8IntYc
+zrbIqweHfD9CZTNIQiobhQmgykT0KQ23Gm2y/e3o63XOqxDv0bEctg4zE83w3g7d
+mJlEyCB0N0qC8UGGsbRm5Cny7H//g3u06NqSMBYbdU+BgZBBj16I5Kcw/kSBb9dA
+wslLlrUjBj6dK83EB1cpyqpyZHIXkR/E424ggfc45rmD60AtU0SvzVNZfIK0PmB0
+3YKiUlO7cl5CzTeTg2BooRvqwblya62SRkhfFL7NCRX1/S9tO/XiaYzgP7J6J09x
+yYs2XhQqJFgtS+1vDp8rHKhcANFVXBJ6rDSbp1qBv7qZkQhlFf8hQtd5iBXvCb0a
+KtN9L4o6t1wvyo0BbERroGU7rkPPUNiMc3gWEf/mgwGLsNNOYqY5eYoeAF7arX5f
+c4LCHiAYMWa/bEY29zmm51GH5ddxFSu1j95Hfd+HlNcX8Oyfed2oCoSamochmbzA
+Kktk0QfCYIv4LlaG5pUliLa6DCLK7yMfT5RC5GGb350p3uDobVj/taY2cVwXOBQb
+MjXK32K9CFrnqKQptPV1ohlWgNiqhvxiGp3Yx17Cn54WL9ksO+8TlwWAttazKVlT
+40tHqGOu6ld90xGZitxL2oA9kBg9Nkxas/f9+9p6sJe5wj09dj/cqRjyiKv7nek1
+TIPtsNbJghDRDQ3uPEYHdX0h490qGMyGARw=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEA+ObJYsnWiUqD8Q5quWfuhAHul1SsXqRLVgb43PSyAXPGdTQ8
+ef8h4BEKmsfIRnh4E6tDQIeEzmF0QUNfyKrfat6S1xCrB8fzeLAP3NfEUEnsi2hY
+bpgA3pYteMQNgQiHEskyVbeR9g4MqE0y+ipfJmLbBPIAkj5oskjult4P5g1vQDA6
+dIflqDmiFkivdBT31QP9VSGd2/g4/wlnAGJXpDLv2NUNVs8UZo5zvRWQxBIIfK3K
+51CJxRmthB0jDUT3zUkX9SlucQItbIHdX7i3Uq1Y5b3S+RSgESPw3F7qRicxo1KN
+CF8pIXrMaZQ5dimgc8KCazy/glqi9DKCjTZCLwIDAQABAoIBAQC/arWb7L+56/2W
+iFDZb62GBfpYlXzOeCmb6la/jsvKxB/vCRItfGGv8Usnh9dlIsat0bsxyEcBdP80
+Jb1nFMonZS6miSIPJN4Ahd5dJ+7JFGD/QWso+mtIw1QLGTONdWJztxmnxDpTcbCY
+Sm6W57kvSz1HC1oXHjnkSqR6kCLH9y6/i7ox6IPYyDA1t/TKJMnKFOPkxKJ8A96v
+1avPrCWfXWYdn6Og5ERd8FJF2L5BYImmmkPpoUeWPyMBfAYqdK5FRijO6JMn/h5k
+XkJm+2bru+cRwcNYUNPuDIa+ZBWhjFfZfSOhOoECeKLe+lhfcFPC7cCSeDJAjGtR
+dakm15ohAoGBAP4+rVBeSCBhPH27T3HWp74qMWkYJzkdqTV0wUUJ1wtuWZFDg/RP
+OYKC+6cM0nW3K+j/9pTWMS1eM61x/VNyFQGUq/rMJGEWFH08NXnV8AxCtwKUV/rP
+Uq3MB4QWfSYGMo9QL+9lu23fMWYpBLo+KIcqPjLb+8FEJGmaC9JCIYQfAoGBAPqe
+qp7NzMmX2b1VR2XXm1CZzwTEFXb4NeDIxSfNbsqXCmws3jgBX3Lx7dQ9k8ymvaA5
+ucYLU3ppozeB//8Ir9lSA1A4w3VN9a+l1ZdQpKQ4SuHtqDwkmKAT85vmGHCPhwlq
+Er9ests3wQ4T/8HPG92QWs+Gg34F+x9U6h2FMv/xAoGBAOM6h1HWAeaWoSbKWvWm
+YKNQOHryMFQW011IbVfTtJOt23U9/1hB2mdvw5SInCzDOgZzhiF90dP3Zn5063FB
++84+3vo2q6jtwAAx6KVsdK+wjLpMdNlfpEhamrkOFGoAjf2SMFVo+fv3x8HDlUsT
+NMuhEJgKDlasHVMYb8pKeoQHAoGBAMAF7ij6+lvD03tz6d6oUkJxduLp8qBTEcUH
+T7hteOQU0lGMFz/GHYIOx/EEtUfqwgQP9r09VFrIsdwH6UNZPpM+eXdv5qLsdsB8
+SalEisGguA9fbrWWPLL6Vn8uz67+6bJW6cJjJps8ntjQjffLXkhnII09PWbD4mNh
+RngT5L2hAoGBANqa+yYSvEGNAxvdfxE0u3U/4OtjCl168nNwHXmyaCKZ1e4XYflz
+wGI4J1ngcCKN37RkCgfu/XRKrc82XhAhV+YYjAUqQYrTyh26b4v9Dp9tBUWiv7bk
+6L+ZlCms+HpsuYmsCAu/od41OWSSpdg+R3VOE0t3rp0r1QdAGYd1nwQC
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/set_ssl_cafile.vtc b/reg-tests/ssl/set_ssl_cafile.vtc
new file mode 100644
index 0000000..bda620f
--- /dev/null
+++ b/reg-tests/ssl/set_ssl_cafile.vtc
@@ -0,0 +1,165 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "set ssl ca-file" command to update a CA file over the CLI.
+# It also tests the "abort ssl ca-file" and "show ssl ca-file" commands.
+#
+# It is based on two CA certificates, set_cafile_interCA1.crt and set_cafile_interCA2.crt,
+# and a client certificate that was signed with set_cafile_interCA1.crt (set_cafile_client.pem)
+# and a server certificate that was signed with set_cafile_interCA2.crt (set_cafile_server.pem).
+# The CA files used by the client and the server will be updated through the CLI until a
+# proper connection can be established between them.
+#
+# It requires socat to upload the certificate
+#
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'set ssl ca-file' feature of the CLI"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 4 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ # dummy bind used to test a change when the same crt is used as server and bind
+ bind "fd@${foobarlst}" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA1.crt verify none
+ server s1 "${tmpdir}/ssl.sock" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA1.crt verify none
+
+ listen clear-verified-lst
+ bind "fd@${clearverifiedlst}"
+ server s1 "${tmpdir}/ssl.sock" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA1.crt verify required
+
+ listen ssl-lst
+ # crt: certificate of the server
+ # ca-file: CA used for client authentication request
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-verify-file ${testdir}/set_cafile_rootCA.crt ca-file ${testdir}/set_cafile_interCA2.crt verify required crt-ignore-err all
+ http-response add-header X-SSL-Client-Verify %[ssl_c_verify]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+# Test the "show ssl ca-file" command
+haproxy h1 -cli {
+ send "show ssl ca-file"
+ expect ~ ".*${testdir}/set_cafile_interCA1.crt - 1 certificate.*"
+ send "show ssl ca-file"
+ expect ~ ".*${testdir}/set_cafile_interCA2.crt - 1 certificate.*"
+
+ send "show ssl ca-file ${testdir}/set_cafile_interCA2.crt"
+ expect ~ ".*SHA1 FingerPrint: 3D3D1D10AD74A8135F05A818E10E5FA91433954D"
+}
+
+
+# This first connection should fail because the client's certificate was signed with the
+# set_cafile_interCA1.crt certificate which is not known by the backend.
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ # unable to verify the client certificate
+ expect resp.http.X-SSL-Client-Verify ~ "20|21"
+} -run
+
+# Set a new ca-file without committing it and check that the new ca-file is not taken into account
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_interCA2.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+}
+
+# Test the "show ssl ca-file" command
+# The transaction should be mentioned in the list
+haproxy h1 -cli {
+ send "show ssl ca-file"
+ expect ~ "\\*${testdir}/set_cafile_interCA2.crt - 1 certificate.*"
+
+# The original CA file did not change
+ send "show ssl ca-file ${testdir}/set_cafile_interCA2.crt"
+ expect ~ ".*SHA1 FingerPrint: 3D3D1D10AD74A8135F05A818E10E5FA91433954D"
+
+# Only the current transaction displays a new certificate
+ send "show ssl ca-file *${testdir}/set_cafile_interCA2.crt"
+ expect ~ ".*SHA1 FingerPrint: 4FFF535278883264693CEA72C4FAD13F995D0098"
+}
+
+# This connection should still fail for the same reasons as previously
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ # unable to verify the client certificate
+ expect resp.http.X-SSL-Client-Verify ~ "20|21"
+} -run
+
+haproxy h1 -cli {
+ send "abort ssl ca-file ${testdir}/set_cafile_interCA2.crt"
+ expect ~ "Transaction aborted for certificate '${testdir}/set_cafile_interCA2.crt'!"
+ send "commit ssl ca-file ${testdir}/set_cafile_interCA2.crt"
+ expect ~ "No ongoing transaction!"
+}
+
+
+# Update the bind line's ca-file in order to accept the client certificate
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_interCA2.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n$(cat ${testdir}/set_cafile_rootCA.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_interCA2.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+
+# The backend's certificate can't be verified by the frontend because it was signed with
+# the set_cafile_interCA2.crt certificate.
+client c1 -connect ${h1_clearverifiedlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+
+# Update the server line's ca-file. The server certificate should now be accepted by
+# the frontend. We replace the single CA by a list of CAs that includes the correct one.
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_interCA1.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n$(cat ${testdir}/set_cafile_interCA2.crt)\n$(cat ${testdir}/set_cafile_rootCA.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_interCA1.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+# Test the "show ssl ca-file" with a certificate index
+haproxy h1 -cli {
+ send "show ssl ca-file"
+ expect ~ ".*${testdir}/set_cafile_interCA1.crt - 3 certificate.*"
+
+ send "show ssl ca-file ${testdir}/set_cafile_interCA1.crt:1"
+ expect ~ ".*SHA1 FingerPrint: 4FFF535278883264693CEA72C4FAD13F995D0098"
+
+ send "show ssl ca-file ${testdir}/set_cafile_interCA1.crt:2"
+ expect !~ ".*SHA1 FingerPrint: 4FFF535278883264693CEA72C4FAD13F995D0098"
+ send "show ssl ca-file ${testdir}/set_cafile_interCA1.crt:2"
+ expect ~ ".*SHA1 FingerPrint: 3D3D1D10AD74A8135F05A818E10E5FA91433954D"
+}
+
+client c1 -connect ${h1_clearverifiedlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ # there should be no error on the backend side but one on the frontend side
+ expect resp.http.X-SSL-Client-Verify == 0
+} -run
diff --git a/reg-tests/ssl/set_ssl_cert.vtc b/reg-tests/ssl/set_ssl_cert.vtc
new file mode 100644
index 0000000..a0fe5e7
--- /dev/null
+++ b/reg-tests/ssl/set_ssl_cert.vtc
@@ -0,0 +1,206 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "set ssl cert" command to update a certificate over the CLI.
+# It requires socat to upload the certificate
+#
+# This check has two separate parts.
+# In the first part, there are 3 requests, the first one will use "www.test1.com" as SNI,
+# the second one with the same but that must fail and the third one will use
+# "localhost". Since vtest can't do SSL, we use haproxy as an SSL client with 2
+# chained listen section.
+#
+# In the second part, we check the update of a default certificate in a crt-list.
+# This corresponds to a bug raised in https://github.com/haproxy/haproxy/issues/1143.
+# A certificate is used as default certificate as well as regular one, and during the update
+# the default certificate would not be properly updated if the default instance did not have
+# any SNI. The test consists in checking that the used certificate is the right one after
+# updating it via a "set ssl cert" call.
+#
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'set ssl cert' feature of the CLI"
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 9 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+ crt-base ${testdir}
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+
+ http-response set-header X-SSL-Server-SHA1 %[ssl_s_sha1,hex]
+
+ retries 0 # 2nd SSL connection must fail so skip the retry
+ server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(www.test1.com)
+ server s2 "${tmpdir}/ssl.sock" ssl verify none sni str(www.test1.com)
+ server s3 "${tmpdir}/ssl.sock" ssl verify none sni str(localhost)
+
+ server s4 "${tmpdir}/other-ssl.sock" ssl verify none sni str(www.test1.com)
+ server s5 "${tmpdir}/other-ssl.sock" ssl verify none sni str(other.test1.com) # uses the default certificate
+ server s6 "${tmpdir}/other-ssl.sock" ssl verify none sni str(www.test1.com)
+ server s7 "${tmpdir}/other-ssl.sock" ssl verify none sni str(other.test1.com) # uses the default certificate
+
+ server s8 "${tmpdir}/other-ssl.sock" ssl verify none sni str(www.test1.com)
+ server s9 "${tmpdir}/other-ssl.sock" ssl verify none sni str(other.test1.com) # uses the default certificate
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem strict-sni
+ server s1 ${s1_addr}:${s1_port}
+ # dummy server used to test a change when the same crt is used as server and bind
+ server s2 ${s1_addr}:${s1_port} ssl crt ${testdir}/common.pem verify none weight 0
+
+ listen other-ssl-lst
+ bind "${tmpdir}/other-ssl.sock" ssl crt-list ${testdir}/set_default_cert.crt-list
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/common.pem"
+ expect ~ ".*SHA1 FingerPrint: 2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+shell {
+ printf "set ssl cert ${testdir}/common.pem <<\n$(cat ${testdir}/ecdsa.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/common.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/common.pem"
+ expect ~ ".*SHA1 FingerPrint: A490D069DBAFBEE66DE434BEC34030ADE8BCCBF1"
+}
+
+# check that the "www.test1.com" SNI was removed
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+shell {
+ printf "set ssl cert ${testdir}/common.pem <<\n$(cat ${testdir}/common.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "abort ssl cert ${testdir}/common.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/common.pem"
+ expect ~ ".*SHA1 FingerPrint: A490D069DBAFBEE66DE434BEC34030ADE8BCCBF1"
+}
+
+
+
+# The following requests are aimed at a backend that uses the set_default_cert.crt-list file
+
+# Uses the www.test1.com sni
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "9DC18799428875976DDE706E9956035EE88A4CB3"
+ expect resp.status == 200
+} -run
+
+# Uses the other.test1.com sni and the default line of the crt-list
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "9DC18799428875976DDE706E9956035EE88A4CB3"
+ expect resp.status == 200
+} -run
+
+shell {
+ printf "set ssl cert ${testdir}/set_default_cert.pem <<\n$(cat ${testdir}/common.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+}
+
+# Certificate should not have changed yet
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/set_default_cert.pem"
+ expect ~ ".*SHA1 FingerPrint: 9DC18799428875976DDE706E9956035EE88A4CB3"
+}
+
+shell {
+ echo "commit ssl cert ${testdir}/set_default_cert.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/set_default_cert.pem"
+ expect ~ ".*SHA1 FingerPrint: 2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+}
+
+# Uses the www.test1.com sni
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+ expect resp.status == 200
+} -run
+
+# Uses the other.test1.com sni and the default line of the crt-list
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+ expect resp.status == 200
+} -run
+
+# Restore original certificate
+shell {
+ printf "set ssl cert ${testdir}/set_default_cert.pem <<\n$(cat ${testdir}/set_default_cert.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/set_default_cert.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/set_default_cert.pem"
+ expect ~ ".*SHA1 FingerPrint: 9DC18799428875976DDE706E9956035EE88A4CB"
+}
+
+# Uses the www.test1.com sni
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "9DC18799428875976DDE706E9956035EE88A4CB3"
+ expect resp.status == 200
+} -run
+
+# Uses the other.test1.com sni and the default line of the crt-list
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "9DC18799428875976DDE706E9956035EE88A4CB3"
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/ssl/set_ssl_cert_bundle.vtc b/reg-tests/ssl/set_ssl_cert_bundle.vtc
new file mode 100644
index 0000000..270cba6
--- /dev/null
+++ b/reg-tests/ssl/set_ssl_cert_bundle.vtc
@@ -0,0 +1,111 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "set ssl cert" command to update a multi-certificate
+# bundle over the CLI.
+# It requires socat to upload the certificate
+#
+# This regtests loads a multi-certificates bundle "cert1-example.com.pem"
+# composed of a .rsa and a .ecdsa
+#
+# After verifying that the RSA and ECDSA algorithms were avalailble with the
+# right certificate, the test changes the certificates and try new requests.
+#
+# If this test does not work anymore:
+# - Check that you have socat
+# - Check that you have at least OpenSSL 1.1.1
+
+varnishtest "Test the 'set ssl cert' feature of the CLI with bundles"
+# could work with haproxy 2.3 but the -cc is not available
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev9)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 9 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+ crt-base ${testdir}
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+
+ http-response set-header X-SSL-Server-SHA1 %[ssl_s_sha1,hex]
+
+ retries 0 # 2nd SSL connection must fail so skip the retry
+ server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(example.com) force-tlsv12 ciphers ECDHE-RSA-AES128-GCM-SHA256
+ server s2 "${tmpdir}/ssl.sock" ssl verify none sni str(example.com) force-tlsv12 ciphers ECDHE-ECDSA-AES256-GCM-SHA384
+
+ server s3 "${tmpdir}/ssl.sock" ssl verify none sni str(example.com) force-tlsv12 ciphers ECDHE-RSA-AES128-GCM-SHA256
+ server s4 "${tmpdir}/ssl.sock" ssl verify none sni str(example.com) force-tlsv12 ciphers ECDHE-ECDSA-AES256-GCM-SHA384
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/cert1-example.com.pem
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/cert1-example.com.pem.rsa"
+ expect ~ ".*SHA1 FingerPrint: 94F720DACA71B8B1A0AC9BD48C65BA688FF047DE"
+ send "show ssl cert ${testdir}/cert1-example.com.pem.ecdsa"
+ expect ~ ".*SHA1 FingerPrint: C1BA055D452F92EB02D449F0498C289F50698300"
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+# RSA
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "94F720DACA71B8B1A0AC9BD48C65BA688FF047DE"
+ expect resp.status == 200
+# ECDSA
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "C1BA055D452F92EB02D449F0498C289F50698300"
+ expect resp.status == 200
+} -run
+
+shell {
+ printf "set ssl cert ${testdir}/cert1-example.com.pem.rsa <<\n$(cat ${testdir}/cert2-example.com.pem.rsa)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/cert1-example.com.pem.rsa" | socat "${tmpdir}/h1/stats" -
+ printf "set ssl cert ${testdir}/cert1-example.com.pem.ecdsa <<\n$(cat ${testdir}/cert2-example.com.pem.ecdsa)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/cert1-example.com.pem.ecdsa" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/cert1-example.com.pem.rsa"
+ expect ~ ".*SHA1 FingerPrint: ADC863817FC40C2A9CA913CE45C9A92232558F90"
+ send "show ssl cert ${testdir}/cert1-example.com.pem.ecdsa"
+ expect ~ ".*SHA1 FingerPrint: F49FFA446D072262445C197B85D2F400B3F58808"
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+# RSA
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "ADC863817FC40C2A9CA913CE45C9A92232558F90"
+ expect resp.status == 200
+# ECDSA
+ txreq
+ rxresp
+ expect resp.http.X-SSL-Server-SHA1 == "F49FFA446D072262445C197B85D2F400B3F58808"
+ expect resp.status == 200
+} -run
+
diff --git a/reg-tests/ssl/set_ssl_cert_noext.vtc b/reg-tests/ssl/set_ssl_cert_noext.vtc
new file mode 100644
index 0000000..4326711
--- /dev/null
+++ b/reg-tests/ssl/set_ssl_cert_noext.vtc
@@ -0,0 +1,90 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "set ssl cert" command to update a certificate over the CLI.
+# It requires socat to upload the certificate
+#
+# this check does 3 requests, the first one will use "www.test1.com" as SNI,
+# the second one with the same but that must fail and the third one will use
+# "localhost". Since vtest can't do SSL, we use haproxy as an SSL client with 2
+# chained listen section.
+#
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'set ssl cert' feature of the CLI with separate key and crt"
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ ssl-load-extra-del-ext
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ retries 0 # 2nd SSL connection must fail so skip the retry
+ server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(www.test1.com)
+ server s2 "${tmpdir}/ssl.sock" ssl verify none sni str(www.test1.com)
+ server s3 "${tmpdir}/ssl.sock" ssl verify none sni str(localhost)
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.crt strict-sni
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/common.crt"
+ expect ~ ".*SHA1 FingerPrint: 2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+}
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+shell {
+ printf "set ssl cert ${testdir}/common.crt <<\n$(cat ${testdir}/ecdsa.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ printf "set ssl cert ${testdir}/common.key <<\n$(cat ${testdir}/ecdsa.key)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/common.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/common.crt"
+ expect ~ ".*SHA1 FingerPrint: A490D069DBAFBEE66DE434BEC34030ADE8BCCBF1"
+}
+
+# check that the "www.test1.com" SNI was removed
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/ssl/set_ssl_crlfile.vtc b/reg-tests/ssl/set_ssl_crlfile.vtc
new file mode 100644
index 0000000..c9ac904
--- /dev/null
+++ b/reg-tests/ssl/set_ssl_crlfile.vtc
@@ -0,0 +1,146 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "set ssl crl-file" command to update a CRL file over the CLI.
+# It also tests the "abort ssl crl-file" and "show ssl crl-file" commands.
+#
+# The frontend's certificate is signed by set_cafile_interCA1.crt and is revoked in interCA1_crl.pem
+# but not in interCA1_crl_empty.pem.
+# The backend's certificate is signed by set_cafile_interCA2.crt and is revoked in interCA2_crl.pem
+# but not in interCA2_crl_empty.pem.
+#
+# The test consists in replacing the two empty CRLs by their not empty equivalent thanks to CLI
+# calls and to check that the certificates (frontend and backend) are indeed revoked after the
+# update.
+#
+# It requires socat to upload the certificate
+#
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'set ssl crl-file' feature of the CLI"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 4 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ server s1 "${tmpdir}/ssl.sock" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA2.crt crl-file ${testdir}/interCA2_crl_empty.pem verify required
+
+ listen ssl-lst
+ # crt: certificate of the server
+ # ca-file: CA used for client authentication request
+ # crl-file: revocation list for client auth
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-file ${testdir}/set_cafile_interCA1.crt ca-verify-file ${testdir}/set_cafile_rootCA.crt crl-file ${testdir}/interCA1_crl_empty.pem verify required crt-ignore-err all
+ http-response add-header X-SSL-Client-Verify %[ssl_c_verify]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+# Test the "show ssl ca-file" command
+haproxy h1 -cli {
+ send "show ssl ca-file"
+ expect ~ ".*${testdir}/set_cafile_interCA1.crt - 1 certificate.*"
+ send "show ssl ca-file"
+ expect ~ ".*${testdir}/set_cafile_interCA2.crt - 1 certificate.*"
+}
+
+# Add the rootCA certificate to set_cafile_interCA2.crt in order for the frontend to
+# be able to validate the server's certificate
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_interCA2.crt <<\n$(cat ${testdir}/set_cafile_interCA2.crt)\n$(cat ${testdir}/set_cafile_rootCA.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_interCA2.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl ca-file"
+ expect ~ ".*${testdir}/set_cafile_interCA2.crt - 2 certificate.*"
+
+ send "show ssl ca-file ${testdir}/set_cafile_interCA2.crt"
+ expect ~ ".*Subject.*/CN=Root CA"
+}
+
+# This first connection should succeed
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.X-SSL-Client-Verify == 0
+} -run
+
+# Change the frontend's crl-file to one in which the server certificate is revoked
+shell {
+ printf "set ssl crl-file ${testdir}/interCA2_crl_empty.pem <<\n$(cat ${testdir}/interCA2_crl.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+}
+
+# Check that the transaction is displayed in the output of "show ssl crl-list"
+haproxy h1 -cli {
+ send "show ssl crl-file"
+ expect ~ "\\*${testdir}/interCA2_crl_empty.pem"
+
+ send "show ssl crl-file \\*${testdir}/interCA2_crl_empty.pem"
+ expect ~ "Revoked Certificates:"
+ send "show ssl crl-file \\*${testdir}/interCA2_crl_empty.pem:1"
+ expect ~ "Serial Number: 1008"
+}
+
+# This connection should still succeed since the transaction was not committed
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.X-SSL-Client-Verify == 0
+} -run
+
+haproxy h1 -cli {
+ send "commit ssl crl-file ${testdir}/interCA2_crl_empty.pem"
+ expect ~ "Committing ${testdir}/interCA2_crl_empty.pem"
+}
+
+# This connection should fail, the server's certificate is revoked in the newly updated CRL file
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+# Restore the frontend's CRL
+shell {
+ printf "set ssl crl-file ${testdir}/interCA2_crl_empty.pem <<\n$(cat ${testdir}/interCA2_crl_empty.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl crl-file ${testdir}/interCA2_crl_empty.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+# Change the backend's CRL file to one in which the frontend's certificate is revoked
+shell {
+ printf "set ssl crl-file ${testdir}/interCA1_crl_empty.pem <<\n$(cat ${testdir}/interCA1_crl.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl crl-file ${testdir}/interCA1_crl_empty.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+# This connection should fail, the client's certificate is revoked in the newly updated CRL file
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ # Revoked certificate
+ expect resp.http.X-SSL-Client-Verify == 23
+} -run
diff --git a/reg-tests/ssl/set_ssl_server_cert.vtc b/reg-tests/ssl/set_ssl_server_cert.vtc
new file mode 100644
index 0000000..2699b37
--- /dev/null
+++ b/reg-tests/ssl/set_ssl_server_cert.vtc
@@ -0,0 +1,129 @@
+#REGTEST_TYPE=devel
+
+# This reg-test uses the "set ssl cert" command to update a backend certificate over the CLI.
+# It requires socat to upload the certificate
+
+varnishtest "Test the 'set ssl cert' feature of the CLI"
+#REQUIRE_VERSION=2.4
+#REQUIRE_OPTIONS=OPENSSL
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 4 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+ nbthread 1
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ retries 0 # 2nd SSL connection must fail so skip the retry
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client1.pem
+
+ listen ssl-lst
+ # crt: certificate of the server
+ # ca-file: CA used for client authentication request
+ # crl-file: revocation list for client auth: the client1 certificate is revoked
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err all crl-file ${testdir}/crl-auth.pem
+
+ acl cert_expired ssl_c_verify 10
+ acl cert_revoked ssl_c_verify 23
+ acl cert_ok ssl_c_verify 0
+
+ http-response add-header X-SSL Ok if cert_ok
+ http-response add-header X-SSL Expired if cert_expired
+ http-response add-header X-SSL Revoked if cert_revoked
+ http-response add-header x-ssl-sha1 %[ssl_c_sha1,hex]
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sha1 == "D9C3BAE37EA5A7EDB7B3C9BDD4DCB2FE58A412E4"
+ expect resp.http.x-ssl == "Ok"
+} -run
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*SHA1 FingerPrint: D9C3BAE37EA5A7EDB7B3C9BDD4DCB2FE58A412E4"
+}
+
+# Replace certificate with an expired one
+shell {
+ printf "set ssl cert ${testdir}/client1.pem <<\n$(cat ${testdir}/client2_expired.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/client1.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*SHA1 FingerPrint: C625EB01A0A660294B9D7F44C5CEEE5AFC495BE4"
+}
+
+
+# The updated client certificate is an expired one so this request should fail
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sha1 == "C625EB01A0A660294B9D7F44C5CEEE5AFC495BE4"
+ expect resp.http.x-ssl == "Expired"
+} -run
+
+# Replace certificate with a revoked one
+shell {
+ printf "set ssl cert ${testdir}/client1.pem <<\n$(cat ${testdir}/client3_revoked.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/client1.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*SHA1 FingerPrint: 992386628A40C9D49C89BAC0058B5D45D8575151"
+}
+
+# The updated client certificate is a revoked one so this request should fail
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sha1 == "992386628A40C9D49C89BAC0058B5D45D8575151"
+ expect resp.http.x-ssl == "Revoked"
+} -run
+
+# Abort a transaction
+shell {
+ printf "set ssl cert ${testdir}/client1.pem <<\n$(cat ${testdir}/client3_revoked.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "abort ssl cert ${testdir}/client1.pem" | socat "${tmpdir}/h1/stats" -
+}
+
+haproxy h1 -cli {
+ send "show ssl cert ${testdir}/client1.pem"
+ expect ~ ".*SHA1 FingerPrint: 992386628A40C9D49C89BAC0058B5D45D8575151"
+}
+
+# The certificate was not updated so it should still be revoked
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Revoked"
+} -run
+
+
diff --git a/reg-tests/ssl/show_ocsp_server.pem b/reg-tests/ssl/show_ocsp_server.pem
new file mode 100644
index 0000000..a652359
--- /dev/null
+++ b/reg-tests/ssl/show_ocsp_server.pem
@@ -0,0 +1,119 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4111 (0x100f)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FR, O=HAProxy Technologies, CN=Root CA
+ Validity
+ Not Before: Jun 10 08:54:19 2021 GMT
+ Not After : Oct 26 08:54:19 2048 GMT
+ Subject: C=FR, O=HAProxy Technologies, CN=Server Certificate
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:e9:88:7e:5e:ec:81:d0:f7:2b:9b:c9:5d:81:ea:
+ 9c:ff:61:2f:4b:a2:ad:08:4d:44:7c:65:fa:ab:3a:
+ f2:be:63:ac:34:5c:c4:05:35:be:d4:79:af:a5:fc:
+ 9e:92:10:75:b1:4d:70:d6:82:a3:7e:7e:b0:e6:2c:
+ ba:ec:1b:e9:7f:55:f3:98:6e:d5:b2:00:37:05:76:
+ df:28:be:3e:89:52:ec:47:58:45:7a:dd:7d:89:ae:
+ 7f:43:d6:a5:ce:f6:8d:8d:32:fe:33:dc:16:15:01:
+ 82:23:d1:77:12:75:a2:e2:2a:08:eb:cd:32:1e:5b:
+ 54:12:68:83:21:3a:6e:07:f5:99:f4:e7:79:eb:f7:
+ d0:d9:71:f2:1d:79:08:a2:63:df:ab:59:f3:ac:33:
+ 18:d6:0a:9c:48:0b:9a:b0:ae:79:7b:8e:5a:1d:d2:
+ fc:5c:6c:a5:d5:61:88:e8:50:c2:0f:f2:5b:0d:0c:
+ 82:18:c8:a1:98:19:8a:fc:28:c6:27:e7:94:de:3d:
+ 13:44:16:12:9e:e1:a8:b0:17:a1:4d:14:84:3e:44:
+ bc:76:5d:cd:4e:67:9c:e6:69:0b:5a:fe:cf:08:bb:
+ 6d:0b:be:d6:8e:5d:c6:fc:53:e2:ab:34:28:2f:ef:
+ 03:5a:c4:ad:b7:e8:4e:1c:89:67:78:f5:a4:41:fd:
+ 80:f3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.haproxy.com
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 14:c3:1a:2c:37:d4:91:74:10:be:eb:f3:1e:f3:da:cf:ed:0d:
+ b1:37:8e:e8:0c:44:cb:28:ce:4b:5c:ed:02:35:13:55:e1:34:
+ 93:aa:7d:91:fa:4c:a7:31:09:6a:23:b7:0a:d3:37:70:dd:48:
+ 9c:b6:af:31:d7:28:c1:cf:7d:44:f0:d5:ac:58:56:74:40:48:
+ a6:21:85:ea:bf:38:52:fc:8e:16:7c:4d:79:d3:b4:18:11:90:
+ 95:a7:f4:b6:5f:91:dc:3e:bd:e7:58:96:ff:c2:d2:59:20:ed:
+ 4e:de:e5:92:c9:a6:5a:37:a1:fd:00:cb:13:51:ef:ce:98:c8:
+ 01:b5:a1:9a:74:63:a0:da:dc:39:1e:08:8b:60:04:7f:96:c8:
+ 02:cd:cc:dc:04:a4:4c:84:8f:a1:30:49:99:e1:6c:0c:39:65:
+ 2c:03:f8:60:46:cb:28:42:6a:c4:b0:bb:7f:be:67:de:1e:55:
+ 10:2a:55:1f:58:d4:fc:b0:74:9e:11:95:0b:c0:cc:f6:fc:6d:
+ ce:25:17:48:dc:30:5e:b3:29:44:10:11:2d:47:2d:06:81:21:
+ 51:55:4a:4d:72:79:49:ad:29:77:64:92:e7:4e:c9:4f:4c:25:
+ 4d:24:3c:49:07:af:53:74:b5:14:05:e2:f2:fc:ba:d7:a0:db:
+ e4:e4:38:74:fe:f0:34:98:78:f4:2c:68:2d:a6:1e:2d:16:d6:
+ 2b:1d:95:3c:ac:9d:16:6a:7e:d4:cd:0c:94:2b:f4:94:1c:ef:
+ 3b:23:13:78:14:ea:ea:2f:08:f4:ed:21:3d:50:77:4b:50:fe:
+ db:47:19:d1:36:92:7d:7e:e3:18:40:1d:65:0e:fe:95:4f:54:
+ 60:15:16:57:72:06:93:03:ee:8c:89:4e:7b:0b:13:a5:ef:52:
+ c9:53:8d:77:b4:7f:11:f8:03:f1:ce:a0:f8:33:06:89:44:7b:
+ f7:14:4a:51:ba:0e:35:88:ea:69:44:bd:3f:76:78:23:86:79:
+ 13:00:40:1a:d0:69:42:41:72:e6:81:a7:b2:11:25:37:73:15:
+ 89:a7:36:5d:75:3c:e9:1b:dc:ea:8c:98:6e:24:f9:98:e1:62:
+ d6:12:34:a4:c1:bc:08:fd:4d:86:8e:43:a9:9a:36:26:ba:f5:
+ ab:13:9c:08:09:8d:bf:13:84:a0:5f:52:78:fc:1d:11:0c:d6:
+ e1:a3:0c:ce:4d:21:79:90:2a:bb:04:03:d9:76:71:81:36:2a:
+ 1c:56:79:e7:32:03:d8:41:cc:73:e5:6e:45:4e:2d:c9:b0:cc:
+ 70:6b:47:93:6b:00:d0:6d:94:5f:db:e1:d5:dd:73:11:9f:b7:
+ c1:75:50:43:17:b5:e6:51
+-----BEGIN CERTIFICATE-----
+MIIEOjCCAiKgAwIBAgICEA8wDQYJKoZIhvcNAQELBQAwPjELMAkGA1UEBhMCRlIx
+HTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRAwDgYDVQQDDAdSb290IENB
+MB4XDTIxMDYxMDA4NTQxOVoXDTQ4MTAyNjA4NTQxOVowSTELMAkGA1UEBhMCRlIx
+HTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMRswGQYDVQQDDBJTZXJ2ZXIg
+Q2VydGlmaWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpiH5e
+7IHQ9yubyV2B6pz/YS9Loq0ITUR8ZfqrOvK+Y6w0XMQFNb7Uea+l/J6SEHWxTXDW
+gqN+frDmLLrsG+l/VfOYbtWyADcFdt8ovj6JUuxHWEV63X2Jrn9D1qXO9o2NMv4z
+3BYVAYIj0XcSdaLiKgjrzTIeW1QSaIMhOm4H9Zn053nr99DZcfIdeQiiY9+rWfOs
+MxjWCpxIC5qwrnl7jlod0vxcbKXVYYjoUMIP8lsNDIIYyKGYGYr8KMYn55TePRNE
+FhKe4aiwF6FNFIQ+RLx2Xc1OZ5zmaQta/s8Iu20LvtaOXcb8U+KrNCgv7wNaxK23
+6E4ciWd49aRB/YDzAgMBAAGjNzA1MDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcw
+AYYXaHR0cDovL29jc3AuaGFwcm94eS5jb20wDQYJKoZIhvcNAQELBQADggIBABTD
+Giw31JF0EL7r8x7z2s/tDbE3jugMRMsozktc7QI1E1XhNJOqfZH6TKcxCWojtwrT
+N3DdSJy2rzHXKMHPfUTw1axYVnRASKYhheq/OFL8jhZ8TXnTtBgRkJWn9LZfkdw+
+vedYlv/C0lkg7U7e5ZLJplo3of0AyxNR786YyAG1oZp0Y6Da3DkeCItgBH+WyALN
+zNwEpEyEj6EwSZnhbAw5ZSwD+GBGyyhCasSwu3++Z94eVRAqVR9Y1PywdJ4RlQvA
+zPb8bc4lF0jcMF6zKUQQES1HLQaBIVFVSk1yeUmtKXdkkudOyU9MJU0kPEkHr1N0
+tRQF4vL8uteg2+TkOHT+8DSYePQsaC2mHi0W1isdlTysnRZqftTNDJQr9JQc7zsj
+E3gU6uovCPTtIT1Qd0tQ/ttHGdE2kn1+4xhAHWUO/pVPVGAVFldyBpMD7oyJTnsL
+E6XvUslTjXe0fxH4A/HOoPgzBolEe/cUSlG6DjWI6mlEvT92eCOGeRMAQBrQaUJB
+cuaBp7IRJTdzFYmnNl11POkb3OqMmG4k+ZjhYtYSNKTBvAj9TYaOQ6maNia69asT
+nAgJjb8ThKBfUnj8HREM1uGjDM5NIXmQKrsEA9l2cYE2KhxWeecyA9hBzHPlbkVO
+LcmwzHBrR5NrANBtlF/b4dXdcxGft8F1UEMXteZR
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA6Yh+XuyB0Pcrm8ldgeqc/2EvS6KtCE1EfGX6qzryvmOsNFzE
+BTW+1HmvpfyekhB1sU1w1oKjfn6w5iy67Bvpf1XzmG7VsgA3BXbfKL4+iVLsR1hF
+et19ia5/Q9alzvaNjTL+M9wWFQGCI9F3EnWi4ioI680yHltUEmiDITpuB/WZ9Od5
+6/fQ2XHyHXkIomPfq1nzrDMY1gqcSAuasK55e45aHdL8XGyl1WGI6FDCD/JbDQyC
+GMihmBmK/CjGJ+eU3j0TRBYSnuGosBehTRSEPkS8dl3NTmec5mkLWv7PCLttC77W
+jl3G/FPiqzQoL+8DWsStt+hOHIlnePWkQf2A8wIDAQABAoIBAQDktypU2zrUpo6O
+F6u9xkIWl17Tq7HddJdDYjkbJDODJWkNK2FLXPTVcYwGe5/tm7M4f4iofe+Tvo6Q
+D3TOMxP/AvX872fY2f8JGf+7Dn9+zLjdsuTxTSVbB4xaq0lepffCNxPhRIZX8k87
+tzTv3kg1SkfMcP3J31Y6ZSMwEuKaZR9bkIT2MlLw89Qrg/o1Z1Yuu4CoJhgJ9x4Q
+smJmu6uu152i0tqQDK76nHfTgK6GTyHQpP/njXZ3gD/4vTOKsZPoXEtM9gq1Ihqm
+c7Pcy71q9nOBWfG3KUVhIlOahyVPewAFG7vNsPWVE0mN3FhCIEUPPLNnvAydSPaV
+vbwohs4BAoGBAPqXF6cTKWIfHTn4TrcOcKslKEzVSgJabZeYw1kTRsSLCsvV3ojx
+txW4A8FM+EVwX+K6FmpAxN9aKERVv1Ez3xvjmZf6czgREd8F2X2j6SwkcSwVZaxz
+FCl81jz6r/9CGP6Wbq0uVKGhEdNYddhc3RvR8oWwnMEgwIkOvfnpCevzAoGBAO6T
+IljTIzsZmLLFdhvS49C4bQ71vQbEnybqHENZcPdjrgbwRDLjQ4ZEGLm/O1zmKVZh
+C5rRqd/fWVtzMPmZJr0aNeVN3dYob/1SS6ixu/D55jRII6RtkTrm8bmOlUXIx3BB
+sgDOhG61U4LJ8n4Utcgv4go1feRNQkIo5qXkLFcBAoGALB0HE+liopxZl8fni4Am
+Q2qiIox1n95tZn+E/BxRm+3iM6ntp+vtUAx51MCJAChdKNubcI8AWVVUu1rg+BmK
+kC1L754uRFN08u7jr6N4O8YaiikmIeqMRRVt3YRAEU6AeejfiOscCOwC6FKtRC5s
+2iXmbLR/k9wBKN+IgAMPNRMCgYEA44MIxDBFbrzQM9u+8HXCr27RAe0y4Fttcszb
+Oxb2ddVnRlKmlujHoikaczh8wfD0Bt3xFSlQmKAENQO69qwolzmBoDULkolpkuiC
+IlOsaPfHoqAQ7WNXlhZa+puQmsYH+3OK7t4CyRi+lQFE8RuK52dSZm3wqmFLCJC8
+tALOjgECgYEAjREmEh/o/moOfIp8x18GYkYkJCv3+/UwMD8kJUu3KtXhER6Kgi2t
+GgqGV7nHm+sZjck+tcWdT7s+SJWQ2t8QkOf9xavy6mhG6ptJT7xoXSCxAUzNjLQZ
+WpoLVecRfaiAwj9DbbVWhjy8RDkyAHcHveVSIH40I7K0oTbNPqyJk6U=
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/show_ocsp_server.pem.issuer b/reg-tests/ssl/show_ocsp_server.pem.issuer
new file mode 100644
index 0000000..bed2061
--- /dev/null
+++ b/reg-tests/ssl/show_ocsp_server.pem.issuer
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFGjCCAwKgAwIBAgIUHgviUJMgCZlOPOhVc09pZ4NhfxcwDQYJKoZIhvcNAQEL
+BQAwPjELMAkGA1UEBhMCRlIxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVz
+MRAwDgYDVQQDDAdSb290IENBMB4XDTIxMDQyMjE0MDEyMFoXDTQ4MDkwNzE0MDEy
+MFowPjELMAkGA1UEBhMCRlIxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVz
+MRAwDgYDVQQDDAdSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
+AgEAti+5onUeFJNyF5s6xlnBxDnFhw7Q5VbBestHeQttjBWN31zq5yaf/+CYXdu+
+lY6gNZj6JBiFJ5P7VXX3DqUIJBX6byXWfIUWM+auBAMKlTz0+hWrF/UxI/3uG67N
++Z6NVffEPYbA4Emqozr0DIicWorRyHnrhEQQP87xBCUboUr3QEkNngfiJ0fPm3fj
+7HfQemGL2OnTA8qdy0q1l4aUhVr9bgedP2Klvs0XhbszCGLI0Gq5lyNadlH1MEiw
+SXa9rklE6NCNcyamO7Wt8LVrg6pxopa7oGnkLbnjzSuE+xsN0isOLaHH5LfYg6gT
+aAHpnBHiWuDZQIyzKc+Z37gNksd46/y9B+oBZoCTcYMOsn7PK+gPzTbu3ic4L9hO
+WCsTV0tn+qUGj6/J98gRgvuvZGA7NPDKNZU5p34oyApBPBUOgpn6pCuT5NlkPYAe
+Rp/ypiy5NCHp0JW3JWkJ4+wEasZM34TZUYrOsicA0GV4ZVkoQ3WYyAjmLvRXmo/w
+Z3sSlmHvCg9MrQ9pk24+OtvCbii0bb/Zmlx0Y4lU5TogcuJffJDVbj7oxTc2gRmI
+SIZsnYLv2qVoeBoMY5otj+ef0Y8v98mKCbiWe2MzBkC2h5wmwyWedez8RysTaFHS
+Z4yOYoCsEAtCxnib9d5fXf0+6aOuFtKMknkuWbYj6En647ECAwEAAaMQMA4wDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAjVzxHzq/87uj24It5hYj4mq4
+ero0zix4fA4tJNuTpZ/5r7GUYaf/uT4xfDilBX2fGMsxVTxJC25KzhdFeTzg1Tde
+/N0LAeLWHfe6jR/P5XDATD0ZA73DQALOxRM5uRMeWJDVaUeco/aXsdQaCz2STDI3
+h7VVFoaOlmxQW3BBEvg2VUp9DS2UjqqdwsUDtzwKfrmj/FqyBvGrvNeIMv28HCu7
+r1WE1Z0UEJhpc1BPbu7F/vl60gRF3bQjh2tL8pWThxTJe6Qy+pLoSShyi85AM9XK
+scCmUtQWjy7KQDL8XVFvuCWvMzknZQjJcncbKddPaaSIDkKUpz9FDv+wSJj/LKf7
+bGSFPM6sblioLbLNJByRYI8G7VHvKDbUnYHbHp75NTGA2eDeNqx5bC2G/EJUTwLM
+bfcZr9hv+z1QpvSLEpar30kJjc1QMQcf60ToGYIC93rsVAKou2GPGry4h/nzwro0
+jjFWNgORTXllfcQDbDNOPkV1kFFibPbAU4faZMgC+xwIwDBsndvcvXjLaRUa4fmw
+1xNkOO5Lj9AuvTXdCc9yUXRzmPZhU6Q4YB2daWvs3vbMTtvkAXGyQL4b2HD+NYZs
+cMUtbteGgQzwM1gpMBn4GX53vhlCXq28r3cH1/1tLDweglSrxyvZbB7pZU7BAmLk
+TEj2fXcvdcX+TtYhC10=
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/show_ocsp_server.pem.ocsp b/reg-tests/ssl/show_ocsp_server.pem.ocsp
new file mode 100644
index 0000000..5ac1457
--- /dev/null
+++ b/reg-tests/ssl/show_ocsp_server.pem.ocsp
Binary files differ
diff --git a/reg-tests/ssl/show_ocsp_server.pem.ocsp.revoked b/reg-tests/ssl/show_ocsp_server.pem.ocsp.revoked
new file mode 100644
index 0000000..bf69b3d
--- /dev/null
+++ b/reg-tests/ssl/show_ocsp_server.pem.ocsp.revoked
Binary files differ
diff --git a/reg-tests/ssl/show_ssl_ocspresponse.vtc b/reg-tests/ssl/show_ssl_ocspresponse.vtc
new file mode 100644
index 0000000..3d67fe5
--- /dev/null
+++ b/reg-tests/ssl/show_ssl_ocspresponse.vtc
@@ -0,0 +1,135 @@
+#REGTEST_TYPE=devel
+
+# broken with BoringSSL.
+
+# This reg-test uses the "show ssl ocsp-response" command to display the details
+# of the OCSP responses used by HAProxy.
+# It also uses the new special cases of the "show ssl cert" command, where an OCSP
+# extension is provided to the certificate name (with or without preceding * for an
+# ongoing transaction).
+#
+# It uses the show_ocsp_server.pem server certificate, signed off by set_cafile_rootCA.crt,
+# which has two OCSP responses, show_ocsp_server.pem.ocsp which is loaded by default and in
+# which it is valid, and show_ocsp_server.pem.ocsp.revoked in which it is revoked.
+# The OSCP response is updated through the two means available in the CLI, the
+# "set ssl ocsp-response" command and the update through a "set ssl cert foo.ocsp".
+#
+# It requires socat to upload the new OCSP responses.
+#
+# If this test does not work anymore:
+# - Check that you have socat
+
+varnishtest "Test the 'show ssl ocsp-response' and 'show ssl cert foo.pem.ocsp' features of the CLI"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && !ssllib_name_startswith(BoringSSL)'"
+feature cmd "command -v socat && command -v openssl"
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ server s1 "${tmpdir}/ssl.sock" ssl ca-file ${testdir}/set_cafile_rootCA.crt verify none
+
+ listen ssl-lst
+ # crt: certificate of the server
+ # ca-file: CA used for client authentication request
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/show_ocsp_server.pem ca-file ${testdir}/set_cafile_rootCA.crt verify none crt-ignore-err all
+ http-response add-header X-SSL-Client-Verify %[ssl_c_verify]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+# Test the "show ssl ocsp-response" command
+haproxy h1 -cli {
+ send "show ssl ocsp-response"
+ expect ~ "Certificate ID key : 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "Responder Id: C = FR, O = HAProxy Technologies, CN = ocsp.haproxy.com"
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "Cert Status: good"
+}
+
+# Test the "show ssl cert foo.pem.ocsp" command
+haproxy h1 -cli {
+ send "show ssl cert"
+ expect ~ ".*show_ocsp_server.pem"
+
+ send "show ssl cert ${testdir}/show_ocsp_server.pem"
+ expect ~ "Serial: 100F"
+ send "show ssl cert ${testdir}/show_ocsp_server.pem"
+ expect ~ "OCSP Response Key: 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+
+ send "show ssl cert ${testdir}/show_ocsp_server.pem.ocsp"
+ expect ~ "Responder Id: C = FR, O = HAProxy Technologies, CN = ocsp.haproxy.com"
+ send "show ssl cert ${testdir}/show_ocsp_server.pem.ocsp"
+ expect ~ "Cert Status: good"
+}
+
+
+# Change the server certificate's OCSP response through "set ssl ocsp-response"
+shell {
+ printf "set ssl ocsp-response <<\n$(cat ${testdir}/show_ocsp_server.pem.ocsp.revoked|openssl base64)\n\n" | socat "${tmpdir}/h1/stats" -
+}
+
+# Check that the change was taken into account
+haproxy h1 -cli {
+ send "show ssl ocsp-response"
+ expect ~ "Certificate ID key : 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "Responder Id: C = FR, O = HAProxy Technologies, CN = ocsp.haproxy.com"
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "Cert Status: revoked"
+
+ send "show ssl cert ${testdir}/show_ocsp_server.pem.ocsp"
+ expect ~ "Cert Status: revoked"
+}
+
+
+# Change the server certificate's OCSP response through a transaction
+shell {
+ printf "set ssl cert ${testdir}/show_ocsp_server.pem <<\n$(cat ${testdir}/show_ocsp_server.pem)\n\n" | socat "${tmpdir}/h1/stats" -
+ printf "set ssl cert ${testdir}/show_ocsp_server.pem.ocsp <<\n$(cat ${testdir}/show_ocsp_server.pem.ocsp|openssl base64)\n\n" | socat "${tmpdir}/h1/stats" -
+}
+
+
+# Check that the actual tree entry was not changed and that the uncommitted
+# transaction's OCSP response is the new one
+haproxy h1 -cli {
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "Cert Status: revoked"
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "This Update: Jun 10 08:57:45 2021 GMT"
+
+ send "show ssl cert *${testdir}/show_ocsp_server.pem.ocsp"
+ expect ~ "Cert Status: good"
+ send "show ssl cert *${testdir}/show_ocsp_server.pem.ocsp"
+ expect ~ "This Update: Jun 10 08:55:04 2021 GMT"
+}
+
+
+# Commit the transaction and check that it was taken into account
+haproxy h1 -cli {
+ send "commit ssl cert ${testdir}/show_ocsp_server.pem"
+ expect ~ "Success!"
+
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "Cert Status: good"
+ send "show ssl ocsp-response 303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a0202100f"
+ expect ~ "This Update: Jun 10 08:55:04 2021 GMT"
+}
diff --git a/reg-tests/ssl/simple.crt-list b/reg-tests/ssl/simple.crt-list
new file mode 100644
index 0000000..9ffacb4
--- /dev/null
+++ b/reg-tests/ssl/simple.crt-list
@@ -0,0 +1,5 @@
+common.pem record1.bug940.domain.tld
+common.pem record2.bug940.domain.tld
+ecdsa.pem record3.bug940.domain.tld
+ecdsa.pem record4.bug940.domain.tld
+
diff --git a/reg-tests/ssl/ssl_client_auth.vtc b/reg-tests/ssl/ssl_client_auth.vtc
new file mode 100644
index 0000000..0278ec0
--- /dev/null
+++ b/reg-tests/ssl/ssl_client_auth.vtc
@@ -0,0 +1,83 @@
+#REGTEST_TYPE=devel
+
+# This reg-test tests the client auth feature of HAProxy for both the backend
+# and frontend section with a CRL list
+#
+# This reg-test uses 2 chained listeners because vtest does not handle the SSL.
+# Test the frontend client auth and the backend side at the same time.
+#
+# The sends 3 requests one with a correct certificate, one with an expired one and one which was revoked.
+# The client then check if we received the right one with the right error.
+#
+# Certificates, CA and CRL are expiring in 2050 so it should be fine for the CI.
+#
+# Detail about configuration is explained there:
+# https://www.haproxy.com/blog/ssl-client-certificate-management-at-application-level/
+
+varnishtest "Test the client auth"
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ # crt: certificate sent for a client certificate request
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client1.pem
+ server s2 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client2_expired.pem # expired
+ server s3 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client3_revoked.pem # revoked
+
+ listen ssl-lst
+ # crt: certificate of the server
+ # ca-file: CA used for client authentication request
+ # crl-file: revocation list for client auth: the client1 certificate is revoked
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err all crl-file ${testdir}/crl-auth.pem
+
+ acl cert_expired ssl_c_verify 10
+ acl cert_revoked ssl_c_verify 23
+ acl cert_ok ssl_c_verify 0
+
+ http-response add-header X-SSL Ok if cert_ok
+ http-response add-header X-SSL Expired if cert_expired
+ http-response add-header X-SSL Revoked if cert_revoked
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Ok"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Expired"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Revoked"
+} -run
diff --git a/reg-tests/ssl/ssl_client_samples.vtc b/reg-tests/ssl/ssl_client_samples.vtc
new file mode 100644
index 0000000..81a52ab
--- /dev/null
+++ b/reg-tests/ssl/ssl_client_samples.vtc
@@ -0,0 +1,72 @@
+#REGTEST_TYPE=devel
+
+varnishtest "Test the ssl_c_* sample fetches"
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ crt-base ${testdir}
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client1.pem
+
+ listen ssl-lst
+ mode http
+
+ http-response add-header x-ssl-der %[ssl_c_der,hex]
+ http-response add-header x-ssl-chain-der %[ssl_c_chain_der,hex]
+ http-response add-header x-ssl-sha1 %[ssl_c_sha1,hex]
+ http-response add-header x-ssl-notafter %[ssl_c_notafter]
+ http-response add-header x-ssl-notbefore %[ssl_c_notbefore]
+ http-response add-header x-ssl-sig_alg %[ssl_c_sig_alg]
+ http-response add-header x-ssl-i_dn %[ssl_c_i_dn]
+ http-response add-header x-ssl-s_dn %[ssl_c_s_dn]
+ http-response add-header x-ssl-s_serial %[ssl_c_serial,hex]
+ http-response add-header x-ssl-key_alg %[ssl_c_key_alg]
+ http-response add-header x-ssl-version %[ssl_c_version]
+
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err all crl-file ${testdir}/crl-auth.pem
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-der ~ 3082052D30820315020102300D0.*995ED3BE2BFB923A3EB71FA07002E
+ expect resp.http.x-ssl-chain-der ~ 3082096B30820553A0030201020.*0237D08F425C8414A23D436415502
+ expect resp.http.x-ssl-sha1 == "D9C3BAE37EA5A7EDB7B3C9BDD4DCB2FE58A412E4"
+ expect resp.http.x-ssl-notafter == "500421185942Z"
+ expect resp.http.x-ssl-notbefore == "200428185942Z"
+ expect resp.http.x-ssl-sig_alg == "RSA-SHA256"
+ expect resp.http.x-ssl-i_dn == "/C=FR/ST=Some-State/O=HAProxy Technologies/CN=HAProxy Technologies CA Test Client Auth"
+ expect resp.http.x-ssl-s_dn == "/C=FR/O=HAProxy Technologies Test/CN=client1"
+ expect resp.http.x-ssl-s_serial == "02"
+ expect resp.http.x-ssl-key_alg == "rsaEncryption"
+ expect resp.http.x-ssl-version == "1"
+} -run
+
+
diff --git a/reg-tests/ssl/ssl_crt-list_filters.vtc b/reg-tests/ssl/ssl_crt-list_filters.vtc
new file mode 100644
index 0000000..099a400
--- /dev/null
+++ b/reg-tests/ssl/ssl_crt-list_filters.vtc
@@ -0,0 +1,64 @@
+#REGTEST_TYPE=bug
+varnishtest "Test for the bug #810 and #818"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
+# This test checks if the multiple certificate types works correctly with the
+# SNI, and that the negative filters are correctly excluded
+
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ crt-base ${testdir}
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ retries 0
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(another-record.bug810.domain.tld) ssl-min-ver TLSv1.2 ssl-max-ver TLSv1.2 ciphers "kRSA"
+ server s2 "${tmpdir}/ssl.sock" ssl verify none sni str(another-record.bug810.domain.tld) ssl-min-ver TLSv1.2 ssl-max-ver TLSv1.2 ciphers "aECDSA"
+ server s3 "${tmpdir}/ssl.sock" ssl verify none sni str(another-record.bug818.domain.tld) ssl-min-ver TLSv1.2 ssl-max-ver TLSv1.2 ciphers "kRSA"
+
+ listen ssl-lst
+ mode http
+ bind "${tmpdir}/ssl.sock" ssl strict-sni ssl-min-ver TLSv1.2 ssl-max-ver TLSv1.2 ciphers "kRSA:aECDSA" crt-list ${testdir}/filters.crt-list
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
diff --git a/reg-tests/ssl/ssl_curves.vtc b/reg-tests/ssl/ssl_curves.vtc
new file mode 100644
index 0000000..5cc70df
--- /dev/null
+++ b/reg-tests/ssl/ssl_curves.vtc
@@ -0,0 +1,134 @@
+#REGTEST_TYPE=devel
+
+# This reg-test checks the behaviour of the 'curves' and 'ecdhe' options on a
+# bind line. Its main point is to ensure that the default curve used in
+# HAProxy is indeed prime256v1 (or P-256 depending on the curve's
+# representation). In order to check this, is uses two ssl frontends that have
+# different lists of accepted curves, one of them accepting this default curve
+# while the other one does not. A backend tries to connect to those two
+# frontends by using the default curve, and it should succeed in one case and
+# fail in the other.
+# For some strange reason, OpenSSL 1.0.2 does not behave the same way as later
+# versions when it comes to ECDH and curves related matters. Instead of trying
+# to make it work the same way as the other (more used) versions, we will
+# ignore it and disable this test on OpenSSL 1.0.2.
+# For the same reason, this test is disabled for other SSL libraries as well.
+#
+
+varnishtest "Test the 'curves' and 'ecdhe' options and default curve value"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
+feature ignore_unknown_macro
+
+server s1 -repeat 2 {
+ rxreq
+ txresp
+} -start
+
+barrier b1 cond 2 -cyclic
+
+syslog Slg_cust_fmt -level info {
+ recv
+ expect ~ "ERROR.*conn_status:\"34:SSL handshake failure\" hsk_err:\".*wrong curve\".*"
+
+ barrier b1 sync
+
+ recv
+ expect ~ "ERROR ECDHE.*conn_status:\"34:SSL handshake failure\" hsk_err:\".*wrong curve\".*"
+} -start
+
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+
+ defaults
+ mode http
+ option httpslog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ retries 0
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+
+ use_backend ssl-curves-be if { path /curves }
+ use_backend ssl-ecdhe-521-be if { path /ecdhe-521 }
+ use_backend ssl-ecdhe-256-be if { path /ecdhe-256 }
+ default_backend ssl-be
+
+ backend ssl-be
+ server s1 "${tmpdir}/ssl1.sock" ssl verify none crt ${testdir}/client.ecdsa.pem force-tlsv12
+
+ backend ssl-curves-be
+ server s1 "${tmpdir}/ssl2.sock" ssl verify none crt ${testdir}/client.ecdsa.pem force-tlsv12
+
+ backend ssl-ecdhe-256-be
+ server s1 "${tmpdir}/ssl-ecdhe-256.sock" ssl verify none crt ${testdir}/client.ecdsa.pem force-tlsv12
+
+ backend ssl-ecdhe-521-be
+ server s1 "${tmpdir}/ssl-ecdhe-521.sock" ssl verify none crt ${testdir}/client.ecdsa.pem force-tlsv12
+
+
+ listen ssl1-lst
+ bind "${tmpdir}/ssl1.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt verify optional curves P-256:P-384
+ server s1 ${s1_addr}:${s1_port}
+
+ # The prime256v1 curve, which is used by default by a backend when no
+ # 'curves' or 'ecdhe' option is specified, is not allowed on this listener
+ listen ssl2-lst
+ log ${Slg_cust_fmt_addr}:${Slg_cust_fmt_port} local0
+ error-log-format "ERROR conn_status:\"%[fc_err]:%[fc_err_str]\" hsk_err:%{+Q}[ssl_fc_err_str]"
+
+ bind "${tmpdir}/ssl2.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt verify optional curves P-384
+ server s1 ${s1_addr}:${s1_port}
+
+ listen ssl-ecdhe-521-lst
+ log ${Slg_cust_fmt_addr}:${Slg_cust_fmt_port} local0
+ error-log-format "ERROR ECDHE-521 conn_status:\"%[fc_err]:%[fc_err_str]\" hsk_err:%{+Q}[ssl_fc_err_str]"
+
+ bind "${tmpdir}/ssl-ecdhe-521.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt verify optional ecdhe secp521r1
+ server s1 ${s1_addr}:${s1_port}
+
+ listen ssl-ecdhe-256-lst
+ log ${Slg_cust_fmt_addr}:${Slg_cust_fmt_port} local0
+ error-log-format "ERROR ECDHE-256 conn_status:\"%[fc_err]:%[fc_err_str]\" hsk_err:%{+Q}[ssl_fc_err_str]"
+
+ bind "${tmpdir}/ssl-ecdhe-256.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt verify optional ecdhe prime256v1
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+# The backend tries to use the prime256v1 curve that is not accepted by the
+# frontend so the handshake should fail.
+client c2 -connect ${h1_clearlst_sock} {
+ txreq -url "/curves"
+ rxresp
+ expect resp.status == 503
+} -run
+
+barrier b1 sync
+
+# The backend tries to use the prime256v1 curve that is not accepted by the
+# frontend so the handshake should fail.
+client c3 -connect ${h1_clearlst_sock} {
+ txreq -url "/ecdhe-521"
+ rxresp
+ expect resp.status == 503
+} -run
+
+client c4 -connect ${h1_clearlst_sock} {
+ txreq -url "/ecdhe-256"
+ rxresp
+ expect resp.status == 200
+} -run
+
+syslog Slg_cust_fmt -wait
diff --git a/reg-tests/ssl/ssl_default_server.vtc b/reg-tests/ssl/ssl_default_server.vtc
new file mode 100644
index 0000000..485a9ba
--- /dev/null
+++ b/reg-tests/ssl/ssl_default_server.vtc
@@ -0,0 +1,142 @@
+#REGTEST_TYPE=devel
+
+# This reg-test ensures that SSL related configuration specified in a
+# default-server option are properly taken into account by the servers
+# (frontend). It mainly focuses on the client certificate used by the frontend,
+# that can either be defined in the server line itself, in the default-server
+# line or in both.
+#
+# It was created following a bug raised in redmine (issue #3906) in which a
+# server used an "empty" SSL context instead of the proper one.
+#
+
+varnishtest "Test the 'set ssl cert' feature of the CLI"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature ignore_unknown_macro
+
+server s1 -repeat 7 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+ crt-base ${testdir}
+ ca-base ${testdir}
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ use_backend first_be if { path /first }
+ use_backend second_be if { path /second }
+ use_backend third_be if { path /third }
+ use_backend fourth_be if { path /fourth }
+ use_backend fifth_be if { path /fifth }
+
+
+ backend first_be
+ default-server ssl crt client1.pem ca-file ca-auth.crt verify none
+ server s1 "${tmpdir}/ssl.sock"
+
+ backend second_be
+ default-server ssl ca-file ca-auth.crt verify none
+ server s1 "${tmpdir}/ssl.sock" crt client1.pem
+
+ backend third_be
+ default-server ssl crt client1.pem ca-file ca-auth.crt verify none
+ server s1 "${tmpdir}/ssl.sock" crt client2_expired.pem
+
+ backend fourth_be
+ default-server ssl crt client1.pem verify none
+ server s1 "${tmpdir}/ssl.sock" ca-file ca-auth.crt
+
+ backend fifth_be
+ balance roundrobin
+ default-server ssl crt client1.pem verify none
+ server s1 "${tmpdir}/ssl.sock"
+ server s2 "${tmpdir}/ssl.sock" crt client2_expired.pem
+ server s3 "${tmpdir}/ssl.sock"
+
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ca-auth.crt verify required crt-ignore-err all
+
+ acl cert_expired ssl_c_verify 10
+ acl cert_revoked ssl_c_verify 23
+ acl cert_ok ssl_c_verify 0
+
+ http-response add-header X-SSL Ok if cert_ok
+ http-response add-header X-SSL Expired if cert_expired
+ http-response add-header X-SSL Revoked if cert_revoked
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/first"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Ok"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/second"
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Ok"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/third"
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Expired"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/fourth"
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Ok"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/fifth"
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Ok"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/fifth"
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Expired"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq -url "/fifth"
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl == "Ok"
+} -run
diff --git a/reg-tests/ssl/ssl_dh.vtc b/reg-tests/ssl/ssl_dh.vtc
new file mode 100644
index 0000000..9553f37
--- /dev/null
+++ b/reg-tests/ssl/ssl_dh.vtc
@@ -0,0 +1,233 @@
+#REGTEST_TYPE=devel
+
+# This reg-tests checks that the DH-related mechanisms works properly.
+# When no DH is specified, either directly in the server's PEM or through a
+# ssl-dh-param-file global option, and no tune.ssl.default-dh-param is defined,
+# DHE ciphers are disabled.
+# If a default-dh-param is defined, we will use DH parameters of the same size
+# as the server's RSA or DSA key, or default-dh-param if it is smaller.
+# This test has three distinct HAProxy instances, one with no DH-related option
+# used, one with the tune.ssl.default-dh-param global parameter set, and one
+# with an ssl-dh-param-file global option.
+# We use "openssl s_client" calls in order to check the size of the "Server
+# Temp Key" (which will be the same as the DH parameters in case a DHE cipher
+# is used).
+#
+# The main goal of this test was to check that the newly added OpenSSLv3
+# specific DH code worked as before, since it needed to be created in order to
+# stop using deprecated APIs.
+
+varnishtest "Test the DH related SSL options"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v openssl && command -v grep && command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 8 {
+ rxreq
+ txresp
+} -start
+
+
+haproxy h1 -conf {
+ global
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httpslog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ retries 0
+
+ frontend clear-fe
+ bind "fd@${clearlst}"
+ use_backend gen_cert_be if { path /gencert }
+ default_backend dflt_be
+
+ backend dflt_be
+ server s1 "${tmpdir}/ssl_dflt.sock" ssl verify none ssl-max-ver TLSv1.2
+
+ backend gen_cert_be
+ server s1 "${tmpdir}/ssl_dflt_gencert.sock" ssl verify none ssl-max-ver TLSv1.2
+
+ listen ssl-dflt-lst
+ bind "${tmpdir}/ssl_dflt.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt verify optional ciphers "DHE-RSA-AES256-GCM-SHA384" ssl-max-ver TLSv1.2
+ http-response set-header x-ssl-cipher %[ssl_fc_cipher]
+ server s1 ${s1_addr}:${s1_port}
+
+ listen ssl-dflt-gencert-lst
+ bind "${tmpdir}/ssl_dflt_gencert.sock" ssl generate-certificates crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt ca-sign-file ${testdir}/generate_certificates/gen_cert_ca.pem verify optional ciphers "DHE-RSA-AES256-GCM-SHA384" ssl-max-ver TLSv1.2
+ http-response set-header x-ssl-cipher %[ssl_fc_cipher]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+haproxy h2 -conf {
+ global
+ stats socket "${tmpdir}/h2/stats" level admin
+
+ global
+ tune.ssl.default-dh-param 4096
+
+ defaults
+ mode http
+ option httpslog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ retries 0
+
+ listen clear-lst
+ bind "fd@${clearlst_dfltdh}"
+ server s1 "${tmpdir}/ssl_dfltdh.sock" ssl verify none ssl-max-ver TLSv1.2
+
+ listen ssl-4096dh-dflt-lst
+ bind "${tmpdir}/ssl_dfltdh.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt verify optional ciphers "DHE-RSA-AES256-GCM-SHA384" ssl-max-ver TLSv1.2
+ http-response set-header x-ssl-cipher %[ssl_fc_cipher]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+haproxy h3 -conf {
+ global
+ stats socket "${tmpdir}/h3/stats" level admin
+
+ global
+ ssl-dh-param-file ${testdir}/common.4096.dh
+
+ defaults
+ mode http
+ option httpslog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ retries 0
+
+ listen clear-lst
+ bind "fd@${clearlst_dhfile}"
+ server s1 "${tmpdir}/ssl_dhfile.sock" ssl verify none ssl-max-ver TLSv1.2
+
+ listen ssl-dhfile-lst
+ bind "${tmpdir}/ssl_dhfile.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/set_cafile_rootCA.crt verify optional ciphers "DHE-RSA-AES256-GCM-SHA384" ssl-max-ver TLSv1.2
+ http-response set-header x-ssl-cipher %[ssl_fc_cipher]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+#
+# Check that all the SSL backend <-> SSL frontend connections work
+#
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ # No DH parameters are defined, DHE ciphers are unavailable
+ expect resp.status == 503
+} -run
+
+client c2 -connect ${h2_clearlst_dfltdh_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-cipher == "DHE-RSA-AES256-GCM-SHA384"
+} -run
+
+client c3 -connect ${h3_clearlst_dhfile_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-cipher == "DHE-RSA-AES256-GCM-SHA384"
+} -run
+
+client c4 -connect ${h1_clearlst_sock} {
+ txreq -url "/gencert"
+ rxresp
+ # No DH parameters are defined, DHE ciphers are unavailable
+ expect resp.status == 503
+} -run
+
+
+# On the second HAProxy instance, even if default-dh-param is set to 4096, this
+# value is only considered as a maximum DH key length and we will always try to
+# match the server's certificate key length in our DHE key exchange (2048 bits
+# in the case of common.pem).
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl_dfltdh.sock" -tls1_2 2>/dev/null | grep -E "Server Temp Key: DH, 2048 bits"
+}
+
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl_dhfile.sock" -tls1_2 2>/dev/null | grep -E "Server Temp Key: DH, 4096 bits"
+}
+
+
+#
+# Add a custom DH to the server's PEM certificate
+#
+shell {
+ printf "set ssl cert ${testdir}/common.pem <<\n$(cat ${testdir}/common.pem)\n$(cat ${testdir}/common.4096.dh)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl cert ${testdir}/common.pem" | socat "${tmpdir}/h1/stats" -
+
+ printf "set ssl cert ${testdir}/common.pem <<\n$(cat ${testdir}/common.pem)\n$(cat ${testdir}/common.4096.dh)\n\n" | socat "${tmpdir}/h2/stats" -
+ echo "commit ssl cert ${testdir}/common.pem" | socat "${tmpdir}/h2/stats" -
+
+ printf "set ssl cert ${testdir}/common.pem <<\n$(cat ${testdir}/common.pem)\n$(cat ${testdir}/common.4096.dh)\n\n" | socat "${tmpdir}/h3/stats" -
+ echo "commit ssl cert ${testdir}/common.pem" | socat "${tmpdir}/h3/stats" -
+}
+
+
+#
+# Check that all the SSL backend <-> SSL frontend connections still work
+# Common.pem now contains DH parameters so the first instance's frontends
+# can now use DHE ciphers.
+#
+client c5 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-cipher == "DHE-RSA-AES256-GCM-SHA384"
+} -run
+
+client c6 -connect ${h2_clearlst_dfltdh_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-cipher == "DHE-RSA-AES256-GCM-SHA384"
+} -run
+
+client c7 -connect ${h3_clearlst_dhfile_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-cipher == "DHE-RSA-AES256-GCM-SHA384"
+} -run
+
+client c8 -connect ${h1_clearlst_sock} {
+ txreq -url "/gencert"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-cipher == "DHE-RSA-AES256-GCM-SHA384"
+} -run
+
+
+
+#
+# Check the new size of the DH key
+#
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl_dflt.sock" -tls1_2 2>/dev/null | grep -E "Server Temp Key: DH, 4096 bits"
+}
+
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl_dfltdh.sock" -tls1_2 2>/dev/null | grep -E "Server Temp Key: DH, 4096 bits"
+}
+
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl_dhfile.sock" -tls1_2 2>/dev/null | grep -E "Server Temp Key: DH, 4096 bits"
+}
+
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl_dflt_gencert.sock" -tls1_2 2>/dev/null | grep -E "Server Temp Key: DH, 4096 bits"
+}
diff --git a/reg-tests/ssl/ssl_errors.vtc b/reg-tests/ssl/ssl_errors.vtc
new file mode 100644
index 0000000..8fb9c5a
--- /dev/null
+++ b/reg-tests/ssl/ssl_errors.vtc
@@ -0,0 +1,439 @@
+#REGTEST_TYPE=devel
+
+# This reg-test checks that the connection and SSL sample fetches related to
+# errors are functioning properly. It also tests the proper behaviour of the
+# default HTTPS log format and of the error-log-format option which allows to
+# define a specific log format used only in case of connection error (otherwise
+# a line following the configured log-format is output).
+#
+# It works by sending request through three different paths, one using a custom
+# log-format line that contains the connection error and SSL handshake error
+# sample fetches, one using the default HTTPS log-format and one using the
+# legacy error log format.
+#
+# The output log lines are caught by syslog blocks (one for each path) and
+# compared to an expected format.
+# Since the syslog is not by design synchronized with the Varnish clients and
+# servers, synchronization is achieved through barriers, which ensure that
+# syslog messages arrive in the right order.
+#
+# In order to ensure that the log line raised in case of connection error if an
+# error-log-format is defined still follows the log-separate-error option, the
+# log lines raised by the https_fmt_lst listener will be sent to two separate
+# syslog servers.
+#
+
+varnishtest "Test the connection and SSL error fetches."
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev2)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && ssllib_name_startswith(OpenSSL)'"
+feature cmd "command -v socat"
+feature ignore_unknown_macro
+
+server s1 -repeat 4 {
+ rxreq
+ txresp
+} -start
+
+barrier b1 cond 4 -cyclic
+barrier b2 cond 2 -cyclic
+
+
+syslog Slg_cust_fmt -level info {
+ recv
+ expect ~ ".*conn_status:\"0:Success\" hsk_err:\"0:-\" CN=\"/C=FR/O=HAProxy Technologies/CN=Client\",serial=1007,hash=063DCC2E6A9159E66994B325D6D2EF3D17A75B6F"
+
+ barrier b1 sync
+
+ recv
+ expect ~ "ERROR.*conn_status:\"30:SSL client CA chain cannot be verified\" hsk_err:\"134:.*:certificate verify failed\" CN=\"/C=FR/O=HAProxy Technologies/CN=Client\",serial=1007,hash=063DCC2E6A9159E66994B325D6D2EF3D17A75B6F"
+
+ barrier b1 sync
+
+ recv
+ expect ~ "ERROR.*conn_status:\"31:SSL client certificate not trusted\" hsk_err:\"134:.*:certificate verify failed\" CN=\"/C=FR/O=HAProxy Technologies/CN=Client\",serial=1007,hash=063DCC2E6A9159E66994B325D6D2EF3D17A75B6F"
+
+ barrier b1 sync
+
+ # In case of an error occurring before the certificate verification process,
+ # the client certificate chain is never parsed and verified so we can't
+ # have information about the client's certificate.
+ recv
+ expect ~ "ERROR.*conn_status:\"34:SSL handshake failure\" hsk_err:\"193:.*:no shared cipher\" CN=\"\",serial=-,hash=-"
+} -start
+
+syslog Slg_https_fmt -level info {
+ recv
+ expect ~ ".*https_logfmt_ssl_lst~ https_logfmt_ssl_lst/s1.*0/0000000000000000/0/0/.? foo.com/TLSv1.2/AES256-GCM-SHA384"
+
+ barrier b1 sync
+} -start
+
+syslog Slg_https_fmt_err -level info {
+ recv
+ expect ~ "ERROR.*https_logfmt_ssl_lst~ https_logfmt_ssl_lst/<NOSRV>.*30/0000000000000086/0/2/.? foo.com/TLSv1.2/\\(NONE\\)"
+
+ barrier b1 sync
+
+ recv
+ expect ~ "ERROR.*https_logfmt_ssl_lst~ https_logfmt_ssl_lst/<NOSRV>.*31/0000000000000086/20/0/.? foo.com/TLSv1.2/\\(NONE\\)"
+
+ barrier b1 sync
+
+ recv
+ expect ~ "ERROR.*https_logfmt_ssl_lst~ https_logfmt_ssl_lst/<NOSRV>.*34/00000000000000C1/0/0/.? foo.com/TLSv1.2/\\(NONE\\)"
+} -start
+
+syslog Slg_logconnerror -level info {
+ recv
+ expect ~ ".*logconnerror_ssl_lst~ logconnerror_ssl_lst/s1"
+
+ barrier b1 sync
+
+ recv
+ expect ~ ".*logconnerror_ssl_lst/1: SSL client CA chain cannot be verified"
+
+ barrier b1 sync
+
+ recv
+ expect ~ ".*logconnerror_ssl_lst/1: SSL client certificate not trusted"
+
+ barrier b1 sync
+
+ recv
+ expect ~ ".*logconnerror_ssl_lst/1: SSL handshake failure"
+} -start
+
+syslog Slg_bcknd -level info {
+ recv
+ expect ~ ".*bc_err:0:\"Success\" ssl_bc_err:0:"
+
+ barrier b2 sync
+
+ recv
+ expect ~ ".*bc_err:34:\"SSL handshake failure\" ssl_bc_err:134:.*:certificate verify failed"
+
+ barrier b2 sync
+
+ recv
+ expect ~ ".*bc_err:33:\"Server presented an SSL certificate different from the expected one\" ssl_bc_err:134:.*:certificate verify failed"
+
+ barrier b2 sync
+
+ # Verify errors on the server side cannot be caught when using TLSv1.3 but it works for TLSv1.2
+ recv
+ expect ~ ".*bc_err:34:\"SSL handshake failure\" ssl_bc_err:1048:.*:tlsv1 alert unknown ca"
+
+ barrier b2 sync
+
+ recv
+ expect ~ ".*bc_err:34:\"SSL handshake failure\" ssl_bc_err:1040:.* alert handshake failure"
+
+ barrier b2 sync
+
+ recv
+ expect ~ ".*bc_err:34:\"SSL handshake failure\" ssl_bc_err:1040:.* alert handshake failure"
+} -start
+
+syslog Slg_bcknd_fe -level info {
+ # Client c13 - No error
+ # Depending on the version of OpenSSL, the TLS version and ciphersuite will change
+ recv
+ expect ~ ".* Server/(TLSv1.3/TLS_AES_256_GCM_SHA384|TLSv1.2/ECDHE-RSA-AES256-GCM-SHA384)"
+
+ # Client c14 - Server certificate rejected
+ # Depending on the version of OpenSSL, the TLS version and ciphersuite will change
+ recv
+ expect ~ ".* foo.com/(TLSv1.3/TLS_AES_256_GCM_SHA384|TLSv1.2/\\(NONE\\))"
+
+ # Client c15 - Server certificate mismatch (verifyhost option on backend)
+ # Depending on the version of OpenSSL, the TLS version and ciphersuite will change
+ recv
+ expect ~ ".* foo.com/(TLSv1.3/TLS_AES_256_GCM_SHA384|TLSv1.2/\\(NONE\\))"
+
+ # Client c16 - Client certificate rejected
+ recv
+ expect ~ ".* foo.com/TLSv1.2/\\(NONE\\)"
+
+ # Client c17 - Wrong ciphers TLSv1.2
+ recv
+ expect ~ ".* foo.com/TLSv1.2/\\(NONE\\)"
+
+ # Client c18
+ # With OpenSSL1.0.2 -Wrong ciphers TLSv1.2 (same as c17)
+ # With newer versions - Wrong ciphers TLSv1.3 - the client does not get to send its certificate because the error happens before
+ recv
+ expect ~ ".* (foo.com/TLSv1.2|-/TLSv1.3)/\\(NONE\\)"
+} -start
+
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ stats socket "${tmpdir}/h1/stats" level admin
+ .if openssl_version_atleast(3.0.0)
+ set-var proc.ssl_error_mask str(7FFFFF),hex2i
+ .else
+ set-var proc.ssl_error_mask str(FFF),hex2i
+ .endif
+
+ defaults
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ retries 0
+
+ listen clear_lst
+ bind "fd@${clearlst}"
+ default-server ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA2.crt verify none no-ssl-reuse force-tlsv12 sni str(foo.com)
+
+ balance roundrobin
+ server cust_fmt "${tmpdir}/cust_logfmt_ssl.sock"
+ server https_fmt "${tmpdir}/https_logfmt_ssl.sock"
+ server logconnerror "${tmpdir}/logconnerror_ssl.sock"
+
+
+ listen clear_wrong_ciphers_lst
+ bind "fd@${wrongcipherslst}"
+ default-server ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA2.crt verify none no-ssl-reuse force-tlsv12 ciphers "aECDSA" sni str(foo.com)
+
+ balance roundrobin
+ server cust_fmt "${tmpdir}/cust_logfmt_ssl.sock"
+ server https_fmt "${tmpdir}/https_logfmt_ssl.sock"
+ server logconnerror "${tmpdir}/logconnerror_ssl.sock"
+
+
+ # This listener will be used to test backend fetches (bc_err and ssl_bc_err)
+ listen clear_backend_errors_lst
+ bind "fd@${backenderrorslst}"
+ log ${Slg_bcknd_addr}:${Slg_bcknd_port} local0
+ log-format "bc_err:%[bc_err]:%{+Q}[bc_err_str]\ ssl_bc_err:%[ssl_bc_err,and(proc.ssl_error_mask)]:%{+Q}[ssl_bc_err_str]"
+ error-log-format "ERROR bc_err:%[bc_err]:%{+Q}[bc_err_str]\ ssl_bc_err:%[ssl_bc_err,and(proc.ssl_error_mask)]:%[ssl_bc_err_str]"
+
+ balance roundrobin
+ server no_err "${tmpdir}/no_err_ssl.sock" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA2.crt verify required sni str(Server)
+ server srv_cert_rejected "${tmpdir}/srv_rejected_ssl.sock" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA1.crt verify required sni str(foo.com)
+ server mismatch_frontend "${tmpdir}/mismatch_fe_ssl.sock" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA2.crt verify required sni str(foo.com) verifyhost str(toto) # We force TLSv1.2 for this specific case because server-side
+ # verification errors cannot be caught by the backend fetches when
+ # using TLSv1.3
+ server clt_cert_rejected "${tmpdir}/rejected_ssl.sock" ssl crt ${testdir}/set_cafile_client.pem ca-file ${testdir}/set_cafile_interCA2.crt verify none force-tlsv12 sni str(foo.com)
+ server wrong_ciphers "${tmpdir}/wrong_ciphers_ssl.sock" ssl verify none crt ${testdir}/client1.pem ca-file ${testdir}/ca-auth.crt force-tlsv12 ciphers "aECDSA" sni str(foo.com)
+
+ # No TLSv1.3 support with OpenSSL 1.0.2 so we duplicate the previous
+ # wrong cipher test in this case so that the error log remains the same
+.if openssl_version_before(1.1.1)
+ server wrong_ciphers2 "${tmpdir}/wrong_ciphers_ssl.sock" ssl verify none crt ${testdir}/client1.pem ca-file ${testdir}/ca-auth.crt force-tlsv12 ciphers "aECDSA" sni str(foo.com)
+.else
+ server wrong_ciphers_tls13 "${tmpdir}/wrong_ciphers_tls13_ssl.sock" ssl verify none crt ${testdir}/client1.pem ca-file ${testdir}/ca-auth.crt ciphersuites "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" force-tlsv13 sni str(foo.com)
+.endif
+
+
+
+
+ listen cust_logfmt_ssl_lst
+ log ${Slg_cust_fmt_addr}:${Slg_cust_fmt_port} local0
+ mode http
+ log-format "conn_status:\"%[fc_err]:%[fc_err_str]\" hsk_err:\"%[ssl_fc_err]:%[ssl_fc_err_str]\" CN=%{+Q}[ssl_c_s_dn],serial=%[ssl_c_serial,hex],hash=%[ssl_c_sha1,hex]"
+ error-log-format "ERROR conn_status:\"%[fc_err]:%[fc_err_str]\" hsk_err:\"%[ssl_fc_err,and(proc.ssl_error_mask)]:%[ssl_fc_err_str]\" CN=%{+Q}[ssl_c_s_dn],serial=%[ssl_c_serial,hex],hash=%[ssl_c_sha1,hex]"
+ bind "${tmpdir}/cust_logfmt_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-verify-file ${testdir}/set_cafile_rootCA.crt ca-file ${testdir}/set_cafile_interCA1.crt verify required ciphers "kRSA"
+ server s1 ${s1_addr}:${s1_port}
+
+ listen https_logfmt_ssl_lst
+ log ${Slg_https_fmt_addr}:${Slg_https_fmt_port} local0 info
+ log ${Slg_https_fmt_err_addr}:${Slg_https_fmt_err_port} local0 err info
+ option log-separate-errors
+ mode http
+ option httpslog
+ error-log-format "ERROR %ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %[fc_err]/%[ssl_fc_err,and(proc.ssl_error_mask),hex]/%[ssl_c_err]/%[ssl_c_ca_err]/%[ssl_fc_is_resumed] %[ssl_fc_sni]/%sslv/%sslc"
+ bind "${tmpdir}/https_logfmt_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-verify-file ${testdir}/set_cafile_rootCA.crt ca-file ${testdir}/set_cafile_interCA1.crt verify required ciphers "kRSA"
+ server s1 ${s1_addr}:${s1_port}
+
+ listen logconnerror_ssl_lst
+ log ${Slg_logconnerror_addr}:${Slg_logconnerror_port} local0 info
+ mode http
+ option httplog
+ bind "${tmpdir}/logconnerror_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-verify-file ${testdir}/set_cafile_rootCA.crt ca-file ${testdir}/set_cafile_interCA1.crt verify required ciphers "kRSA"
+ server s1 ${s1_addr}:${s1_port}
+
+
+
+ defaults bknd_err_dflt
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ retries 0
+ log ${Slg_bcknd_fe_addr}:${Slg_bcknd_fe_port} local0
+ log-format "%ci:%cp %[ssl_fc_sni]/%sslv/%sslc"
+ error-log-format "ERROR %ci:%cp %[ssl_fc_sni]/%sslv/%sslc"
+
+ # The following listeners allow to test backend error fetches
+ listen no_backend_err_ssl_lst from bknd_err_dflt
+ bind "${tmpdir}/no_err_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-file ${testdir}/set_cafile_interCA2.crt verify none
+ server s1 ${s1_addr}:${s1_port}
+
+ listen srv_rejected_ssl_lst from bknd_err_dflt
+ bind "${tmpdir}/srv_rejected_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-file ${testdir}/set_cafile_interCA2.crt verify none
+ server s1 ${s1_addr}:${s1_port}
+
+ listen mismatch_fe_ssl_lst from bknd_err_dflt
+ bind "${tmpdir}/mismatch_fe_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-file ${testdir}/set_cafile_interCA2.crt verify none
+ server s1 ${s1_addr}:${s1_port}
+
+ listen rejected_clt_ssl_lst from bknd_err_dflt
+ bind "${tmpdir}/rejected_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-file ${testdir}/set_cafile_interCA2.crt verify required
+ server s1 ${s1_addr}:${s1_port}
+
+ listen wrong_ciphers_ssl_lst from bknd_err_dflt
+ bind "${tmpdir}/wrong_ciphers_ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify none force-tlsv12 ciphers "kRSA"
+ server s1 ${s1_addr}:${s1_port}
+
+.if openssl_version_atleast(1.1.1)
+ listen wrong_ciphers_tls13_ssl_lst from bknd_err_dflt
+ bind "${tmpdir}/wrong_ciphers_tls13_ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify none force-tlsv13 ciphersuites "TLS_AES_128_GCM_SHA256"
+ server s1 ${s1_addr}:${s1_port}
+.endif
+
+} -start
+
+
+# The three following requests should all succeed
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c3 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+
+barrier b1 sync
+
+
+# Change the root CA in the frontends
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_rootCA.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_rootCA.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+client c4 -connect ${h1_clearlst_sock} {
+ txreq
+ expect_close
+} -run
+
+client c5 -connect ${h1_clearlst_sock} {
+ txreq
+ expect_close
+} -run
+
+client c6 -connect ${h1_clearlst_sock} {
+ txreq
+ expect_close
+} -run
+
+barrier b1 sync
+
+
+
+# Restore the root CA
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_rootCA.crt <<\n$(cat ${testdir}/set_cafile_rootCA.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_rootCA.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+# Change the intermediate CA in the frontends
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_interCA1.crt <<\n$(cat ${testdir}/set_cafile_interCA2.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_interCA1.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+client c7 -connect ${h1_clearlst_sock} {
+ txreq
+ expect_close
+} -run
+
+client c8 -connect ${h1_clearlst_sock} {
+ txreq
+ expect_close
+} -run
+
+client c9 -connect ${h1_clearlst_sock} {
+ txreq
+ expect_close
+} -run
+
+barrier b1 sync
+
+
+# Restore the intermediate CA in the frontends
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_interCA1.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_interCA1.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+# "No shared cipher" errors
+client c10 -connect ${h1_wrongcipherslst_sock} {
+ txreq
+ expect_close
+} -run
+client c11 -connect ${h1_wrongcipherslst_sock} {
+ txreq
+ expect_close
+} -run
+client c12 -connect ${h1_wrongcipherslst_sock} {
+ txreq
+ expect_close
+} -run
+
+
+shell {
+ printf "set ssl ca-file ${testdir}/set_cafile_interCA2.crt <<\n$(cat ${testdir}/set_cafile_interCA2.crt)\n$(cat ${testdir}/set_cafile_rootCA.crt)\n\n" | socat "${tmpdir}/h1/stats" -
+ echo "commit ssl ca-file ${testdir}/set_cafile_interCA2.crt" | socat "${tmpdir}/h1/stats" -
+}
+
+client c13 -connect ${h1_backenderrorslst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+barrier b2 sync
+client c14 -connect ${h1_backenderrorslst_sock} {
+ txreq
+ expect_close
+} -run
+barrier b2 sync
+client c15 -connect ${h1_backenderrorslst_sock} {
+ txreq
+ expect_close
+} -run
+barrier b2 sync
+client c16 -connect ${h1_backenderrorslst_sock} {
+ txreq
+ expect_close
+} -run
+barrier b2 sync
+client c17 -connect ${h1_backenderrorslst_sock} {
+ txreq
+ expect_close
+} -run
+barrier b2 sync
+client c18 -connect ${h1_backenderrorslst_sock} {
+ txreq
+ expect_close
+} -run
+
+syslog Slg_cust_fmt -wait
+syslog Slg_https_fmt -wait
+syslog Slg_https_fmt_err -wait
+syslog Slg_logconnerror -wait
+syslog Slg_bcknd -wait
+syslog Slg_bcknd_fe -wait
diff --git a/reg-tests/ssl/ssl_frontend_samples.vtc b/reg-tests/ssl/ssl_frontend_samples.vtc
new file mode 100644
index 0000000..e94a37a
--- /dev/null
+++ b/reg-tests/ssl/ssl_frontend_samples.vtc
@@ -0,0 +1,69 @@
+#REGTEST_TYPE=devel
+
+varnishtest "Test the ssl_f_* sample fetches"
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ crt-base ${testdir}
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ server s1 "${tmpdir}/ssl.sock" ssl verify none
+
+ listen ssl-lst
+ mode http
+
+ http-response add-header x-ssl-der %[ssl_f_der,hex]
+ http-response add-header x-ssl-sha1 %[ssl_f_sha1,hex]
+ http-response add-header x-ssl-notafter %[ssl_f_notafter]
+ http-response add-header x-ssl-notbefore %[ssl_f_notbefore]
+ http-response add-header x-ssl-sig_alg %[ssl_f_sig_alg]
+ http-response add-header x-ssl-i_dn %[ssl_f_i_dn]
+ http-response add-header x-ssl-s_dn %[ssl_f_s_dn]
+ http-response add-header x-ssl-s_serial %[ssl_f_serial,hex]
+ http-response add-header x-ssl-key_alg %[ssl_f_key_alg]
+ http-response add-header x-ssl-version %[ssl_f_version]
+
+ bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-der ~ 3082067930820461A0030201020201.*207B5E3D4498BB847BC4DE093F9AD1AD3661C93EE43EB
+ expect resp.http.x-ssl-sha1 == "2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+ expect resp.http.x-ssl-notafter == "180116230238Z"
+ expect resp.http.x-ssl-notbefore == "160117230238Z"
+ expect resp.http.x-ssl-sig_alg == "RSA-SHA256"
+ expect resp.http.x-ssl-i_dn == "/C=FR/ST=Ile-de-France/L=Paris/O=ozon.io/CN=Ozon Test CA/emailAddress=support@ozon.io"
+ expect resp.http.x-ssl-s_dn == "/C=FR/ST=Ile-de-France/L=Neuilly-sur-Seine/O=TOAD Consulting/OU=eParapher Team/CN=www.test1.com/emailAddress=arnault.michel@toad-consulting.fr"
+ expect resp.http.x-ssl-s_serial == "02"
+ expect resp.http.x-ssl-key_alg == "rsaEncryption"
+ expect resp.http.x-ssl-version == "3"
+} -run
+
+
diff --git a/reg-tests/ssl/ssl_generate_certificate.vtc b/reg-tests/ssl/ssl_generate_certificate.vtc
new file mode 100644
index 0000000..96549df
--- /dev/null
+++ b/reg-tests/ssl/ssl_generate_certificate.vtc
@@ -0,0 +1,168 @@
+#REGTEST_TYPE=devel
+
+# This reg-test checks that the 'generate-certificates' SSL option works
+# properly. This option allows to generate server-side certificates on the fly
+# for clients that use an SNI for which no certificate was specified in the
+# configuration file.
+# This test also aims at checking that the 'generate-certificates' and the
+# 'ecdhe' bind options work correctly together.
+# Any bind line having a 'generate-certificates' needs to have a ca-sign-file
+# option as well that specifies the path to a CA pem file (containing a
+# certificate as well as its private key). For this reason, a new
+# ssl_gen_ca.pem CA certificate was created, along with the ssl_gen_server.pem
+# server certificate signed by the CA. This server certificate will be used as
+# a default certificate and will serve as a base for any newly created
+# certificate.
+
+varnishtest "Test the 'generate-certificates' SSL option"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature cmd "command -v openssl && command -v grep"
+feature ignore_unknown_macro
+
+server s1 -repeat 6 {
+ rxreq
+ txresp
+} -start
+
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 2048
+
+ defaults
+ mode http
+ option httpslog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ option httpslog
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ http-request set-var(sess.sni) hdr(x-sni)
+
+ use_backend P-384_backend if { path /P-384 }
+ default_backend default_backend
+
+ backend default_backend
+ server s1 "${tmpdir}/ssl.sock" ssl verify none ssl-max-ver TLSv1.2 sni var(sess.sni)
+
+ backend P-384_backend
+ server s1 "${tmpdir}/ssl_P-384.sock" ssl verify none ssl-max-ver TLSv1.2 sni var(sess.sni)
+
+ listen ssl-lst
+ bind "${tmpdir}/ssl.sock" ssl generate-certificates crt ${testdir}/generate_certificates/gen_cert_server.pem ca-sign-file ${testdir}/generate_certificates/gen_cert_ca.pem ca-file ${testdir}/generate_certificates/gen_cert_ca.pem verify optional
+ http-response add-header x-ssl-s_dn %[ssl_f_s_dn(CN)]
+ http-response add-header x-ssl-i_dn %[ssl_f_i_dn(CN)]
+ http-response add-header x-ssl-sig_alg %[ssl_f_sig_alg]
+ http-response add-header x-ssl-key_alg %[ssl_f_key_alg]
+ http-response add-header x-ssl-sha1 %[ssl_f_sha1,hex]
+
+ server s1 ${s1_addr}:${s1_port}
+
+ listen ssl-lst-P-384
+ bind "${tmpdir}/ssl_P-384.sock" ssl generate-certificates crt ${testdir}/generate_certificates/gen_cert_server.pem ca-sign-file ${testdir}/generate_certificates/gen_cert_ca.pem ca-file ${testdir}/generate_certificates/gen_cert_ca.pem verify optional ecdhe secp384r1
+ http-response add-header x-ssl-s_dn %[ssl_f_s_dn(CN)]
+ http-response add-header x-ssl-i_dn %[ssl_f_i_dn(CN)]
+ http-response add-header x-ssl-sig_alg %[ssl_f_sig_alg]
+ http-response add-header x-ssl-key_alg %[ssl_f_key_alg]
+ http-response add-header x-ssl-sha1 %[ssl_f_sha1,hex]
+
+ server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+# Use default certificate
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sig_alg == "ecdsa-with-SHA256"
+ expect resp.http.x-ssl-i_dn == "ECDSA CA"
+ expect resp.http.x-ssl-s_dn == "server.ecdsa.com"
+ expect resp.http.x-ssl-key_alg == "id-ecPublicKey"
+ expect resp.http.x-ssl-sha1 == "66AC64728CEA0C1F614A89C278FA2F94EDE9AB11"
+} -run
+
+
+# Use default certificate's sni
+client c2 -connect ${h1_clearlst_sock} {
+ txreq -hdr "x-sni: server.ecdsa.com"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sig_alg == "ecdsa-with-SHA256"
+ expect resp.http.x-ssl-i_dn == "ECDSA CA"
+ expect resp.http.x-ssl-s_dn == "server.ecdsa.com"
+ expect resp.http.x-ssl-key_alg == "id-ecPublicKey"
+ expect resp.http.x-ssl-sha1 == "66AC64728CEA0C1F614A89C278FA2F94EDE9AB11"
+} -run
+
+
+
+# Use another SNI - the server certificate should be generated and different
+# than the default one
+client c3 -connect ${h1_clearlst_sock} {
+ txreq -hdr "x-sni: unknown-sni.com"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sig_alg == "ecdsa-with-SHA256"
+ expect resp.http.x-ssl-i_dn == "ECDSA CA"
+ expect resp.http.x-ssl-s_dn == "ECDSA CA"
+ expect resp.http.x-ssl-key_alg == "id-ecPublicKey"
+ expect resp.http.x-ssl-sha1 != "66AC64728CEA0C1F614A89C278FA2F94EDE9AB11"
+} -run
+
+
+# Use default certificate
+client c4 -connect ${h1_clearlst_sock} {
+ txreq -url "/P-384"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sig_alg == "ecdsa-with-SHA256"
+ expect resp.http.x-ssl-i_dn == "ECDSA CA"
+ expect resp.http.x-ssl-s_dn == "server.ecdsa.com"
+ expect resp.http.x-ssl-key_alg == "id-ecPublicKey"
+ expect resp.http.x-ssl-sha1 == "66AC64728CEA0C1F614A89C278FA2F94EDE9AB11"
+} -run
+
+
+# Use default certificate's sni
+client c5 -connect ${h1_clearlst_sock} {
+ txreq -url "/P-384" -hdr "x-sni: server.ecdsa.com"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sig_alg == "ecdsa-with-SHA256"
+ expect resp.http.x-ssl-i_dn == "ECDSA CA"
+ expect resp.http.x-ssl-s_dn == "server.ecdsa.com"
+ expect resp.http.x-ssl-key_alg == "id-ecPublicKey"
+ expect resp.http.x-ssl-sha1 == "66AC64728CEA0C1F614A89C278FA2F94EDE9AB11"
+} -run
+
+
+# Use another SNI - the server certificate should be generated and different
+# than the default one
+client c6 -connect ${h1_clearlst_sock} {
+ txreq -url "/P-384" -hdr "x-sni: unknown-sni.com"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sig_alg == "ecdsa-with-SHA256"
+ expect resp.http.x-ssl-i_dn == "ECDSA CA"
+ expect resp.http.x-ssl-s_dn == "ECDSA CA"
+ expect resp.http.x-ssl-key_alg == "id-ecPublicKey"
+ expect resp.http.x-ssl-sha1 != "66AC64728CEA0C1F614A89C278FA2F94EDE9AB11"
+} -run
+
+# Check that the curves that the server accepts to use correspond to what we
+# expect it to be (according to ecdhe option).
+# The curve with the highest priority is X25519 for OpenSSL 1.1.1 and later,
+# and P-256 for OpenSSL 1.0.2.
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl.sock" -servername server.ecdsa.com -tls1_2 2>/dev/null | grep -E "Server Temp Key: (ECDH, P-256, 256 bits|ECDH, prime256v1, 256 bits|X25519, 253 bits)"
+}
+
+shell {
+ echo "Q" | openssl s_client -unix "${tmpdir}/ssl_P-384.sock" -servername server.ecdsa.com 2>/dev/null| grep -E "Temp Key: ECDH,.+, 384 bits"
+}
diff --git a/reg-tests/ssl/ssl_reuse.vtc b/reg-tests/ssl/ssl_reuse.vtc
new file mode 100644
index 0000000..4ebd34e
--- /dev/null
+++ b/reg-tests/ssl/ssl_reuse.vtc
@@ -0,0 +1,141 @@
+#REGTEST_TYPE=devel
+
+# This reg-test tests 4 scenarios with and without resumption tickets, with TLSv1.3 and TLSv1.2
+# Each client will try to established a connection, then try to reconnect 20 times resuming.
+
+
+varnishtest "Test if the SSL session/ticket reuse work correctly"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
+feature ignore_unknown_macro
+
+server s1 -repeat 84 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ # forced to 1 here, because there is a cached session per thread
+ nbthread 1
+
+
+ defaults
+ mode http
+ option httplog
+ option logasap
+ log stderr local0 debug err
+ option httpclose
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen clst1
+ bind "fd@${clst1}"
+ server s1 "${h1_fe1_addr}:${h1_fe1_port}" ssl verify none sni str(www.test1.com)
+ http-response add-header x-ssl-bc-resumed %[ssl_bc_is_resumed]
+
+ listen clst2
+ bind "fd@${clst2}"
+ server s1 "${h1_fe2_addr}:${h1_fe2_port}" ssl verify none sni str(www.test1.com)
+ http-response add-header x-ssl-bc-resumed %[ssl_bc_is_resumed]
+
+ listen clst3
+ bind "fd@${clst3}"
+ server s1 "${h1_fe3_addr}:${h1_fe3_port}" ssl verify none sni str(www.test1.com)
+ http-response add-header x-ssl-bc-resumed %[ssl_bc_is_resumed]
+
+ listen clst4
+ bind "fd@${clst4}"
+ server s1 "${h1_fe4_addr}:${h1_fe4_port}" ssl verify none sni str(www.test1.com)
+ http-response add-header x-ssl-bc-resumed %[ssl_bc_is_resumed]
+
+ listen ssl
+ bind "fd@${fe1}" ssl crt ${testdir}/common.pem ssl-max-ver TLSv1.2
+ bind "fd@${fe2}" ssl crt ${testdir}/common.pem ssl-max-ver TLSv1.2 no-tls-tickets
+ bind "fd@${fe3}" ssl crt ${testdir}/common.pem ssl-min-ver TLSv1.3
+ bind "fd@${fe4}" ssl crt ${testdir}/common.pem ssl-min-ver TLSv1.3 no-tls-tickets
+
+ http-response add-header x-ssl-resumed %[ssl_fc_is_resumed]
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+# first bind
+# the first connection is not resumed
+client c1 -connect ${h1_clst1_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 0
+} -run
+# the next 20 connections are resumed
+client c1 -connect ${h1_clst1_sock} -repeat 20 {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 1
+} -run
+
+# second bind
+client c2 -connect ${h1_clst2_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 0
+} -run
+
+client c2 -connect ${h1_clst2_sock} -repeat 20 {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 1
+} -run
+
+# third bind
+client c3 -connect ${h1_clst3_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 0
+} -run
+
+client c3 -connect ${h1_clst3_sock} -repeat 20 {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 1
+} -run
+
+# fourth bind
+client c4 -connect ${h1_clst4_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 0
+} -run
+
+client c4 -connect ${h1_clst4_sock} -repeat 20 {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-resumed == 1
+} -run
+
+
+# Could be useful to debug the result, the ssl_fc_is_resumed field in the log must be 1 after the 2nd command
+#shell {
+#
+# HOST=${h1_fe4_addr}
+# if [ "${h1_fe4_addr}" = "::1" ] ; then
+# HOST="\[::1\]"
+# fi
+#
+# rm sess.pem; (echo -e -n "GET / HTTP/1.1\r\n\r\n"; sleep 1) | openssl s_client -connect $HOST:${h1_fe4_port} -tls1_3 -sess_out sess.pem -keylogfile keys1.txt -servername www.test1.com > /tmp/ssl_debug1; echo | openssl s_client -connect ${HOST}:${h1_fe4_port} -tls1_3 -sess_in sess.pem -keylogfile keys2.txt -servername www.test1.com >> /tmp/ssl_debug1
+# echo "GET / HTTP/1.1" | openssl s_client -connect $HOST:${h1_fe4_port} -tls1_3 -servername www.test1.com
+#}
+
+haproxy h1 -cli {
+ send "show info"
+ expect ~ ".*SslFrontendSessionReuse_pct: 95.*"
+}
+
diff --git a/reg-tests/ssl/ssl_server_samples.vtc b/reg-tests/ssl/ssl_server_samples.vtc
new file mode 100644
index 0000000..ebfaad0
--- /dev/null
+++ b/reg-tests/ssl/ssl_server_samples.vtc
@@ -0,0 +1,73 @@
+#REGTEST_TYPE=devel
+
+varnishtest "Test the ssl_s_* sample fetches"
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+ crt-base ${testdir}
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ http-response add-header x-ssl-sha1 %[ssl_s_sha1,hex]
+ http-response add-header x-ssl-notafter %[ssl_s_notafter]
+ http-response add-header x-ssl-notbefore %[ssl_s_notbefore]
+ http-response add-header x-ssl-sig_alg %[ssl_s_sig_alg]
+ http-response add-header x-ssl-i_dn %[ssl_s_i_dn]
+ http-response add-header x-ssl-s_dn %[ssl_s_s_dn]
+ http-response add-header x-ssl-s_serial %[ssl_s_serial,hex]
+ http-response add-header x-ssl-key_alg %[ssl_s_key_alg]
+ http-response add-header x-ssl-der %[ssl_s_der,hex]
+ http-response add-header x-ssl-chain-der %[ssl_s_chain_der,hex]
+ http-response add-header x-ssl-version %[ssl_s_version]
+
+ server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(www.test1.com)
+
+ listen ssl-lst
+ mode http
+
+ bind "${tmpdir}/ssl.sock" ssl strict-sni crt-list ${testdir}/localhost.crt-list
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+client c1 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-ssl-sha1 == "2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+ expect resp.http.x-ssl-notafter == "180116230238Z"
+ expect resp.http.x-ssl-notbefore == "160117230238Z"
+ expect resp.http.x-ssl-sig_alg == "RSA-SHA256"
+ expect resp.http.x-ssl-i_dn == "/C=FR/ST=Ile-de-France/L=Paris/O=ozon.io/CN=Ozon Test CA/emailAddress=support@ozon.io"
+ expect resp.http.x-ssl-s_dn == "/C=FR/ST=Ile-de-France/L=Neuilly-sur-Seine/O=TOAD Consulting/OU=eParapher Team/CN=www.test1.com/emailAddress=arnault.michel@toad-consulting.fr"
+ expect resp.http.x-ssl-s_serial == "02"
+ expect resp.http.x-ssl-key_alg == "rsaEncryption"
+ expect resp.http.x-ssl-version == "3"
+ expect resp.http.x-ssl-der ~ 3082067930820461A0030201020201.*5E3D4498BB847BC4DE093F9AD1AD3
+ expect resp.http.x-ssl-chain-der ~ 3082067930820461A0030201020201.*527A6D6780A610484CE356C4C4E1C
+} -run
+
+
diff --git a/reg-tests/ssl/ssl_simple_crt-list.vtc b/reg-tests/ssl/ssl_simple_crt-list.vtc
new file mode 100644
index 0000000..7f15056
--- /dev/null
+++ b/reg-tests/ssl/ssl_simple_crt-list.vtc
@@ -0,0 +1,50 @@
+#REGTEST_TYPE=bug
+varnishtest "Test for the bug #940"
+# Test that the SNI are correctly inserted with the same file multiple times.
+
+#REQUIRE_VERSION=2.2
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 4 {
+ rxreq
+ txresp
+} -start
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ crt-base ${testdir}
+ stats socket "${tmpdir}/h1/stats" level admin
+
+ defaults
+ mode http
+ option httplog
+ log stderr local0 debug err
+ option logasap
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+
+ listen clear-lst
+ bind "fd@${clearlst}"
+ balance roundrobin
+ server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(record1.bug940.domain.tld)
+ server s2 "${tmpdir}/ssl.sock" ssl verify none sni str(record2.bug940.domain.tld)
+ server s3 "${tmpdir}/ssl.sock" ssl verify none sni str(record3.bug940.domain.tld)
+ server s4 "${tmpdir}/ssl.sock" ssl verify none sni str(record4.bug940.domain.tld)
+
+ listen ssl-lst
+ mode http
+ bind "${tmpdir}/ssl.sock" ssl strict-sni crt-list ${testdir}/simple.crt-list
+
+ server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+client c1 -repeat 4 -connect ${h1_clearlst_sock} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/ssl/wrong_ctx_storage.vtc b/reg-tests/ssl/wrong_ctx_storage.vtc
new file mode 100644
index 0000000..c6cb19a
--- /dev/null
+++ b/reg-tests/ssl/wrong_ctx_storage.vtc
@@ -0,0 +1,45 @@
+# commit 28962c9
+# BUG/MAJOR: ssl: OpenSSL context is stored in non-reserved memory slot
+#
+# We never saw unexplicated crash with SSL, so I suppose that we are
+# luck, or the slot 0 is always reserved. Anyway the usage of the macro
+# SSL_get_app_data() and SSL_set_app_data() seem wrong. This patch change
+# the deprecated functions SSL_get_app_data() and SSL_set_app_data()
+# by the new functions SSL_get_ex_data() and SSL_set_ex_data(), and
+# it reserves the slot in the SSL memory space.
+#
+# For information, this is the two declaration which seems wrong or
+# incomplete in the OpenSSL ssl.h file. We can see the usage of the
+# slot 0 whoch is hardcoded, but never reserved.
+#
+# #define SSL_set_app_data(s,arg) (SSL_set_ex_data(s,0,(char *)arg))
+# #define SSL_get_app_data(s) (SSL_get_ex_data(s,0))
+
+#REGTEST_TYPE=bug
+
+varnishtest "OpenSSL bug: Random crashes"
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+
+haproxy h1 -conf {
+ global
+ tune.ssl.default-dh-param 2048
+ tune.ssl.capture-buffer-size 1
+
+ listen frt
+ mode http
+ bind "fd@${frt}" ssl crt ${testdir}/common.pem
+ http-request redirect location /
+} -start
+
+shell {
+ HOST=${h1_frt_addr}
+ if [ "${h1_frt_addr}" = "::1" ] ; then
+ HOST="\[::1\]"
+ fi
+ for i in 1 2 3 4 5; do
+ curl -i -k https://$HOST:${h1_frt_port} & pids="$pids $!"
+ done
+ wait $pids
+}
diff --git a/reg-tests/startup/automatic_maxconn.vtc b/reg-tests/startup/automatic_maxconn.vtc
new file mode 100644
index 0000000..0173916
--- /dev/null
+++ b/reg-tests/startup/automatic_maxconn.vtc
@@ -0,0 +1,104 @@
+varnishtest "Automatic maxconn computation"
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "$HAPROXY_PROGRAM -cc '!feature(OBSOLETE_LINKER)'"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature ignore_unknown_macro
+#REGTEST_TYPE=broken
+
+
+# Check the maxconn computation with the -m parameter
+# Broken because it can't work with ASAN.
+
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+
+haproxy h1 -arg "-m 1024" -conf {
+} -start
+
+haproxy h1 -cli {
+ send "show info"
+ expect ~ ".*Maxconn: (29000|28000)\n.*"
+}
+
+haproxy h2 -arg "-m 384" -conf {
+} -start
+
+haproxy h2 -cli {
+ send "show info"
+ expect ~ ".*Maxconn: (11000|10000)\n.*"
+}
+
+haproxy h3 -arg "-m 256" -conf {
+} -start
+
+haproxy h3 -cli {
+ send "show info"
+ expect ~ ".*Maxconn: (7300|7000)\n.*"
+}
+
+# 1 SSL front but no back
+
+haproxy h4 -arg "-m 256" -conf {
+ defaults
+ mode http
+ timeout connect 1s
+ timeout client 1s
+ timeout server 1s
+
+ frontend fe1
+ bind "fd@${fe1}" ssl crt ${testdir}/common.pem
+
+} -start
+
+haproxy h4 -cli {
+ send "show info"
+ expect ~ ".*Maxconn: 1900\n.*"
+}
+
+# 1 SSL back but not front
+
+haproxy h5 -arg "-m 256" -conf {
+ defaults
+ mode http
+ timeout connect 1s
+ timeout client 1s
+ timeout server 1s
+
+ listen li2
+ bind "fd@${li2}"
+ server ssl "${s1_addr}:${s1_port}" ssl verify none
+
+} -start
+
+haproxy h5 -cli {
+ send "show info"
+ expect ~ ".*Maxconn: 1900\n.*"
+}
+
+
+# 1 SSL front and 1 back
+
+haproxy h6 -arg "-m 256" -conf {
+ defaults
+ mode http
+ timeout connect 1s
+ timeout client 1s
+ timeout server 1s
+
+ listen li3
+ bind "fd@${li3}" ssl crt ${testdir}/common.pem
+ server ssl "${s1_addr}:${s1_port}" ssl verify none
+
+} -start
+
+haproxy h6 -cli {
+ send "show info"
+ expect ~ ".*Maxconn: 1700\n.*"
+}
+
diff --git a/reg-tests/startup/check_condition.vtc b/reg-tests/startup/check_condition.vtc
new file mode 100644
index 0000000..3ab6ae4
--- /dev/null
+++ b/reg-tests/startup/check_condition.vtc
@@ -0,0 +1,32 @@
+varnishtest "Tests the -cc argument"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+
+shell {
+ set -e
+ $HAPROXY_PROGRAM -cc "version_atleast(2.4)"
+ ! $HAPROXY_PROGRAM -cc "version_atleast(1024)"
+
+ $HAPROXY_PROGRAM -cc "streq(foo,'foo')"
+ $HAPROXY_PROGRAM -cc "streq(\"foo bar\",'foo bar')"
+ ! $HAPROXY_PROGRAM -cc "streq(foo,bar)"
+
+ if $HAPROXY_PROGRAM -cc "version_atleast(2.5-dev2)"; then
+ export TESTVAR=1
+ $HAPROXY_PROGRAM -cc 'defined(TESTVAR) && streq("$TESTVAR","1")'
+ $HAPROXY_PROGRAM -cc 'feature(OPENSSL) || !feature(OPENSSL)'
+ $HAPROXY_PROGRAM -cc '1&&!0&&!((streq(a,b)||!streq(a,a)&&1)||strneq(a,a))'
+ $HAPROXY_PROGRAM -cc '1 &&! 0&& !((streq(a,b)||!streq(a,a)&&1)||strneq(a,a))'
+ $HAPROXY_PROGRAM -cc '1 && !0 && !((streq(a,b) || !streq(a,a) && 1) || strneq(a,a))'
+ ! $HAPROXY_PROGRAM -cc '1 && !0 && !((streq(a,b) || !streq(a,a) && 1) || strneq(a,b))'
+ ! $HAPROXY_PROGRAM -cc '1 && !0 && !((streq(a,a) || !streq(a,a) && 1) || strneq(a,a))'
+ # empty string is always false
+ ! $HAPROXY_PROGRAM -cc ''
+ # non-zero is true
+ $HAPROXY_PROGRAM -cc '-1000 && 200'
+ # check for various parsing errors (extra/missing chars)
+ ! $HAPROXY_PROGRAM -cc '200rrr'
+ ! $HAPROXY_PROGRAM -cc '!(0))'
+ ! $HAPROXY_PROGRAM -cc 'streq(a,"a)'
+ fi
+} -run
diff --git a/reg-tests/startup/common.pem b/reg-tests/startup/common.pem
new file mode 100644
index 0000000..206e417
--- /dev/null
+++ b/reg-tests/startup/common.pem
@@ -0,0 +1,117 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAnb0BDF7FsqzslakNg7u/n/JQkq6nheuKwvyTqECfpc9y7uSB
+e/vrEFqBaDSLQagJxuZdL5geFeVtRbdAoB97N1/LZa6vecjjgGSP0Aag/gS/ocnM
+RIyvlVWWT9MrD46OG3qZY1ORU1ltrVL0NKttJP8xME7j3bTwIDElx/hNI0n7L+yS
+kAe2xb/7CbZRfoOhjTVAcGv4aSLVc/Hi8k6VkIzdOEtH6TcghXmuGcuqvLNH9Buo
+syngKTcQ8zg6J+e64aVvC+e7vi94uil9Qu+JHm0pkDzAZ2WluNsuXlrJToPirWyj
+6/YdN6xgSI1hbZkBmUPAebgYuxBt6huvfyQd3wIDAQABAoIBABojc8UE/2W4WgwC
+04Z82ig7Ezb7Ui9S9M+S4zUCYHItijIkE4DkIfO3y7Hk4x6iJdyb191HK9UdC5p9
+32upS9XFPgM/izx3GZvxDhO+xXbSep7ovbyuQ3pPkHTx3TTavpm3GyvmcTKKoy4R
+jP4dWhzDXPdQW1ol3ZS4EDau4rlyClY6oi1mq9aBEX3MqVjB/nO7s2AbdgclAgP2
+OZMhTzWYR1k5tYySHCXh3ggGMCikyvHU0+SsGyrstYzP1VYi/n3f0VgqW/5ZjG8x
+6SHpe04unErPF3HuSun2ZMCFdBxaTFZ8FENb8evrSXe3nQOc9W21RQdRRrNNUbjl
+JYI4veECgYEA0ATYKMS1VCUYRZoQ49b5GTg7avUYqfW4bEo4fSfBue8NrnKR3Wu8
+PPBiCTuIYq1vSF+60B7Vu+hW0A8OuQ2UuMxLpYcQ7lKfNad/+yAfoWWafIqCqNU9
+at0QMdbW6A69d6jZt7OrXtleBsphCnN58jTz4ch4PIa2Oyq46NUXCvUCgYEAwh8t
+G6BOHOs3yRNI2s9Y9EEfwoil2uIKrZhqiL3AwdIpu5uNIMuPnbaEpXvRX6jv/qtL
+321i8vZLc31aM7zfxQ6B4ReQFJfYC80FJsWvcLwT9hB9mTJpLS4sIu5tzQc87O6w
+RtjFMom+5ns5hfPB4Eccy0EtbQWVY4nCzUeO6QMCgYBSvqqRRPXwG7VU8lznlHqP
+upuABzChYrnScY+Y0TixUlL54l79Wb6N6vzEOWceAWkzu8iewrU4QspNhr/PgoR3
+IeSxWlG0yy7Dc/ZnmTabx8O06I/iwrfkizzG5nOj6UEamRLJjPGNEB/jyZriQl7u
+pnugg1K4mMliLbNSAnlhBQKBgQCmYepbv260Qrex1KGhSg9Ia3k5V74weYYFfJnz
+UhChD+1NK+ourcsOtp3C6PlwMHBjq5aAjlU9QfUxq8NgjQaO8/xGXdfUjsFSfAtq
+TA4vZkUFpuTAJgEYBHc4CXx7OzTxLzRPxQRgaMgC7KNFOMR34vu/CsJQq3R7uFwL
+bsYC2QKBgQCtEmg1uDZVdByX9zyUMuRxz5Tq/vDcp+A5lJj2mha1+bUMaKX2+lxQ
+vPxY55Vaw/ukWkJirRrpGv6IytBn0dLAFSlKZworZGBaxsm8OGTFJ5Oe9+kZTjI9
+hvjpClOA1otbmj2F2uZAbuIjxQGDNUkLoifN5yDYCC8JPujHuHmULw==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIGeTCCBGGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJGUjEW
+MBQGA1UECBMNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxEDAOBgNVBAoT
+B296b24uaW8xFTATBgNVBAMTDE96b24gVGVzdCBDQTEeMBwGCSqGSIb3DQEJARYP
+c3VwcG9ydEBvem9uLmlvMB4XDTE2MDExNzIzMDIzOFoXDTE4MDExNjIzMDIzOFow
+gb4xCzAJBgNVBAYTAkZSMRYwFAYDVQQIEw1JbGUtZGUtRnJhbmNlMRowGAYDVQQH
+ExFOZXVpbGx5LXN1ci1TZWluZTEYMBYGA1UEChMPVE9BRCBDb25zdWx0aW5nMRcw
+FQYDVQQLEw5lUGFyYXBoZXIgVGVhbTEWMBQGA1UEAxMNd3d3LnRlc3QxLmNvbTEw
+MC4GCSqGSIb3DQEJARYhYXJuYXVsdC5taWNoZWxAdG9hZC1jb25zdWx0aW5nLmZy
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnb0BDF7FsqzslakNg7u/
+n/JQkq6nheuKwvyTqECfpc9y7uSBe/vrEFqBaDSLQagJxuZdL5geFeVtRbdAoB97
+N1/LZa6vecjjgGSP0Aag/gS/ocnMRIyvlVWWT9MrD46OG3qZY1ORU1ltrVL0NKtt
+JP8xME7j3bTwIDElx/hNI0n7L+ySkAe2xb/7CbZRfoOhjTVAcGv4aSLVc/Hi8k6V
+kIzdOEtH6TcghXmuGcuqvLNH9BuosyngKTcQ8zg6J+e64aVvC+e7vi94uil9Qu+J
+Hm0pkDzAZ2WluNsuXlrJToPirWyj6/YdN6xgSI1hbZkBmUPAebgYuxBt6huvfyQd
+3wIDAQABo4IBvzCCAbswCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUFBwMB
+MB0GA1UdDgQWBBTIihFNVNgOseQnsWEcAQxAbIKE4TCBsgYDVR0jBIGqMIGngBRv
+G9At9gzk2MW5Z7JVey1LtPIZ8KGBg6SBgDB+MQswCQYDVQQGEwJGUjEWMBQGA1UE
+CBMNSWxlLWRlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxEDAOBgNVBAoTB296b24u
+aW8xFTATBgNVBAMTDE96b24gVGVzdCBDQTEeMBwGCSqGSIb3DQEJARYPc3VwcG9y
+dEBvem9uLmlvggkA15FtIaGcrk8wDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg9j
+b21tb25OYW1lOmNvcHkwCQYDVR0SBAIwADBIBgNVHR8EQTA/MD2gO6A5hjdodHRw
+Oi8vb3BlbnNzbGNhLnRvYWQtY29uc3VsdGluZy5jb20vb3BlbnZwbi9MYXRlc3Qu
+Y3JsMBEGCWCGSAGG+EIBAQQEAwIGQDAxBglghkgBhvhCAQ0EJBYiVE9BRC1Db25z
+dWx0aW5nIHNlcnZlciBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAgEAewDa
+9BukGNJMex8gsXmmdaczTr8yh9Uvw4NJcZS38I+26o//2g+d6i7wxcQg8hIm62Hj
+0TblGU3+RsJo4uzcWxxA5YUYlVszbHNBRpQengEE5pjwHvoXVMNES6Bt8xP04+Vj
+0qVnA8gUaDMk9lN5anK7tF/mbHOIJwHJZYCa2t3y95dIOVEXFwOIzzbSbaprjkLN
+w0BgR5paJz7NZWNqo4sZHUUz94uH2bPEd01SqHO0dJwEVxadgxuPnD05I9gqGpGX
+Zf3Rn7EQylvUtX9mpPaulQPXc3emefewLUSSAdnZrVikZK2J/B4lSi9FpUwl4iQH
+pZoE0QLQHtB1SBKacnOAddGSTLSdFvpzjErjjWSpMukF0vutmrP86GG3xtshWVhI
+u+yLfDJVm/pXfaeDtWMXpxIT/U1i0avpk5MZtFMRC0MTaxEWBTnnJm+/yiaAXQYg
+E1ZIP0mkZkiUojIawTR7JTjHGhIraP9UVPNceVy0DLfETHEou3vhwBn7PFOz7piJ
+wjp3A47DStJD4fapaX6B1fqM+n34CMD9ZAiJFgQEIQfObAWC9hyr4m+pqkp1Qfuw
+vsAP/ZoS1CBirJfm3i+Gshh+VeH+TAmO/NBBYCfzBdgkNz4tJCkOc7CUT/NQTR/L
+N2OskR/Fkge149RJi7hHvE3gk/mtGtNmHJPuQ+s=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIJazCCBVOgAwIBAgIUWHoc5e2FUECgyCvyVf8wCtt8gTYwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA4MDQxODU4MTZaFw0yMDA5
+MDMxODU4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggQiMA0GCSqGSIb3DQEB
+AQUAA4IEDwAwggQKAoIEAQDARiuHkhrnf38Md1nxGDSneJfwv/QksdNNMNTJBdjg
+OVmaRCIAyz43oefTWDQ/TebbSwB+Lg9pud1zadGWhlZRhCgBPP8JDMhIKH4eXIRk
+5IIa8WD08EwvSlqJL0r4gsMtVsxy7BZHAkka/2Ket9pyGt4kG5n75RFdc6BI80/8
+RwJt/MDxPrcVBAT7LnCluxQpyya9mZCabj7l+9a2yU2hgWS6QqfZJ133krkP/MMh
+AEQkSoA4mmBwWk9yPqXmUqiOi7v6iLkIUEh5SgYVPRk9BtU/kDaUdSwuqRrpCZo4
+SsWZWFLxBmLHkSh+G+BWjCVYMQr2ye7e+VMT/20+5xAfq4fj9n5BsPcx3QcVuTof
+RAc/Oygnt4MYnIcUb7zRFvCAvgpUHL7BnEn6nhyXjHJGqGDchsg8m9t3v/Y3ohq+
+qmrSzdeuylE1n3W5aWJlbFmyXegNP45MJ0xicesVrXEWF7YD/ir9mGJ8bQYr4blf
+77PrbF02komC6AzVPKOJa0jR+eW1wErzYlkYgez6ylBWCiHJd1dhEHlK3h2rXdYa
+Gnb45ILCLpEDjNEUrHifLLNXwqJpgZQsJU6BgMgk7ZgBfAKrCfTeg0rkCqCAPeVb
+8eSLf7FBF7YBRJ5P6u8qXc4RtgEu607GaWV0gIMfyVBY52oV+OaNsEdFetrJnp3c
+friG8vJ+7jdq6zjUCGgnfUIHoViJPh3JuFfhA3jT0gQDKW5PeI7dxhrNvlqdYfHI
+fxX7Y1/J6cTQkqJ1cai2f0bwJIJiTAThNbG+zrtjJ7fZ3wJ4udyU/IKrwShqtmTb
+1Ofj0tJDdwOH8i84vIySLUvR9aAb7ClFlnsx6rzwOxG90W7C0LA2M0EHm4FezJm/
+FfujnZwEWr1T9Wki6qE0MHCbdN/TTDws//EKkkE44FC+amL96w0IQl70vpE37j2A
+zlDWvFFID95SIxfmpkwWDvXDKv6gr1GMLeysCl2fgpY05Xidw5cEo9/tEkuWn/dG
+x/D9hnLBGeroA0251ES12jemqDjI2U0tfaeHakjwSsoWElf94Qmuh2iPZ+1zIxQs
+7o6nAWN8X9hfsmrDTTHlww0TEfrjlbzG5Yh+0ZRxmejgiUyOCXck+eh/ZXMXvfWh
+y3CorIIuWgkRjm80PYkdaRDJdZuyP6R7tXfTXNVzAiSQf0Qx9ru2KB2Fs/XZPamH
+KjItAU5Q6msIVvaRMS0muQgV+b6hqSEBzqXqJfAlpVLHXr5FqK+U7EB9y02B6piB
+tAmxqXP8OOCoQql6/vgIcrDFUOo6KtGBW36ef74XE3KCUVaIzVJZSIt6i/Vi0bZj
+bAjsJUQ3qDlHdorv9TRVOhnC1GUz7SuYnpEOyiXmyx3LAgMBAAGjUzBRMB0GA1Ud
+DgQWBBQ62csZcH/meQcENHhNbqz9LMzwjjAfBgNVHSMEGDAWgBQ62csZcH/meQcE
+NHhNbqz9LMzwjjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IEAQBA
+wLsGf3R1+/I2zQE+lsj7RasZtA/Cos92iEGDAPvFbx9e+roG8Gg8KBsEJu/HN0JH
+lMMiQ8dDRHSBMvRBENL5/57oOOhmqc+1u5sazLuANhzAYPZG17Klib7YpEwWoXar
+FDDiJYtCyLW0oNLpCswYopWK9GC0RJNucB0NFvOxehJ2sP2/fxGBQMB09L6mjKjd
+4KsOzyd3dNf0VYS6jB+/1pcKSHKQUo9HRHB5FK04PsYHoh4AtmEHvmYQKcWWidgU
+v26ftlH00ERzuW2juqBbz9mghlNRqXi0IyZ9b4tSj29dxW+WWFzo7j2zEPaD6z2W
+DEHq7zvON+g+q6qLgWeszqMgJzjvWjMj00E/t06PoHPiz/cAnDKEqp+ZzxCIFrxj
+/qneChpogDWyLbawhyyzbZvbirx5znOSbWjPZgydqaNEFViqbxwinBx4Xxabo6XN
+TU020FuMWmgfbIcvtgjKgyKqc97l7JMNNm7LQV9+9W0U5zdIqQKLZ9MMrd2w3xh4
+MAB8NKnwzHReK0TWwUU9HSgFAGdEX6HnyZ3bQ13ijg+sNBRMEi0gBHaqZKDdyoft
+B2u2uasSwioV48dbSIcHl+rTBKxiMh5XQ7ENnaGOJkjsIqTVzizqnPHU8eMBnSbb
+dsXlamROYII44+j3Ku6OGt51w86eGk4VxI3tmaECcJKqTkwUFD8AcNDrkjtmLuxK
+12yjnoM+u1cclfqQ5NOtRc6MJZ27jCobfBBhVdKVDp4X1WNyqGlbsU5adDAzknuI
+GT7MJO7lGjkZX2n54BNPSfrSknYMOVYcZqL0Dbcrhx5IyEmg+iOlOu1HO1tdnZop
+ej4vT+1V2w9Sa4Wo3UCo84jcm5v/4z7jCYh4BRQ60CFb7GLxZoqXIslcGSPool3n
+jl8JWoaLXrJUPfZGXo1iAlayJ5EiMyZl4eB/TBUf6TMm8vLvsPiUT+CEsjLppOdS
+eYppZAZ6H1JrJGs5kKBdOJHGn6Pkp5QsHIswOBd1HqHrBbYbZmDaDLRHduILWLrM
+e0/IfDdeXB/bKfmZoEpT8xRiauw15p0AHLumiK7KISAehfgBqUnxx+YmgGoZ7EWX
+KnMYAfCuC6oJ1DL0gp4Z9yMK1eu+GV1sLxPq9ZruEHW1R+H+4sGyiA5Gso2tgB6/
+XW//wxKclNp5LZR7hqfs/kGuh5asrJrnEbMwWn2+tr/LqfYtYh1D6nHfIXpT0o1d
+rNy/HrsKnRDMWxjm03r4hCViuNVD3Zb9anAF/NSPDVu8ATM5JbJNrCYX4eipz6ZE
+aQBkwIBkTPgtgP4r8v2G+uMYDw8nq7xh72FK107aeTTwc6MgU5jfeFNMr2XJisJd
+lSem1ngKYQSEzjVsTE4c
+-----END CERTIFICATE-----
diff --git a/reg-tests/startup/default_rules.vtc b/reg-tests/startup/default_rules.vtc
new file mode 100644
index 0000000..cd86f74
--- /dev/null
+++ b/reg-tests/startup/default_rules.vtc
@@ -0,0 +1,185 @@
+varnishtest "Misuses of defaults section defining TCP/HTTP rules"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+#
+# anonymous defaults section cannot define TCP/HTTP rules
+#
+haproxy h1 -conf-BAD {} {
+ defaults
+ http-request set-header X-Hdr 1
+}
+
+haproxy h2 -conf-BAD {} {
+ defaults
+ http-response set-header X-Hdr 1
+}
+
+haproxy h3 -conf-BAD {} {
+ defaults
+ http-after-request set-header X-Hdr 1
+}
+
+haproxy h4 -conf-BAD {} {
+ defaults
+ tcp-request connection accept
+}
+
+haproxy h5 -conf-BAD {} {
+ defaults
+ tcp-request session accept
+}
+
+haproxy h6 -conf-BAD {} {
+ defaults
+ tcp-request inspect-delay 5s
+ tcp-request content accept
+}
+
+haproxy h7 -conf-BAD {} {
+ defaults
+ tcp-response inspect-delay 5s
+ tcp-response content accept
+}
+
+#
+# defaults section defining TCP/HTTP rules cannot be used to init another
+# defaults section
+#
+haproxy h8 -conf-BAD {} {
+ defaults invalid
+ tcp-response inspect-delay 5s
+ tcp-response content accept
+
+ defaults from invalid
+ mode tcp
+}
+
+#
+# defaults section defining TCP/HTTP rules cannot be used to init a listen
+# section
+#
+haproxy h9 -conf-BAD {} {
+ defaults invalid
+ tcp-request inspect-delay 5s
+ tcp-request content accept
+
+ listen li from invalid
+ mode tcp
+ bind "fd@${lih9}"
+ server www 127.0.0.1:80
+}
+
+#
+# defaults section defining TCP/HTTP rules cannot be used to init frontend and
+# backend sections at the same time
+#
+#
+haproxy h10 -conf-BAD {} {
+ defaults invalid
+ tcp-request inspect-delay 5s
+ tcp-request content accept
+
+ frontend fe from invalid
+ mode tcp
+ bind "fd@${feh10}"
+ default_backend be1
+
+ backend be from invalid
+ mode tcp
+ server www 127.0.0.1:80
+}
+
+#
+# defaults section defining 'tcp-request connection' or 'tcp-request session'
+# rules cannot be used to init backend sections
+#
+haproxy h11 -conf-BAD {} {
+ defaults invalid
+ tcp-request connection accept
+
+ backend be from invalid
+ mode tcp
+ server www 127.0.0.1:80
+}
+
+haproxy h12 -conf-BAD {} {
+ defaults invalid
+ tcp-request session accept
+
+ backend be from invalid
+ mode tcp
+ server www 127.0.0.1:80
+}
+
+#
+# defaults section defining 'tcp-response content' rules cannot be used to init
+# a frontend section
+#
+haproxy h13 -conf-BAD {} {
+ defaults invalid
+ tcp-response inspect-delay 5s
+ tcp-response content accept
+
+ frontend fe from invalid
+ mode tcp
+ bind "fd@${feh10}"
+}
+
+haproxy h14 -conf-OK {
+ defaults tcp
+ tcp-response inspect-delay 5s
+ tcp-response content accept
+
+ backend be from tcp
+ mode tcp
+ server www 127.0.0.1:80
+}
+
+#
+# Check arguments resolutions in rules. FE/BE arguments must be resolved, but
+# SRV/TAB arguments without an explicit proxy name are not allowed.
+#
+
+haproxy h15 -conf-BAD {} {
+ defaults invalid
+ mode http
+ http-request set-header x-test "%[srv_conn(www)]"
+
+ backend be from invalid
+ server www 127.0.0.1:80
+}
+
+haproxy h16 -conf-BAD {} {
+ defaults invalid
+ mode http
+ http-request track-sc0 src
+ http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 }
+
+ backend be
+ stick-table type ip size 100k expire 30s store http_req_rate(10s)
+ server www 127.0.0.1:80
+}
+
+haproxy h17 -conf-OK {
+ defaults common
+ mode http
+
+ defaults def_front from common
+ http-request set-header x-test1 "%[fe_conn]"
+
+ defaults def_back from common
+ http-request track-sc0 src table be
+ http-request deny deny_status 429 if { sc_http_req_rate(0,be) gt 20 }
+ http-request set-header x-test2 "%[be_conn]"
+ http-request set-header x-test3 "%[srv_conn(be/www)]"
+
+ frontend fe from def_front
+ bind "fd@${feh15}"
+ default_backend be
+
+ backend be from def_back
+ stick-table type ip size 100k expire 30s store http_req_rate(10s)
+ server www 127.0.0.1:80
+}
diff --git a/reg-tests/stick-table/converteers_ref_cnt_never_dec.vtc b/reg-tests/stick-table/converteers_ref_cnt_never_dec.vtc
new file mode 100644
index 0000000..e571810
--- /dev/null
+++ b/reg-tests/stick-table/converteers_ref_cnt_never_dec.vtc
@@ -0,0 +1,75 @@
+# commit 3e60b11
+# BUG/MEDIUM: stick-tables: Decrement ref_cnt in table_* converters
+#
+# When using table_* converters ref_cnt was incremented
+# and never decremented causing entries to not expire.
+#
+# The root cause appears to be that stktable_lookup_key()
+# was called within all sample_conv_table_* functions which was
+# incrementing ref_cnt and not decrementing after completion.
+#
+# Added stktable_release() to the end of each sample_conv_table_*
+# function and reworked the end logic to ensure that ref_cnt is
+# always decremented after use.
+#
+# This should be backported to 1.8
+
+#REGTEST_TYPE=bug
+#REQUIRE_VERSION=2.4
+
+varnishtest "stick-tables: Test expirations when used with table_*"
+
+# As some macros for haproxy are used in this file, this line is mandatory.
+feature ignore_unknown_macro
+
+# Do nothing.
+server s1 {
+} -start
+
+haproxy h1 -conf {
+ # Configuration file of 'h1' haproxy instance.
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend http1
+ bind "fd@${my_frontend_fd}"
+ stick-table size 1k expire 1ms type ip store conn_rate(10s),http_req_cnt,http_err_cnt,http_fail_cnt,http_req_rate(10s),http_err_rate(10s),http_fail_rate(10s),gpc0,gpc0_rate(10s),gpt0
+ http-request track-sc0 req.hdr(X-Forwarded-For)
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_http_req_cnt(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_trackers(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),in_table(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_bytes_in_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_bytes_out_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_conn_cnt(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_conn_cur(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_conn_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_gpt0(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_gpc0(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_gpc0_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_http_err_cnt(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_http_err_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_http_fail_cnt(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_http_fail_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_http_req_cnt(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_http_req_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_kbytes_in(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_kbytes_out(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_server_id(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_sess_cnt(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_sess_rate(http1) -m int lt 0 }
+ http-request redirect location https://${s1_addr}:${s1_port}/ if { req.hdr(X-Forwarded-For),table_trackers(http1) -m int lt 0 }
+} -start
+
+client c1 -connect ${h1_my_frontend_fd_sock} {
+ txreq -url "/" -hdr "X-Forwarded-For: 127.0.0.1"
+ rxresp
+ expect resp.status == 503
+} -run
+
+haproxy h1 -cli {
+ send "show table http1"
+ expect ~ "table: http1, type: ip, size:1024, used:(0|1\\n0x[0-9a-f]*: key=127\\.0\\.0\\.1 use=0 exp=[0-9]* gpt0=0 gpc0=0 gpc0_rate\\(10000\\)=0 conn_rate\\(10000\\)=1 http_req_cnt=1 http_req_rate\\(10000\\)=1 http_err_cnt=0 http_err_rate\\(10000\\)=0 http_fail_cnt=0 http_fail_rate\\(10000\\)=0)\\n$"
+} -wait
diff --git a/reg-tests/stick-table/src_conn_rate.vtc b/reg-tests/stick-table/src_conn_rate.vtc
new file mode 100644
index 0000000..bdf8869
--- /dev/null
+++ b/reg-tests/stick-table/src_conn_rate.vtc
@@ -0,0 +1,43 @@
+varnishtest "stick table: src_conn_rate"
+feature ignore_unknown_macro
+
+haproxy h0 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen li
+ bind "fd@${fe1}"
+ http-request track-sc0 src table conn_rate_table
+ http-request deny if { src_conn_rate(conn_rate_table) gt 3 }
+ http-request return status 200
+
+ backend conn_rate_table
+ stick-table type ip size 1m expire 1m store conn_rate(1m)
+} -start
+
+client c0 -connect ${h0_fe1_addr}:${h0_fe1_port} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c1 -connect ${h0_fe1_addr}:${h0_fe1_port} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -connect ${h0_fe1_addr}:${h0_fe1_port} {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c3 -connect ${h0_fe1_addr}:${h0_fe1_port} {
+ txreq
+ rxresp
+ expect resp.status == 403
+} -run
diff --git a/reg-tests/stick-table/unknown_key.vtc b/reg-tests/stick-table/unknown_key.vtc
new file mode 100644
index 0000000..f0307cb
--- /dev/null
+++ b/reg-tests/stick-table/unknown_key.vtc
@@ -0,0 +1,32 @@
+# Shipped with the commit fixing the bug.
+
+#REGTEST_TYPE=bug
+
+varnishtest "Stick Table: Crash when accessing unknown key."
+feature ignore_unknown_macro
+
+server s0 {
+ rxreq
+ txresp
+} -start
+
+haproxy h0 -conf {
+ defaults
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend test
+ mode http
+ bind "fd@${fe1}"
+ stick-table type ip size 1m expire 1h store gpc0
+ http-request deny if { src,table_trackers(test) eq 1 }
+ http-request deny if { src,in_table(test) }
+ http-request deny deny_status 200
+} -start
+
+client c0 -connect ${h0_fe1_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+} -run
diff --git a/reg-tests/stickiness/lb-services.vtc b/reg-tests/stickiness/lb-services.vtc
new file mode 100644
index 0000000..a4e016f
--- /dev/null
+++ b/reg-tests/stickiness/lb-services.vtc
@@ -0,0 +1,291 @@
+vtest "A reg test for stickiness"
+feature ignore_unknown_macro
+
+
+# The aim of this test is to check that "stick on" rules
+# do the job they are supposed to do.
+# If we remove one of the "stick on" rule, this script fails.
+
+#REQUIRE_VERSION=2.0
+
+server s_not_used_1 {}
+server s_not_used_2 {}
+server s_not_used_3 {}
+server s_not_used_4 {}
+server s_not_used_5 {}
+server s_not_used_6 {}
+server s_not_used_7 {}
+server s_not_used_8 {}
+server s_not_used_9 {}
+server s_not_used_10 {}
+server s_not_used_11 {}
+server s_not_used_12 {}
+
+# h1/be1 servers
+server s1 {
+ rxreq
+ txresp -hdr "Server: be1/s1"
+} -repeat 2 -start
+
+server s2 {
+ rxreq
+ txresp -hdr "Server: be1/s2"
+} -repeat 2 -start
+
+# h1/be2 servers
+server s3 {
+ rxreq
+ txresp -hdr "Server: be2/s3"
+} -repeat 2 -start
+
+server s4 {
+ rxreq
+ txresp -hdr "Server: be2/s4"
+} -repeat 2 -start
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ mode http
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ log stdout format raw local0 debug
+
+ peers mypeers
+ bind "fd@${A}"
+ server A
+ server B ${h2_B_addr}:${h2_B_port}
+ table mytable type string size 10m
+
+ backend be1
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s2_addr}:${s2_port}
+
+ backend be2
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server s_not_used_1 ${s_not_used_1_addr}:${s_not_used_1_port}
+ server s_not_used_2 ${s_not_used_2_addr}:${s_not_used_2_port}
+ server s_not_used_3 ${s_not_used_3_addr}:${s_not_used_3_port}
+ server srv2 ${s4_addr}:${s4_port}
+ server s_not_used_4 ${s_not_used_4_addr}:${s_not_used_4_port}
+ server s_not_used_5 ${s_not_used_5_addr}:${s_not_used_5_port}
+ server s_not_used_6 ${s_not_used_6_addr}:${s_not_used_6_port}
+ server srv1 ${s3_addr}:${s3_port}
+
+ frontend fe
+ acl acl_be1 path_beg /be1
+ acl acl_be2 path_beg /be2
+ use_backend be1 if acl_be1
+ use_backend be2 if acl_be2
+ bind "fd@${fe}"
+} -start
+
+# h2/be1 servers
+server s5 {
+ rxreq
+ txresp -hdr "Server: be1/s5"
+} -repeat 2 -start
+
+server s6 {
+ rxreq
+ txresp -hdr "Server: be1/s6"
+} -repeat 2 -start
+
+# h2/be2 servers
+server s7 {
+ rxreq
+ txresp -hdr "Server: be2/s7"
+} -repeat 2 -start
+
+server s8 {
+ rxreq
+ txresp -hdr "Server: be2/s8"
+} -repeat 2 -start
+
+
+haproxy h2 -arg "-L B" -conf {
+ defaults
+ mode http
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ peers mypeers
+ bind "fd@${B}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B
+ table mytable type string size 10m
+
+ backend be1
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server s_not_used_7 ${s_not_used_7_addr}:${s_not_used_7_port}
+ server s_not_used_8 ${s_not_used_8_addr}:${s_not_used_8_port}
+ server s_not_used_9 ${s_not_used_9_addr}:${s_not_used_9_port}
+ server srv1 ${s5_addr}:${s5_port}
+ server s_not_used_10 ${s_not_used_10_addr}:${s_not_used_10_port}
+ server s_not_used_11 ${s_not_used_11_addr}:${s_not_used_11_port}
+ server s_not_used_12 ${s_not_used_12_addr}:${s_not_used_12_port}
+ server srv2 ${s6_addr}:${s6_port}
+
+ backend be2
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server s_not_used_1 ${s_not_used_1_addr}:${s_not_used_1_port}
+ server s_not_used_2 ${s_not_used_2_addr}:${s_not_used_2_port}
+ server s_not_used_3 ${s_not_used_3_addr}:${s_not_used_3_port}
+ server s_not_used_4 ${s_not_used_4_addr}:${s_not_used_4_port}
+ server s_not_used_5 ${s_not_used_5_addr}:${s_not_used_5_port}
+ server s_not_used_6 ${s_not_used_6_addr}:${s_not_used_6_port}
+ server srv1 ${s7_addr}:${s7_port}
+ server srv2 ${s8_addr}:${s8_port}
+
+ frontend fe
+ acl acl_be1 path_beg /be1
+ acl acl_be2 path_beg /be2
+ use_backend be1 if acl_be1
+ use_backend be2 if acl_be2
+ bind "fd@${fe}"
+} -start
+
+delay 0.2
+
+client cx -connect ${h1_fe_sock} {
+ txreq -url "/be1?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be1/s1
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cy -connect ${h1_fe_sock} {
+ txreq -url "/be2?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be2/s3
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cx -connect ${h2_fe_sock} {
+ txreq -url "/be1?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be1/s5
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cy -connect ${h2_fe_sock} {
+ txreq -url "/be2?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be2/s7
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cX -connect ${h1_fe_sock} {
+ txreq -url "/be1?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be1/s2
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cY -connect ${h1_fe_sock} {
+ txreq -url "/be2?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be2/s4
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cX -connect ${h2_fe_sock} {
+ txreq -url "/be1?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be1/s6
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cY -connect ${h2_fe_sock} {
+ txreq -url "/be2?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ be2/s8
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
diff --git a/reg-tests/stickiness/srvkey-addr.vtc b/reg-tests/stickiness/srvkey-addr.vtc
new file mode 100644
index 0000000..99a4d8b
--- /dev/null
+++ b/reg-tests/stickiness/srvkey-addr.vtc
@@ -0,0 +1,260 @@
+vtest "A reg test for stickiness with srvkey addr"
+feature ignore_unknown_macro
+
+
+# The aim of this test is to check that "stick on" rules
+# do the job they are supposed to do.
+# If we remove one of the "stick on" rule, this script fails.
+
+#REQUIRE_VERSION=2.4
+
+server s_not_used_1 {}
+server s_not_used_2 {}
+server s_not_used_3 {}
+server s_not_used_4 {}
+server s_not_used_5 {}
+server s_not_used_6 {}
+server s_not_used_7 {}
+server s_not_used_8 {}
+server s_not_used_9 {}
+server s_not_used_10 {}
+server s_not_used_11 {}
+server s_not_used_12 {}
+
+# h1/be1 servers
+server s1 {
+ rxreq
+ txresp -hdr "Server: s1"
+} -repeat 8 -start
+
+server s2 {
+ rxreq
+ txresp -hdr "Server: s2"
+} -repeat 8 -start
+
+haproxy h1 -arg "-L A" -conf {
+ defaults
+ mode http
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ log stdout format raw local0 debug
+
+ peers mypeers
+ bind "fd@${A}"
+ server A
+ server B ${h2_B_addr}:${h2_B_port}
+ table mytable type string size 10m srvkey addr
+
+ backend be1
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s2_addr}:${s2_port}
+
+ backend be2
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server s_not_used_1 ${s_not_used_1_addr}:${s_not_used_1_port}
+ server s_not_used_2 ${s_not_used_2_addr}:${s_not_used_2_port}
+ server s_not_used_3 ${s_not_used_3_addr}:${s_not_used_3_port}
+ server srv2_2 ${s2_addr}:${s2_port}
+ server s_not_used_4 ${s_not_used_4_addr}:${s_not_used_4_port}
+ server s_not_used_5 ${s_not_used_5_addr}:${s_not_used_5_port}
+ server s_not_used_6 ${s_not_used_6_addr}:${s_not_used_6_port}
+ server srv1_2 ${s1_addr}:${s1_port}
+ server s_no_addr_1 unresolvable1.addr.local init-addr none
+
+ frontend fe
+ acl acl_be1 path_beg /be1
+ acl acl_be2 path_beg /be2
+ use_backend be1 if acl_be1
+ use_backend be2 if acl_be2
+ bind "fd@${fe}"
+} -start
+
+haproxy h2 -arg "-L B" -conf {
+ defaults
+ mode http
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ peers mypeers
+ bind "fd@${B}"
+ server A ${h1_A_addr}:${h1_A_port}
+ server B
+ table mytable type string size 10m srvkey addr
+
+ backend be1
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server s_not_used_7 ${s_not_used_7_addr}:${s_not_used_7_port}
+ server s_not_used_8 ${s_not_used_8_addr}:${s_not_used_8_port}
+ server s_not_used_9 ${s_not_used_9_addr}:${s_not_used_9_port}
+ server srv1_h2_1 ${s1_addr}:${s1_port}
+ server s_not_used_10 ${s_not_used_10_addr}:${s_not_used_10_port}
+ server s_not_used_11 ${s_not_used_11_addr}:${s_not_used_11_port}
+ server s_not_used_12 ${s_not_used_12_addr}:${s_not_used_12_port}
+ server srv2_h2_1 ${s2_addr}:${s2_port}
+ server s_no_addr_1 unresolvable1.addr.local init-addr none
+
+ backend be2
+ balance roundrobin
+ stick on urlp(client) table mypeers/mytable
+ server s_not_used_1 ${s_not_used_1_addr}:${s_not_used_1_port}
+ server s_not_used_2 ${s_not_used_2_addr}:${s_not_used_2_port}
+ server s_not_used_3 ${s_not_used_3_addr}:${s_not_used_3_port}
+ server s_not_used_4 ${s_not_used_4_addr}:${s_not_used_4_port}
+ server s_not_used_5 ${s_not_used_5_addr}:${s_not_used_5_port}
+ server s_not_used_6 ${s_not_used_6_addr}:${s_not_used_6_port}
+ server srv1_h2_2 ${s1_addr}:${s1_port}
+ server srv2_h2_2 ${s2_addr}:${s2_port}
+ server s_no_addr_2 unresolvable2.addr.local init-addr none
+
+ frontend fe
+ acl acl_be1 path_beg /be1
+ acl acl_be2 path_beg /be2
+ use_backend be1 if acl_be1
+ use_backend be2 if acl_be2
+ bind "fd@${fe}"
+} -start
+
+delay 0.2
+
+client cx -connect ${h1_fe_sock} {
+ txreq -url "/be1?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cy -connect ${h1_fe_sock} {
+ txreq -url "/be2?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cx -connect ${h2_fe_sock} {
+ txreq -url "/be1?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cy -connect ${h2_fe_sock} {
+ txreq -url "/be2?client=c1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s1
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cX -connect ${h1_fe_sock} {
+ txreq -url "/be1?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cY -connect ${h1_fe_sock} {
+ txreq -url "/be2?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cX -connect ${h2_fe_sock} {
+ txreq -url "/be1?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+client cY -connect ${h2_fe_sock} {
+ txreq -url "/be2?client=c2"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Server ~ s2
+} -repeat 2 -run
+
+haproxy h1 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
+haproxy h2 -cli {
+ send "show table mypeers/mytable"
+ expect ~ .*
+}
+
diff --git a/reg-tests/stream/unique-id-from-proxy.vtc b/reg-tests/stream/unique-id-from-proxy.vtc
new file mode 100644
index 0000000..eaac065
--- /dev/null
+++ b/reg-tests/stream/unique-id-from-proxy.vtc
@@ -0,0 +1,38 @@
+varnishtest "Check that we are able to read a unique-id from PROXYv2"
+
+#REQUIRE_VERSION=2.2
+
+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 echo
+ bind "fd@${fe1}" accept-proxy
+ http-after-response set-header echo %[fc_pp_unique_id,hex]
+ http-request return status 200
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ # PROXY v2 signature
+ sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+ # version + PROXY
+ sendhex "21"
+ # TCP4
+ sendhex "11"
+ # length of the address (12) + length of the TLV (8)
+ sendhex "00 14"
+ # 127.0.0.1 42 127.0.0.1 1337
+ sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+ # PP2_TYPE_UNIQUE_ID + length of the value + "12345"
+ sendhex "05 00 05 31 32 33 34 35"
+
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.echo == "3132333435"
+} -run
diff --git a/reg-tests/stream/unique-id.vtc b/reg-tests/stream/unique-id.vtc
new file mode 100644
index 0000000..3cb5a70
--- /dev/null
+++ b/reg-tests/stream/unique-id.vtc
@@ -0,0 +1,49 @@
+varnishtest "unique-id test"
+
+#REQUIRE_VERSION=2.0
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+} -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 stable
+ bind "fd@${fe1}"
+ unique-id-format TEST-%[uuid]
+ http-response set-header A %[unique-id]
+ http-response set-header B %[unique-id]
+ default_backend be
+
+ frontend request_data
+ bind "fd@${fe2}"
+ unique-id-format TEST-%[req.hdr(in)]
+ http-response set-header out %[unique-id]
+ default_backend be
+
+ backend be
+ server srv1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.a == resp.http.b
+} -run
+
+client c2 -connect ${h1_fe2_sock} {
+ txreq -url "/" \
+ -hdr "in: 12345678"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.out == "TEST-12345678"
+} -run
diff --git a/reg-tests/tcp-rules/default_rules.vtc b/reg-tests/tcp-rules/default_rules.vtc
new file mode 100644
index 0000000..8c05f43
--- /dev/null
+++ b/reg-tests/tcp-rules/default_rules.vtc
@@ -0,0 +1,61 @@
+varnishtest "Test declaration of TCP rules in default sections"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ txresp
+ expect req.http.x-test1-frt == "def_front"
+ expect req.http.x-test1-bck == "def_back"
+} -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
+ tcp-request connection accept
+ tcp-request session accept
+ tcp-request inspect-delay 5s
+ tcp-request content set-var(txn.test1) "str(def_front)"
+ tcp-request content accept
+
+ defaults def_back from common
+ tcp-request inspect-delay 5s
+ tcp-request content set-var(txn.test1) "str(def_back)"
+ tcp-request content accept
+
+ tcp-response inspect-delay 5s
+ tcp-response content set-var(txn.test2) "str(def_back)"
+ tcp-response content accept
+
+ frontend fe from def_front
+ bind "fd@${feh1}"
+ tcp-request connection reject
+ tcp-request session reject
+ tcp-request content reject
+
+ http-request set-header x-test1-frt "%[var(txn.test1)]"
+
+ default_backend be
+
+ backend be from def_back
+ tcp-response content reject
+
+ http-request set-header x-test1-bck "%[var(txn.test1)]"
+ http-response set-header x-test2 "%[var(txn.test2)]"
+
+ 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.x-test2 == "def_back"
+} -run
diff --git a/reg-tests/webstats/webstats-scope-and-post-change.vtc b/reg-tests/webstats/webstats-scope-and-post-change.vtc
new file mode 100644
index 0000000..e896c05
--- /dev/null
+++ b/reg-tests/webstats/webstats-scope-and-post-change.vtc
@@ -0,0 +1,84 @@
+varnishtest "Webgui stats page check filtering with scope and changing server state"
+
+feature ignore_unknown_macro
+
+server s1 {
+} -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
+
+ defaults
+ mode http
+
+ frontend fe1
+ bind "fd@${fe1}"
+ stats enable
+ stats refresh 5s
+ stats uri /
+ stats admin if TRUE
+
+ backend b1
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s1_addr}:${s1_port}
+ server srv3 ${s1_addr}:${s1_port}
+
+ backend b2
+ server srv1 ${s1_addr}:${s1_port}
+ server srv2 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -url "/;csv;"
+ rxresp
+ expect resp.status == 200
+} -run
+
+client c2 -connect ${h1_fe1_sock} {
+ txreq -url "/?;csv;scope=b1"
+ rxresp
+ expect resp.status == 200
+} -run
+
+haproxy h1 -cli {
+ send "show stat"
+ expect ~ .*
+}
+
+client c3 -connect ${h1_fe1_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+
+ txreq -url "/?;csv;scope=b1"
+ rxresp
+ expect resp.status == 200
+ expect resp.body ~ ".*\nb1,BACKEND.*"
+ expect resp.body !~ ".*\nb2,BACKEND.*"
+
+ txreq -req "POST" -url "/?scope=b2" -body "s=srv1&s=srv2&s=srv3&action=maint&b=%233"
+ rxresp
+ expect resp.status == 303
+
+ txreq -req "POST" -url "/" -body "s=srv2&action=drain&b=%233"
+ rxresp
+ expect resp.status == 303
+
+ txreq -req "POST" -url "/" -body "s=srv1&action=maint&b=%234"
+ rxresp
+ expect resp.status == 303
+
+ txreq -url "/?;csv;scope=fe1"
+ rxresp
+ expect resp.status == 200
+} -run
+
+haproxy h1 -cli {
+ send "show stat"
+ expect ~ "\nb1,srv1.*MAINT.*\nb1,srv2.*DRAIN.*\nb1,srv3.*MAINT.*\nb1,BACKEND.*DOWN.*\nb2,srv1.*MAINT.*\nb2,srv2.*no check.*\nb2,BACKEND.*UP"
+} -wait