Merge tag 'v2.4.17'

HAProxy 2.4.17
diff --git a/.cirrus.yml b/.cirrus.yml
index fdabfdc..e754f83 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,7 +1,7 @@
 FreeBSD_task:
   freebsd_instance:
     matrix:
-      image_family: freebsd-12-2
+      image_family: freebsd-13-0
   only_if: $CIRRUS_BRANCH =~ 'master|next'
   install_script:
     - pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua53 socat pcre
diff --git a/.github/matrix.py b/.github/matrix.py
index 74bfcc7..8e7130f 100644
--- a/.github/matrix.py
+++ b/.github/matrix.py
@@ -108,9 +108,9 @@
     for ssl in [
         "stock",
         "OPENSSL_VERSION=1.0.2u",
-        "OPENSSL_VERSION=3.0.1",
+        "OPENSSL_VERSION=3.0.2",
         "LIBRESSL_VERSION=2.9.2",
-        "LIBRESSL_VERSION=3.3.3",
+        "LIBRESSL_VERSION=3.5.2",
 #        "BORINGSSL=yes",
     ]:
         flags = ["USE_OPENSSL=1"]
diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
index de49f43..7b6370e 100644
--- a/.github/workflows/codespell.yml
+++ b/.github/workflows/codespell.yml
@@ -10,7 +10,7 @@
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: install prerequisites
       run: sudo pip install codespell
     - name: check
diff --git a/.github/workflows/compliance.yml b/.github/workflows/compliance.yml
index 4696c06..66bc154 100644
--- a/.github/workflows/compliance.yml
+++ b/.github/workflows/compliance.yml
@@ -15,15 +15,16 @@
         - TARGET: linux-glibc
           CC: gcc
           os: ubuntu-latest
-    env:
-      H2SPEC_VERSION: '2.6.0'
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v3
     - name: Install h2spec
+      id: install-h2spec
       run: |
-        curl -fsSL https://github.com/summerwind/h2spec/releases/download/v${H2SPEC_VERSION}/h2spec_linux_amd64.tar.gz -o h2spec.tar.gz
+        H2SPEC_VERSION=`curl --silent "https://api.github.com/repos/summerwind/h2spec/releases/latest" | jq -r -j '.tag_name'`
+        curl -fsSL https://github.com/summerwind/h2spec/releases/download/${H2SPEC_VERSION}/h2spec_linux_amd64.tar.gz -o h2spec.tar.gz
         tar xvf h2spec.tar.gz
         sudo install -m755 h2spec /usr/local/bin/h2spec
+        echo "::set-output name=version::${H2SPEC_VERSION}"
     - name: Compile HAProxy with ${{ matrix.CC }}
       run: |
         make -j$(nproc) all \
@@ -49,5 +50,5 @@
         echo "::set-output name=version::$(haproxy -v |awk 'NR==1{print $3}')"
     - name: Launch HAProxy ${{ steps.show-version.outputs.version }}
       run: haproxy -f .github/h2spec.config -D
-    - name: Run h2spec
+    - name: Run h2spec ${{ steps.install-h2spec.outputs.version }}
       run: h2spec -Svtk -h 127.0.0.1 -p 8443
diff --git a/.github/workflows/contrib.yml b/.github/workflows/contrib.yml
index 53f6025..f147b3d 100644
--- a/.github/workflows/contrib.yml
+++ b/.github/workflows/contrib.yml
@@ -9,7 +9,7 @@
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Compile admin/halog/halog
       run: |
         make admin/halog/halog
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index fd5a0e2..d9d3bce 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -19,7 +19,7 @@
       COVERITY_SCAN_NOTIFICATION_EMAIL: 'chipitsine@gmail.com'
       COVERITY_SCAN_BUILD_COMMAND: "make CC=clang TARGET=linux-glibc USE_ZLIB=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_LUA=1 USE_OPENSSL=1 USE_SYSTEMD=1 USE_WURFL=1 WURFL_INC=addons/wurfl/dummy WURFL_LIB=addons/wurfl/dummy USE_DEVICEATLAS=1 DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_51DEGREES=1 51DEGREES_SRC=addons/51degrees/dummy/pattern"
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Install apt dependencies
       run: |
         sudo apt-get update
diff --git a/.github/workflows/openssl-nodeprecated.yml b/.github/workflows/openssl-nodeprecated.yml
index b853fe2..ec8a8ec 100644
--- a/.github/workflows/openssl-nodeprecated.yml
+++ b/.github/workflows/openssl-nodeprecated.yml
@@ -20,8 +20,8 @@
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v1
-    - name: prepare VTest
+    - uses: actions/checkout@v3
+    - name: Install VTest
       run: |
         git clone https://github.com/VTest/VTest.git ../vtest
         make -C ../vtest FLAGS="-O2 -s -Wall"
diff --git a/.github/workflows/vtest.yml b/.github/workflows/vtest.yml
index 8083a4f..ea39662 100644
--- a/.github/workflows/vtest.yml
+++ b/.github/workflows/vtest.yml
@@ -20,7 +20,7 @@
     outputs:
       matrix: ${{ steps.set-matrix.outputs.matrix }}
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - name: Generate Build Matrix
         id: set-matrix
         run: python3 .github/matrix.py "${{ github.event_name }}"
@@ -41,7 +41,7 @@
       ASAN_OPTIONS: log_path=asan.log
       OT_CPP_VERSION: 1.5.0
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
       with:
         fetch-depth: 100
 #
@@ -55,7 +55,7 @@
     - name: Cache SSL libs
       if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && matrix.ssl != 'QUICTLS=yes' }}
       id: cache_ssl
-      uses: actions/cache@v2
+      uses: actions/cache@v3
       with:
         path: '~/opt/'
         key: ssl-${{ steps.generate-cache-key.outputs.key }}
@@ -63,7 +63,7 @@
     - name: Cache OpenTracing
       if: ${{ contains(matrix.FLAGS, 'USE_OT=1') }}
       id: cache_ot
-      uses: actions/cache@v2
+      uses: actions/cache@v3
       with:
         path: '~/opt-ot/'
         key: ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index deeb36b..58283ff 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -32,7 +32,7 @@
           - USE_THREAD=1
           - USE_ZLIB=1
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - uses: msys2/setup-msys2@v2
       with:
         install: >-
diff --git a/CHANGELOG b/CHANGELOG
index 123a6ca..4bf6378 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,99 @@
 ChangeLog :
 ===========
 
+2022/05/13 : 2.4.17
+    - CI: github actions: update LibreSSL to 3.5.2
+    - SCRIPTS: announce-release: add URL of dev packages
+    - BUG/MEDIUM: mux-fcgi: Be sure to never set EOM flag on an empty HTX message
+    - BUG/MEDIUM: mux-h1: Be able to handle trailers when C-L header was specified
+    - BUG/MAJOR: dns: multi-thread concurrency issue on UDP socket
+    - BUG/MINOR: mux-h2: mark the stream as open before processing it not after
+    - MINOR: mux-h2: report a trace event when failing to create a new stream
+    - DOC: config: Update doc for PR/PH session states to warn about rewrite failures
+    - BUG/MINOR: tcp/http: release the expr of set-{src,dst}[-port]
+    - BUG/MEDIUM: resolvers: make "show resolvers" properly yield
+    - BUG/MEDIUM: cli: make "show cli sockets" really yield
+    - BUG/MINOR: map/cli: protect the backref list during "show map" errors
+    - BUG/MINOR: map/cli: make sure patterns don't vanish under "show map"'s init
+    - DOC: fix typo "ant" for "and" in INSTALL
+    - CI: dynamically determine actual version of h2spec
+    - BUILD: ssl: work around bogus warning in gcc 12's -Wformat-truncation
+    - BUILD: debug: work around gcc-12 excessive -Warray-bounds warnings
+    - BUILD: listener: shut report of possible null-deref in listener_accept()
+    - BUG/MEDIUM: ssl: fix the gcc-12 broken fix :-(
+    - DOC: install: update gcc version requirements
+    - BUG/MINOR: server: Make SRV_STATE_LINE_MAXLEN value from 512 to 2kB (2000 bytes).
+    - BUG/MINOR: conn_stream: do not confirm a connection from the frontend path
+    - BUG/MEDIUM: wdt: don't trigger the watchdog when p is unitialized
+    - CLEANUP: mux-h1: Fix comments and error messages for global options
+
+2022/04/29 : 2.4.16
+    - BUG/MINOR: tools: fix url2sa return value with IPv4
+    - BUG/MINOR: server/ssl: free the SNI sample expression
+    - CI: github actions: switch to LibreSSL-3.5.1
+    - BUG/MEDIUM: mux-h1: only turn CO_FL_ERROR to CS_FL_ERROR with empty ibuf
+    - BUG/MEDIUM: trace: avoid race condition when retrieving session from conn->owner
+    - MEDIUM: mqtt: support mqtt_is_valid and mqtt_field_value converters for MQTTv3.1
+    - DOC: config: Explictly add supported MQTT versions
+    - BUG/MINOR: tools: url2sa reads too far when no port nor path
+    - BUG/MEDIUM: mux-fcgi: Properly handle return value of headers/trailers parsing
+    - BUG/MEDIUM: mux-h1: Properly detect full buffer cases during message parsing
+    - BUG/MEDIUM: stream-int: do not rely on the connection error once established
+    - MEDIUM: mux-h2: slightly relax timeout management rules
+    - BUG/MEDIUM: mux-h2: make use of http-request and keep-alive timeouts
+    - DOC: reflect H2 timeout changes
+    - BUG/MINOR: samples: add missing context names for sample fetch functions
+    - BUG/MINOR: cli/stream: fix "shutdown session" to iterate over all threads
+    - BUG/MAJOR: mux_pt: always report the connection error to the conn_stream
+    - CI: github actions: update OpenSSL to 3.0.2
+    - BUG/MINOR: fcgi-app: Don't add C-L header on response to HEAD requests
+    - BUG/MEDIUM: stats: Be sure to never set EOM flag on an empty HTX message
+    - BUG/MEDIUM: hlua: Don't set EOM flag on an empty HTX message in HTTP applet
+    - BUG/MEDIUM: promex: Be sure to never set EOM flag on an empty HTX message
+    - BUG/MEDIUM: mux-h1: Set outgoing message to DONE when payload length is reached
+    - BUG/MEDIUM: http-conv: Fix url_enc() to not crush const samples
+    - BUG/MEDIUM: http-act: Don't replace URI if path is not found or invalid
+    - BUG/MINOR: opentracing: setting the return value in function flt_ot_var_set()
+    - EXAMPLES: opentracing: refined shell scripts for testing filter performance
+    - DOC: opentracing: corrected comments in function descriptions
+    - CLEANUP: opentracing: removed unused function flt_ot_var_unset()
+    - CLEANUP: opentracing: removed unused function flt_ot_var_get()
+    - CLEANUP: opentracing: added flt_ot_smp_init() function
+    - CLEANUP: opentracing: added variable to store variable length
+    - DEBUG: opentracing: show return values of all functions in the debug output
+    - CI: Update to actions/checkout@v3
+    - CI: Update to actions/cache@v3
+    - CI: cirrus: switch to FreeBSD-13.0
+    - BUG/MINOR: mux-h2: do not send GOAWAY if SETTINGS were not sent
+    - BUG/MINOR: cache: do not display expired entries in "show cache"
+    - BUILD: debug: mark the __start_mem_stats/__stop_mem_stats symbols as weak
+    - BUG/MINOR: mux-h2: do not use timeout http-keep-alive on backend side
+    - BUG/MINOR: mux-h2: use timeout http-request as a fallback for http-keep-alive
+    - BUG/MEDIUM: mux-h1: Don't request more room on partial trailers
+    - BUILD: sched: workaround crazy and dangerous warning in Clang 14
+    - BUILD: compiler: use a more portable set of asm(".weak") statements
+    - BUG/MEDIUM: stream: do not abort connection setup too early
+    - BUG/MEDIUM: fcgi-app: Use http_msg flags to know if C-L header can be added
+    - BUG/MEDIUM: compression: Don't forget to update htx_sl and http_msg flags
+    - SCRIPTS: announce-release: update the doc's URL
+    - DOC: lua: update a few doc URLs
+    - SCRIPTS: announce-release: add shortened links to pending issues
+    - BUG/MINOR: cache: Disable cache if applet creation fails
+    - BUG/MAJOR: connection: Never remove connection from idle lists outside the lock
+    - DOC: remove my name from the config doc
+    - BUG/MINOR: rules: Fix check_capture() function to use the right rule arguments
+    - MINOR: task: add a new task_instant_wakeup() function
+    - MEDIUM: queue: use tasklet_instant_wakeup() to wake tasks
+    - REGTESTS: fix the race conditions in be2dec.vtc ad field.vtc
+    - BUILD: compiler: properly distinguish weak and global symbols
+    - BUG/MINOR: resolvers: Fix memory leak in resolvers_deinit()
+    - BUG/MINOR: pools: make sure to also destroy shared pools in pool_destroy_all()
+    - CLEANUP: acl: Remove unused variable when releasing an acl expression
+    - BUILD: fd: remove unused variable totlen in fd_write_frag_line()
+    - BUILD: sockpair: do not set unused flag
+    - BUILD: proto_uxst: do not set unused flag
+    - BUILD: opentracing: Fix OT build due to misuse of var_clear()
+
 2022/03/14 : 2.4.15
     - CI: github actions: add the output of $CC -dM -E-
     - CI: github actions: add OpenTracing builds
diff --git a/INSTALL b/INSTALL
index 85e8e8f..526bf08 100644
--- a/INSTALL
+++ b/INSTALL
@@ -102,7 +102,7 @@
     may want to retry with "gmake" which is the name commonly used for GNU make
     on BSD systems.
 
-  - GCC >= 3.4 (up to 11 tested). Older versions can be made to work with a
+  - GCC >= 4.2 (up to 12 tested). Older versions can be made to work with a
     few minor adaptations if really needed. Newer versions may sometimes break
     due to compiler regressions or behaviour changes. The version shipped with
     your operating system is very likely to work with no trouble. Clang >= 3.0
@@ -225,7 +225,7 @@
 4.5) Cryptography
 -----------------
 For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
-supports the OpenSSL library, and is known to build ant work with branches
+supports the OpenSSL library, and is known to build and work with branches
 0.9.8, 1.0.0, 1.0.1, 1.0.2, 1.1.0 and 1.1.1. OpenSSL follows a long-term
 support cycle similar to HAProxy's, and each of the branches above receives its
 own fixes, without forcing you to upgrade to another branch. There is no excuse
diff --git a/VERDATE b/VERDATE
index 0ce30a2..cce1228 100644
--- a/VERDATE
+++ b/VERDATE
@@ -1,2 +1,2 @@
 $Format:%ci$
-2022/03/14
+2022/05/13
diff --git a/VERSION b/VERSION
index 84cc65e..1324c03 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.4.15
+2.4.17
diff --git a/addons/ot/include/debug.h b/addons/ot/include/debug.h
index 7becdf7..6311216 100644
--- a/addons/ot/include/debug.h
+++ b/addons/ot/include/debug.h
@@ -39,6 +39,9 @@
 	} while (0)
 #  define FLT_OT_FUNC(f, ...)       do { FLT_OT_DBG(1, "%s(" f ") {", __func__, ##__VA_ARGS__); dbg_indent_level += 3; } while (0)
 #  define FLT_OT_RETURN(a)          do { dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
+#  define FLT_OT_RETURN_EX(a,t,f)   do { dbg_indent_level -= 3; { t _r = (a); FLT_OT_DBG(1, "} = " f, _r); return _r; } } while (0)
+#  define FLT_OT_RETURN_INT(a)      FLT_OT_RETURN_EX((a), int, "%d")
+#  define FLT_OT_RETURN_PTR(a)      FLT_OT_RETURN_EX((a), void *, "%p")
 #  define FLT_OT_DBG_IFDEF(a,b)     a
 #  define FLT_OT_DBG_ARGS(a, ...)   a, ##__VA_ARGS__
 
@@ -58,8 +61,12 @@
 #  define FLT_OT_DBG(...)           while (0)
 #  define FLT_OT_FUNC(...)          while (0)
 #  define FLT_OT_RETURN(a)          return a
+#  define FLT_OT_RETURN_EX(a,t,f)   return a
+#  define FLT_OT_RETURN_INT(a)      return a
+#  define FLT_OT_RETURN_PTR(a)      return a
 #  define FLT_OT_DBG_IFDEF(a,b)     b
 #  define FLT_OT_DBG_ARGS(...)
+#  define FLT_OT_DBG_BUF(a,b)       while (0)
 #endif /* DEBUG_OT */
 
 /*
diff --git a/addons/ot/include/vars.h b/addons/ot/include/vars.h
index 5ce8879..c683b6b 100644
--- a/addons/ot/include/vars.h
+++ b/addons/ot/include/vars.h
@@ -32,9 +32,7 @@
 #endif
 int                  flt_ot_var_register(const char *scope, const char *prefix, const char *name, char **err);
 int                  flt_ot_var_set(struct stream *s, const char *scope, const char *prefix, const char *name, const char *value, uint opt, char **err);
-int                  flt_ot_var_unset(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err);
 int                  flt_ot_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err);
-int                  flt_ot_var_get(struct stream *s, const char *scope, const char *prefix, const char *name, char **value, uint opt, char **err);
 struct otc_text_map *flt_ot_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err);
 
 #endif /* _OPENTRACING_VARS_H_ */
diff --git a/addons/ot/src/cli.c b/addons/ot/src/cli.c
index 529c687..f9feeca 100644
--- a/addons/ot/src/cli.c
+++ b/addons/ot/src/cli.c
@@ -97,7 +97,7 @@
 
 	cmn_cli_set_msg(appctx, err, msg, CLI_ST_PRINT_FREE);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 #endif /* DEBUG_OT */
@@ -137,7 +137,7 @@
 
 	cmn_cli_set_msg(appctx, NULL, msg, CLI_ST_PRINT_FREE);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -175,7 +175,7 @@
 
 	cmn_cli_set_msg(appctx, NULL, msg, CLI_ST_PRINT_FREE);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -238,7 +238,7 @@
 
 	cmn_cli_set_msg(appctx, err, msg, CLI_ST_PRINT_FREE);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -289,7 +289,7 @@
 
 	cmn_cli_set_msg(appctx, err, msg, CLI_ST_PRINT_FREE);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -343,7 +343,7 @@
 
 	cmn_cli_set_msg(appctx, NULL, msg, CLI_ST_PRINT_FREE);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
diff --git a/addons/ot/src/conf.c b/addons/ot/src/conf.c
index d07be2c..d575e3a 100644
--- a/addons/ot/src/conf.c
+++ b/addons/ot/src/conf.c
@@ -48,7 +48,7 @@
 			if (strcmp(ptr->id, id) == 0) {
 				FLT_OT_ERR("'%s' : already defined", id);
 
-				FLT_OT_RETURN(retptr);
+				FLT_OT_RETURN_PTR(retptr);
 			}
 
 	retptr = FLT_OT_CALLOC(1, size);
@@ -72,7 +72,7 @@
 		FLT_OT_ERR("out of memory");
 	}
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -102,7 +102,7 @@
 	if (retptr != NULL)
 		FLT_OT_DBG_CONF_PH("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -162,7 +162,7 @@
 	if (retptr != NULL)
 		FLT_OT_DBG_CONF_SAMPLE_EXPR("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -221,13 +221,13 @@
 
 	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), args[1], linenum, head, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	flt_ot_args_to_str(args, 2, &(retptr->value));
 	if (retptr->value == NULL) {
 		FLT_OT_FREE_CLEAR(retptr);
 
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	}
 
 	retptr->num_exprs = flt_ot_args_count(args) - 2;
@@ -235,7 +235,7 @@
 
 	FLT_OT_DBG_CONF_SAMPLE("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -297,7 +297,7 @@
 	if (retptr != NULL)
 		FLT_OT_DBG_CONF_STR("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -357,7 +357,7 @@
 	if (retptr != NULL)
 		FLT_OT_DBG_CONF_CONTEXT("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -415,7 +415,7 @@
 
 	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	LIST_INIT(&(retptr->tags));
 	LIST_INIT(&(retptr->logs));
@@ -423,7 +423,7 @@
 
 	FLT_OT_DBG_CONF_SPAN("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -486,7 +486,7 @@
 
 	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	LIST_INIT(&(retptr->acls));
 	LIST_INIT(&(retptr->contexts));
@@ -495,7 +495,7 @@
 
 	FLT_OT_DBG_CONF_SCOPE("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 /***
@@ -566,13 +566,13 @@
 
 	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	LIST_INIT(&(retptr->ph_scopes));
 
 	FLT_OT_DBG_CONF_GROUP("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -630,7 +630,7 @@
 
 	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, NULL, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	retptr->rate_limit = FLT_OT_FLOAT_U32(FLT_OT_RATE_LIMIT_MAX, FLT_OT_RATE_LIMIT_MAX);
 	init_new_proxy(&(retptr->proxy_log));
@@ -640,7 +640,7 @@
 
 	FLT_OT_DBG_CONF_TRACER("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -713,7 +713,7 @@
 
 	retptr = FLT_OT_CALLOC(1, sizeof(*retptr));
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	retptr->proxy = px;
 	LIST_INIT(&(retptr->groups));
@@ -721,7 +721,7 @@
 
 	FLT_OT_DBG_CONF("- init ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
diff --git a/addons/ot/src/event.c b/addons/ot/src/event.c
index 90b5828..a96d27c 100644
--- a/addons/ot/src/event.c
+++ b/addons/ot/src/event.c
@@ -55,7 +55,7 @@
 	FLT_OT_FUNC("%p, %p, %p, %u, %p, %p, %p, %p, %p:%p", s, f, chn, dir, span, data, conf_span, ts, FLT_OT_DPTR_ARGS(err));
 
 	if (span == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	if (span->span == NULL) {
 		span->span = ot_span_init(conf->tracer->tracer, span->id, ts, NULL, span->ref_type, FLT_OT_DEREF(span->ref_ctx, idx, -1), span->ref_span, data->tags, data->num_tags, err);
@@ -104,7 +104,7 @@
 		}
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -179,7 +179,7 @@
 					break;
 				}
 
-			FLT_OT_RETURN(retval);
+			FLT_OT_RETURN_INT(retval);
 		}
 	}
 
@@ -257,7 +257,7 @@
 	flt_ot_scope_finish_marked(f->ctx, ts);
 	flt_ot_scope_free_unused(f->ctx, chn);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -319,7 +319,7 @@
 
 	FLT_OT_DBG(3, "event = %d, chn = %p, s->req = %p, s->res = %p", event, chn, &(s->req), &(s->res));
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 /*
diff --git a/addons/ot/src/filter.c b/addons/ot/src/filter.c
index 04c4a67..6c931fa 100644
--- a/addons/ot/src/filter.c
+++ b/addons/ot/src/filter.c
@@ -162,7 +162,7 @@
 	FLT_OT_FUNC("%p, %p", p, fconf);
 
 	if (conf == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	flt_ot_cli_init();
 
@@ -178,7 +178,7 @@
 		FLT_OT_ERR_FREE(err);
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -256,7 +256,7 @@
 	FLT_OT_FUNC("%p, %p", p, fconf);
 
 	if (conf == NULL)
-		FLT_OT_RETURN(++retval);
+		FLT_OT_RETURN_INT(++retval);
 
 	/*
 	 * If only the proxy specified with the <p> parameter is checked, then
@@ -421,7 +421,7 @@
 		FLT_OT_DBG_LIST(conf->tracer, ph_scope, "   ", "used", _scope, FLT_OT_DBG_CONF_PH("      ", _scope));
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -450,7 +450,7 @@
 	FLT_OT_FUNC("%p, %p", p, fconf);
 
 	if (conf == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	/*
 	 * Start the OpenTracing library tracer thread.
@@ -469,7 +469,7 @@
 		retval = FLT_OT_RET_OK;
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -529,7 +529,7 @@
 	if (conf->tracer->flag_disabled) {
 		FLT_OT_DBG(2, "filter '%s', type: %s (disabled)", conf->id, flt_ot_type(f));
 
-		FLT_OT_RETURN(FLT_OT_RET_IGNORE);
+		FLT_OT_RETURN_INT(FLT_OT_RET_IGNORE);
 	}
 	else if (conf->tracer->rate_limit < FLT_OT_FLOAT_U32(FLT_OT_RATE_LIMIT_MAX, FLT_OT_RATE_LIMIT_MAX)) {
 		uint32_t rnd = ha_random32();
@@ -537,7 +537,7 @@
 		if (conf->tracer->rate_limit <= rnd) {
 			FLT_OT_DBG(2, "filter '%s', type: %s (ignored: %u <= %u)", conf->id, flt_ot_type(f), conf->tracer->rate_limit, rnd);
 
-			FLT_OT_RETURN(FLT_OT_RET_IGNORE);
+			FLT_OT_RETURN_INT(FLT_OT_RET_IGNORE);
 		}
 	}
 
@@ -548,7 +548,7 @@
 	if (f->ctx == NULL) {
 		FLT_OT_LOG(LOG_EMERG, "failed to create context");
 
-		FLT_OT_RETURN(FLT_OT_RET_IGNORE);
+		FLT_OT_RETURN_INT(FLT_OT_RET_IGNORE);
 	}
 
 	/*
@@ -563,7 +563,7 @@
 	flt_ot_vars_dump(s);
 	flt_ot_http_headers_dump(&(s->req));
 
-	FLT_OT_RETURN(FLT_OT_RET_OK);
+	FLT_OT_RETURN_INT(FLT_OT_RET_OK);
 }
 
 
@@ -593,9 +593,9 @@
 	FLT_OT_FUNC("%p, %p", s, f);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -624,11 +624,11 @@
 	FLT_OT_FUNC("%p, %p, %p", s, f, be);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	FLT_OT_DBG(3, "backend: %s", be->id);
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -748,7 +748,7 @@
 	FLT_OT_FUNC("%p, %p, %p", s, f, chn);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OT_EVENT_RES_SERVER_SESS_START : FLT_OT_EVENT_REQ_CLIENT_SESS_START)))
-		FLT_OT_RETURN(FLT_OT_RET_OK);
+		FLT_OT_RETURN_INT(FLT_OT_RET_OK);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
 
@@ -772,7 +772,7 @@
 
 //	register_data_filter(s, chn, f);
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -808,7 +808,7 @@
 		}
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, event)))
-		FLT_OT_RETURN(FLT_OT_RET_OK);
+		FLT_OT_RETURN_INT(FLT_OT_RET_OK);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), analyzer: %s", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), flt_ot_analyzer(an_bit));
 
@@ -819,7 +819,7 @@
 		channel_dont_close(chn);
 	}
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -857,13 +857,13 @@
 		}
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, event)))
-		FLT_OT_RETURN(FLT_OT_RET_OK);
+		FLT_OT_RETURN_INT(FLT_OT_RET_OK);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), analyzer: %s", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), flt_ot_analyzer(an_bit));
 
 	retval = flt_ot_event_run(s, f, chn, event, &err);
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -891,7 +891,7 @@
 	FLT_OT_FUNC("%p, %p, %p", s, f, chn);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OT_EVENT_RES_SERVER_SESS_END : FLT_OT_EVENT_REQ_CLIENT_SESS_END)))
-		FLT_OT_RETURN(FLT_OT_RET_OK);
+		FLT_OT_RETURN_INT(FLT_OT_RET_OK);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
 
@@ -913,7 +913,7 @@
 		}
 	}
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -945,11 +945,11 @@
 	FLT_OT_FUNC("%p, %p, %p", s, f, msg);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), %.*s %.*s %.*s", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), HTX_SL_P1_LEN(sl), HTX_SL_P1_PTR(sl), HTX_SL_P2_LEN(sl), HTX_SL_P2_PTR(sl), HTX_SL_P3_LEN(sl), HTX_SL_P3_PTR(sl));
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -978,14 +978,14 @@
 	FLT_OT_FUNC("%p, %p, %p, %u, %u", s, f, msg, offset, len);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
-		FLT_OT_RETURN(len);
+		FLT_OT_RETURN_INT(len);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), offset, len, retval);
 
 	if (retval != len)
 		task_wakeup(s->task, TASK_WOKEN_MSG);
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -1013,11 +1013,11 @@
 	FLT_OT_FUNC("%p, %p, %p", s, f, msg);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 
@@ -1111,7 +1111,7 @@
 	FLT_OT_FUNC("%p, %p, %p, %u, %u", s, f, chn, offset, len);
 
 	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
-		FLT_OT_RETURN(len);
+		FLT_OT_RETURN_INT(len);
 
 	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), offset, len, retval);
 
@@ -1122,7 +1122,7 @@
 	if (retval != len)
 		task_wakeup(s->task, TASK_WOKEN_MSG);
 
-	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
 }
 
 #endif /* DEBUG_OT */
diff --git a/addons/ot/src/group.c b/addons/ot/src/group.c
index f9fdecc..52b872d 100644
--- a/addons/ot/src/group.c
+++ b/addons/ot/src/group.c
@@ -66,13 +66,13 @@
 	if ((fconf == NULL) || (conf == NULL) || (conf_group == NULL)) {
 		FLT_OT_LOG(LOG_ERR, FLT_OT_ACTION_GROUP ": internal error, invalid group action");
 
-		FLT_OT_RETURN(ACT_RET_CONT);
+		FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
 	}
 
 	if (conf->tracer->flag_disabled) {
 		FLT_OT_DBG(1, "filter '%s' disabled, group action '%s' ignored", conf->id, conf_group->id);
 
-		FLT_OT_RETURN(ACT_RET_CONT);
+		FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
 	}
 
 	/* Find the OpenTracing filter instance from the current stream. */
@@ -86,10 +86,10 @@
 	if (rt_ctx == NULL) {
 		FLT_OT_DBG(1, "cannot find filter, probably not attached to the stream");
 
-		FLT_OT_RETURN(ACT_RET_CONT);
+		FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
 	}
 	else if (flt_ot_is_disabled(filter FLT_OT_DBG_ARGS(, -1))) {
-		FLT_OT_RETURN(ACT_RET_CONT);
+		FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
 	}
 	else {
 		FLT_OT_DBG(3, "run group '%s'", conf_group->id);
@@ -107,7 +107,7 @@
 	if (i >= FLT_OT_TABLESIZE(flt_ot_group_data)) {
 		FLT_OT_LOG(LOG_ERR, FLT_OT_ACTION_GROUP ": internal error, invalid rule->from=%d", rule->from);
 
-		FLT_OT_RETURN(ACT_RET_CONT);
+		FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
 	}
 
 	list_for_each_entry(ph_scope, &(conf_group->ph_scopes), list) {
@@ -117,7 +117,7 @@
 		}
 	}
 
-	FLT_OT_RETURN(ACT_RET_CONT);
+	FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
 }
 
 
@@ -166,7 +166,7 @@
 	if (i >= FLT_OT_TABLESIZE(flt_ot_group_data)) {
 		FLT_OT_ERR("internal error, unexpected rule->from=%d, please report this bug!", rule->from);
 
-		FLT_OT_RETURN(0);
+		FLT_OT_RETURN_INT(0);
 	}
 
 	/*
@@ -191,7 +191,7 @@
 	if (fconf == NULL) {
 		FLT_OT_ERR("unable to find the OpenTracing filter '%s' used by the " FLT_OT_ACTION_GROUP " '%s'", filter_id, group_id);
 
-		FLT_OT_RETURN(0);
+		FLT_OT_RETURN_INT(0);
 	}
 
 	/*
@@ -208,7 +208,7 @@
 	if (!flag_found) {
 		FLT_OT_ERR("unable to find group '%s' in the OpenTracing filter '%s' configuration", group_id, filter_id);
 
-		FLT_OT_RETURN(0);
+		FLT_OT_RETURN_INT(0);
 	}
 
 	FLT_OT_FREE_CLEAR(rule->arg.act.p[FLT_OT_ARG_FILTER_ID]);
@@ -218,7 +218,7 @@
 	rule->arg.act.p[FLT_OT_ARG_CONF]     = conf;
 	rule->arg.act.p[FLT_OT_ARG_GROUP]    = ph_group;
 
-	FLT_OT_RETURN(1);
+	FLT_OT_RETURN_INT(1);
 }
 
 
@@ -272,7 +272,7 @@
 	     (strcmp(args[*cur_arg + 2], FLT_OT_CONDITION_UNLESS) != 0))) {
 		FLT_OT_ERR("expects: <filter-id> <group-id> [{ if | unless } ...]");
 
-		FLT_OT_RETURN(ACT_RET_PRS_ERR);
+		FLT_OT_RETURN_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
 	}
 
 	/* Copy the OpenTracing filter id. */
@@ -280,7 +280,7 @@
 	if (rule->arg.act.p[FLT_OT_ARG_FILTER_ID] == NULL) {
 		FLT_OT_ERR("%s : out of memory", args[*cur_arg]);
 
-		FLT_OT_RETURN(ACT_RET_PRS_ERR);
+		FLT_OT_RETURN_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
 	}
 
 	/* Copy the OpenTracing group id. */
@@ -290,7 +290,7 @@
 
 		FLT_OT_FREE_CLEAR(rule->arg.act.p[FLT_OT_ARG_FILTER_ID]);
 
-		FLT_OT_RETURN(ACT_RET_PRS_ERR);
+		FLT_OT_RETURN_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
 	}
 
 	rule->action      = ACT_CUSTOM;
@@ -300,7 +300,7 @@
 
 	*cur_arg += 2;
 
-	FLT_OT_RETURN(ACT_RET_PRS_OK);
+	FLT_OT_RETURN_EX(ACT_RET_PRS_OK, enum act_parse_ret, "%d");
 }
 
 
diff --git a/addons/ot/src/http.c b/addons/ot/src/http.c
index 4a12ed8..ea22469 100644
--- a/addons/ot/src/http.c
+++ b/addons/ot/src/http.c
@@ -97,7 +97,7 @@
 	FLT_OT_FUNC("%p, \"%s\", %zu, %p:%p", chn, prefix, len, FLT_OT_DPTR_ARGS(err));
 
 	if (chn == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	/*
 	 * The keyword 'inject' allows you to define the name of the OpenTracing
@@ -161,7 +161,7 @@
 		otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
 	}
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -194,7 +194,7 @@
 	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %p:%p", chn, prefix, name, value, FLT_OT_DPTR_ARGS(err));
 
 	if ((chn == NULL) || (!FLT_OT_STR_ISVALID(prefix) && !FLT_OT_STR_ISVALID(name)))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	htx = htxbuf(&(chn->buf));
 
@@ -205,7 +205,7 @@
 	if (htx_is_empty(htx)) {
 		FLT_OT_ERR("HTX is empty");
 
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 	}
 
 	if (!FLT_OT_STR_ISVALID(prefix)) {
@@ -219,7 +219,7 @@
 	else {
 		buffer = flt_ot_trash_alloc(0, err);
 		if (buffer == NULL)
-			FLT_OT_RETURN(retval);
+			FLT_OT_RETURN_INT(retval);
 
 		(void)chunk_printf(buffer, "%s-%s", prefix, name);
 
@@ -264,7 +264,7 @@
 
 	flt_ot_trash_free(&buffer);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -291,7 +291,7 @@
 
 	retval = flt_ot_http_header_set(chn, prefix, NULL, NULL, err);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 /*
diff --git a/addons/ot/src/opentracing.c b/addons/ot/src/opentracing.c
index b79ba29..8ae5f02 100644
--- a/addons/ot/src/opentracing.c
+++ b/addons/ot/src/opentracing.c
@@ -162,13 +162,13 @@
 	if (getcwd(cwd, sizeof(cwd)) == NULL) {
 		FLT_OT_ERR("failed to get current working directory");
 
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 	}
 	rc = snprintf(path, sizeof(path), "%s/%s", cwd, plugin);
 	if ((rc == -1) || (rc >= sizeof(path))) {
 		FLT_OT_ERR("failed to construct the OpenTracing plugin path");
 
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 	}
 
 	*tracer = otc_tracer_load(path, errbuf, sizeof(errbuf));
@@ -180,7 +180,7 @@
 		retval = 0;
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -207,13 +207,13 @@
 	FLT_OT_FUNC("%p, %p, %p:%p", tracer, cfgbuf, FLT_OT_DPTR_ARGS(err));
 
 	if (cfgbuf == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	retval = otc_tracer_start(NULL, cfgbuf, errbuf, sizeof(errbuf));
 	if (retval == -1)
 		FLT_OT_ERR("%s", (*errbuf == '\0') ? "failed to start tracer" : errbuf);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -277,9 +277,9 @@
 	FLT_OT_FUNC("%p, \"%s\", %p, %p, %d, %d, %p, %p, %d, %p:%p", tracer, operation_name, ts_steady, ts_system, ref_type, ref_ctx_idx, ref_span, tags, num_tags, FLT_OT_DPTR_ARGS(err));
 
 	if (operation_name == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	else if (tracer == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	(void)memset(&options, 0, sizeof(options));
 
@@ -303,7 +303,7 @@
 	else
 		FLT_OT_DBG(2, "span %p:%zd initialized", retptr, retptr->idx);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -354,7 +354,7 @@
 
 	retptr = ot_span_init(tracer, operation_name, ts_steady, ts_system, ref_type, ref_ctx_idx, ref_span, tags, num_tags, err);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -380,12 +380,12 @@
 	FLT_OT_FUNC("%p, %p, %d", span, tags, num_tags);
 
 	if ((span == NULL) || (tags == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	for (retval = 0; retval < num_tags; retval++)
 		span->set_tag(span, tags[retval].key, &(tags[retval].value));
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -414,7 +414,7 @@
 	FLT_OT_FUNC("%p, \"%s\", %d, ...", span, key, type);
 
 	if ((span == NULL) || (key == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	va_start(ap, type);
 	for (retval = 0; (key != NULL) && FLT_OT_IN_RANGE(type, otc_value_bool, otc_value_null); retval++) {
@@ -439,7 +439,7 @@
 	}
 	va_end(ap);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -465,13 +465,13 @@
 	FLT_OT_FUNC("%p, %p, %d", span, log_fields, num_fields);
 
 	if ((span == NULL) || (log_fields == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	retval = MIN(OTC_MAXLOGFIELDS, num_fields);
 
 	span->log_fields(span, log_fields, retval);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -499,7 +499,7 @@
 	FLT_OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value);
 
 	if ((span == NULL) || (key == NULL) || (value == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	va_start(ap, value);
 	for (retval = 0; (retval < FLT_OT_TABLESIZE(log_field)) && (key != NULL); retval++) {
@@ -515,7 +515,7 @@
 
 	span->log_fields(span, log_field, retval);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -543,7 +543,7 @@
 	FLT_OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, format);
 
 	if ((span == NULL) || (key == NULL) || (format == NULL))
-		FLT_OT_RETURN(-1);
+		FLT_OT_RETURN_INT(-1);
 
 	va_start(ap, format);
 	n = vsnprintf(value, sizeof(value), format, ap);
@@ -554,7 +554,7 @@
 	}
 	va_end(ap);
 
-	FLT_OT_RETURN(ot_span_log_va(span, key, value, NULL));
+	FLT_OT_RETURN_INT(ot_span_log_va(span, key, value, NULL));
 }
 
 
@@ -580,10 +580,10 @@
 	FLT_OT_FUNC("%p, %p", span, baggage);
 
 	if ((span == NULL) || (baggage == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	if ((baggage->key == NULL) || (baggage->value == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	for (retval = i = 0; i < baggage->count; i++) {
 		FLT_OT_DBG(3, "set baggage: \"%s\" -> \"%s\"", baggage->key[i], baggage->value[i]);
@@ -595,7 +595,7 @@
 		}
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -622,7 +622,7 @@
 	FLT_OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value);
 
 	if ((span == NULL) || (key == NULL) || (value == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	va_start(ap, value);
 	for (retval = 0; (key != NULL); retval++) {
@@ -636,7 +636,7 @@
 	}
 	va_end(ap);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -663,7 +663,7 @@
 	FLT_OT_FUNC("%p, \"%s\", ...", span, key);
 
 	if ((span == NULL) || (key == NULL))
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	va_start(ap, key);
 	for (n = 1; va_arg(ap, typeof(key)) != NULL; n++);
@@ -671,7 +671,7 @@
 
 	retptr = otc_text_map_new(NULL, n);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	va_start(ap, key);
 	for (i = 0; (i < n) && (key != NULL); i++) {
@@ -689,7 +689,7 @@
 	}
 	va_end(ap);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -716,13 +716,13 @@
 	FLT_OT_FUNC("%p, %p, %p", tracer, span, carrier);
 
 	if ((span == NULL) || (carrier == NULL))
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	else if (tracer == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	retptr = span->span_context((struct otc_span *)span);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	(void)memset(carrier, 0, sizeof(*carrier));
 
@@ -737,7 +737,7 @@
 #endif
 	}
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -765,15 +765,15 @@
 	FLT_OT_FUNC("%p, %p, %p, %p:%p", tracer, span, carrier, FLT_OT_DPTR_ARGS(err));
 
 	if ((span == NULL) || (carrier == NULL))
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	else if (tracer == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	retptr = span->span_context((struct otc_span *)span);
 	if (retptr == NULL) {
 		FLT_OT_ERR("failed to create span context");
 
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	}
 
 	(void)memset(carrier, 0, sizeof(*carrier));
@@ -791,7 +791,7 @@
 #endif
 	}
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -818,13 +818,13 @@
 	FLT_OT_FUNC("%p, %p, %p", tracer, span, carrier);
 
 	if ((span == NULL) || (carrier == NULL))
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	else if (tracer == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	retptr = span->span_context((struct otc_span *)span);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	(void)memset(carrier, 0, sizeof(*carrier));
 
@@ -844,7 +844,7 @@
 #endif
 	}
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -871,9 +871,9 @@
 	FLT_OT_FUNC("%p, %p, %p", tracer, carrier, text_map);
 
 	if (carrier == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	else if (tracer == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	if (text_map != NULL) {
 		(void)memset(carrier, 0, sizeof(*carrier));
@@ -888,7 +888,7 @@
 	else if (retptr != NULL)
 		FLT_OT_DBG_SPAN_CONTEXT(retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -916,9 +916,9 @@
 	FLT_OT_FUNC("%p, %p, %p, %p:%p", tracer, carrier, text_map, FLT_OT_DPTR_ARGS(err));
 
 	if (carrier == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	else if (tracer == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	if (text_map != NULL) {
 		(void)memset(carrier, 0, sizeof(*carrier));
@@ -936,7 +936,7 @@
 	else if (retptr != NULL)
 		FLT_OT_DBG_SPAN_CONTEXT(retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -963,9 +963,9 @@
 	FLT_OT_FUNC("%p, %p, %p", tracer, carrier, binary_data);
 
 	if (carrier == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	else if (tracer == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	if ((FLT_OT_DEREF(binary_data, data, NULL) != NULL) && (binary_data->size > 0)) {
 		(void)memset(carrier, 0, sizeof(*carrier));
@@ -980,7 +980,7 @@
 	else if (retptr != NULL)
 		FLT_OT_DBG_SPAN_CONTEXT(retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
diff --git a/addons/ot/src/parser.c b/addons/ot/src/parser.c
index e26ca18..513054b 100644
--- a/addons/ot/src/parser.c
+++ b/addons/ot/src/parser.c
@@ -67,7 +67,7 @@
 		retval |= ERR_ABORT | ERR_ALERT;
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -112,7 +112,7 @@
 		retval = flt_ot_parse_strdup(ptr, args[pos + 1], err, args[cur_arg]);
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -137,7 +137,7 @@
 	FLT_OT_FUNC("\"%s\", %d", name, type);
 
 	if (!FLT_OT_STR_ISVALID(name))
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_EX(retptr, const char *, "%p");
 
 	if (type == 1) {
 		retptr = invalid_char(name);
@@ -162,7 +162,7 @@
 			retptr = NULL;
 	}
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_EX(retptr, const char *, "%p");
 }
 
 
@@ -241,7 +241,7 @@
 	if (!(retval & ERR_CODE) && (*pdata)->flag_check_id && (id == NULL))
 		FLT_OT_PARSE_ERR(err, "'%s' : %s ID not set (use '%s%s')", args[0], parse_data[1].name, parse_data[1].name, parse_data[1].usage);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -285,7 +285,7 @@
 	if (retval & ERR_CODE)
 		flt_ot_conf_sample_expr_free(&expr);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -335,7 +335,7 @@
 	else
 		FLT_OT_DBG(3, "sample '%s' -> '%s' added", sample->key, sample->value);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -371,7 +371,7 @@
 	if (retval & ERR_CODE)
 		flt_ot_conf_str_free(&str);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -409,7 +409,7 @@
 	else
 		retval = flt_ot_parse_keyword(ptr, args, 0, 0, err, err_msg);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -470,13 +470,13 @@
 	FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
 
 	if (flt_ot_parse_check_scope())
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_tracer, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
 	if (retval & ERR_CODE) {
 		FLT_OT_PARSE_IFERR_ALERT();
 
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 	}
 
 	if (pdata->keyword == FLT_OT_PARSE_TRACER_ID) {
@@ -554,7 +554,7 @@
 	if ((retval & ERR_CODE) && (flt_ot_current_tracer != NULL))
 		flt_ot_conf_tracer_free(&flt_ot_current_tracer);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -580,12 +580,12 @@
 	FLT_OT_FUNC("");
 
 	if (flt_ot_current_tracer == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	flt_ot_current_config->tracer = flt_ot_current_tracer;
 
 	if (flt_ot_current_tracer->id == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	if (flt_ot_current_tracer->config == NULL) {
 		FLT_OT_POST_PARSE_ALERT("tracer '%s' has no configuration file specified", flt_ot_current_tracer->cfg_line, flt_ot_current_tracer->id);
@@ -600,7 +600,7 @@
 
 	flt_ot_current_tracer = NULL;
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -633,13 +633,13 @@
 	FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
 
 	if (flt_ot_parse_check_scope())
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_group, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
 	if (retval & ERR_CODE) {
 		FLT_OT_PARSE_IFERR_ALERT();
 
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 	}
 
 	if (pdata->keyword == FLT_OT_PARSE_GROUP_ID) {
@@ -658,7 +658,7 @@
 	if ((retval & ERR_CODE) && (flt_ot_current_group != NULL))
 		flt_ot_conf_group_free(&flt_ot_current_group);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -683,7 +683,7 @@
 	FLT_OT_FUNC("");
 
 	if (flt_ot_current_group == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	/* Check that the group has at least one scope defined. */
 	if (LIST_ISEMPTY(&(flt_ot_current_group->ph_scopes)))
@@ -691,7 +691,7 @@
 
 	flt_ot_current_group = NULL;
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -734,7 +734,7 @@
 
 	FLT_OT_DBG(2, "ctx_flags: 0x%02hhx (0x%02hhx)", flt_ot_current_span->ctx_flags, flags);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -774,7 +774,7 @@
 	if ((retptr != NULL) && (err != NULL))
 		FLT_OT_FREE_CLEAR(*err);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -807,13 +807,13 @@
 	FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
 
 	if (flt_ot_parse_check_scope())
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_span, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
 	if (retval & ERR_CODE) {
 		FLT_OT_PARSE_IFERR_ALERT();
 
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 	}
 
 	if (pdata->keyword == FLT_OT_PARSE_SCOPE_ID) {
@@ -1005,7 +1005,7 @@
 		flt_ot_current_span = NULL;
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -1034,7 +1034,7 @@
 	FLT_OT_FUNC("");
 
 	if (flt_ot_current_scope == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	/* If span context inject is used, check that this is possible. */
 	list_for_each_entry(conf_span, &(flt_ot_current_scope->spans), list)
@@ -1048,7 +1048,7 @@
 	flt_ot_current_scope = NULL;
 	flt_ot_current_span  = NULL;
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -1099,7 +1099,7 @@
 
 	flt_ot_current_config = NULL;
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -1144,7 +1144,7 @@
 		if (otc_dbg_mem_init(&dbg_mem, dbg_mem_data, FLT_OT_TABLESIZE(dbg_mem_data), 0xff) == -1) {
 			FLT_OT_PARSE_ERR(err, "cannot initialize memory debugger");
 
-			FLT_OT_RETURN(retval);
+			FLT_OT_RETURN_INT(retval);
 		}
 	);
 #endif
@@ -1155,7 +1155,7 @@
 	if (conf == NULL) {
 		FLT_OT_PARSE_ERR(err, "'%s' : out of memory", args[*cur_arg]);
 
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 	}
 
 	for (pos = *cur_arg + 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(pos); pos++) {
@@ -1198,7 +1198,7 @@
 		FLT_OT_DBG(3, "filter set: id '%s', config '%s'", conf->id, conf->cfg_file);
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
diff --git a/addons/ot/src/pool.c b/addons/ot/src/pool.c
index ead53e1..fbcdbfc 100644
--- a/addons/ot/src/pool.c
+++ b/addons/ot/src/pool.c
@@ -55,7 +55,7 @@
 	else if (flag_clear)
 		(void)memset(retptr, 0, size);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -97,7 +97,7 @@
 	else
 		FLT_OT_ERR("out of memory");
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -147,7 +147,7 @@
  *   -
  *
  * RETURN VALUE
- *   This function does not return a value.
+ *   -
  */
 struct buffer *flt_ot_trash_alloc(bool flag_clear, char **err)
 {
@@ -175,7 +175,7 @@
 	else if (flag_clear)
 		(void)memset(retptr->area, 0, retptr->size);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
diff --git a/addons/ot/src/scope.c b/addons/ot/src/scope.c
index 8d70a08..ec5e81f 100644
--- a/addons/ot/src/scope.c
+++ b/addons/ot/src/scope.c
@@ -101,7 +101,7 @@
 
 	retptr = flt_ot_pool_alloc(pool_head_ot_runtime_context, sizeof(*retptr), 1, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	retptr->stream        = s;
 	retptr->filter        = f;
@@ -125,7 +125,7 @@
 
 	FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -209,13 +209,13 @@
 	FLT_OT_FUNC("%p, \"%s\", %zu, %d, \"%s\", %zu, %u, %p:%p", rt_ctx, id, id_len, ref_type, ref_id, ref_id_len, dir, FLT_OT_DPTR_ARGS(err));
 
 	if ((rt_ctx == NULL) || (id == NULL))
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	list_for_each_entry(span, &(rt_ctx->spans), list)
 		if ((span->id_len == id_len) && (memcmp(span->id, id, id_len) == 0)) {
 			FLT_OT_DBG(2, "found span %p", span);
 
-			FLT_OT_RETURN(span);
+			FLT_OT_RETURN_PTR(span);
 		}
 
 	if (ref_id != NULL) {
@@ -241,14 +241,14 @@
 			} else {
 				FLT_OT_ERR("cannot find referenced span/context '%s'", ref_id);
 
-				FLT_OT_RETURN(retptr);
+				FLT_OT_RETURN_PTR(retptr);
 			}
 		}
 	}
 
 	retptr = flt_ot_pool_alloc(pool_head_ot_scope_span, sizeof(*retptr), 1, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	retptr->id          = id;
 	retptr->id_len      = id_len;
@@ -260,7 +260,7 @@
 
 	FLT_OT_DBG_SCOPE_SPAN("new span ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -328,24 +328,24 @@
 	FLT_OT_FUNC("%p, %p, \"%s\", %zu, %p, %u, %p:%p", rt_ctx, tracer, id, id_len, text_map, dir, FLT_OT_DPTR_ARGS(err));
 
 	if ((rt_ctx == NULL) || (tracer == NULL) || (id == NULL) || (text_map == NULL))
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	list_for_each_entry(retptr, &(rt_ctx->contexts), list)
 		if ((retptr->id_len == id_len) && (memcmp(retptr->id, id, id_len) == 0)) {
 			FLT_OT_DBG(2, "found context %p", retptr);
 
-			FLT_OT_RETURN(retptr);
+			FLT_OT_RETURN_PTR(retptr);
 		}
 
 	retptr = flt_ot_pool_alloc(pool_head_ot_scope_context, sizeof(*retptr), 1, err);
 	if (retptr == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	span_ctx = ot_extract_http_headers(tracer, &reader, text_map, err);
 	if (span_ctx == NULL) {
 		flt_ot_scope_context_free(&retptr);
 
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 	}
 
 	retptr->id          = id;
@@ -356,7 +356,7 @@
 
 	FLT_OT_DBG_SCOPE_CONTEXT("new context ", retptr);
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 
@@ -523,7 +523,7 @@
 
 	retval = span_cnt + ctx_cnt;
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
diff --git a/addons/ot/src/util.c b/addons/ot/src/util.c
index f6812bc..767685e 100644
--- a/addons/ot/src/util.c
+++ b/addons/ot/src/util.c
@@ -327,7 +327,7 @@
 	FLT_OT_FUNC("%p, %p, %zu, %p:%p", chk, src, n, FLT_OT_DPTR_ARGS(err));
 
 	if ((chk == NULL) || (src == NULL))
-		FLT_OT_RETURN(-1);
+		FLT_OT_RETURN_EX(-1, ssize_t, "%ld");
 
 	if (chk->area == NULL)
 		chunk_init(chk, FLT_OT_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
@@ -335,18 +335,18 @@
 	if (chk->area == NULL) {
 		FLT_OT_ERR("out of memory");
 
-		FLT_OT_RETURN(-1);
+		FLT_OT_RETURN_EX(-1, ssize_t, "%ld");
 	}
 	else if (n > (chk->size - chk->data)) {
 		FLT_OT_ERR("chunk size too small");
 
-		FLT_OT_RETURN(-1);
+		FLT_OT_RETURN_EX(-1, ssize_t, "%ld");
 	}
 
 	(void)memcpy(chk->area + chk->data, src, n);
 	chk->data += n;
 
-	FLT_OT_RETURN(chk->data);
+	FLT_OT_RETURN_EX(chk->data, ssize_t, "%ld");
 }
 
 
@@ -512,7 +512,7 @@
 	FLT_OT_FUNC("%p, %p, %zu, %p:%p", data, value, size, FLT_OT_DPTR_ARGS(err));
 
 	if ((data == NULL) || (value == NULL) || (size == 0))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	*value = '\0';
 
@@ -623,7 +623,7 @@
 		FLT_OT_ERR("invalid HTTP method");
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -650,7 +650,7 @@
 	FLT_OT_FUNC("\"%s\", %p, %p, %p:%p", key, data, value, FLT_OT_DPTR_ARGS(err));
 
 	if ((data == NULL) || (value == NULL))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	if (data->type == SMP_T_BOOL) {
 		value->type             = otc_value_bool;
@@ -674,7 +674,7 @@
 			retval = flt_ot_sample_to_str(data, (char *)value->value.string_value, global.tune.bufsize, err);
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -802,7 +802,7 @@
 			FLT_OT_DBG(3, "baggage[%zu]: '%s' -> '%s'", data->baggage->count - 1, data->baggage->key[data->baggage->count - 1], data->baggage->value[data->baggage->count - 1]);
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 /*
diff --git a/addons/ot/src/vars.c b/addons/ot/src/vars.c
index fff54ac..a17189c 100644
--- a/addons/ot/src/vars.c
+++ b/addons/ot/src/vars.c
@@ -31,7 +31,8 @@
  *   scope -
  *
  * DESCRIPTION
- *   -
+ *   Function prints the contents of all variables defined for a particular
+ *   scope.
  *
  * RETURN VALUE
  *   This function does not return a value.
@@ -58,7 +59,8 @@
  *   s -
  *
  * DESCRIPTION
- *   -
+ *   Function prints the contents of all variables grouped by individual
+ *   scope.
  *
  * RETURN VALUE
  *   This function does not return a value.
@@ -85,6 +87,38 @@
 
 /***
  * NAME
+ *   flt_ot_smp_init -
+ *
+ * ARGUMENTS
+ *   s    -
+ *   smp  -
+ *   opt  -
+ *   type -
+ *   data -
+ *
+ * DESCRIPTION
+ *   The function initializes the value of the 'smp' structure.  If the 'data'
+ *   argument is set, then the 'sample_data' member of the 'smp' structure is
+ *   also initialized.
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static inline void flt_ot_smp_init(struct stream *s, struct sample *smp, uint opt, int type, const char *data)
+{
+	(void)memset(smp, 0, sizeof(*smp));
+	(void)smp_set_owner(smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
+
+	if (data != NULL) {
+		smp->data.type = type;
+
+		chunk_initstr(&(smp->data.u.str), data);
+	}
+}
+
+
+/***
+ * NAME
  *   flt_ot_get_vars -
  *
  * ARGUMENTS
@@ -95,7 +129,8 @@
  *   -
  *
  * RETURN VALUE
- *   -
+ *   Returns the struct vars pointer for a stream and scope, or NULL if it does
+ *   not exist.
  */
 static inline struct vars *flt_ot_get_vars(struct stream *s, const char *scope)
 {
@@ -138,7 +173,7 @@
 	FLT_OT_FUNC("%p, %zu, %p, \"%s\", %p:%p", var_name, size, len, name, FLT_OT_DPTR_ARGS(err));
 
 	if (!FLT_OT_STR_ISVALID(name))
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	/*
 	 * In case the name of the variable consists of several elements,
@@ -184,7 +219,7 @@
 	if (retval == -1)
 		*len = retval;
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -201,7 +236,9 @@
  *   err      -
  *
  * DESCRIPTION
- *   -
+ *   The function initializes the value of the 'smp' structure.  If the 'data'
+ *   argument is set, then the 'sample_data' member of the 'smp' structure is
+ *   also initialized.
  *
  * RETURN VALUE
  *   -
@@ -219,7 +256,7 @@
 	if (retval == -1)
 		FLT_OT_ERR("failed to construct variable name '%s.%s.%s'", scope, prefix, name);
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -243,29 +280,29 @@
 {
 	struct arg arg;
 	char       var_name[BUFSIZ];
-	int        retval;
+	int        retval = -1, var_name_len;
 
 	FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %p:%p", scope, prefix, name, FLT_OT_DPTR_ARGS(err));
 
-	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
-	if (retval == -1)
-		FLT_OT_RETURN(retval);
+	var_name_len = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
+	if (var_name_len == -1)
+		FLT_OT_RETURN_INT(retval);
 
 	/* Set <size> to 0 to not release var_name memory in vars_check_arg(). */
 	(void)memset(&arg, 0, sizeof(arg));
 	arg.type          = ARGT_STR;
 	arg.data.str.area = var_name;
-	arg.data.str.data = retval;
+	arg.data.str.data = var_name_len;
 
 	if (vars_check_arg(&arg, err) == 0) {
 		FLT_OT_ERR_APPEND("failed to register variable '%s': %s", var_name, *err);
-
-		retval = -1;
 	} else {
 		FLT_OT_DBG(2, "variable '%s' registered", arg.data.var.name);
+
+		retval = var_name_len;
 	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -292,62 +329,25 @@
 {
 	struct sample smp;
 	char          var_name[BUFSIZ];
-	int           retval;
+	int           retval = -1, var_name_len;
 
 	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, value, opt, FLT_OT_DPTR_ARGS(err));
 
-	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
-	if (retval == -1)
-		FLT_OT_RETURN(retval);
-
-	(void)memset(&smp, 0, sizeof(smp));
-	(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
-	smp.data.type       = SMP_T_STR;
-	smp.data.u.str.area = (char *)value;
-	smp.data.u.str.data = strlen(value);
-
-	vars_set_by_name_ifexist(var_name, retval, &smp);
-
-	FLT_OT_RETURN(retval);
-}
-
-
-/***
- * NAME
- *   flt_ot_var_unset -
- *
- * ARGUMENTS
- *   s      -
- *   scope  -
- *   prefix -
- *   name   -
- *   opt    -
- *   err    -
- *
- * DESCRIPTION
- *   -
- *
- * RETURN VALUE
- *   -
- */
-int flt_ot_var_unset(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err)
-{
-	struct sample smp;
-	char          var_name[BUFSIZ];
-	int           retval;
-
-	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, opt, FLT_OT_DPTR_ARGS(err));
+	var_name_len = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
+	if (var_name_len == -1)
+		FLT_OT_RETURN_INT(retval);
 
-	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
-	if (retval == -1)
-		FLT_OT_RETURN(retval);
+	flt_ot_smp_init(s, &smp, opt, SMP_T_STR, value);
 
-	(void)memset(&smp, 0, sizeof(smp));
-	(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
+	if (vars_set_by_name_ifexist(var_name, var_name_len, &smp) == 0) {
+		FLT_OT_ERR("failed to set variable '%s'", var_name);
+	} else {
+		FLT_OT_DBG(2, "variable '%s' set", var_name);
 
-	vars_unset_by_name_ifexist(var_name, retval, &smp);
+		retval = var_name_len;
+	}
 
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -381,11 +381,11 @@
 
 	vars = flt_ot_get_vars(s, scope);
 	if (vars == NULL)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	var_prefix_len = flt_ot_var_name(NULL, prefix, NULL, var_prefix, sizeof(var_prefix), err);
 	if (var_prefix_len == -1)
-		FLT_OT_RETURN(retval);
+		FLT_OT_RETURN_INT(retval);
 
 	retval = 0;
 
@@ -403,10 +403,8 @@
 
 			FLT_OT_DBG(2, "- '%s' -> '%.*s'", var_name, (int)var->data.u.str.data, var->data.u.str.area);
 
-			(void)memset(&smp, 0, sizeof(smp));
-			(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
-
 			size = var_clear(var);
+			flt_ot_smp_init(s, &smp, opt, 0, NULL);
 			var_accounting_diff(vars, smp.sess, smp.strm, -size);
 
 			retval++;
@@ -414,55 +412,7 @@
 	}
 	HA_RWLOCK_WRUNLOCK(VARS_LOCK, &(vars->rwlock));
 
-	FLT_OT_RETURN(retval);
-}
-
-
-/***
- * NAME
- *   flt_ot_var_get -
- *
- * ARGUMENTS
- *   s      -
- *   scope  -
- *   prefix -
- *   name   -
- *   value  -
- *   opt    -
- *   err    -
- *
- * DESCRIPTION
- *   -
- *
- * RETURN VALUE
- *   -
- */
-int flt_ot_var_get(struct stream *s, const char *scope, const char *prefix, const char *name, char **value, uint opt, char **err)
-{
-	struct sample smp;
-	char          var_name[BUFSIZ], var_value[BUFSIZ];
-	int           retval;
-
-	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %p:%p, %u, %p:%p", s, scope, prefix, name, FLT_OT_DPTR_ARGS(value), opt, FLT_OT_DPTR_ARGS(err));
-
-	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
-	if (retval == -1)
-		FLT_OT_RETURN(retval);
-
-	(void)memset(&smp, 0, sizeof(smp));
-	(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
-
-	if (vars_get_by_name(var_name, retval, &smp)) {
-		retval = flt_ot_sample_to_str(&(smp.data), var_value, sizeof(var_value), err);
-		if (retval != -1)
-			FLT_OT_DBG(3, "data type %d: '%s' = '%s'", smp.data.type, var_name, var_value);
-	} else {
-		FLT_OT_ERR("failed to get variable '%s'", var_name);
-
-		retval = -1;
-	}
-
-	FLT_OT_RETURN(retval);
+	FLT_OT_RETURN_INT(retval);
 }
 
 
@@ -495,11 +445,11 @@
 
 	vars = flt_ot_get_vars(s, scope);
 	if (vars == NULL)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	rc = flt_ot_var_name(NULL, prefix, NULL, var_name, sizeof(var_name), err);
 	if (rc == -1)
-		FLT_OT_RETURN(retptr);
+		FLT_OT_RETURN_PTR(retptr);
 
 	HA_RWLOCK_RDLOCK(VARS_LOCK, &(vars->rwlock));
 	list_for_each_entry(var, &(vars->head), l) {
@@ -565,7 +515,7 @@
 		otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
 	}
 
-	FLT_OT_RETURN(retptr);
+	FLT_OT_RETURN_PTR(retptr);
 }
 
 /*
diff --git a/addons/ot/test/run-cmp.sh b/addons/ot/test/run-cmp.sh
index 77b48bd..8e678b7 100755
--- a/addons/ot/test/run-cmp.sh
+++ b/addons/ot/test/run-cmp.sh
@@ -3,7 +3,7 @@
 _ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
        _ARGS="-f cmp/haproxy.cfg"
     _LOG_DIR="_logs"
-        _LOG="${_LOG_DIR}/_log-$(basename ${0} .sh)-$(date +%s)"
+        _LOG="${_LOG_DIR}/_log-$(basename "${0}" .sh)-$(date +%s)"
 
 
 test -x "${_ARG_HAPROXY}" || exit 1
diff --git a/addons/ot/test/run-ctx.sh b/addons/ot/test/run-ctx.sh
index 064fa7d..bfac617 100755
--- a/addons/ot/test/run-ctx.sh
+++ b/addons/ot/test/run-ctx.sh
@@ -3,7 +3,7 @@
 _ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
        _ARGS="-f ctx/haproxy.cfg"
     _LOG_DIR="_logs"
-        _LOG="${_LOG_DIR}/_log-$(basename ${0} .sh)-$(date +%s)"
+        _LOG="${_LOG_DIR}/_log-$(basename "${0}" .sh)-$(date +%s)"
 
 
 test -x "${_ARG_HAPROXY}" || exit 1
diff --git a/addons/ot/test/run-fe-be.sh b/addons/ot/test/run-fe-be.sh
index 7e70ad6..68b250c 100755
--- a/addons/ot/test/run-fe-be.sh
+++ b/addons/ot/test/run-fe-be.sh
@@ -5,8 +5,8 @@
     _ARGS_BE="-f be/haproxy.cfg"
        _TIME="$(date +%s)"
     _LOG_DIR="_logs"
-     _LOG_FE="${_LOG_DIR}/_log-$(basename ${0} fe-be.sh)fe-${_TIME}"
-     _LOG_BE="${_LOG_DIR}/_log-$(basename ${0} fe-be.sh)be-${_TIME}"
+     _LOG_FE="${_LOG_DIR}/_log-$(basename "${0}" fe-be.sh)fe-${_TIME}"
+     _LOG_BE="${_LOG_DIR}/_log-$(basename "${0}" fe-be.sh)be-${_TIME}"
 
 
 __exit ()
diff --git a/addons/ot/test/run-sa.sh b/addons/ot/test/run-sa.sh
index e5682ea..04a303a 100755
--- a/addons/ot/test/run-sa.sh
+++ b/addons/ot/test/run-sa.sh
@@ -3,7 +3,7 @@
 _ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
        _ARGS="-f sa/haproxy.cfg"
     _LOG_DIR="_logs"
-        _LOG="${_LOG_DIR}/_log-$(basename ${0} .sh)-$(date +%s)"
+        _LOG="${_LOG_DIR}/_log-$(basename "${0}" .sh)-$(date +%s)"
 
 
 test -x "${_ARG_HAPROXY}" || exit 1
diff --git a/addons/ot/test/test-speed.sh b/addons/ot/test/test-speed.sh
index ef2ccf0..f2ac514 100755
--- a/addons/ot/test/test-speed.sh
+++ b/addons/ot/test/test-speed.sh
@@ -1,11 +1,28 @@
 #!/bin/sh
 #
       _ARG_CFG="${1}"
-      _ARG_DIR="${2}"
+      _ARG_DIR="${2:-${1}}"
       _LOG_DIR="_logs"
 _HTTPD_PIDFILE="${_LOG_DIR}/thttpd.pid"
+    _USAGE_MSG="usage: $(basename "${0}") cfg [dir]"
 
 
+sh_exit ()
+{
+	test -z "${2}" && {
+		echo
+		echo "Script killed!"
+	}
+
+	test -n "${1}" && {
+		echo
+		echo "${1}"
+		echo
+	}
+
+	exit ${2:-64}
+}
+
 httpd_run ()
 {
 
@@ -63,18 +80,22 @@
 }
 
 
-mkdir -p "${_LOG_DIR}" || exit 1
+command -v thttpd >/dev/null 2>&1 || sh_exit "thttpd: command not found" 5
+command -v wrk >/dev/null 2>&1    || sh_exit "wrk: command not found" 6
+
+mkdir -p "${_LOG_DIR}" || sh_exit "${_LOG_DIR}: Cannot create log directory" 1
 
 if test "${_ARG_CFG}" = "all"; then
-	${0} fe-be fe > "${_LOG_DIR}/README-speed-fe-be"
-	${0} sa sa    > "${_LOG_DIR}/README-speed-sa"
-	${0} cmp cmp  > "${_LOG_DIR}/README-speed-cmp"
-	${0} ctx ctx  > "${_LOG_DIR}/README-speed-ctx"
+	"${0}" fe-be fe > "${_LOG_DIR}/README-speed-fe-be"
+	"${0}" sa sa    > "${_LOG_DIR}/README-speed-sa"
+	"${0}" cmp cmp  > "${_LOG_DIR}/README-speed-cmp"
+	"${0}" ctx ctx  > "${_LOG_DIR}/README-speed-ctx"
 	exit 0
 fi
 
-test -n "${_ARG_CFG}" -a -f "run-${_ARG_CFG}.sh" || exit 2
-test -n "${_ARG_DIR}" -a -d "${_ARG_DIR}"        || exit 3
+test -z "${_ARG_CFG}" -o -z "${_ARG_DIR}" && sh_exit "${_USAGE_MSG}" 4
+test -f "run-${_ARG_CFG}.sh"              || sh_exit "run-${_ARG_CFG}.sh: No such configuration script" 2
+test -d "${_ARG_DIR}"                     || sh_exit "${_ARG_DIR}: No such directory" 3
 
 test -e "${_ARG_DIR}/haproxy.cfg.in" || cp -af "${_ARG_DIR}/haproxy.cfg" "${_ARG_DIR}/haproxy.cfg.in"
 test -e "${_ARG_DIR}/ot.cfg.in"      || cp -af "${_ARG_DIR}/ot.cfg" "${_ARG_DIR}/ot.cfg.in"
diff --git a/addons/promex/service-prometheus.c b/addons/promex/service-prometheus.c
index bdd63e1..b267f98 100644
--- a/addons/promex/service-prometheus.c
+++ b/addons/promex/service-prometheus.c
@@ -1508,8 +1508,20 @@
 			/* fall through */
 
 		case PROMEX_ST_DONE:
-			/* no more data are expected. Don't add TLR because mux-h1 will take care of it */
-			res_htx->flags |= HTX_FL_EOM;
+			/* no more data are expected. If the response buffer is
+			 * empty, be sure to add something (EOT block in this
+			 * case) to have something to send. It is important to
+			 * be sure the EOM flags will be handled by the
+			 * endpoint.
+			 */
+			if (htx_is_empty(res_htx)) {
+				if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
+					si_rx_room_blk(si);
+					goto out;
+				}
+				channel_add_input(res, 1);
+			}
+		        res_htx->flags |= HTX_FL_EOM;
 			res->flags |= CF_EOI;
 			appctx->st0 = PROMEX_ST_END;
 			/* fall through */
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 0d4e64b..af41588 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3,8 +3,7 @@
                           Configuration Manual
                          ----------------------
                               version 2.4
-                             willy tarreau
-                              2022/03/14
+                              2022/05/13
 
 
 This document covers the configuration language as implemented in the version
@@ -12812,8 +12811,6 @@
   during startup because it may result in accumulation of expired sessions in
   the system if the system's timeouts are not configured either.
 
-  This also applies to HTTP/2 connections, which will be closed with GOAWAY.
-
   See also : "timeout server", "timeout tunnel", "timeout http-request".
 
 
@@ -12908,10 +12905,6 @@
   set in the frontend to take effect, unless the frontend is in TCP mode, in
   which case the HTTP backend's timeout will be used.
 
-  When using HTTP/2 "timeout client" is applied instead. This is so we can keep
-  using short keep-alive timeouts in HTTP/1.1 while using longer ones in HTTP/2
-  (where we only have one connection per client and a connection setup).
-
   See also : "timeout http-request", "timeout client".
 
 
@@ -16604,6 +16597,8 @@
   CONNECT and CONNACK packet types. CONNECT is the first message sent by the
   client and CONNACK is the first response sent by the server.
 
+  Only MQTT 3.1, 3.1.1 and 5.0 are supported.
+
   Example:
 
       acl data_in_buffer req.len ge 4
@@ -21273,12 +21268,18 @@
           rare, that the proxy blocked a chunked-encoding request from the
           client due to an invalid syntax, before the server responded. In this
           case, an HTTP 400 error is sent to the client and reported in the
-          logs.
+          logs. Finally, it may be due to an HTTP header rewrite failure on the
+          response. In this case, an HTTP 500 error is sent (see
+          "tune.maxrewrite" and "http-response strict-mode" for more
+          inforomation).
 
      PR   The proxy blocked the client's HTTP request, either because of an
           invalid HTTP syntax, in which case it returned an HTTP 400 error to
           the client, or because a deny filter matched, in which case it
-          returned an HTTP 403 error.
+          returned an HTTP 403 error.  It may also be due to an HTTP header
+          rewrite failure on the request. In this case, an HTTP 500 error is
+          sent (see "tune.maxrewrite" and "http-request strict-mode" for more
+          inforomation).
 
      PT   The proxy blocked the client's request and has tarpitted its
           connection before returning it a 500 server error. Nothing was sent
diff --git a/doc/lua.txt b/doc/lua.txt
index 4a63e57..289bd18 100644
--- a/doc/lua.txt
+++ b/doc/lua.txt
@@ -83,9 +83,9 @@
 Reading the following documentation links is required to understand the
 current paragraph:
 
-   HAProxy doc: http://cbonte.github.io/haproxy-dconv/
+   HAProxy doc: http://docs.haproxy.org/
    Lua API:     http://www.lua.org/manual/5.3/
-   HAProxy API: http://www.arpalert.org/src/haproxy-lua-api/1.9dev/index.html
+   HAProxy API: http://www.arpalert.org/src/haproxy-lua-api/2.6/index.html
    Lua guide:   http://www.lua.org/pil/
 
 more about Lua choice
diff --git a/include/haproxy/bug.h b/include/haproxy/bug.h
index 13815fe..a2a9f6d 100644
--- a/include/haproxy/bug.h
+++ b/include/haproxy/bug.h
@@ -37,14 +37,28 @@
 #define DPRINTF(x...)
 #endif
 
+static inline __attribute((always_inline)) void ha_crash_now(void)
+{
+#if __GNUC_PREREQ__(5, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#pragma GCC diagnostic ignored "-Wnull-dereference"
+#endif
+	*(volatile char *)1 = 0;
+#if __GNUC_PREREQ__(5, 0)
+#pragma GCC diagnostic pop
+#endif
+	my_unreachable();
+}
+
 #ifdef DEBUG_USE_ABORT
 /* abort() is better recognized by code analysis tools */
 #define ABORT_NOW() do { extern void ha_backtrace_to_stderr(); ha_backtrace_to_stderr(); abort(); } while (0)
 #else
 /* More efficient than abort() because it does not mangle the
-  * stack and stops at the exact location we need.
-  */
-#define ABORT_NOW() do { extern void ha_backtrace_to_stderr(); ha_backtrace_to_stderr(); (*(volatile int*)1=0); } while (0)
+ * stack and stops at the exact location we need.
+ */
+#define ABORT_NOW() do { ha_crash_now(); } while (0)
 #endif
 
 /* BUG_ON: complains if <cond> is true when DEBUG_STRICT or DEBUG_STRICT_NOCRASH
@@ -128,8 +142,8 @@
 		.file = __FILE__, .line = __LINE__,			\
 		.type = MEM_STATS_TYPE_CALLOC,				\
 	};								\
-	__asm__(".globl __start_mem_stats");				\
-	__asm__(".globl __stop_mem_stats");				\
+	HA_WEAK("__start_mem_stats");					\
+	HA_WEAK("__stop_mem_stats");					\
 	_HA_ATOMIC_INC(&_.calls);					\
 	_HA_ATOMIC_ADD(&_.size, __x * __y);				\
 	calloc(__x,__y);						\
@@ -145,8 +159,8 @@
 		.file = __FILE__, .line = __LINE__,			\
 		.type = MEM_STATS_TYPE_FREE,				\
 	};								\
-	__asm__(".globl __start_mem_stats");				\
-	__asm__(".globl __stop_mem_stats");				\
+	HA_WEAK("__start_mem_stats");					\
+	HA_WEAK("__stop_mem_stats");					\
 	if (__x)							\
 		_HA_ATOMIC_INC(&_.calls);				\
 	free(__x);							\
@@ -159,8 +173,8 @@
 		.file = __FILE__, .line = __LINE__,			\
 		.type = MEM_STATS_TYPE_FREE,				\
 	};								\
-	__asm__(".globl __start_mem_stats");				\
-	__asm__(".globl __stop_mem_stats");				\
+	HA_WEAK("__start_mem_stats");					\
+	HA_WEAK("__stop_mem_stats");					\
 	if (__builtin_constant_p((x)) || __builtin_constant_p(*(x))) {  \
 		HA_LINK_ERROR(call_to_ha_free_attempts_to_free_a_constant); \
 	}								\
@@ -177,8 +191,8 @@
 		.file = __FILE__, .line = __LINE__,			\
 		.type = MEM_STATS_TYPE_MALLOC,				\
 	};								\
-	__asm__(".globl __start_mem_stats");				\
-	__asm__(".globl __stop_mem_stats");				\
+	HA_WEAK("__start_mem_stats");					\
+	HA_WEAK("__stop_mem_stats");					\
 	_HA_ATOMIC_INC(&_.calls);					\
 	_HA_ATOMIC_ADD(&_.size, __x);					\
 	malloc(__x);							\
@@ -191,8 +205,8 @@
 		.file = __FILE__, .line = __LINE__,			\
 		.type = MEM_STATS_TYPE_REALLOC,				\
 	};								\
-	__asm__(".globl __start_mem_stats");				\
-	__asm__(".globl __stop_mem_stats");				\
+	HA_WEAK("__start_mem_stats");					\
+	HA_WEAK("__stop_mem_stats");					\
 	_HA_ATOMIC_INC(&_.calls);					\
 	_HA_ATOMIC_ADD(&_.size, __y);					\
 	realloc(__x,__y);						\
@@ -205,8 +219,8 @@
 		.file = __FILE__, .line = __LINE__,			\
 		.type = MEM_STATS_TYPE_STRDUP,				\
 	};								\
-	__asm__(".globl __start_mem_stats");				\
-	__asm__(".globl __stop_mem_stats");				\
+	HA_WEAK("__start_mem_stats");					\
+	HA_WEAK("__stop_mem_stats");					\
 	_HA_ATOMIC_INC(&_.calls);					\
 	_HA_ATOMIC_ADD(&_.size, __y);					\
 	strdup(__x);							\
diff --git a/include/haproxy/compiler.h b/include/haproxy/compiler.h
index b975412..39bb996 100644
--- a/include/haproxy/compiler.h
+++ b/include/haproxy/compiler.h
@@ -88,6 +88,25 @@
 
 #endif // USE_OBSOLETE_LINKER
 
+/* Declare a symbol as weak if possible, otherwise global. Since we don't want to
+ * error on multiple definitions, the symbol is declared weak. On MacOS ".weak"
+ * does not exist and we must continue to use ".globl" instead. Note that
+ * ".global" is to be avoided on other platforms as llvm complains about it
+ * being used for symbols declared as weak elsewhere in the code. It may or may
+ * not work depending on linkers and assemblers, this is only for advanced use
+ * anyway (and most likely it will only work with !USE_OBSOLETE_LINKER).
+ */
+#if defined(__APPLE__)
+#  define __HA_WEAK(sym)   __asm__(".globl " #sym)
+#else
+#  define __HA_WEAK(sym)   __asm__(".weak " #sym)
+#endif
+#define HA_WEAK(sym)    __HA_WEAK(sym)
+
+/* declare a symbol as global */
+#define __HA_GLOBL(sym)   __asm__(".globl " #sym)
+#define HA_GLOBL(sym)     __HA_GLOBL(sym)
+
 /* use this attribute on a variable to move it to the read_mostly section */
 #if !defined(__read_mostly)
 #define __read_mostly           HA_SECTION("read_mostly")
diff --git a/include/haproxy/dgram-t.h b/include/haproxy/dgram-t.h
index 1a6f4c8..4e4c2af 100644
--- a/include/haproxy/dgram-t.h
+++ b/include/haproxy/dgram-t.h
@@ -28,6 +28,7 @@
  * datagram related structure
  */
 struct dgram_conn {
+	__decl_thread(HA_SPINLOCK_T lock);
 	const struct dgram_data_cb *data;	/* data layer callbacks. Must be set before */
 	void *owner;				/* pointer to upper layer's entity */
 	union {					/* definitions which depend on connection type */
diff --git a/include/haproxy/initcall.h b/include/haproxy/initcall.h
index 50dedb7..9f2c2ac 100644
--- a/include/haproxy/initcall.h
+++ b/include/haproxy/initcall.h
@@ -96,11 +96,9 @@
  * as a pointer (args are cast to (void*)). Do not use this macro directly,
  * use INITCALL{0..3}() instead.
  */
-#define __HA_GLOBL1(sym)   __asm__(".globl " #sym)
-#define __HA_GLOBL(sym)    __HA_GLOBL1(sym)
 #define __DECLARE_INITCALL(stg, linenum, function, a1, a2, a3)     \
-        __HA_GLOBL(__start_i_##stg );                              \
-        __HA_GLOBL(__stop_i_##stg );                               \
+        HA_GLOBL(__start_i_##stg );                                \
+        HA_GLOBL(__stop_i_##stg );                                 \
 	static const struct initcall *__initcb_##linenum           \
 	    __attribute__((__used__)) HA_INIT_SECTION(stg) =	   \
 	        (stg < STG_SIZE) ? &(const struct initcall) {      \
diff --git a/include/haproxy/mqtt-t.h b/include/haproxy/mqtt-t.h
index 9377021..710fd87 100644
--- a/include/haproxy/mqtt-t.h
+++ b/include/haproxy/mqtt-t.h
@@ -27,6 +27,7 @@
 /* MQTT protocol version
  * In MQTT 3.1.1, version is called "level"
  */
+#define MQTT_VERSION_3_1      3
 #define MQTT_VERSION_3_1_1    4
 #define MQTT_VERSION_5_0      5
 
diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h
index 8882388..dcf93e2 100644
--- a/include/haproxy/server-t.h
+++ b/include/haproxy/server-t.h
@@ -134,7 +134,7 @@
 #define SRV_STATE_FILE_MAX_FIELDS 25
 #define SRV_STATE_FILE_MIN_FIELDS_VERSION_1 20
 #define SRV_STATE_FILE_MAX_FIELDS_VERSION_1 25
-#define SRV_STATE_LINE_MAXLEN 512
+#define SRV_STATE_LINE_MAXLEN 2000
 
 /* server flags -- 32 bits */
 #define SRV_F_BACKUP       0x0001        /* this server is a backup server */
diff --git a/include/haproxy/task-t.h b/include/haproxy/task-t.h
index 1b64b8f..5848bfd 100644
--- a/include/haproxy/task-t.h
+++ b/include/haproxy/task-t.h
@@ -137,6 +137,11 @@
 struct task {
 	TASK_COMMON;			/* must be at the beginning! */
 	struct eb32sc_node rq;		/* ebtree node used to hold the task in the run queue */
+	/* WARNING: the struct task is often aliased as a struct tasklet when
+	 * it is NOT in the run queue. The tasklet has its struct list here
+	 * where rq starts and this works because both are exclusive. Never
+	 * ever reorder these fields without taking this into account!
+	 */
 	struct eb32_node wq;		/* ebtree node used to hold the task in the wait queue */
 	int expire;			/* next expiration date for this task, in ticks */
 	short nice;                     /* task prio from -1024 to +1024 */
@@ -151,6 +156,11 @@
 struct tasklet {
 	TASK_COMMON;			/* must be at the beginning! */
 	struct list list;
+	/* WARNING: the struct task is often aliased as a struct tasklet when
+	 * it is not in the run queue. The task has its struct rq here where
+	 * list starts and this works because both are exclusive. Never ever
+	 * reorder these fields without taking this into account!
+	 */
 #ifdef DEBUG_TASK
 	uint64_t call_date;		/* date of the last tasklet wakeup or call */
 #endif
diff --git a/include/haproxy/task.h b/include/haproxy/task.h
index 277629f..d31c893 100644
--- a/include/haproxy/task.h
+++ b/include/haproxy/task.h
@@ -190,10 +190,10 @@
 /* returns true if the current thread has some work to do */
 static inline int thread_has_tasks(void)
 {
-	return (!!(global_tasks_mask & tid_bit) |
-		!eb_is_empty(&sched->rqueue) |
-	        !!sched->tl_class_mask |
-		!MT_LIST_ISEMPTY(&sched->shared_tasklet_list));
+	return ((int)!!(global_tasks_mask & tid_bit) |
+		(int)!eb_is_empty(&sched->rqueue) |
+	        (int)!!sched->tl_class_mask |
+		(int)!MT_LIST_ISEMPTY(&sched->shared_tasklet_list));
 }
 
 /* puts the task <t> in run queue with reason flags <f>, and returns <t> */
@@ -417,6 +417,50 @@
  */
 #define tasklet_wakeup(tl) _tasklet_wakeup_on(tl, (tl)->tid, __FILE__, __LINE__)
 
+/* instantly wakes up task <t> on its owner thread even if it's not the current
+ * one, bypassing the run queue. The purpose is to be able to avoid contention
+ * in the global run queue for massively remote tasks (e.g. queue) when there's
+ * no value in passing the task again through the priority ordering since it has
+ * already been subject to it once (e.g. before entering process_stream). The
+ * task goes directly into the shared mt_list as a tasklet and will run as
+ * TL_URGENT. Great care is taken to be certain it's not queued nor running
+ * already.
+ */
+#define task_instant_wakeup(t, f) _task_instant_wakeup(t, f, __FILE__, __LINE__)
+static inline void _task_instant_wakeup(struct task *t, unsigned int f, const char *file, int line)
+{
+	struct tasklet *tl = (struct tasklet *)t;
+	int thr = my_ffsl(t->thread_mask) - 1;
+	unsigned int state;
+
+	/* first, let's update the task's state with the wakeup condition */
+	state = _HA_ATOMIC_OR_FETCH(&tl->state, f);
+
+	/* next we need to make sure the task was not/will not be added to the
+	 * run queue because the tasklet list's mt_list uses the same storage
+	 * as the task's run_queue.
+	 */
+	do {
+		/* do nothing if someone else already added it */
+		if (state & (TASK_QUEUED|TASK_RUNNING))
+			return;
+	} while (!_HA_ATOMIC_CAS(&tl->state, &state, state | TASK_QUEUED));
+
+	BUG_ON(task_in_rq(t));
+
+	/* at this point we're the first ones to add this task to the list */
+#ifdef DEBUG_TASK
+	if ((unsigned int)tl->debug.caller_idx > 1)
+		ABORT_NOW();
+	tl->debug.caller_idx = !tl->debug.caller_idx;
+	tl->debug.caller_file[tl->debug.caller_idx] = file;
+	tl->debug.caller_line[tl->debug.caller_idx] = line;
+	if (task_profiling_mask & tid_bit)
+		tl->call_date = now_mono_time();
+#endif
+	__tasklet_wakeup_on(tl, thr);
+}
+
 /* This macro shows the current function name and the last known caller of the
  * task (or tasklet) wakeup.
  */
diff --git a/reg-tests/converter/field.vtc b/reg-tests/converter/field.vtc
index 29608fe..e5a80e3 100644
--- a/reg-tests/converter/field.vtc
+++ b/reg-tests/converter/field.vtc
@@ -4,7 +4,7 @@
 
 server s1 {
 	rxreq
-	txresp
+	txresp -hdr "Connection: close"
 } -repeat 3 -start
 
 haproxy h1 -conf {
diff --git a/reg-tests/converter/mqtt.vtc b/reg-tests/converter/mqtt.vtc
index ea2cbb4..9f7e6b1 100644
--- a/reg-tests/converter/mqtt.vtc
+++ b/reg-tests/converter/mqtt.vtc
@@ -42,6 +42,11 @@
     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 {
@@ -225,3 +230,9 @@
     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/scripts/announce-release b/scripts/announce-release
index 2246cc4..37e2ac4 100755
--- a/scripts/announce-release
+++ b/scripts/announce-release
@@ -211,15 +211,19 @@
 
 (echo "Please find the usual URLs below :"
  echo "   Site index       : http://www.haproxy.org/"
+ echo "   Documentation    : http://docs.haproxy.org/"
+ echo "   Wiki             : https://github.com/haproxy/wiki/wiki"
  echo "   Discourse        : http://discourse.haproxy.org/"
  echo "   Slack channel    : https://slack.haproxy.org/"
  echo "   Issue tracker    : https://github.com/haproxy/haproxy/issues"
- echo "   Wiki             : https://github.com/haproxy/wiki/wiki"
  echo "   Sources          : http://www.haproxy.org/download/${BRANCH}/src/"
  echo "   Git repository   : http://git.haproxy.org/git/${gitdir}/"
  echo "   Git Web browsing : http://git.haproxy.org/?p=${gitdir}"
  echo "   Changelog        : http://www.haproxy.org/download/${BRANCH}/src/CHANGELOG"
- echo "   Cyril's HTML doc : http://cbonte.github.io/haproxy-dconv/"
+ echo "   Pending bugs     : http://www.haproxy.org/l/pending-bugs"
+ echo "   Reviewed bugs    : http://www.haproxy.org/l/reviewed-bugs"
+ echo "   Code reports     : http://www.haproxy.org/l/code-reports"
+ echo "   Latest builds    : http://www.haproxy.org/l/dev-packages"
 ) >> "$OUTPUT"
 
 # sign
diff --git a/src/acl.c b/src/acl.c
index e3fcf62..6d11a0b 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -101,7 +101,6 @@
 static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
 {
 	struct arg *arg;
-	int unresolved = 0;
 
 	pattern_prune(&expr->pat);
 
@@ -110,7 +109,6 @@
 			break;
 		if (arg->type == ARGT_STR || arg->unresolved) {
 			chunk_destroy(&arg->data.str);
-			unresolved |= arg->unresolved;
 			arg->unresolved = 0;
 		}
 	}
diff --git a/src/action.c b/src/action.c
index d2178d9..51193e7 100644
--- a/src/action.c
+++ b/src/action.c
@@ -130,7 +130,7 @@
 int check_capture(struct act_rule *rule, struct proxy *px, char **err)
 {
 	if (rule->from == ACT_F_TCP_REQ_CNT && (px->cap & PR_CAP_FE) && !px->tcp_req.inspect_delay &&
-	    !(rule->arg.trk_ctr.expr->fetch->val & SMP_VAL_FE_SES_ACC)) {
+	    !(rule->arg.cap.expr->fetch->val & SMP_VAL_FE_SES_ACC)) {
 		ha_warning("config : %s '%s' : a 'tcp-request capture' rule explicitly depending on request"
 			   " contents without any 'tcp-request inspect-delay' setting."
 			   " This means that this rule will randomly find its contents. This can be fixed by"
diff --git a/src/backend.c b/src/backend.c
index 2999086..a864fc1 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1604,7 +1604,9 @@
 
 			if (avail <= 1) {
 				/* No more streams available, remove it from the list */
+				HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 				conn_delete_from_tree(&srv_conn->hash_node->node);
+				HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 			}
 
 			if (avail >= 1) {
diff --git a/src/cache.c b/src/cache.c
index f10cf15..ad4e715 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -1829,10 +1829,11 @@
 				_HA_ATOMIC_INC(&px->be_counters.p.http.cache_hits);
 			return ACT_RET_CONT;
 		} else {
+			s->target = NULL;
 			shctx_lock(shctx_ptr(cache));
 			shctx_row_dec_hot(shctx_ptr(cache), entry_block);
 			shctx_unlock(shctx_ptr(cache));
-			return ACT_RET_YIELD;
+			return ACT_RET_CONT;
 		}
 	}
 	shctx_unlock(shctx_ptr(cache));
@@ -2599,12 +2600,21 @@
 			}
 
 			entry = container_of(node, struct cache_entry, eb);
-			chunk_printf(&trash, "%p hash:%u vary:0x", entry, read_u32(entry->hash));
-			for (i = 0; i < HTTP_CACHE_SEC_KEY_LEN; ++i)
-				chunk_appendf(&trash, "%02x", (unsigned char)entry->secondary_key[i]);
-			chunk_appendf(&trash, " size:%u (%u blocks), refcount:%u, expire:%d\n", block_ptr(entry)->len, block_ptr(entry)->block_count, block_ptr(entry)->refcount, entry->expire - (int)now.tv_sec);
-
 			next_key = node->key + 1;
+
+			if (entry->expire > now.tv_sec) {
+				chunk_printf(&trash, "%p hash:%u vary:0x", entry, read_u32(entry->hash));
+				for (i = 0; i < HTTP_CACHE_SEC_KEY_LEN; ++i)
+					chunk_appendf(&trash, "%02x", (unsigned char)entry->secondary_key[i]);
+				chunk_appendf(&trash, " size:%u (%u blocks), refcount:%u, expire:%d\n",
+					      block_ptr(entry)->len, block_ptr(entry)->block_count,
+					      block_ptr(entry)->refcount, entry->expire - (int)now.tv_sec);
+			} else {
+				/* time to remove that one */
+				delete_entry(entry);
+				entry->eb.key = 0;
+			}
+
 			appctx->ctx.cli.i0 = next_key;
 
 			shctx_unlock(shctx_ptr(cache));
diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c
index 44cad4b..654b020 100644
--- a/src/cfgparse-ssl.c
+++ b/src/cfgparse-ssl.c
@@ -651,11 +651,11 @@
 	}
 
 	if ((*args[cur_arg + 1] != '/' ) && global_ssl.crt_base) {
-		if ((strlen(global_ssl.crt_base) + 1 + strlen(args[cur_arg + 1]) + 1) > MAXPATHLEN) {
+		if ((strlen(global_ssl.crt_base) + 1 + strlen(args[cur_arg + 1]) + 1) > sizeof(path) ||
+		    snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, args[cur_arg + 1]) > sizeof(path)) {
 			memprintf(err, "'%s' : path too long", args[cur_arg]);
 			return ERR_ALERT | ERR_FATAL;
 		}
-		snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, args[cur_arg + 1]);
 		return ssl_sock_load_cert(path, conf, err);
 	}
 
diff --git a/src/cli.c b/src/cli.c
index b410c78..c414382 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -1475,11 +1475,11 @@
 
 /*
  * CLI IO handler for `show cli sockets`.
- * Uses ctx.cli.p0 to store the restart pointer.
+ * Uses ctx.cli.p0 to store the bind_conf pointer, and cli.p1 for the listener.
  */
 static int cli_io_handler_show_cli_sock(struct appctx *appctx)
 {
-	struct bind_conf *bind_conf;
+	struct bind_conf *bind_conf = appctx->ctx.cli.p0;
 	struct stream_interface *si = appctx->owner;
 
 	chunk_reset(&trash);
@@ -1496,23 +1496,16 @@
 
 		case STAT_ST_LIST:
 			if (global.cli_fe) {
-				list_for_each_entry(bind_conf, &global.cli_fe->conf.bind, by_fe) {
-					struct listener *l;
+				if (!bind_conf)
+					bind_conf = LIST_ELEM(global.cli_fe->conf.bind.n, typeof(bind_conf), by_fe);
 
-					/*
-					 * get the latest dumped node in appctx->ctx.cli.p0
-					 * if the current node is the first of the list
-					 */
+				list_for_each_entry_from(bind_conf, &global.cli_fe->conf.bind, by_fe) {
+					struct listener *l = appctx->ctx.cli.p1;
 
-					if (appctx->ctx.cli.p0  &&
-					    &bind_conf->by_fe == (&global.cli_fe->conf.bind)->n) {
-						/* change the current node to the latest dumped and continue the loop */
-						bind_conf = LIST_ELEM(appctx->ctx.cli.p0, typeof(bind_conf), by_fe);
-						continue;
-					}
-
-					list_for_each_entry(l, &bind_conf->listeners, by_bind) {
+					if (!l)
+						l = LIST_ELEM(bind_conf->listeners.n, typeof(l), by_bind);
 
+					list_for_each_entry_from(l, &bind_conf->listeners, by_bind) {
 						char addr[46];
 						char port[6];
 
@@ -1562,11 +1555,13 @@
 						}
 
 						if (ci_putchk(si_ic(si), &trash) == -1) {
+							/* buffer full, we must yield */
+							appctx->ctx.cli.p0 = bind_conf;
+							appctx->ctx.cli.p1 = l;
 							si_rx_room_blk(si);
 							return 0;
 						}
 					}
-					appctx->ctx.cli.p0 = &bind_conf->by_fe; /* store the latest list node dumped */
 				}
 			}
 			/* fall through */
diff --git a/src/dns.c b/src/dns.c
index 17fba5f..1ef5e87 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -89,11 +89,15 @@
 
 	if (ns->dgram) {
 		struct dgram_conn *dgram = &ns->dgram->conn;
-		int fd = dgram->t.sock.fd;
+		int fd;
 
-		if (dgram->t.sock.fd == -1) {
-			if (dns_connect_nameserver(ns) == -1)
+		HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
+		fd = dgram->t.sock.fd;
+		if (fd == -1) {
+			if (dns_connect_nameserver(ns) == -1) {
+				HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 				return -1;
+			}
 			fd = dgram->t.sock.fd;
 		}
 
@@ -106,17 +110,21 @@
 				ret = ring_write(ns->dgram->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
 				if (!ret) {
 					ns->counters->snd_error++;
+					HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 					return -1;
 				}
 				fd_cant_send(fd);
+				HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 				return ret;
 			}
 			ns->counters->snd_error++;
 			fd_delete(fd);
 			dgram->t.sock.fd = -1;
+			HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 			return -1;
 		}
 		ns->counters->sent++;
+		HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 	}
 	else if (ns->stream) {
 		struct ist myist;
@@ -147,20 +155,27 @@
 
 	if (ns->dgram) {
 		struct dgram_conn *dgram = &ns->dgram->conn;
-		int fd = dgram->t.sock.fd;
+		int fd;
 
-		if (fd == -1)
+		HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
+		fd = dgram->t.sock.fd;
+		if (fd == -1) {
+			HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 			return -1;
+		}
 
 		if ((ret = recv(fd, data, size, 0)) < 0) {
 			if (errno == EAGAIN) {
 				fd_cant_recv(fd);
+				HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 				return 0;
 			}
 			fd_delete(fd);
 			dgram->t.sock.fd = -1;
+			HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 			return -1;
 		}
+		HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 	}
 	else if (ns->stream) {
 		struct dns_stream_server *dss = ns->stream;
@@ -234,19 +249,26 @@
 	struct dns_nameserver *ns;
 	int fd;
 
+	HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
+
 	fd = dgram->t.sock.fd;
 
 	/* check if ready for reading */
-	if (!fd_recv_ready(fd))
+	if ((fd == -1) || !fd_recv_ready(fd)) {
+		HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 		return;
+	}
 
 	/* no need to go further if we can't retrieve the nameserver */
 	if ((ns = dgram->owner) == NULL) {
 		_HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
 		fd_stop_recv(fd);
+		HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 		return;
 	}
 
+	HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
+
 	ns->process_responses(ns);
 }
 
@@ -260,16 +282,21 @@
 	uint64_t msg_len;
 	size_t len, cnt, ofs;
 
+	HA_SPIN_LOCK(DNS_LOCK, &dgram->lock);
+
 	fd = dgram->t.sock.fd;
 
 	/* check if ready for sending */
-	if (!fd_send_ready(fd))
+	if ((fd == -1) || !fd_send_ready(fd)) {
+		HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 		return;
+	}
 
 	/* no need to go further if we can't retrieve the nameserver */
 	if ((ns = dgram->owner) == NULL) {
 		_HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
 		fd_stop_send(fd);
+		HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 		return;
 	}
 
@@ -343,6 +370,7 @@
 	ofs += ring->ofs;
 	ns->dgram->ofs_req = ofs;
 	HA_RWLOCK_RDUNLOCK(DNS_LOCK, &ring->lock);
+	HA_SPIN_UNLOCK(DNS_LOCK, &dgram->lock);
 
 }
 
@@ -365,6 +393,7 @@
 	dgram->conn.data      = &dns_dgram_cb;
 	dgram->conn.t.sock.fd = -1;
 	dgram->conn.addr.to = *sk;
+	HA_SPIN_INIT(&dgram->conn.lock);
 	ns->dgram = dgram;
 
 	dgram->ofs_req = ~0; /* init ring offset */
diff --git a/src/fcgi-app.c b/src/fcgi-app.c
index b8c6a39..52b82b9 100644
--- a/src/fcgi-app.c
+++ b/src/fcgi-app.c
@@ -349,8 +349,8 @@
 
 		/* Add the header "Content-Length:" if possible */
 		sl = http_get_stline(htx);
-		if (sl &&
-		    (sl->flags & (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN|HTX_SL_F_CHNK)) == HTX_SL_F_XFER_LEN &&
+		if (s->txn->meth != HTTP_METH_HEAD && sl &&
+		    (msg->flags & (HTTP_MSGF_XFER_LEN|HTTP_MSGF_CNT_LEN|HTTP_MSGF_TE_CHNK)) == HTTP_MSGF_XFER_LEN &&
 		    (htx->flags & HTX_FL_EOM)) {
 			struct htx_blk * blk;
 			char *end;
@@ -365,8 +365,10 @@
 					len += htx_get_blksz(blk);
 			}
 			end = ultoa_o(len, trash.area, trash.size);
-			if (http_add_header(htx, ist("content-length"), ist2(trash.area, end-trash.area)))
+			if (http_add_header(htx, ist("content-length"), ist2(trash.area, end-trash.area))) {
 				sl->flags |= HTX_SL_F_CLEN;
+				msg->flags |= HTTP_MSGF_CNT_LEN;
+			}
 		}
 
 		return 1;
diff --git a/src/fd.c b/src/fd.c
index 5f465f0..fe712a9 100644
--- a/src/fd.c
+++ b/src/fd.c
@@ -459,7 +459,6 @@
 ssize_t fd_write_frag_line(int fd, size_t maxlen, const struct ist pfx[], size_t npfx, const struct ist msg[], size_t nmsg, int nl)
 {
 	struct iovec iovec[32];
-	size_t totlen = 0;
 	size_t sent = 0;
 	int vec = 0;
 	int attempts = 0;
@@ -486,7 +485,6 @@
 		iovec[vec].iov_base = pfx->ptr;
 		iovec[vec].iov_len  = MIN(maxlen, pfx->len);
 		maxlen -= iovec[vec].iov_len;
-		totlen += iovec[vec].iov_len;
 		if (iovec[vec].iov_len)
 			vec++;
 		pfx++; npfx--;
diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c
index 774b982..a9c49f5 100644
--- a/src/flt_http_comp.c
+++ b/src/flt_http_comp.c
@@ -301,6 +301,7 @@
 set_compression_response_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
 {
 	struct htx *htx = htxbuf(&msg->chn->buf);
+	struct htx_sl *sl;
 	struct http_hdr_ctx ctx;
 
 	/*
@@ -316,17 +317,25 @@
 			goto error;
 	}
 
+	sl = http_get_stline(htx);
+	if (!sl)
+		goto error;
+
 	/* remove Content-Length header */
 	if (msg->flags & HTTP_MSGF_CNT_LEN) {
 		ctx.blk = NULL;
 		while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
 			http_remove_header(htx, &ctx);
+		msg->flags &= ~HTTP_MSGF_CNT_LEN;
+		sl->flags &= ~HTX_SL_F_CLEN;
 	}
 
 	/* add "Transfer-Encoding: chunked" header */
 	if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
 		if (!http_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
 			goto error;
+		msg->flags |= HTTP_MSGF_TE_CHNK;
+		sl->flags |= (HTX_SL_F_XFER_ENC|HTX_SL_F_CHNK);
 	}
 
 	/* convert "ETag" header to a weak ETag */
diff --git a/src/h1_htx.c b/src/h1_htx.c
index 46730e5..6aa389e 100644
--- a/src/h1_htx.c
+++ b/src/h1_htx.c
@@ -573,8 +573,14 @@
 	struct h1m tlr_h1m;
 	int ret = 0;
 
-	if (!max || !b_data(srcbuf))
+	if (b_data(srcbuf) == ofs) {
+		/* Nothing to parse */
 		goto end;
+	}
+	if (!max) {
+		/* No more room */
+		goto output_full;
+	}
 
 	/* Realing input buffer if necessary */
 	if (b_peek(srcbuf, ofs) > b_tail(srcbuf))
diff --git a/src/hlua.c b/src/hlua.c
index 55a8f86..0af3eb0 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -7470,7 +7470,19 @@
 		if (!(ctx->ctx.hlua_apphttp.flags & APPLET_HDR_SENT))
 			goto error;
 
-		/* no more data are expected. Don't add TLR because mux-h1 will take care of it */
+		/* no more data are expected. If the response buffer is empty
+		 * for a chunked message, be sure to add something (EOT block in
+		 * this case) to have something to send. It is important to be
+		 * sure the EOM flags will be handled by the endpoint.
+		 */
+		if (htx_is_empty(res_htx) && (strm->txn->rsp.flags & (HTTP_MSGF_XFER_LEN|HTTP_MSGF_CNT_LEN)) == HTTP_MSGF_XFER_LEN) {
+			if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
+				si_rx_room_blk(si);
+				goto out;
+			}
+			channel_add_input(res, 1);
+		}
+
 		res_htx->flags |= HTX_FL_EOM;
 		res->flags |= CF_EOI;
 		strm->txn->status = ctx->ctx.hlua_apphttp.status;
diff --git a/src/http_act.c b/src/http_act.c
index 12a9a9f..8574a7d 100644
--- a/src/http_act.c
+++ b/src/http_act.c
@@ -526,6 +526,9 @@
 	else if (rule->action == 4) // replace-pathq
 		uri = http_get_path(uri);
 
+	if (!istlen(uri))
+		goto leave;
+
 	if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
 		goto leave;
 
diff --git a/src/http_conv.c b/src/http_conv.c
index 121a498..f33336a 100644
--- a/src/http_conv.c
+++ b/src/http_conv.c
@@ -324,16 +324,13 @@
 	enc_type = ENC_QUERY;
 	enc_type = args->data.sint;
 
-	/* Add final \0 required by encode_string() */
-	smp->data.u.str.area[smp->data.u.str.data] = '\0';
-
 	if (enc_type == ENC_QUERY)
 		encode_map = query_encode_map;
 	else
 		return 0;
 
-	ret = encode_string(trash->area, trash->area + trash->size, '%',
-			    encode_map, smp->data.u.str.area);
+	ret = encode_chunk(trash->area, trash->area + trash->size, '%',
+			   encode_map, &smp->data.u.str);
 	if (ret == NULL || *ret != '\0')
 		return 0;
 	trash->data = ret - trash->area;
diff --git a/src/listener.c b/src/listener.c
index b467627..0ffd0fe 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -891,10 +891,10 @@
 		if (l->counters)
 			HA_ATOMIC_UPDATE_MAX(&l->counters->conn_max, next_conn);
 
-		if (p)
+		if (p) {
 			HA_ATOMIC_UPDATE_MAX(&p->fe_counters.conn_max, next_feconn);
-
-		proxy_inc_fe_conn_ctr(l, p);
+			proxy_inc_fe_conn_ctr(l, p);
+		}
 
 		if (!(l->options & LI_O_UNLIMITED)) {
 			count = update_freq_ctr(&global.conn_per_sec, 1);
diff --git a/src/map.c b/src/map.c
index f1b2be9..f482b77 100644
--- a/src/map.c
+++ b/src/map.c
@@ -331,10 +331,12 @@
 		 * reference to the last ref_elt being dumped.
 		 */
 		if (appctx->st2 == STAT_ST_LIST) {
+			HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
 			if (!LIST_ISEMPTY(&appctx->ctx.map.bref.users)) {
 				LIST_DELETE(&appctx->ctx.map.bref.users);
 				LIST_INIT(&appctx->ctx.map.bref.users);
 			}
+			HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
 		}
 		return 1;
 	}
@@ -342,27 +344,17 @@
 	switch (appctx->st2) {
 
 	case STAT_ST_INIT:
-		/* the function had not been called yet, let's prepare the
-		 * buffer for a response. We initialize the current stream
-		 * pointer to the first in the global list. When a target
-		 * stream is being destroyed, it is responsible for updating
-		 * this pointer. We know we have reached the end when this
-		 * pointer points back to the head of the streams list.
-		 */
-		HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
-		LIST_INIT(&appctx->ctx.map.bref.users);
-		appctx->ctx.map.bref.ref = appctx->ctx.map.ref->head.n;
-		HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
 		appctx->st2 = STAT_ST_LIST;
 		/* fall through */
 
 	case STAT_ST_LIST:
-
 		HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
 
 		if (!LIST_ISEMPTY(&appctx->ctx.map.bref.users)) {
 			LIST_DELETE(&appctx->ctx.map.bref.users);
 			LIST_INIT(&appctx->ctx.map.bref.users);
+		} else {
+			appctx->ctx.map.bref.ref = appctx->ctx.map.ref->head.n;
 		}
 
 		while (appctx->ctx.map.bref.ref != &appctx->ctx.map.ref->head) {
@@ -714,6 +706,7 @@
 		else
 			appctx->ctx.cli.i0 = appctx->ctx.map.ref->curr_gen;
 
+		LIST_INIT(&appctx->ctx.map.bref.users);
 		appctx->io_handler = cli_io_handler_pat_list;
 		appctx->io_release = cli_release_show_map;
 		return 0;
diff --git a/src/mqtt.c b/src/mqtt.c
index 6624201..75759b7 100644
--- a/src/mqtt.c
+++ b/src/mqtt.c
@@ -40,14 +40,14 @@
 const struct ist mqtt_fields_string[MQTT_FN_ENTRIES] = {
 	[MQTT_FN_INVALID]                            = IST(""),
 
-	/* it's MQTT 3.1.1 and 5.0, those fields have no unique id, so we use strings */
+	/* it's MQTT 3.1, 3.1.1 and 5.0, those fields have no unique id, so we use strings */
 	[MQTT_FN_FLAGS]                              = IST("flags"),
-	[MQTT_FN_REASON_CODE]                        = IST("reason_code"),       /* MQTT 3.1.1: return_code */
+	[MQTT_FN_REASON_CODE]                        = IST("reason_code"),       /* MQTT 3.1 and 3.1.1: return_code */
 	[MQTT_FN_PROTOCOL_NAME]                      = IST("protocol_name"),
 	[MQTT_FN_PROTOCOL_VERSION]                   = IST("protocol_version"),  /* MQTT 3.1.1: protocol_level */
 	[MQTT_FN_CLIENT_IDENTIFIER]                  = IST("client_identifier"),
 	[MQTT_FN_WILL_TOPIC]                         = IST("will_topic"),
-	[MQTT_FN_WILL_PAYLOAD]                       = IST("will_payload"),      /* MQTT 3.1.1: will_message */
+	[MQTT_FN_WILL_PAYLOAD]                       = IST("will_payload"),      /* MQTT 3.1 and 3.1.1: will_message */
 	[MQTT_FN_USERNAME]                           = IST("username"),
 	[MQTT_FN_PASSWORD]                           = IST("password"),
 	[MQTT_FN_KEEPALIVE]                          = IST("keepalive"),
@@ -695,6 +695,7 @@
 }
 
 /* Parses a CONNECT packet :
+ *   https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect
  *   https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
  *   https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901033
  *
@@ -718,14 +719,15 @@
 	 */
 	/* read protocol_name */
 	parser = mqtt_read_string(parser, &mpkt->data.connect.var_hdr.protocol_name);
-	if (!isttest(parser) || !isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQTT")))
+	if (!isttest(parser) || !(isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQTT")) || isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQIsdp"))))
 		goto end;
 
 	/* read protocol_version */
 	parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.protocol_version);
 	if (!isttest(parser))
 		goto end;
-	if (mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1_1 &&
+	if (mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1 &&
+	    mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1_1 &&
 	    mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
 		goto end;
 
diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c
index 0064e8d..d916496 100644
--- a/src/mux_fcgi.c
+++ b/src/mux_fcgi.c
@@ -3336,13 +3336,14 @@
 
 	TRACE_ENTER(FCGI_EV_RSP_DATA|FCGI_EV_RSP_HDRS, fstrm->fconn->conn, fstrm, 0, (size_t[]){max});
 	ret = h1_parse_msg_hdrs(h1m, NULL, htx, buf, *ofs, max);
-	if (!ret) {
+	if (ret <= 0) {
 		TRACE_DEVEL("leaving on missing data or error", FCGI_EV_RSP_DATA|FCGI_EV_RSP_HDRS, fstrm->fconn->conn, fstrm);
 		if (htx->flags & HTX_FL_PARSING_ERROR) {
 			TRACE_ERROR("parsing error, reject H1 response", FCGI_EV_RSP_DATA|FCGI_EV_RSP_HDRS|FCGI_EV_FSTRM_ERR, fstrm->fconn->conn, fstrm);
 			fcgi_strm_error(fstrm);
 			fcgi_strm_capture_bad_message(fstrm->fconn, fstrm, h1m, buf);
 		}
+		ret = 0;
 		goto end;
 	}
 
@@ -3382,13 +3383,14 @@
 
 	TRACE_ENTER(FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fstrm->fconn->conn, fstrm, 0, (size_t[]){max});
 	ret = h1_parse_msg_tlrs(h1m, htx, buf, *ofs, max);
-	if (!ret) {
+	if (ret <= 0) {
 		TRACE_DEVEL("leaving on missing data or error", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fstrm->fconn->conn, fstrm);
 		if (htx->flags & HTX_FL_PARSING_ERROR) {
 			TRACE_ERROR("parsing error, reject H1 response", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS|FCGI_EV_FSTRM_ERR, fstrm->fconn->conn, fstrm);
 			fcgi_strm_error(fstrm);
 			fcgi_strm_capture_bad_message(fstrm->fconn, fstrm, h1m, buf);
 		}
+		ret = 0;
 		goto end;
 	}
 	*ofs += ret;
@@ -3440,6 +3442,8 @@
 			if (!(h1m->flags & H1_MF_XFER_LEN) && fstrm->state != FCGI_SS_ERROR &&
 			    (fstrm->flags & FCGI_SF_ES_RCVD) && b_data(&fstrm->rxbuf) == total) {
 				TRACE_DEVEL("end of data", FCGI_EV_RSP_DATA, fconn->conn, fstrm);
+				if (htx_is_empty(htx) && !htx_add_endof(htx, HTX_BLK_EOT))
+					break;
 				htx->flags |= HTX_FL_EOM;
 				h1m->state = H1_MSG_DONE;
 				TRACE_USER("H1 response fully rcvd", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fconn->conn, fstrm, htx);
diff --git a/src/mux_h1.c b/src/mux_h1.c
index c362dfd..fd568e9 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -1831,6 +1831,8 @@
 						goto error;
 					}
 					h1m->curr_len -= count;
+					if (!h1m->curr_len)
+						last_data = 1;
 				}
 				if (chn_htx->flags & HTX_FL_EOM) {
 					TRACE_DEVEL("last message block", H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s);
@@ -2128,8 +2130,9 @@
 					TRACE_STATE("1xx response xferred", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
 				}
 				else {
-					/* EOM flag is set and it is the last block */
-					if (htx_is_unique_blk(chn_htx, blk) && (chn_htx->flags & HTX_FL_EOM)) {
+					/* EOM flag is set or empty payload (C-L to 0) and it is the last block */
+					if (htx_is_unique_blk(chn_htx, blk) &&
+					    ((chn_htx->flags & HTX_FL_EOM) || ((h1m->flags & H1_MF_CLEN) && !h1m->curr_len))) {
 						if (h1m->flags & H1_MF_CHNK) {
 							if (!chunk_memcat(&tmp, "\r\n0\r\n\r\n", 7))
 								goto full;
@@ -2227,8 +2230,11 @@
 						    H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, 0, (size_t[]){v.len});
 
 			  skip_data:
-				if (h1m->state == H1_MSG_DATA && (h1m->flags & H1_MF_CLEN))
+				if (h1m->state == H1_MSG_DATA && (h1m->flags & H1_MF_CLEN)) {
 					h1m->curr_len -= vlen;
+					if (!h1m->curr_len)
+						last_data = 1;
+				}
 				if (last_data)
 					goto done;
 				break;
@@ -2239,13 +2245,8 @@
 			  trailers:
 				h1m->state = H1_MSG_TRAILERS;
 
-				/* If the message is not chunked, ignore
-				 * trailers. It may happen with H2 messages. */
-				if (!(h1m->flags & H1_MF_CHNK)) {
-					if (type == HTX_BLK_EOT)
-						goto done;
-					break;
-				}
+				if (!(h1m->flags & H1_MF_CHNK))
+					goto done;
 
 				if ((h1m->flags & H1_MF_RESP) && (h1s->flags & H1S_F_BODYLESS_RESP)) {
 					TRACE_PROTO("Skip trailers for bodyless response", H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, chn_htx);
@@ -2274,15 +2275,15 @@
 				break;
 
 			case H1_MSG_DONE:
+				/* If the message is not chunked, ignore
+				 * trailers. It may happen with H2 messages. */
+				if ((type == HTX_BLK_TLR || type == HTX_BLK_EOT) && !(h1m->flags & H1_MF_CHNK))
+					break;
+
 				TRACE_STATE("unexpected data xferred in done state", H1_EV_TX_DATA|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
 				goto error; /* For now return an error */
 
 			  done:
-				if (!(chn_htx->flags & HTX_FL_EOM)) {
-					TRACE_STATE("No EOM flags in done state", H1_EV_TX_DATA|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
-					goto error; /* For now return an error */
-				}
-
 				h1m->state = H1_MSG_DONE;
 				if (!(h1m->flags & H1_MF_RESP) && h1s->meth == HTTP_METH_CONNECT) {
 					h1s->flags |= H1S_F_TX_BLK;
@@ -2830,7 +2831,7 @@
 				h1s->flags |= H1S_F_REOS;
 				TRACE_STATE("read0 on connection", H1_EV_H1C_RECV, conn, h1s);
 			}
-			if ((h1c->flags & H1C_F_ST_ERROR) || (conn->flags & CO_FL_ERROR))
+			if ((h1c->flags & H1C_F_ST_ERROR) || ((conn->flags & CO_FL_ERROR) && !b_data(&h1c->ibuf)))
 				h1s->cs->flags |= CS_FL_ERROR;
 			TRACE_POINT(H1_EV_STRM_WAKE, h1c->conn, h1s);
 			h1_alert(h1s);
@@ -3824,7 +3825,7 @@
 
 	file = fopen(hdrs_map.name, "r");
 	if (!file) {
-		ha_alert("config : h1-outgoing-headers-case-adjust-file '%s': failed to open file.\n",
+		ha_alert("config : h1-headers-case-adjust-file '%s': failed to open file.\n",
 			 hdrs_map.name);
                 err_code |= ERR_ALERT | ERR_FATAL;
 		goto end;
@@ -3873,14 +3874,14 @@
 		err = NULL;
 		rc = add_hdr_case_adjust(key_beg, value_beg, &err);
 		if (rc < 0) {
-			ha_alert("config : h1-outgoing-headers-case-adjust-file '%s' : %s at line %d.\n",
+			ha_alert("config : h1-headers-case-adjust-file '%s' : %s at line %d.\n",
 				 hdrs_map.name, err, line);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			free(err);
 			goto end;
 		}
 		if (rc > 0) {
-			ha_warning("config : h1-outgoing-headers-case-adjust-file '%s' : %s at line %d.\n",
+			ha_warning("config : h1-headers-case-adjust-file '%s' : %s at line %d.\n",
 				   hdrs_map.name, err, line);
 			err_code |= ERR_WARN;
 			free(err);
@@ -3895,7 +3896,7 @@
 }
 
 
-/* config parser for global "h1-outgoing-header-case-adjust" */
+/* config parser for global "h1-header-case-adjust" */
 static int cfg_parse_h1_header_case_adjust(char **args, int section_type, struct proxy *curpx,
 					   const struct proxy *defpx, const char *file, int line,
 					   char **err)
@@ -3909,7 +3910,7 @@
 	return add_hdr_case_adjust(args[1], args[2], err);
 }
 
-/* config parser for global "h1-outgoing-headers-case-adjust-file" */
+/* config parser for global "h1-headers-case-adjust-file" */
 static int cfg_parse_h1_headers_case_adjust_file(char **args, int section_type, struct proxy *curpx,
 						 const struct proxy *defpx, const char *file, int line,
 						 char **err)
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 11c660e..5916ee8 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -131,6 +131,8 @@
 
 	int timeout;        /* idle timeout duration in ticks */
 	int shut_timeout;   /* idle timeout duration in ticks after GOAWAY was sent */
+	int idle_start;     /* date of the last time the connection went idle */
+	/* 32-bit hole here */
 	unsigned int nb_streams;  /* number of streams in the tree */
 	unsigned int nb_cs;       /* number of attached conn_streams */
 	unsigned int nb_reserved; /* number of reserved streams */
@@ -689,24 +691,60 @@
 }
 
 /* returns true if the connection is allowed to expire, false otherwise. A
- * connection may expire when:
- *   - it has no stream
- *   - it has data in the mux buffer
- *   - it has streams in the blocked list
- *   - it has streams in the fctl list
- *   - it has streams in the send list
- * Otherwise it means some streams are waiting in the data layer and it should
- * not expire.
+ * connection may expire when it has no attached streams. As long as streams
+ * are attached, the application layer is responsible for timeout management,
+ * and each layer will detach when it doesn't want to wait anymore. When the
+ * last one leaves, the connection must take over timeout management.
  */
 static inline int h2c_may_expire(const struct h2c *h2c)
 {
-	return eb_is_empty(&h2c->streams_by_id) ||
-	       br_data(h2c->mbuf) ||
-	       !LIST_ISEMPTY(&h2c->blocked_list) ||
-	       !LIST_ISEMPTY(&h2c->fctl_list) ||
-	       !LIST_ISEMPTY(&h2c->send_list);
+	return !h2c->nb_cs;
 }
 
+/* update h2c timeout if needed */
+static void h2c_update_timeout(struct h2c *h2c)
+{
+	TRACE_ENTER(H2_EV_H2C_WAKE, h2c->conn);
+
+	if (!h2c->task)
+		goto leave;
+
+	if (h2c_may_expire(h2c)) {
+		/* no more streams attached */
+		if (h2c->last_sid >= 0) {
+			/* GOAWAY sent, closing in progress */
+			h2c->task->expire = tick_add_ifset(now_ms, h2c->shut_timeout);
+		} else if (br_data(h2c->mbuf)) {
+			/* pending output data: always the regular data timeout */
+			h2c->task->expire = tick_add_ifset(now_ms, h2c->timeout);
+		} else if (!(h2c->flags & H2_CF_IS_BACK) && h2c->max_id > 0 && !b_data(&h2c->dbuf)) {
+			/* idle after having seen one stream => keep-alive */
+			int to;
+
+			if (tick_isset(h2c->proxy->timeout.httpka))
+				to = h2c->proxy->timeout.httpka;
+			else
+				to = h2c->proxy->timeout.httpreq;
+
+			h2c->task->expire = tick_add_ifset(h2c->idle_start, to);
+		} else {
+			/* before first request, or started to deserialize a
+			 * new req => http-request, but only set, not refresh.
+			 */
+			int exp = (h2c->flags & H2_CF_IS_BACK) ? TICK_ETERNITY : h2c->proxy->timeout.httpreq;
+			h2c->task->expire = tick_add_ifset(h2c->idle_start, exp);
+		}
+		/* if a timeout above was not set, fall back to the default one */
+		if (!tick_isset(h2c->task->expire))
+			h2c->task->expire = tick_add_ifset(now_ms, h2c->timeout);
+	} else {
+		h2c->task->expire = TICK_ETERNITY;
+	}
+	task_queue(h2c->task);
+ leave:
+	TRACE_LEAVE(H2_EV_H2C_WAKE);
+}
+
 static __inline int
 h2c_is_dead(const struct h2c *h2c)
 {
@@ -948,6 +986,7 @@
 
 	h2c->proxy = prx;
 	h2c->task = NULL;
+	h2c->idle_start = now_ms;
 	if (tick_isset(h2c->timeout)) {
 		t = task_new(tid_bit);
 		if (!t)
@@ -1180,11 +1219,17 @@
 	return 1;
 }
 
-/* marks an error on the connection */
+/* marks an error on the connection. Before settings are sent, we must not send
+ * a GOAWAY frame, and the error state will prevent h2c_send_goaway_error()
+ * from verifying this so we set H2_CF_GOAWAY_FAILED to make sure it will not
+ * even try.
+ */
 static inline __maybe_unused void h2c_error(struct h2c *h2c, enum h2_err err)
 {
 	TRACE_POINT(H2_EV_H2C_ERR, h2c->conn, 0, 0, (void *)(long)(err));
 	h2c->errcode = err;
+	if (h2c->st0 < H2_CS_SETTINGS1)
+		h2c->flags |= H2_CF_GOAWAY_FAILED;
 	h2c->st0 = H2_CS_ERROR;
 }
 
@@ -1520,12 +1565,14 @@
 
 	TRACE_ENTER(H2_EV_H2S_NEW, h2c->conn);
 
-	if (h2c->nb_streams >= h2_settings_max_concurrent_streams)
+	if (h2c->nb_streams >= h2_settings_max_concurrent_streams) {
+		TRACE_ERROR("HEADERS frame causing MAX_CONCURRENT_STREAMS to be exceeded", H2_EV_H2S_NEW|H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn);
 		goto out;
+	}
 
 	h2s = h2s_new(h2c, id);
 	if (!h2s)
-		goto out;
+		goto out_alloc;
 
 	cs = cs_new(h2c->conn, h2c->conn->target);
 	if (!cs)
@@ -1569,10 +1616,14 @@
 
  out_free_cs:
 	h2c->nb_cs--;
+	if (!h2c->nb_cs)
+		h2c->idle_start = now_ms;
 	cs_free(cs);
 	h2s->cs = NULL;
  out_close:
 	h2s_destroy(h2s);
+ out_alloc:
+	TRACE_ERROR("Failed to allocate a new stream", H2_EV_H2S_NEW|H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn);
  out:
 	sess_log(sess);
 	TRACE_LEAVE(H2_EV_H2S_NEW|H2_EV_H2S_ERR|H2_EV_H2S_END, h2c->conn);
@@ -1589,16 +1640,22 @@
 
 	TRACE_ENTER(H2_EV_H2S_NEW, h2c->conn);
 
-	if (h2c->nb_streams >= h2c->streams_limit)
+	if (h2c->nb_streams >= h2c->streams_limit) {
+		TRACE_ERROR("Aborting stream since negotiated limit is too low", H2_EV_H2S_NEW, h2c->conn);
 		goto out;
+	}
 
-	if (h2_streams_left(h2c) < 1)
+	if (h2_streams_left(h2c) < 1) {
+		TRACE_ERROR("Aborting stream since no more streams left", H2_EV_H2S_NEW, h2c->conn);
 		goto out;
+	}
 
 	/* Defer choosing the ID until we send the first message to create the stream */
 	h2s = h2s_new(h2c, 0);
-	if (!h2s)
+	if (!h2s) {
+		TRACE_ERROR("Failed to allocate a new stream", H2_EV_H2S_NEW, h2c->conn);
 		goto out;
+	}
 
 	h2s->cs = cs;
 	h2s->sess = sess;
@@ -1809,7 +1866,8 @@
  * the message, it subscribes the requester (either <h2s> or <h2c>) to future
  * notifications. It sets H2_CF_GOAWAY_SENT on success, and H2_CF_GOAWAY_FAILED
  * on unrecoverable failure. It will not attempt to send one again in this last
- * case so that it is safe to use h2c_error() to report such errors.
+ * case, nor will it send one if settings were not sent (e.g. still waiting for
+ * a preface) so that it is safe to use h2c_error() to report such errors.
  */
 static int h2c_send_goaway_error(struct h2c *h2c, struct h2s *h2s)
 {
@@ -1819,7 +1877,7 @@
 
 	TRACE_ENTER(H2_EV_TX_FRAME|H2_EV_TX_GOAWAY, h2c->conn);
 
-	if (h2c->flags & H2_CF_GOAWAY_FAILED) {
+	if ((h2c->flags & H2_CF_GOAWAY_FAILED) || h2c->st0 < H2_CS_SETTINGS1) {
 		ret = 1; // claim that it worked
 		goto out;
 	}
@@ -2759,6 +2817,12 @@
 
 	TRACE_USER("rcvd H2 request  ", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_STRM_NEW, h2c->conn, 0, &rxbuf);
 
+	/* Now we cannot roll back and we won't come back here anymore for this
+	 * stream, this stream ID is open.
+	 */
+	if (h2c->dsi > h2c->max_id)
+		h2c->max_id = h2c->dsi;
+
 	/* Note: we don't emit any other logs below because ff we return
 	 * positively from h2c_frt_stream_new(), the stream will report the error,
 	 * and if we return in error, h2c_frt_stream_new() will emit the error.
@@ -2786,11 +2850,6 @@
 		else
 			h2s_close(h2s);
 	}
-
-	/* update the max stream ID if the request is being processed */
-	if (h2s->id > h2c->max_id)
-		h2c->max_id = h2s->id;
-
 	return h2s;
 
  conn_err:
@@ -3363,6 +3422,12 @@
 				HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
 				goto done;
 			}
+
+			/* transition to HEADERS frame ends the keep-alive idle
+			 * timer and starts the http-request idle delay.
+			 */
+			if (hdr.ft == H2_FT_HEADERS)
+				h2c->idle_start = now_ms;
 		}
 
 		/* Only H2_CS_FRAME_P, H2_CS_FRAME_A and H2_CS_FRAME_E here.
@@ -4031,14 +4096,7 @@
 	     ((h2c->flags & H2_CF_MUX_BLOCK_ANY) || LIST_ISEMPTY(&h2c->send_list))))
 		h2_release_mbuf(h2c);
 
-	if (h2c->task) {
-		if (h2c_may_expire(h2c))
-			h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
-		else
-			h2c->task->expire = TICK_ETERNITY;
-		task_queue(h2c->task);
-	}
-
+	h2c_update_timeout(h2c);
 	h2_send(h2c);
 	TRACE_LEAVE(H2_EV_H2C_WAKE, conn);
 	return 0;
@@ -4284,6 +4342,9 @@
 	h2c = h2s->h2c;
 	h2s->cs = NULL;
 	h2c->nb_cs--;
+	if (!h2c->nb_cs)
+		h2c->idle_start = now_ms;
+
 	if ((h2c->flags & (H2_CF_IS_BACK|H2_CF_DEM_TOOMANY)) == H2_CF_DEM_TOOMANY &&
 	    !h2_frt_has_too_many_cs(h2c)) {
 		/* frontend connection was blocking new streams creation */
@@ -4299,6 +4360,11 @@
 	    (h2s->flags & (H2_SF_BLK_MBUSY | H2_SF_BLK_MROOM | H2_SF_BLK_MFCTL)) &&
 	    ((h2s->flags & (H2_SF_WANT_SHUTR | H2_SF_WANT_SHUTW)) || h2s->subs)) {
 		TRACE_DEVEL("leaving on stream blocked", H2_EV_STRM_END|H2_EV_H2S_BLK, h2c->conn, h2s);
+		/* refresh the timeout if none was active, so that the last
+		 * leaving stream may arm it.
+		 */
+		if (!tick_isset(h2c->task->expire))
+			h2c_update_timeout(h2c);
 		return;
 	}
 
@@ -4387,11 +4453,7 @@
 		h2_release(h2c);
 	}
 	else if (h2c->task) {
-		if (h2c_may_expire(h2c))
-			h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
-		else
-			h2c->task->expire = TICK_ETERNITY;
-		task_queue(h2c->task);
+		h2c_update_timeout(h2c);
 		TRACE_DEVEL("leaving, refreshing connection's timeout", H2_EV_STRM_END, h2c->conn);
 	}
 	else
diff --git a/src/mux_pt.c b/src/mux_pt.c
index ea0056d..9b9490d 100644
--- a/src/mux_pt.c
+++ b/src/mux_pt.c
@@ -534,6 +534,11 @@
 	if (ret > 0)
 		b_del(buf, ret);
 
+	if (cs->conn->flags & CO_FL_ERROR) {
+		cs->flags |= CS_FL_ERROR;
+		TRACE_DEVEL("error on connection", PT_EV_TX_DATA|PT_EV_CONN_ERR, cs->conn, cs);
+	}
+
 	TRACE_LEAVE(PT_EV_TX_DATA, cs->conn, cs, buf, (size_t[]){ret});
 	return ret;
 }
@@ -589,6 +594,11 @@
 
 	ret = cs->conn->xprt->snd_pipe(cs->conn, cs->conn->xprt_ctx, pipe);
 
+	if (cs->conn->flags & CO_FL_ERROR) {
+		cs->flags |= CS_FL_ERROR;
+		TRACE_DEVEL("error on connection", PT_EV_TX_DATA|PT_EV_CONN_ERR, cs->conn, cs);
+	}
+
 	TRACE_LEAVE(PT_EV_TX_DATA, cs->conn, cs, 0, (size_t[]){ret});
 	return ret;
 }
diff --git a/src/pool.c b/src/pool.c
index 75c67d0..b74ffd2 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -511,8 +511,14 @@
 {
 	struct pool_head *entry, *back;
 
-	list_for_each_entry_safe(entry, back, &pools, list)
+	list_for_each_entry_safe(entry, back, &pools, list) {
+		/* there's only one occurrence of each pool in the list,
+		 * and we're existing instead of looping on the whole
+		 * list just to decrement users, force it to 1 here.
+		 */
+		entry->users = 1;
 		pool_destroy(entry);
+	}
 }
 
 /* This function dumps memory usage information into the trash buffer. */
diff --git a/src/proto_sockpair.c b/src/proto_sockpair.c
index 0cb9ab1..0357552 100644
--- a/src/proto_sockpair.c
+++ b/src/proto_sockpair.c
@@ -331,10 +331,6 @@
 		return SF_ERR_INTERNAL;
 	}
 
-	/* if a send_proxy is there, there are data */
-	if (conn->send_proxy_ofs)
-		flags |= CONNECT_HAS_DATA;
-
 	if (global.tune.server_sndbuf)
                 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
 
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 02d7069..621eb39 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -285,10 +285,6 @@
 		return SF_ERR_INTERNAL;
 	}
 
-	/* if a send_proxy is there, there are data */
-	if (conn->send_proxy_ofs)
-		flags |= CONNECT_HAS_DATA;
-
 	if (global.tune.server_sndbuf)
                 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
 
diff --git a/src/queue.c b/src/queue.c
index 08e1b51..d7b92b7 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -331,7 +331,7 @@
 		px->lbprm.server_take_conn(srv, 1);
 	stream_add_srv_conn(p->strm, srv);
 
-	task_wakeup(p->strm->task, TASK_WOKEN_RES);
+	task_instant_wakeup(p->strm->task, TASK_WOKEN_RES);
 
 	return 1;
 }
@@ -465,7 +465,7 @@
 		__pendconn_unlink_srv(p);
 		p->strm_flags &= ~(SF_DIRECT | SF_ASSIGNED | SF_ADDR_SET);
 
-		task_wakeup(p->strm->task, TASK_WOKEN_RES);
+		task_instant_wakeup(p->strm->task, TASK_WOKEN_RES);
 		xferred++;
 	}
 	if (xferred) {
@@ -507,7 +507,7 @@
 		__pendconn_unlink_prx(p);
 		p->target = s;
 
-		task_wakeup(p->strm->task, TASK_WOKEN_RES);
+		task_instant_wakeup(p->strm->task, TASK_WOKEN_RES);
 		xferred++;
 	}
 	HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &s->proxy->lock);
diff --git a/src/resolvers.c b/src/resolvers.c
index 49e16f6..52a1ced 100644
--- a/src/resolvers.c
+++ b/src/resolvers.c
@@ -2474,6 +2474,7 @@
 			abort_resolution(res);
 		}
 
+		free_proxy(resolvers->px);
 		free(resolvers->id);
 		free((char *)resolvers->conf.file);
 		task_destroy(resolvers->t);
@@ -2765,12 +2766,13 @@
 /* Dumps counters from all resolvers section and associated name servers. It
  * returns 0 if the output buffer is full and it needs to be called again,
  * otherwise non-zero. It may limit itself to the resolver pointed to by
- * <cli.p0> if it's not null.
+ * <cli.p0> if it's not null, and takes the current resolver section from p1
+ * and the current resolver from p2.
  */
 static int cli_io_handler_dump_resolvers_to_buffer(struct appctx *appctx)
 {
 	struct stream_interface *si = appctx->owner;
-	struct resolvers    *resolvers;
+	struct resolvers    *resolvers = appctx->ctx.cli.p1;
 	struct dns_nameserver   *ns;
 
 	chunk_reset(&trash);
@@ -2782,15 +2784,31 @@
 
 	case STAT_ST_LIST:
 		if (LIST_ISEMPTY(&sec_resolvers)) {
-			chunk_appendf(&trash, "No resolvers found\n");
+			if (ci_putstr(si_ic(si), "No resolvers found\n") == -1)
+				goto full;
 		}
 		else {
-			list_for_each_entry(resolvers, &sec_resolvers, list) {
+			if (!resolvers)
+				resolvers = LIST_ELEM(sec_resolvers.n, typeof(resolvers), list);
+
+			list_for_each_entry_from(resolvers, &sec_resolvers, list) {
 				if (appctx->ctx.cli.p0 != NULL && appctx->ctx.cli.p0 != resolvers)
 					continue;
 
+				appctx->ctx.cli.p1 = resolvers;
+				ns = appctx->ctx.cli.p2;
+
-				chunk_appendf(&trash, "Resolvers section %s\n", resolvers->id);
-				list_for_each_entry(ns, &resolvers->nameservers, list) {
+				if (!ns) {
+					chunk_printf(&trash, "Resolvers section %s\n", resolvers->id);
+					if (ci_putchk(si_ic(si), &trash) == -1)
+						goto full;
+
+					ns = LIST_ELEM(resolvers->nameservers.n, typeof(ns), list);
+					appctx->ctx.cli.p2 = ns;
+				}
+
+				list_for_each_entry_from(ns, &resolvers->nameservers, list) {
+					chunk_reset(&trash);
 					chunk_appendf(&trash, " nameserver %s:\n", ns->id);
 					chunk_appendf(&trash, "  sent:        %lld\n", ns->counters->sent);
 					chunk_appendf(&trash, "  snd_error:   %lld\n", ns->counters->snd_error);
@@ -2807,25 +2825,29 @@
 					chunk_appendf(&trash, "  too_big:     %lld\n", ns->counters->too_big);
 					chunk_appendf(&trash, "  truncated:   %lld\n", ns->counters->truncated);
 					chunk_appendf(&trash, "  outdated:    %lld\n",  ns->counters->outdated);
+					if (ci_putchk(si_ic(si), &trash) == -1)
+						goto full;
+					appctx->ctx.cli.p2 = ns;
 				}
-				chunk_appendf(&trash, "\n");
+
+				appctx->ctx.cli.p2 = NULL;
+
+				/* was this the only section to dump ? */
+				if (appctx->ctx.cli.p0)
+					break;
 			}
 		}
 
-		/* display response */
-		if (ci_putchk(si_ic(si), &trash) == -1) {
-			/* let's try again later from this session. We add ourselves into
-			 * this session's users so that it can remove us upon termination.
-			 */
-			si_rx_room_blk(si);
-			return 0;
-		}
 		/* fall through */
 
 	default:
 		appctx->st2 = STAT_ST_FIN;
 		return 1;
 	}
+ full:
+	/* the output buffer is full, retry later */
+	si_rx_room_blk(si);
+	return 0;
 }
 
 /* register cli keywords */
diff --git a/src/sample.c b/src/sample.c
index abce9b9..9b16621 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -325,6 +325,8 @@
 	[SMP_CKP_FE_HRS_BDY] = "frontend http-response body rule",
 	[SMP_CKP_FE_LOG_END] = "logs",
 	[SMP_CKP_BE_CHK_RUL] = "backend tcp-check rule",
+	[SMP_CKP_CFG_PARSER] = "configuration parser",
+	[SMP_CKP_CLI_PARSER] = "CLI parser",
 };
 
 /* This function returns the type of the data returned by the sample_expr.
diff --git a/src/ssl_crtlist.c b/src/ssl_crtlist.c
index f4f26ed..6b400e2 100644
--- a/src/ssl_crtlist.c
+++ b/src/ssl_crtlist.c
@@ -515,13 +515,13 @@
 		}
 
 		if (*crt_path != '/' && global_ssl.crt_base) {
-			if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > MAXPATHLEN) {
+			if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
+			    snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, crt_path) > sizeof(path)) {
 				memprintf(err, "parsing [%s:%d]: '%s' : path too long",
 					  file, linenum, crt_path);
 				cfgerr |= ERR_ALERT | ERR_FATAL;
 				goto error;
 			}
-			snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, crt_path);
 			crt_path = path;
 		}
 
@@ -1239,12 +1239,12 @@
 	}
 
 	if (*cert_path != '/' && global_ssl.crt_base) {
-		if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > MAXPATHLEN) {
+		if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
+		    snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, cert_path) > sizeof(path)) {
 			memprintf(&err, "'%s' : path too long", cert_path);
 			cfgerr |= ERR_ALERT | ERR_FATAL;
 			goto error;
 		}
-		snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, cert_path);
 		cert_path = path;
 	}
 
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 233b520..7f74977 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -70,6 +70,7 @@
 #include <haproxy/ssl_crtlist.h>
 #include <haproxy/ssl_sock.h>
 #include <haproxy/ssl_utils.h>
+#include <haproxy/sample.h>
 #include <haproxy/stats.h>
 #include <haproxy/stream-t.h>
 #include <haproxy/stream_interface.h>
@@ -5075,6 +5076,8 @@
 	ha_free(&srv->ssl_ctx.verify_host);
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 	ha_free(&srv->sni_expr);
+	release_sample_expr(srv->ssl_ctx.sni);
+	srv->ssl_ctx.sni = NULL;
 #endif
 	ha_free(&srv->ssl_ctx.ciphers);
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
diff --git a/src/stats.c b/src/stats.c
index b0f1a68..ceb6bd8 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -4265,7 +4265,18 @@
 	}
 
 	if (appctx->st0 == STAT_HTTP_DONE) {
-		/* no more data are expected. Don't add TLR because mux-h1 will take care of it */
+		/* no more data are expected. If the response buffer is empty,
+		 * be sure to add something (EOT block in this case) to have
+		 * something to send. It is important to be sure the EOM flags
+		 * will be handled by the endpoint.
+		 */
+		if (htx_is_empty(res_htx)) {
+			if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
+				si_rx_room_blk(si);
+				goto out;
+			}
+			channel_add_input(res, 1);
+		}
 		res_htx->flags |= HTX_FL_EOM;
 		res->flags |= CF_EOI;
 		appctx->st0 = STAT_HTTP_END;
diff --git a/src/stream.c b/src/stream.c
index d5b4c34..c241642 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -2255,10 +2255,12 @@
 	/* first, let's check if the request buffer needs to shutdown(write), which may
 	 * happen either because the input is closed or because we want to force a close
 	 * once the server has begun to respond. If a half-closed timeout is set, we adjust
-	 * the other side's timeout as well.
+	 * the other side's timeout as well. However this doesn't have effect during the
+	 * connection setup unless the backend has abortonclose set.
 	 */
 	if (unlikely((req->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
-		     (CF_AUTO_CLOSE|CF_SHUTR))) {
+		     (CF_AUTO_CLOSE|CF_SHUTR) &&
+		     (si_b->state != SI_ST_CON || (s->be->options & PR_O_ABRT_CLOSE)))) {
 		channel_shutw_now(req);
 	}
 
@@ -3668,16 +3670,16 @@
 	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
 		return 1;
 
-	if (!*args[2])
+	ptr = (void *)strtoul(args[2], NULL, 0);
+	if (!ptr)
 		return cli_err(appctx, "Session pointer expected (use 'show sess').\n");
 
-	ptr = (void *)strtoul(args[2], NULL, 0);
 	strm = NULL;
 
 	thread_isolate();
 
 	/* first, look for the requested stream in the stream table */
-	for (thr = 0; !strm && thr < global.nbthread; thr++) {
+	for (thr = 0; strm != ptr && thr < global.nbthread; thr++) {
 		list_for_each_entry(strm, &ha_thread_info[thr].streams, list) {
 			if (strm == ptr) {
 				stream_shutdown(strm, SF_ERR_KILLED);
@@ -3689,7 +3691,7 @@
 	thread_release();
 
 	/* do we have the stream ? */
-	if (!strm)
+	if (strm != ptr)
 		return cli_err(appctx, "No such session (use 'show sess').\n");
 
 	return 1;
diff --git a/src/stream_interface.c b/src/stream_interface.c
index afcf3c0..830db34 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -554,6 +554,23 @@
 		ic->flags &= ~CF_READ_DONTWAIT;
 }
 
+/* The stream interface is only responsible for the connection during the early
+ * states, before plugging a mux. Thus it should only care about CO_FL_ERROR
+ * before SI_ST_EST, and after that it must absolutely ignore it since the mux
+ * may hold pending data. This function returns true if such an error was
+ * reported. Both the CS and the CONN must be valid.
+ */
+static inline int si_is_conn_error(const struct stream_interface *si)
+{
+	struct connection *conn;
+
+	if (si->state >= SI_ST_EST)
+		return 0;
+
+	conn = __objt_cs(si->end)->conn;
+	BUG_ON(!conn);
+	return !!(conn->flags & CO_FL_ERROR);
+}
 
 /* Called by I/O handlers after completion.. It propagates
  * connection flags to the stream interface, updates the stream (which may or
@@ -584,9 +601,11 @@
 	 *       wake callback. Otherwise si_cs_recv()/si_cs_send() already take
 	 *       care of it.
 	 */
-	if (si->state >= SI_ST_CON &&
-	    (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR))
-		si->flags |= SI_FL_ERR;
+
+	if (si->state >= SI_ST_CON) {
+		if ((cs->flags & CS_FL_ERROR) || si_is_conn_error(si))
+			si->flags |= SI_FL_ERR;
+	}
 
 	/* If we had early data, and the handshake ended, then
 	 * we can remove the flag, and attempt to wake the task up,
@@ -655,7 +674,7 @@
 	int ret;
 	int did_send = 0;
 
-	if (conn->flags & CO_FL_ERROR || cs->flags & (CS_FL_ERROR|CS_FL_ERR_PENDING)) {
+	if (cs->flags & (CS_FL_ERROR|CS_FL_ERR_PENDING) || si_is_conn_error(si)) {
 		/* We're probably there because the tasklet was woken up,
 		 * but process_stream() ran before, detected there were an
 		 * error and put the si back to SI_ST_TAR. There's still
@@ -778,7 +797,7 @@
 		si_rx_room_rdy(si_opposite(si));
 	}
 
-	if (conn->flags & CO_FL_ERROR || cs->flags & (CS_FL_ERROR|CS_FL_ERR_PENDING)) {
+	if (cs->flags & (CS_FL_ERROR|CS_FL_ERR_PENDING)) {
 		si->flags |= SI_FL_ERR;
 		return 1;
 	}
@@ -1119,7 +1138,7 @@
 	struct channel *oc = si_oc(si);
 	struct conn_stream *cs = __objt_cs(si->end);
 
-	if (unlikely(!si_state_in(si->state, SI_SB_CON|SI_SB_RDY|SI_SB_EST) ||
+	if (unlikely(!si_state_in(si->state, SI_SB_RDY|SI_SB_EST) ||
 	    (oc->flags & CF_SHUTW)))
 		return;
 
@@ -1133,7 +1152,7 @@
 	if (!(si->wait_event.events & SUB_RETRY_SEND) && !channel_is_empty(si_oc(si)))
 		si_cs_send(cs);
 
-	if (cs->flags & (CS_FL_ERROR|CS_FL_ERR_PENDING) || cs->conn->flags & CO_FL_ERROR) {
+	if (cs->flags & (CS_FL_ERROR|CS_FL_ERR_PENDING) || si_is_conn_error(si)) {
 		/* Write error on the file descriptor */
 		if (si->state >= SI_ST_CON)
 			si->flags |= SI_FL_ERR;
@@ -1248,7 +1267,7 @@
 	if (!(cs->flags & CS_FL_RCV_MORE)) {
 		if (!conn_xprt_ready(conn))
 			return 0;
-		if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
+		if (cs->flags & CS_FL_ERROR)
 			goto end_recv;
 	}
 
@@ -1305,7 +1324,7 @@
 			ic->flags |= CF_READ_PARTIAL;
 		}
 
-		if (conn->flags & CO_FL_ERROR || cs->flags & (CS_FL_EOS|CS_FL_ERROR))
+		if (cs->flags & (CS_FL_EOS|CS_FL_ERROR))
 			goto end_recv;
 
 		if (conn->flags & CO_FL_WAIT_ROOM) {
@@ -1361,7 +1380,7 @@
 	 * recv().
 	 */
 	while ((cs->flags & CS_FL_RCV_MORE) ||
-	    (!(conn->flags & (CO_FL_ERROR | CO_FL_HANDSHAKE)) &&
+	       (!(conn->flags & CO_FL_HANDSHAKE) &&
 	       (!(cs->flags & (CS_FL_ERROR|CS_FL_EOS))) && !(ic->flags & CF_SHUTR))) {
 		int cur_flags = flags;
 
@@ -1507,8 +1526,7 @@
 		ret = 1;
 	}
 
-	if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
-		cs->flags |= CS_FL_ERROR;
+	if (cs->flags & CS_FL_ERROR) {
 		si->flags |= SI_FL_ERR;
 		ret = 1;
 	}
diff --git a/src/tcp_act.c b/src/tcp_act.c
index d55475c..211280c 100644
--- a/src/tcp_act.c
+++ b/src/tcp_act.c
@@ -236,6 +236,14 @@
 	return ACT_RET_ABRT;
 }
 
+/*
+ * Release the sample expr when releasing a set src/dst action
+ */
+static void release_set_src_dst_action(struct act_rule *rule)
+{
+	release_sample_expr(rule->arg.expr);
+}
+
 /* parse "set-{src,dst}[-port]" action */
 static enum act_parse_ret tcp_parse_set_src_dst(const char **args, int *orig_arg, struct proxy *px,
                                                 struct act_rule *rule, char **err)
@@ -277,6 +285,7 @@
 		return ACT_RET_PRS_ERR;
 	}
 
+	rule->release_ptr = release_set_src_dst_action;
 	(*orig_arg)++;
 
 	return ACT_RET_PRS_OK;
diff --git a/src/tools.c b/src/tools.c
index bdf78ff..bbd6a76 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -1664,7 +1664,7 @@
 		end++;
 
 		/* Decode port. */
-		if (*end == ':') {
+		if (end < url + ulen && *end == ':') {
 			end++;
 			default_port = read_uint(&end, url + ulen);
 		}
@@ -1694,12 +1694,10 @@
 				out->host_len = ret;
 			}
 
-			/* we need to assign again curr and end from the trash */
-			url = trash.area;
-			curr = trash.area + ret;
+			curr += ret;
 
 			/* Decode port. */
-			if (*curr == ':') {
+			if (curr < url + ulen && *curr == ':') {
 				curr++;
 				default_port = read_uint(&curr, url + ulen);
 			}
@@ -1733,7 +1731,7 @@
 			}
 
 			/* Decode port. */
-			if (*end == ':') {
+			if (end < url + ulen && *end == ':') {
 				end++;
 				default_port = read_uint(&end, url + ulen);
 			}
diff --git a/src/trace.c b/src/trace.c
index f075787..4c5270c 100644
--- a/src/trace.c
+++ b/src/trace.c
@@ -114,7 +114,7 @@
 
 	if (!sess && strm)
 		sess = strm->sess;
-	else if (!sess && conn)
+	else if (!sess && conn && LIST_INLIST(&conn->session_list))
 		sess = conn->owner;
 	else if (!sess && check)
 		sess = check->sess;
diff --git a/src/wdt.c b/src/wdt.c
index 3c0b3d1..7b9df8d 100644
--- a/src/wdt.c
+++ b/src/wdt.c
@@ -66,8 +66,10 @@
 		p = ha_thread_info[thr].prev_cpu_time;
 		n = now_cpu_time_thread(&ha_thread_info[thr]);
 
-		/* not yet reached the deadline of 1 sec */
-		if (n - p < 1000000000UL)
+		/* not yet reached the deadline of 1 sec,
+		 * or p wasn't initialized yet
+		 */
+		if (!p || n - p < 1000000000UL)
 			goto update_and_leave;
 
 		if ((threads_harmless_mask|sleeping_thread_mask|threads_to_dump) & (1UL << thr)) {