[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#928461: unblock: haproxy/1.8.20-1 (pre-approval)



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hey!

HAProxy 1.8.20 was released last week. This is a bugfix only release.

Upstream has stated his dislike of cherry-picking only some of the
fixes and would prefer us to ship a pristine 1.8.19 instead of a
patched version if we do not want to ship 1.8.20 as is. See the full
rationale here:
 <https://www.mail-archive.com/haproxy@formilux.org/msg33550.html>

Here is upstream changelog:

    - BUG/MAJOR: listener: Make sure the listener exist before using it.
    - BUG/MINOR: listener: keep accept rate counters accurate under saturation
    - BUG/MEDIUM: logs: Only attempt to free startup_logs once.
    - BUG/MEDIUM: 51d: fix possible segfault on deinit_51degrees()
    - BUG/MINOR: ssl: fix warning about ssl-min/max-ver support
    - MEDIUM: threads: Use __ATOMIC_SEQ_CST when using the newer atomic API.
    - BUG/MEDIUM: threads/fd: do not forget to take into account epoll_fd/pipes
    - BUG/MAJOR: spoe: Fix initialization of thread-dependent fields
    - BUG/MAJOR: stats: Fix how huge POST data are read from the channel
    - BUG/MINOR: http/counters: fix missing increment of fe->srv_aborts
    - BUG/MEDIUM: ssl: ability to set TLS 1.3 ciphers using ssl-default-server-ciphersuites
    - DOC: The option httplog is no longer valid in a backend.
    - BUG/MAJOR: checks: segfault during tcpcheck_main
    - BUILD: makefile: work around an old bug in GNU make-3.80
    - MINOR: tools: make memvprintf() never pass a NULL target to vsnprintf()
    - BUILD: makefile: fix build of IPv6 header on aix51
    - BUILD: makefile: add _LINUX_SOURCE_COMPAT to build on AIX-51
    - BUILD: Makefile: disable shared cache on AIX 5.1
    - BUG/MINOR: cli: correctly handle abns in 'show cli sockets'
    - MINOR: cli: start addresses by a prefix in 'show cli sockets'
    - BUG/MEDIUM: peers: fix a case where peer session is not cleanly reset on release.
    - BUILD: use inttypes.h instead of stdint.h
    - BUILD: connection: fix naming of ip_v field
    - BUG/MEDIUM: pattern: assign pattern IDs after checking the config validity
    - BUG/MEDIUM: spoe: Queue message only if no SPOE applet is attached to the stream
    - BUG/MEDIUM: spoe: Return an error if nothing is encoded for fragmented messages
    - BUG/MINOR: threads: fix the process range of thread masks
    - MINOR: lists: Implement locked variations.
    - BUG/MEDIUM: lists: Properly handle the case we're removing the first elt.
    - BUG/MEDIUM: list: fix the rollback on addq in the locked liss
    - BUG/MEDIUM: list: fix LIST_POP_LOCKED's removal of the last pointer
    - BUG/MEDIUM: list: add missing store barriers when updating elements and head
    - MINOR: list: make the delete and pop operations idempotent
    - BUG/MEDIUM: list: correct fix for LIST_POP_LOCKED's removal of last element
    - BUG/MEDIUM: list: fix again LIST_ADDQ_LOCKED
    - BUG/MEDIUM: list: fix incorrect pointer unlocking in LIST_DEL_LOCKED()
    - MAJOR: listener: do not hold the listener lock in listener_accept()
    - BUG/MEDIUM: listener: use a self-locked list for the dequeue lists
    - BUG/MEDIUM: listener: make sure the listener never accepts too many conns
    - BUILD/MINOR: listener: Silent a few signedness warnings.
    - MINOR: skip get_gmtime where tm is unused
    - BUG/MAJOR: http_fetch: Get the channel depending on the keyword used
    - BUG/MEDIUM: maps: only try to parse the default value when it's present
    - BUG/MINOR: acl: properly detect pattern type SMP_T_ADDR
    - BUG/MEDIUM: thread/http: Add missing locks in set-map and add-acl HTTP rules
    - BUG/MINOR: 51d: Get the request channel to call CHECK_HTTP_MESSAGE_FIRST()
    - BUG/MINOR: da: Get the request channel to call CHECK_HTTP_MESSAGE_FIRST()
    - BUG/MINOR: spoe: Don't systematically wakeup SPOE stream in the applet handler

Here is a diffstat:

 CHANGELOG                                         |   50 +++
 Makefile                                          |    6
 README                                            |    2
 VERDATE                                           |    2
 VERSION                                           |    2
 contrib/hpack/gen-rht.c                           |    2
 contrib/plug_qdisc/plug_qdisc.c                   |    2
 contrib/spoa_example/include/spop_functions.h     |    2
 contrib/wireshark-dissectors/peers/packet-happp.c |    2
 debian/changelog                                  |   11
 debian/tests/control                              |    4
 debian/tests/proxy-localhost                      |   48 +++
 doc/configuration.txt                             |    4
 examples/haproxy.spec                             |    5
 include/common/hathreads.h                        |   18 -
 include/common/hpack-dec.h                        |    2
 include/common/hpack-enc.h                        |    2
 include/common/hpack-huff.h                       |    2
 include/common/hpack-tbl.h                        |    2
 include/common/http-hdr.h                         |    2
 include/common/mini-clist.h                       |  156 ++++++++++
 include/proto/proto_http.h                        |   10
 include/proto/shctx.h                             |    2
 src/51d.c                                         |    5
 src/acl.c                                         |    1
 src/checks.c                                      |    2
 src/cli.c                                         |   10
 src/connection.c                                  |    8
 src/da.c                                          |    2
 src/flt_spoe.c                                    |   33 +-
 src/h2.c                                          |    2
 src/haproxy.c                                     |    7
 src/hlua.c                                        |    2
 src/hpack-dec.c                                   |    2
 src/hpack-enc.c                                   |    2
 src/hpack-huff.c                                  |    2
 src/hpack-tbl.c                                   |    2
 src/listener.c                                    |  247 +++++++++-------
 src/log.c                                         |    7
 src/map.c                                         |    6
 src/peers.c                                       |   77 +++-
 src/proto_http.c                                  |  339 +++++++++++-----------
 src/session.c                                     |    5
 src/sha1.c                                        |    2
 src/ssl_sock.c                                    |    4
 src/standard.c                                    |    4
 src/stats.c                                       |   25 +
 src/xxhash.c                                      |    2
 48 files changed, 756 insertions(+), 380 deletions(-)

The two main contributors to these changes are:

BUG/MAJOR: http_fetch: Get the channel depending on the keyword used
<http://git.haproxy.org/?p=haproxy-1.8.git;a=commitdiff;h=b05ee4aa74a95be49c78198ca601000b47c93da2>

BUG/MEDIUM: listener: make sure the listener never accepts too many conns
<http://git.haproxy.org/?p=haproxy-1.8.git;a=commitdiff;h=c98cdf7cc755c579a8b9cceee4809089267615ce>

I am attaching the full debdiff.

Is it OK to push this version in unstable?

unblock haproxy/1.8.20-1

- -- System Information:
Debian Release: 10.0
  APT prefers unstable-debug
  APT policy: (500, 'unstable-debug'), (500, 'unstable'), (101, 'experimental-debug'), (101, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 4.19.0-4-amd64 (SMP w/4 CPU cores)
Kernel taint flags: TAINT_USER, TAINT_WARN, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE
Locale: LANG=fr_FR.utf8, LC_CTYPE=fr_FR.utf8 (charmap=UTF-8), LANGUAGE=en_US:en (charmap=UTF-8)
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

-----BEGIN PGP SIGNATURE-----

iQJGBAEBCAAwFiEErvI0h2bzccaJpzYAlaQv6DU1JfkFAlzOlbgSHGJlcm5hdEBk
ZWJpYW4ub3JnAAoJEJWkL+g1NSX5Wv4P/jhvcbjoUhLUZrXzMb6SvOUn6DN7RNiW
LDDkidjSi699+VbBlqwOcsG18E7YoB2AYzhhpeizx2o+yQSM2zweAjddRJ+RnEvl
Fcnv5FXw5dXK5Km6Mw3rTZkENt4zlHFN8GbbiVr8egmujmj/xNjSAUU7YAjNrycP
FjIZUaGGKnR57DgQUc6wjhC10LAD6+Hwa+M1dPz51u+kqpxXQ6o0TLoAqY3Icwu3
lkt6kG5c+yppHlKaEouquRM8Hkf+heDyL6xPDMbd4WeG1YxAG2rcxRgmTUYhH4Qt
MQo5yQMtzQtU3u8ingvpG5P7dwqwPnKfQurDYnslD2YDPyPFk708tqtoCkSjFB7f
35ZCuUvphm/2gAE38n7ZBxb/+Emyy0IHsMfLiq9I6R4b0/8DSbgPFUTZc2i/WXt5
lHoTLKITNhSdZ9154xQIn7QvbHSH3Weh8ax3eC35iDdoKJBs01FmSpSgCWPtYjvj
DHUsDJ2QcpXSN8cvxDgUs8Bhjk/nZAU4ly2EYZkAzUgl97izfODv9zHuRpCoZJ/9
96xG6YS2ooRkMPQLMnKVp1lRfKl9kr93uDvlci8Y94kG8jWZYJPBSLb6CJ4aUDWZ
4c/+v3KHqclxP3TxzRI6Ag1yvQOm10hkXn5qTXTx5Vls3cCx1e9Y6xHACM0QYc7C
2NDyVc0MDKpb
=Y7yl
-----END PGP SIGNATURE-----
diff -Nru haproxy-1.8.19/CHANGELOG haproxy-1.8.20/CHANGELOG
--- haproxy-1.8.19/CHANGELOG	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/CHANGELOG	2019-04-25 23:59:27.000000000 +0200
@@ -1,6 +1,56 @@
 ChangeLog :
 ===========
 
+2019/04/25 : 1.8.20
+    - BUG/MAJOR: listener: Make sure the listener exist before using it.
+    - BUG/MINOR: listener: keep accept rate counters accurate under saturation
+    - BUG/MEDIUM: logs: Only attempt to free startup_logs once.
+    - BUG/MEDIUM: 51d: fix possible segfault on deinit_51degrees()
+    - BUG/MINOR: ssl: fix warning about ssl-min/max-ver support
+    - MEDIUM: threads: Use __ATOMIC_SEQ_CST when using the newer atomic API.
+    - BUG/MEDIUM: threads/fd: do not forget to take into account epoll_fd/pipes
+    - BUG/MAJOR: spoe: Fix initialization of thread-dependent fields
+    - BUG/MAJOR: stats: Fix how huge POST data are read from the channel
+    - BUG/MINOR: http/counters: fix missing increment of fe->srv_aborts
+    - BUG/MEDIUM: ssl: ability to set TLS 1.3 ciphers using ssl-default-server-ciphersuites
+    - DOC: The option httplog is no longer valid in a backend.
+    - BUG/MAJOR: checks: segfault during tcpcheck_main
+    - BUILD: makefile: work around an old bug in GNU make-3.80
+    - MINOR: tools: make memvprintf() never pass a NULL target to vsnprintf()
+    - BUILD: makefile: fix build of IPv6 header on aix51
+    - BUILD: makefile: add _LINUX_SOURCE_COMPAT to build on AIX-51
+    - BUILD: Makefile: disable shared cache on AIX 5.1
+    - BUG/MINOR: cli: correctly handle abns in 'show cli sockets'
+    - MINOR: cli: start addresses by a prefix in 'show cli sockets'
+    - BUG/MEDIUM: peers: fix a case where peer session is not cleanly reset on release.
+    - BUILD: use inttypes.h instead of stdint.h
+    - BUILD: connection: fix naming of ip_v field
+    - BUG/MEDIUM: pattern: assign pattern IDs after checking the config validity
+    - BUG/MEDIUM: spoe: Queue message only if no SPOE applet is attached to the stream
+    - BUG/MEDIUM: spoe: Return an error if nothing is encoded for fragmented messages
+    - BUG/MINOR: threads: fix the process range of thread masks
+    - MINOR: lists: Implement locked variations.
+    - BUG/MEDIUM: lists: Properly handle the case we're removing the first elt.
+    - BUG/MEDIUM: list: fix the rollback on addq in the locked liss
+    - BUG/MEDIUM: list: fix LIST_POP_LOCKED's removal of the last pointer
+    - BUG/MEDIUM: list: add missing store barriers when updating elements and head
+    - MINOR: list: make the delete and pop operations idempotent
+    - BUG/MEDIUM: list: correct fix for LIST_POP_LOCKED's removal of last element
+    - BUG/MEDIUM: list: fix again LIST_ADDQ_LOCKED
+    - BUG/MEDIUM: list: fix incorrect pointer unlocking in LIST_DEL_LOCKED()
+    - MAJOR: listener: do not hold the listener lock in listener_accept()
+    - BUG/MEDIUM: listener: use a self-locked list for the dequeue lists
+    - BUG/MEDIUM: listener: make sure the listener never accepts too many conns
+    - BUILD/MINOR: listener: Silent a few signedness warnings.
+    - MINOR: skip get_gmtime where tm is unused
+    - BUG/MAJOR: http_fetch: Get the channel depending on the keyword used
+    - BUG/MEDIUM: maps: only try to parse the default value when it's present
+    - BUG/MINOR: acl: properly detect pattern type SMP_T_ADDR
+    - BUG/MEDIUM: thread/http: Add missing locks in set-map and add-acl HTTP rules
+    - BUG/MINOR: 51d: Get the request channel to call CHECK_HTTP_MESSAGE_FIRST()
+    - BUG/MINOR: da: Get the request channel to call CHECK_HTTP_MESSAGE_FIRST()
+    - BUG/MINOR: spoe: Don't systematically wakeup SPOE stream in the applet handler
+
 2019/02/11 : 1.8.19
     - DOC: ssl: Clarify when pre TLSv1.3 cipher can be used
     - DOC: ssl: Stop documenting ciphers example to use
diff -Nru haproxy-1.8.19/contrib/hpack/gen-rht.c haproxy-1.8.20/contrib/hpack/gen-rht.c
--- haproxy-1.8.19/contrib/hpack/gen-rht.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/contrib/hpack/gen-rht.c	2019-04-25 23:59:27.000000000 +0200
@@ -9,7 +9,7 @@
  *   00 => 0x0a, 01 => 0x0d, 10 => 0x16, 11 => EOS
  */
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff -Nru haproxy-1.8.19/contrib/plug_qdisc/plug_qdisc.c haproxy-1.8.20/contrib/plug_qdisc/plug_qdisc.c
--- haproxy-1.8.19/contrib/plug_qdisc/plug_qdisc.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/contrib/plug_qdisc/plug_qdisc.c	2019-04-25 23:59:27.000000000 +0200
@@ -1,4 +1,4 @@
-#include <stdint.h>
+#include <inttypes.h>
 #include <netlink/cache.h>
 #include <netlink/cli/utils.h>
 #include <netlink/cli/tc.h>
diff -Nru haproxy-1.8.19/contrib/spoa_example/include/spop_functions.h haproxy-1.8.20/contrib/spoa_example/include/spop_functions.h
--- haproxy-1.8.19/contrib/spoa_example/include/spop_functions.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/contrib/spoa_example/include/spop_functions.h	2019-04-25 23:59:27.000000000 +0200
@@ -1,7 +1,7 @@
 #ifndef _SPOP_FUNCTIONS_H
 #define _SPOP_FUNCTIONS_H
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <string.h>
 #include <spoe_types.h>
 
diff -Nru haproxy-1.8.19/contrib/wireshark-dissectors/peers/packet-happp.c haproxy-1.8.20/contrib/wireshark-dissectors/peers/packet-happp.c
--- haproxy-1.8.19/contrib/wireshark-dissectors/peers/packet-happp.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/contrib/wireshark-dissectors/peers/packet-happp.c	2019-04-25 23:59:27.000000000 +0200
@@ -22,7 +22,7 @@
  */
 
 #include <stdio.h>
-#include <stdint.h>
+#include <inttypes.h>
 #include <inttypes.h>
 #include <arpa/inet.h>
 
diff -Nru haproxy-1.8.19/debian/changelog haproxy-1.8.20/debian/changelog
--- haproxy-1.8.19/debian/changelog	2019-02-12 09:30:54.000000000 +0100
+++ haproxy-1.8.20/debian/changelog	2019-05-05 09:34:08.000000000 +0200
@@ -1,3 +1,14 @@
+haproxy (1.8.20-1) unstable; urgency=medium
+
+  * New upstream version 1.8.20
+    - BUG/MAJOR: checks: segfault during tcpcheck_main
+    - BUG/MAJOR: http_fetch: Get the channel depending on the keyword used
+    - BUG/MAJOR: listener: Make sure the listener exist before using it.
+    - BUG/MAJOR: spoe: Fix initialization of thread-dependent fields
+    - BUG/MAJOR: stats: Fix how huge POST data are read from the channel
+
+ -- Vincent Bernat <bernat@debian.org>  Sun, 05 May 2019 09:34:08 +0200
+
 haproxy (1.8.19-1) unstable; urgency=medium
 
   * New upstream version 1.8.19
diff -Nru haproxy-1.8.19/debian/tests/control haproxy-1.8.20/debian/tests/control
--- haproxy-1.8.19/debian/tests/control	2018-12-14 12:05:34.000000000 +0100
+++ haproxy-1.8.20/debian/tests/control	2019-05-05 09:34:08.000000000 +0200
@@ -1,3 +1,7 @@
 Tests: cli
 Depends: haproxy, socat
 Restrictions: needs-root
+
+Tests: proxy-localhost
+Depends: haproxy, wget, apache2
+Restrictions: needs-root, allow-stderr, isolation-container
diff -Nru haproxy-1.8.19/debian/tests/proxy-localhost haproxy-1.8.20/debian/tests/proxy-localhost
--- haproxy-1.8.19/debian/tests/proxy-localhost	1970-01-01 01:00:00.000000000 +0100
+++ haproxy-1.8.20/debian/tests/proxy-localhost	2019-05-05 09:34:08.000000000 +0200
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+set -eux
+
+cat > /etc/haproxy/haproxy.cfg <<EOF
+global
+        chroot /var/lib/haproxy
+        user haproxy
+        group haproxy
+        daemon
+        maxconn 4096
+
+defaults
+        log global
+        option dontlognull
+        option redispatch
+        retries 3
+        timeout client 50s
+        timeout connect 10s
+        timeout http-request 5s
+        timeout server 50s
+        maxconn 4096
+
+frontend test-front
+    bind *:8080
+    mode http
+    default_backend test-back
+
+backend test-back
+    mode http
+    stick store-request src
+    stick-table type ip size 256k expire 30m
+    server test-1 localhost:80
+EOF
+
+service haproxy restart
+
+# index.html is shipped with apache2
+# Download it via haproxy and compare
+if wget -t1 http://localhost:8080 -O- | cmp /var/www/html/index.html -; then
+    echo "OK: index.html downloaded via haproxy matches the source file."
+else
+    echo "FAIL: downloaded index.html via haproxy is different from the"
+    echo "      file delivered by apache."
+    exit 1
+fi
+
+exit 0
diff -Nru haproxy-1.8.19/doc/configuration.txt haproxy-1.8.20/doc/configuration.txt
--- haproxy-1.8.19/doc/configuration.txt	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/doc/configuration.txt	2019-04-25 23:59:27.000000000 +0200
@@ -4,7 +4,7 @@
                          ----------------------
                               version 1.8
                              willy tarreau
-                              2019/02/11
+                              2019/04/25
 
 
 This document covers the configuration language as implemented in the version
@@ -2118,7 +2118,7 @@
 option http-use-proxy-header         (*)  X          X         X         -
 option httpchk                            X          -         X         X
 option httpclose                     (*)  X          X         X         X
-option httplog                            X          X         X         X
+option httplog                            X          X         X         -
 option http_proxy                    (*)  X          X         X         X
 option independent-streams           (*)  X          X         X         X
 option ldap-check                         X          -         X         X
diff -Nru haproxy-1.8.19/examples/haproxy.spec haproxy-1.8.20/examples/haproxy.spec
--- haproxy-1.8.19/examples/haproxy.spec	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/examples/haproxy.spec	2019-04-25 23:59:27.000000000 +0200
@@ -1,6 +1,6 @@
 Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments
 Name: haproxy
-Version: 1.8.19
+Version: 1.8.20
 Release: 1
 License: GPL
 Group: System Environment/Daemons
@@ -74,6 +74,9 @@
 %attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name}
 
 %changelog
+* jeu. avril 25 2019 Christopher Faulet <cfaulet@haproxy.com>
+- updated to 1.8.20
+
 * Mon Feb 11 2019 Willy Tarreau <w@1wt.eu>
 - updated to 1.8.19
 
diff -Nru haproxy-1.8.19/include/common/hathreads.h haproxy-1.8.20/include/common/hathreads.h
--- haproxy-1.8.19/include/common/hathreads.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/common/hathreads.h	2019-04-25 23:59:27.000000000 +0200
@@ -213,14 +213,14 @@
 	})
 #else
 /* gcc >= 4.7 */
-#define HA_ATOMIC_CAS(val, old, new) __atomic_compare_exchange_n(val, old, new, 0, 0, 0)
-#define HA_ATOMIC_ADD(val, i)        __atomic_add_fetch(val, i, 0)
-#define HA_ATOMIC_XADD(val, i)       __atomic_fetch_add(val, i, 0)
-#define HA_ATOMIC_SUB(val, i)        __atomic_sub_fetch(val, i, 0)
-#define HA_ATOMIC_AND(val, flags)    __atomic_and_fetch(val, flags, 0)
-#define HA_ATOMIC_OR(val, flags)     __atomic_or_fetch(val,  flags, 0)
-#define HA_ATOMIC_XCHG(val, new)     __atomic_exchange_n(val, new, 0)
-#define HA_ATOMIC_STORE(val, new)    __atomic_store_n(val, new, 0)
+#define HA_ATOMIC_CAS(val, old, new) __atomic_compare_exchange_n(val, old, new, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_ADD(val, i)        __atomic_add_fetch(val, i, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_XADD(val, i)       __atomic_fetch_add(val, i, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_SUB(val, i)        __atomic_sub_fetch(val, i, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_AND(val, flags)    __atomic_and_fetch(val, flags, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_OR(val, flags)     __atomic_or_fetch(val,  flags, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_XCHG(val, new)     __atomic_exchange_n(val, new, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_STORE(val, new)    __atomic_store_n(val, new, __ATOMIC_SEQ_CST)
 #endif
 
 #define HA_ATOMIC_UPDATE_MAX(val, new)					\
@@ -344,7 +344,6 @@
 	TASK_WQ_LOCK,
 	POOL_LOCK,
 	LISTENER_LOCK,
-	LISTENER_QUEUE_LOCK,
 	PROXY_LOCK,
 	SERVER_LOCK,
 	UPDATED_SERVERS_LOCK,
@@ -467,7 +466,6 @@
 	case TASK_WQ_LOCK:         return "TASK_WQ";
 	case POOL_LOCK:            return "POOL";
 	case LISTENER_LOCK:        return "LISTENER";
-	case LISTENER_QUEUE_LOCK:  return "LISTENER_QUEUE";
 	case PROXY_LOCK:           return "PROXY";
 	case SERVER_LOCK:          return "SERVER";
 	case UPDATED_SERVERS_LOCK: return "UPDATED_SERVERS";
diff -Nru haproxy-1.8.19/include/common/hpack-dec.h haproxy-1.8.20/include/common/hpack-dec.h
--- haproxy-1.8.19/include/common/hpack-dec.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/common/hpack-dec.h	2019-04-25 23:59:27.000000000 +0200
@@ -28,7 +28,7 @@
 #ifndef _COMMON_HPACK_DEC_H
 #define _COMMON_HPACK_DEC_H
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <common/chunk.h>
 #include <common/config.h>
 #include <common/hpack-tbl.h>
diff -Nru haproxy-1.8.19/include/common/hpack-enc.h haproxy-1.8.20/include/common/hpack-enc.h
--- haproxy-1.8.19/include/common/hpack-enc.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/common/hpack-enc.h	2019-04-25 23:59:27.000000000 +0200
@@ -28,7 +28,7 @@
 #ifndef _COMMON_HPACK_ENC_H
 #define _COMMON_HPACK_ENC_H
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <common/chunk.h>
 #include <common/config.h>
 #include <common/ist.h>
diff -Nru haproxy-1.8.19/include/common/hpack-huff.h haproxy-1.8.20/include/common/hpack-huff.h
--- haproxy-1.8.19/include/common/hpack-huff.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/common/hpack-huff.h	2019-04-25 23:59:27.000000000 +0200
@@ -27,7 +27,7 @@
 #ifndef _PROTO_HPACK_HUFF_H
 #define _PROTO_HPACK_HUFF_H
 
-#include <stdint.h>
+#include <inttypes.h>
 
 int huff_enc(const char *s, char *out);
 int huff_dec(const uint8_t *huff, int hlen, char *out, int olen);
diff -Nru haproxy-1.8.19/include/common/hpack-tbl.h haproxy-1.8.20/include/common/hpack-tbl.h
--- haproxy-1.8.19/include/common/hpack-tbl.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/common/hpack-tbl.h	2019-04-25 23:59:27.000000000 +0200
@@ -27,7 +27,7 @@
 #ifndef _COMMON_HPACK_TBL_H
 #define _COMMON_HPACK_TBL_H
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <common/config.h>
 #include <common/http-hdr.h>
diff -Nru haproxy-1.8.19/include/common/http-hdr.h haproxy-1.8.20/include/common/http-hdr.h
--- haproxy-1.8.19/include/common/http-hdr.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/common/http-hdr.h	2019-04-25 23:59:27.000000000 +0200
@@ -27,7 +27,7 @@
 #ifndef _COMMON_HTTP_HDR_H
 #define _COMMON_HTTP_HDR_H
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <common/ist.h>
 
 /* a header field made of a name and a value. Such structure stores 4 longs so
diff -Nru haproxy-1.8.19/include/common/mini-clist.h haproxy-1.8.20/include/common/mini-clist.h
--- haproxy-1.8.19/include/common/mini-clist.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/common/mini-clist.h	2019-04-25 23:59:27.000000000 +0200
@@ -163,5 +163,161 @@
 	     &item->member != (list_head);                                \
 	     item = back, back = LIST_ELEM(back->member.n, typeof(back), member))
 
+#include <common/hathreads.h>
+#define LLIST_BUSY ((struct list *)1)
+
+/*
+ * Locked version of list manipulation macros.
+ * It is OK to use those concurrently from multiple threads, as long as the
+ * list is only used with the locked variants. The only "unlocked" macro you
+ * can use with a locked list is LIST_INIT.
+ */
+#define LIST_ADD_LOCKED(lh, el)                                            \
+	do {                                                               \
+		while (1) {                                                \
+			struct list *n;                                    \
+			struct list *p;                                    \
+			n = HA_ATOMIC_XCHG(&(lh)->n, LLIST_BUSY);          \
+			if (n == LLIST_BUSY)                               \
+			        continue;                                  \
+			__ha_barrier_store();                              \
+			p = HA_ATOMIC_XCHG(&n->p, LLIST_BUSY);             \
+			if (p == LLIST_BUSY) {                             \
+				(lh)->n = n;                               \
+				__ha_barrier_store();                      \
+				continue;                                  \
+			}                                                  \
+			(el)->n = n;                                       \
+			(el)->p = p;                                       \
+			__ha_barrier_store();                              \
+			n->p = (el);                                       \
+			__ha_barrier_store();                              \
+			p->n = (el);                                       \
+			__ha_barrier_store();                              \
+			break;                                             \
+		}                                                          \
+	} while (0)
+
+#define LIST_ADDQ_LOCKED(lh, el)                                           \
+	do {                                                               \
+		while (1) {                                                \
+			struct list *n;                                    \
+			struct list *p;                                    \
+			p = HA_ATOMIC_XCHG(&(lh)->p, LLIST_BUSY);          \
+			if (p == LLIST_BUSY)                               \
+			        continue;                                  \
+			__ha_barrier_store();                              \
+			n = HA_ATOMIC_XCHG(&p->n, LLIST_BUSY);             \
+			if (n == LLIST_BUSY) {                             \
+				(lh)->p = p;                               \
+				__ha_barrier_store();                      \
+				continue;                                  \
+			}                                                  \
+			(el)->n = n;                                       \
+			(el)->p = p;                                       \
+			__ha_barrier_store();                              \
+			p->n = (el);                                       \
+			__ha_barrier_store();                              \
+			n->p = (el);                                       \
+			__ha_barrier_store();                              \
+			break;                                             \
+		}                                                          \
+	} while (0)
+
+#define LIST_DEL_LOCKED(el)                                                \
+	do {                                                               \
+		while (1) {                                                \
+			struct list *n, *n2;                               \
+			struct list *p, *p2 = NULL;                        \
+			n = HA_ATOMIC_XCHG(&(el)->n, LLIST_BUSY);          \
+			if (n == LLIST_BUSY)                               \
+			        continue;                                  \
+			p = HA_ATOMIC_XCHG(&(el)->p, LLIST_BUSY);          \
+			if (p == LLIST_BUSY) {                             \
+				(el)->n = n;                               \
+				__ha_barrier_store();                      \
+				continue;                                  \
+			}                                                  \
+			if (p != (el)) {                                   \
+			        p2 = HA_ATOMIC_XCHG(&p->n, LLIST_BUSY);    \
+			        if (p2 == LLIST_BUSY) {                    \
+			                (el)->p = p;                       \
+					(el)->n = n;                       \
+					__ha_barrier_store();              \
+					continue;                          \
+				}                                          \
+			}                                                  \
+			if (n != (el)) {                                   \
+			        n2 = HA_ATOMIC_XCHG(&n->p, LLIST_BUSY);    \
+				if (n2 == LLIST_BUSY) {                    \
+					if (p2 != NULL)                    \
+						p->n = p2;                 \
+					(el)->p = p;                       \
+					(el)->n = n;                       \
+					__ha_barrier_store();              \
+					continue;                          \
+				}                                          \
+			}                                                  \
+			n->p = p;                                          \
+			p->n = n;                                          \
+			__ha_barrier_store();                              \
+			(el)->p = (el);                                    \
+			(el)->n = (el);	                                   \
+			__ha_barrier_store();                              \
+			break;                                             \
+		}                                                          \
+	} while (0)
+
+
+/* Remove the first element from the list, and return it */
+#define LIST_POP_LOCKED(lh, pt, el)                                        \
+	({                                                                 \
+		 void *_ret;                                               \
+		 while (1) {                                               \
+			 struct list *n, *n2;                              \
+			 struct list *p, *p2;                              \
+			 n = HA_ATOMIC_XCHG(&(lh)->n, LLIST_BUSY);         \
+			 if (n == LLIST_BUSY)                              \
+			         continue;                                 \
+			 if (n == (lh)) {                                  \
+				 (lh)->n = lh;                             \
+				 __ha_barrier_store();                     \
+				 _ret = NULL;                              \
+				 break;                                    \
+			 }                                                 \
+			 p = HA_ATOMIC_XCHG(&n->p, LLIST_BUSY);            \
+			 if (p == LLIST_BUSY) {                            \
+				 (lh)->n = n;                              \
+				 __ha_barrier_store();                     \
+				 continue;                                 \
+			 }                                                 \
+			 n2 = HA_ATOMIC_XCHG(&n->n, LLIST_BUSY);           \
+			 if (n2 == LLIST_BUSY) {                           \
+				 n->p = p;                                 \
+				 __ha_barrier_store();                     \
+				 (lh)->n = n;                              \
+				 __ha_barrier_store();                     \
+				 continue;                                 \
+			 }                                                 \
+			 p2 = HA_ATOMIC_XCHG(&n2->p, LLIST_BUSY);          \
+			 if (p2 == LLIST_BUSY) {                           \
+				 n->n = n2;                                \
+				 n->p = p;                                 \
+				 __ha_barrier_store();                     \
+				 (lh)->n = n;                              \
+				 __ha_barrier_store();                     \
+				 continue;                                 \
+			 }                                                 \
+			 (lh)->n = n2;                                     \
+			 (n2)->p = (lh);                                   \
+			 __ha_barrier_store();                             \
+			 (n)->p = (n);                                     \
+			 (n)->n = (n);	                                   \
+			 __ha_barrier_store();                             \
+			 _ret = LIST_ELEM(n, pt, el);                      \
+			 break;                                            \
+		 }                                                         \
+		 (_ret);                                                   \
+	 })
 
 #endif /* _COMMON_MINI_CLIST_H */
diff -Nru haproxy-1.8.19/include/proto/proto_http.h haproxy-1.8.20/include/proto/proto_http.h
--- haproxy-1.8.19/include/proto/proto_http.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/proto/proto_http.h	2019-04-25 23:59:27.000000000 +0200
@@ -132,7 +132,7 @@
 int val_hdr(struct arg *arg, char **err_msg);
 
 int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt,
-                  const struct arg *args, struct sample *smp, int req_vol);
+                  const struct channel *chn, struct sample *smp, int req_vol);
 
 enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
                                               struct session *sess, struct stream *s, int flags);
@@ -144,11 +144,11 @@
 /* Note: these functions *do* modify the sample. Even in case of success, at
  * least the type and uint value are modified.
  */
-#define CHECK_HTTP_MESSAGE_FIRST() \
-	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 1); if (r <= 0) return r; } while (0)
+#define CHECK_HTTP_MESSAGE_FIRST(chn) \
+	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, (chn), smp, 1); if (r <= 0) return r; } while (0)
 
-#define CHECK_HTTP_MESSAGE_FIRST_PERM() \
-	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 0); if (r <= 0) return r; } while (0)
+#define CHECK_HTTP_MESSAGE_FIRST_PERM(chn) \
+	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, (chn), smp, 0); if (r <= 0) return r; } while (0)
 
 static inline void http_req_keywords_register(struct action_kw_list *kw_list)
 {
diff -Nru haproxy-1.8.19/include/proto/shctx.h haproxy-1.8.20/include/proto/shctx.h
--- haproxy-1.8.19/include/proto/shctx.h	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/include/proto/shctx.h	2019-04-25 23:59:27.000000000 +0200
@@ -17,7 +17,7 @@
 #include <common/mini-clist.h>
 #include <types/shctx.h>
 
-#include <stdint.h>
+#include <inttypes.h>
 
 #ifndef USE_PRIVATE_CACHE
 #ifdef USE_PTHREAD_PSHARED
diff -Nru haproxy-1.8.19/Makefile haproxy-1.8.20/Makefile
--- haproxy-1.8.19/Makefile	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/Makefile	2019-04-25 23:59:27.000000000 +0200
@@ -358,7 +358,8 @@
   # This is for AIX 5.1
   USE_POLL        = implicit
   USE_LIBCRYPT    = implicit
-  TARGET_CFLAGS   = -Dss_family=__ss_family
+  USE_PRIVATE_CACHE = implicit
+  TARGET_CFLAGS   = -Dss_family=__ss_family -Dip6_hdr=ip6hdr -DSTEVENS_API -D_LINUX_SOURCE_COMPAT -Dunsetenv=my_unsetenv
   DEBUG_CFLAGS    =
 else
 ifeq ($(TARGET),aix52)
@@ -905,7 +906,8 @@
 DEP = $(INCLUDES) .build_opts
 
 # Used only to force a rebuild if some build options change
-.build_opts: $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(VERBOSE_CFLAGS)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
+build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(VERBOSE_CFLAGS)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
+.build_opts: $(build_opts)
 
 haproxy: $(OPTIONS_OBJS) $(EBTREE_OBJS) $(OBJS)
 	$(LD) $(LDFLAGS) -o $@ $^ $(LDOPTS)
diff -Nru haproxy-1.8.19/README haproxy-1.8.20/README
--- haproxy-1.8.19/README	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/README	2019-04-25 23:59:27.000000000 +0200
@@ -3,7 +3,7 @@
                          ----------------------
                               version 1.8
                              willy tarreau
-                               2019/02/11
+                               2019/04/25
 
 
 1) How to build it
diff -Nru haproxy-1.8.19/src/51d.c haproxy-1.8.20/src/51d.c
--- haproxy-1.8.19/src/51d.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/51d.c	2019-04-25 23:59:27.000000000 +0200
@@ -384,7 +384,7 @@
 	 * Data type has to be reset to ensure the string output is processed
 	 * correctly.
 	 */
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST((smp->strm ? &smp->strm->req : NULL));
 	smp->data.type = SMP_T_STR;
 
 	/* Flags the sample to show it uses constant memory*/
@@ -639,7 +639,8 @@
 
 	free(global_51degrees.header_names);
 #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
-	fiftyoneDegreesWorksetPoolFree(global_51degrees.pool);
+	if (global_51degrees.pool)
+		fiftyoneDegreesWorksetPoolFree(global_51degrees.pool);
 #endif
 #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
 	free(global_51degrees.device_offsets.firstOffset);
diff -Nru haproxy-1.8.19/src/acl.c haproxy-1.8.20/src/acl.c
--- haproxy-1.8.19/src/acl.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/acl.c	2019-04-25 23:59:27.000000000 +0200
@@ -400,6 +400,7 @@
 			expr->pat.prune = pat_prune_fcts[PAT_MATCH_INT];
 			expr->pat.expect_type = pat_match_types[PAT_MATCH_INT];
 			break;
+		case SMP_T_ADDR:
 		case SMP_T_IPV4:
 		case SMP_T_IPV6:
 			expr->pat.parse = pat_parse_fcts[PAT_MATCH_IP];
diff -Nru haproxy-1.8.19/src/checks.c haproxy-1.8.20/src/checks.c
--- haproxy-1.8.19/src/checks.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/checks.c	2019-04-25 23:59:27.000000000 +0200
@@ -2771,7 +2771,7 @@
 			conn_install_mux(conn, &mux_pt_ops, cs);
 
 			ret = SF_ERR_INTERNAL;
-			if (proto->connect)
+			if (proto && proto->connect)
 				ret = proto->connect(conn,
 						     1 /* I/O polling is always needed */,
 						     (next && next->action == TCPCHK_ACT_EXPECT) ? 0 : 2);
diff -Nru haproxy-1.8.19/src/cli.c haproxy-1.8.20/src/cli.c
--- haproxy-1.8.19/src/cli.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/cli.c	2019-04-25 23:59:27.000000000 +0200
@@ -972,15 +972,19 @@
 							const struct sockaddr_un *un;
 
 							un = (struct sockaddr_un *)&l->addr;
-							chunk_appendf(&trash, "%s ", un->sun_path);
+							if (un->sun_path[0] == '\0') {
+								chunk_appendf(&trash, "abns@%s ", un->sun_path+1);
+							} else {
+								chunk_appendf(&trash, "unix@%s ", un->sun_path);
+							}
 						} else if (l->addr.ss_family == AF_INET) {
 							addr_to_str(&l->addr, addr, sizeof(addr));
 							port_to_str(&l->addr, port, sizeof(port));
-							chunk_appendf(&trash, "%s:%s ", addr, port);
+							chunk_appendf(&trash, "ipv4@%s:%s ", addr, port);
 						} else if (l->addr.ss_family == AF_INET6) {
 							addr_to_str(&l->addr, addr, sizeof(addr));
 							port_to_str(&l->addr, port, sizeof(port));
-							chunk_appendf(&trash, "[%s]:%s ", addr, port);
+							chunk_appendf(&trash, "ipv6@[%s]:%s ", addr, port);
 						} else
 							continue;
 
diff -Nru haproxy-1.8.19/src/connection.c haproxy-1.8.20/src/connection.c
--- haproxy-1.8.19/src/connection.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/connection.c	2019-04-25 23:59:27.000000000 +0200
@@ -699,7 +699,7 @@
 {
 	char *line;
 	uint32_t hdr_len;
-	uint8_t ip_v;
+	uint8_t ip_ver;
 
 	/* we might have been called just after an asynchronous shutr */
 	if (conn->flags & CO_FL_SOCK_RD_SH)
@@ -765,9 +765,9 @@
 		goto missing;
 
 	/* Get IP version from the first four bits */
-	ip_v = (*line & 0xf0) >> 4;
+	ip_ver = (*line & 0xf0) >> 4;
 
-	if (ip_v == 4) {
+	if (ip_ver == 4) {
 		struct ip *hdr_ip4;
 		struct my_tcphdr *hdr_tcp;
 
@@ -797,7 +797,7 @@
 
 		conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET;
 	}
-	else if (ip_v == 6) {
+	else if (ip_ver == 6) {
 		struct ip6_hdr *hdr_ip6;
 		struct my_tcphdr *hdr_tcp;
 
diff -Nru haproxy-1.8.19/src/da.c haproxy-1.8.20/src/da.c
--- haproxy-1.8.19/src/da.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/da.c	2019-04-25 23:59:27.000000000 +0200
@@ -293,7 +293,7 @@
 		return 1;
 	}
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST((smp->strm ? &smp->strm->req : NULL));
 	smp->data.type = SMP_T_STR;
 
 	/**
diff -Nru haproxy-1.8.19/src/flt_spoe.c haproxy-1.8.20/src/flt_spoe.c
--- haproxy-1.8.19/src/flt_spoe.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/flt_spoe.c	2019-04-25 23:59:27.000000000 +0200
@@ -1929,8 +1929,6 @@
 
 	if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
 		task_queue(SPOE_APPCTX(appctx)->task);
-	si_oc(si)->flags |= CF_READ_DONTWAIT;
-	task_wakeup(si_strm(si)->task, TASK_WOKEN_IO);
 }
 
 struct applet spoe_applet = {
@@ -2086,11 +2084,14 @@
 		return -1;
 	}
 
-	/* Add the SPOE context in the sending queue and update all running
-	 * info */
-	LIST_ADDQ(&agent->rt[tid].sending_queue, &ctx->list);
+	/* Add the SPOE context in the sending queue if the stream has no applet
+	 * already assigned and wakeup all idle applets. Otherwise, don't queue
+	 * it. */
 	if (agent->rt[tid].sending_rate)
 		agent->rt[tid].sending_rate--;
+	if (ctx->frag_ctx.spoe_appctx)
+		return 1;
+	LIST_ADDQ(&agent->rt[tid].sending_queue, &ctx->list);
 
 	SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
 		    " - Add stream in sending queue"
@@ -2273,7 +2274,9 @@
 	return 1;
 
   too_big:
-	if (!(agent->flags & SPOE_FL_SND_FRAGMENTATION)) {
+	/* Return an error if fragmentation is unsupported or if nothing has
+	 * been encoded because its too big and not splittable. */
+	if (!(agent->flags & SPOE_FL_SND_FRAGMENTATION) || p == ctx->buffer->p) {
 		ctx->status_code = SPOE_CTX_ERR_TOO_BIG;
 		return -1;
 	}
@@ -2937,20 +2940,20 @@
 	if (global.nbthread == 1)
 		conf->agent->flags |= SPOE_FL_ASYNC;
 
-	if ((curagent->rt = calloc(global.nbthread, sizeof(*curagent->rt))) == NULL) {
+	if ((conf->agent->rt = calloc(global.nbthread, sizeof(*conf->agent->rt))) == NULL) {
 		ha_alert("Proxy %s : out of memory initializing SPOE agent '%s' declared at %s:%d.\n",
 			 px->id, conf->agent->id, conf->agent->conf.file, conf->agent->conf.line);
 		return 1;
 	}
 	for (i = 0; i < global.nbthread; ++i) {
-		curagent->rt[i].frame_size   = curagent->max_frame_size;
-		curagent->rt[i].applets_act  = 0;
-		curagent->rt[i].applets_idle = 0;
-		curagent->rt[i].sending_rate = 0;
-		LIST_INIT(&curagent->rt[i].applets);
-		LIST_INIT(&curagent->rt[i].sending_queue);
-		LIST_INIT(&curagent->rt[i].waiting_queue);
-		HA_SPIN_INIT(&curagent->rt[i].lock);
+		conf->agent->rt[i].frame_size   = conf->agent->max_frame_size;
+		conf->agent->rt[i].applets_act  = 0;
+		conf->agent->rt[i].applets_idle = 0;
+		conf->agent->rt[i].sending_rate = 0;
+		LIST_INIT(&conf->agent->rt[i].applets);
+		LIST_INIT(&conf->agent->rt[i].sending_queue);
+		LIST_INIT(&conf->agent->rt[i].waiting_queue);
+		HA_SPIN_INIT(&conf->agent->rt[i].lock);
 	}
 
 	free(conf->agent->b.name);
diff -Nru haproxy-1.8.19/src/h2.c haproxy-1.8.20/src/h2.c
--- haproxy-1.8.19/src/h2.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/h2.c	2019-04-25 23:59:27.000000000 +0200
@@ -25,7 +25,7 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <common/config.h>
 #include <common/h2.h>
 #include <common/http-hdr.h>
diff -Nru haproxy-1.8.19/src/haproxy.c haproxy-1.8.20/src/haproxy.c
--- haproxy-1.8.19/src/haproxy.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/haproxy.c	2019-04-25 23:59:27.000000000 +0200
@@ -1570,14 +1570,14 @@
 		exit(1);
 	}
 
-	pattern_finalize_config();
-
 	err_code |= check_config_validity();
 	if (err_code & (ERR_ABORT|ERR_FATAL)) {
 		ha_alert("Fatal errors found in configuration.\n");
 		exit(1);
 	}
 
+	pattern_finalize_config();
+
 	/* recompute the amount of per-process memory depending on nbproc and
 	 * the shared SSL cache size (allowed to exist in all processes).
 	 */
@@ -1828,6 +1828,9 @@
 	global.hardmaxconn = global.maxconn;  /* keep this max value */
 	global.maxsock += global.maxconn * 2; /* each connection needs two sockets */
 	global.maxsock += global.maxpipes * 2; /* each pipe needs two FDs */
+	global.maxsock += global.nbthread;     /* one epoll_fd/kqueue_fd per thread */
+	global.maxsock += 2 * global.nbthread; /* one wake-up pipe (2 fd) per thread */
+
 	/* compute fd used by async engines */
 	if (global.ssl_used_async_engines) {
 		int sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
diff -Nru haproxy-1.8.19/src/hlua.c haproxy-1.8.20/src/hlua.c
--- haproxy-1.8.19/src/hlua.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/hlua.c	2019-04-25 23:59:27.000000000 +0200
@@ -6458,7 +6458,7 @@
 	const char *error;
 
 	/* Wait for a full HTTP request. */
-	if (!smp_prefetch_http(px, strm, 0, NULL, &smp, 0)) {
+	if (!smp_prefetch_http(px, strm, 0, req, &smp, 0)) {
 		if (smp.flags & SMP_F_MAY_CHANGE)
 			return -1;
 		return 0;
diff -Nru haproxy-1.8.19/src/hpack-dec.c haproxy-1.8.20/src/hpack-dec.c
--- haproxy-1.8.19/src/hpack-dec.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/hpack-dec.c	2019-04-25 23:59:27.000000000 +0200
@@ -25,7 +25,7 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff -Nru haproxy-1.8.19/src/hpack-enc.c haproxy-1.8.20/src/hpack-enc.c
--- haproxy-1.8.19/src/hpack-enc.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/hpack-enc.c	2019-04-25 23:59:27.000000000 +0200
@@ -25,7 +25,7 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff -Nru haproxy-1.8.19/src/hpack-huff.c haproxy-1.8.20/src/hpack-huff.c
--- haproxy-1.8.19/src/hpack-huff.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/hpack-huff.c	2019-04-25 23:59:27.000000000 +0200
@@ -26,7 +26,7 @@
  */
 
 #include <stdio.h>
-#include <stdint.h>
+#include <inttypes.h>
 #include <string.h>
 
 #include <common/config.h>
diff -Nru haproxy-1.8.19/src/hpack-tbl.c haproxy-1.8.20/src/hpack-tbl.c
--- haproxy-1.8.19/src/hpack-tbl.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/hpack-tbl.c	2019-04-25 23:59:27.000000000 +0200
@@ -25,7 +25,7 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#include <stdint.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff -Nru haproxy-1.8.19/src/listener.c haproxy-1.8.20/src/listener.c
--- haproxy-1.8.19/src/listener.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/listener.c	2019-04-25 23:59:27.000000000 +0200
@@ -39,9 +39,6 @@
 #include <proto/stream.h>
 #include <proto/task.h>
 
- /* listner_queue lock (same for global and per proxy queues) */
-__decl_hathreads(static HA_SPINLOCK_T lq_lock);
-
 /* List head of all known bind keywords */
 static struct bind_kw_list bind_keywords = {
 	.list = LIST_HEAD_INIT(bind_keywords.list)
@@ -94,11 +91,7 @@
 		goto end;
 	if (listener->state == LI_READY)
 		fd_stop_recv(listener->fd);
-	if (listener->state == LI_LIMITED) {
-		HA_SPIN_LOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-		LIST_DEL(&listener->wait_queue);
-		HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-	}
+	LIST_DEL_LOCKED(&listener->wait_queue);
 	listener->state = LI_LISTEN;
   end:
 	HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
@@ -134,11 +127,7 @@
 			goto end;
 	}
 
-	if (l->state == LI_LIMITED) {
-		HA_SPIN_LOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-		LIST_DEL(&l->wait_queue);
-		HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-	}
+	LIST_DEL_LOCKED(&l->wait_queue);
 
 	fd_stop_recv(l->fd);
 	l->state = LI_PAUSED;
@@ -157,7 +146,7 @@
  * stopped it. If the resume fails, 0 is returned and an error might be
  * displayed.
  */
-static int __resume_listener(struct listener *l)
+int resume_listener(struct listener *l)
 {
 	int ret = 1;
 
@@ -199,8 +188,7 @@
 	if (l->state == LI_READY)
 		goto end;
 
-	if (l->state == LI_LIMITED)
-		LIST_DEL(&l->wait_queue);
+	LIST_DEL_LOCKED(&l->wait_queue);
 
 	if (l->nbconn >= l->maxconn) {
 		l->state = LI_FULL;
@@ -214,51 +202,34 @@
 	return ret;
 }
 
-int resume_listener(struct listener *l)
-{
-	int ret;
-
-	HA_SPIN_LOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-	ret = __resume_listener(l);
-	HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-	return ret;
-}
-
 /* Marks a ready listener as full so that the stream code tries to re-enable
  * it upon next close() using resume_listener().
- *
- * Note: this function is only called from listener_accept so <l> is already
- *       locked.
  */
 static void listener_full(struct listener *l)
 {
+	HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
 	if (l->state >= LI_READY) {
-		if (l->state == LI_LIMITED) {
-			HA_SPIN_LOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-			LIST_DEL(&l->wait_queue);
-			HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
+		LIST_DEL_LOCKED(&l->wait_queue);
+		if (l->state != LI_FULL) {
+			fd_stop_recv(l->fd);
+			l->state = LI_FULL;
 		}
-
-		fd_stop_recv(l->fd);
-		l->state = LI_FULL;
 	}
+	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
 }
 
 /* Marks a ready listener as limited so that we only try to re-enable it when
  * resources are free again. It will be queued into the specified queue.
- *
- * Note: this function is only called from listener_accept so <l> is already
- *       locked.
  */
 static void limit_listener(struct listener *l, struct list *list)
 {
+	HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
 	if (l->state == LI_READY) {
-		HA_SPIN_LOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-		LIST_ADDQ(list, &l->wait_queue);
-		HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
+		LIST_ADDQ_LOCKED(list, &l->wait_queue);
 		fd_stop_recv(l->fd);
 		l->state = LI_LIMITED;
 	}
+	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
 }
 
 /* This function adds all of the protocol's listener's file descriptors to the
@@ -293,17 +264,14 @@
 /* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
 void dequeue_all_listeners(struct list *list)
 {
-	struct listener *listener, *l_back;
+	struct listener *listener;
 
-	HA_SPIN_LOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-	list_for_each_entry_safe(listener, l_back, list, wait_queue) {
+	while ((listener = LIST_POP_LOCKED(list, struct listener *, wait_queue))) {
 		/* This cannot fail because the listeners are by definition in
-		 * the LI_LIMITED state. The function also removes the entry
-		 * from the queue.
+		 * the LI_LIMITED state.
 		 */
-		__resume_listener(listener);
+		resume_listener(listener);
 	}
-	HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
 }
 
 /* Must be called with the lock held. Depending on <do_close> value, it does
@@ -314,11 +282,7 @@
 	if (listener->state == LI_READY)
 		fd_stop_recv(listener->fd);
 
-	if (listener->state == LI_LIMITED) {
-		HA_SPIN_LOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-		LIST_DEL(&listener->wait_queue);
-		HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
-	}
+	LIST_DEL_LOCKED(&listener->wait_queue);
 
 	if (listener->state >= LI_PAUSED) {
 		if (do_close) {
@@ -400,6 +364,7 @@
 
 		l->fd = fd;
 		memcpy(&l->addr, ss, sizeof(*ss));
+		LIST_INIT(&l->wait_queue);
 		l->state = LI_INIT;
 
 		proto->add(l, port);
@@ -422,15 +387,14 @@
  */
 void delete_listener(struct listener *listener)
 {
-	if (listener->state != LI_ASSIGNED)
-		return;
-
 	HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
-	listener->state = LI_INIT;
-	LIST_DEL(&listener->proto_list);
-	listener->proto->nb_listeners--;
-	HA_ATOMIC_SUB(&jobs, 1);
-	HA_ATOMIC_SUB(&listeners, 1);
+	if (listener->state == LI_ASSIGNED) {
+		listener->state = LI_INIT;
+		LIST_DEL(&listener->proto_list);
+		listener->proto->nb_listeners--;
+		HA_ATOMIC_SUB(&jobs, 1);
+		HA_ATOMIC_SUB(&listeners, 1);
+	}
 	HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
 }
 
@@ -441,8 +405,11 @@
 void listener_accept(int fd)
 {
 	struct listener *l = fdtab[fd].owner;
-	struct proxy *p = l->bind_conf->frontend;
-	int max_accept = l->maxaccept ? l->maxaccept : 1;
+	struct proxy *p;
+	int max_accept;
+	int next_conn = 0;
+	int next_feconn = 0;
+	int next_actconn = 0;
 	int expire;
 	int cfd;
 	int ret;
@@ -450,13 +417,10 @@
 	static int accept4_broken;
 #endif
 
-	if (HA_SPIN_TRYLOCK(LISTENER_LOCK, &l->lock))
+	if (!l)
 		return;
-
-	if (unlikely(l->nbconn >= l->maxconn)) {
-		listener_full(l);
-		goto end;
-	}
+	p = l->bind_conf->frontend;
+	max_accept = l->maxaccept ? l->maxaccept : 1;
 
 	if (!(l->options & LI_O_UNLIMITED) && global.sps_lim) {
 		int max = freq_ctr_remain(&global.sess_per_sec, global.sps_lim, 0);
@@ -516,20 +480,68 @@
 	 * worst case. If we fail due to system limits or temporary resource
 	 * shortage, we try again 100ms later in the worst case.
 	 */
-	while (max_accept--) {
+	for (; max_accept-- > 0; next_conn = next_feconn = next_actconn = 0) {
 		struct sockaddr_storage addr;
 		socklen_t laddr = sizeof(addr);
 		unsigned int count;
 
-		if (unlikely(actconn >= global.maxconn) && !(l->options & LI_O_UNLIMITED)) {
-			limit_listener(l, &global_listener_queue);
-			task_schedule(global_listener_queue_task, tick_add(now_ms, 1000)); /* try again in 1 second */
-			goto end;
+		/* pre-increase the number of connections without going too far.
+		 * We process the listener, then the proxy, then the process.
+		 * We know which ones to unroll based on the next_xxx value.
+		 */
+		do {
+			count = l->nbconn;
+			if (count >= l->maxconn) {
+				/* the listener was marked full or another
+				 * thread is going to do it.
+				 */
+				next_conn = 0;
+				goto end;
+			}
+			next_conn = count + 1;
+		} while (!HA_ATOMIC_CAS(&l->nbconn, (int *)&count, next_conn));
+
+		if (next_conn == l->maxconn) {
+			/* we filled it, mark it full */
+			listener_full(l);
 		}
 
-		if (unlikely(p && p->feconn >= p->maxconn)) {
-			limit_listener(l, &p->listener_queue);
-			goto end;
+		if (p) {
+			do {
+				count = p->feconn;
+				if (count >= p->maxconn) {
+					/* the frontend was marked full or another
+					 * thread is going to do it.
+					 */
+					next_feconn = 0;
+					goto end;
+				}
+				next_feconn = count + 1;
+			} while (!HA_ATOMIC_CAS(&p->feconn, &count, next_feconn));
+
+			if (unlikely(next_feconn == p->maxconn)) {
+				/* we just filled it */
+				limit_listener(l, &p->listener_queue);
+			}
+		}
+
+		if (!(l->options & LI_O_UNLIMITED)) {
+			do {
+				count = actconn;
+				if (count >= global.maxconn) {
+					/* the process was marked full or another
+					 * thread is going to do it.
+					 */
+					next_actconn = 0;
+					goto end;
+				}
+				next_actconn = count + 1;
+			} while (!HA_ATOMIC_CAS(&actconn, (int *)&count, next_actconn));
+
+			if (unlikely(next_actconn == global.maxconn)) {
+				limit_listener(l, &global_listener_queue);
+				task_schedule(global_listener_queue_task, tick_add(now_ms, 1000)); /* try again in 1 second */
+			}
 		}
 
 #ifdef USE_ACCEPT4
@@ -563,6 +575,11 @@
 				goto transient_error;
 			case EINTR:
 			case ECONNABORTED:
+				HA_ATOMIC_SUB(&l->nbconn, 1);
+				if (p)
+					HA_ATOMIC_SUB(&p->feconn, 1);
+				if (!(l->options & LI_O_UNLIMITED))
+					HA_ATOMIC_SUB(&actconn, 1);
 				continue;
 			case ENFILE:
 				if (p)
@@ -589,6 +606,20 @@
 			}
 		}
 
+		/* The connection was accepted, it must be counted as such */
+		if (l->counters)
+			HA_ATOMIC_UPDATE_MAX(&l->counters->conn_max, next_conn);
+
+		if (p)
+			HA_ATOMIC_UPDATE_MAX(&p->fe_counters.conn_max, next_feconn);
+
+		proxy_inc_fe_conn_ctr(l, p);
+
+		if (!(l->options & LI_O_UNLIMITED)) {
+			count = update_freq_ctr(&global.conn_per_sec, 1);
+			HA_ATOMIC_UPDATE_MAX(&global.cps_max, count);
+		}
+
 		if (unlikely(cfd >= global.maxsock)) {
 			send_log(p, LOG_EMERG,
 				 "Proxy %s reached the configured maximum connection limit. Please check the global 'maxconn' value.\n",
@@ -599,16 +630,14 @@
 			goto end;
 		}
 
-		/* increase the per-process number of cumulated connections */
-		if (!(l->options & LI_O_UNLIMITED)) {
-			count = update_freq_ctr(&global.conn_per_sec, 1);
-			HA_ATOMIC_UPDATE_MAX(&global.cps_max, count);
-			HA_ATOMIC_ADD(&actconn, 1);
-		}
-
-		count = HA_ATOMIC_ADD(&l->nbconn, 1);
-		if (l->counters)
-			HA_ATOMIC_UPDATE_MAX(&l->counters->conn_max, count);
+		/* past this point, l->accept() will automatically decrement
+		 * l->nbconn, feconn and actconn once done. Setting next_*conn=0
+		 * allows the error path not to rollback on nbconn. It's more
+		 * convenient than duplicating all exit labels.
+		 */
+		next_conn = 0;
+		next_feconn = 0;
+		next_actconn = 0;
 
 		ret = l->accept(l, cfd, &addr);
 		if (unlikely(ret <= 0)) {
@@ -623,12 +652,9 @@
 			goto transient_error;
 		}
 
-		if (l->nbconn >= l->maxconn) {
-			listener_full(l);
-			goto end;
-		}
-
-		/* increase the per-process number of cumulated connections */
+		/* increase the per-process number of cumulated sessions, this
+		 * may only be done once l->accept() has accepted the connection.
+		 */
 		if (!(l->options & LI_O_UNLIMITED)) {
 			count = update_freq_ctr(&global.sess_per_sec, 1);
 			HA_ATOMIC_UPDATE_MAX(&global.sps_max, count);
@@ -640,7 +666,7 @@
 		}
 #endif
 
-	} /* end of while (max_accept--) */
+	} /* end of for (max_accept--) */
 
 	/* we've exhausted max_accept, so there is no need to poll again */
  stop:
@@ -655,7 +681,28 @@
 	limit_listener(l, &global_listener_queue);
 	task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
  end:
-	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
+	if (next_conn)
+		HA_ATOMIC_SUB(&l->nbconn, 1);
+
+	if (p && next_feconn)
+		HA_ATOMIC_SUB(&p->feconn, 1);
+
+	if (next_actconn)
+		HA_ATOMIC_SUB(&actconn, 1);
+
+	if ((l->state == LI_FULL && l->nbconn < l->maxconn) ||
+	    (l->state == LI_LIMITED && ((!p || p->feconn < p->maxconn) && (actconn < global.maxconn)))) {
+		/* at least one thread has to this when quitting */
+		resume_listener(l);
+
+		/* Dequeues all of the listeners waiting for a resource */
+		if (!LIST_ISEMPTY(&global_listener_queue))
+			dequeue_all_listeners(&global_listener_queue);
+
+		if (!LIST_ISEMPTY(&p->listener_queue) &&
+		    (!p->fe_sps_lim || freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0) > 0))
+			dequeue_all_listeners(&p->listener_queue);
+	}
 }
 
 /* Notify the listener that a connection initiated from it was released. This
@@ -668,8 +715,11 @@
 
 	if (!(l->options & LI_O_UNLIMITED))
 		HA_ATOMIC_SUB(&actconn, 1);
+	if (fe)
+		HA_ATOMIC_SUB(&fe->feconn, 1);
 	HA_ATOMIC_SUB(&l->nbconn, 1);
-	if (l->state == LI_FULL)
+
+	if (l->state == LI_FULL || l->state == LI_LIMITED)
 		resume_listener(l);
 
 	/* Dequeues all of the listeners waiting for a resource */
@@ -957,7 +1007,7 @@
 
 	conf->bind_proc |= proc;
 	if (thread) {
-		for (i = 0; i < MAX_THREADS; i++)
+		for (i = 0; i < LONGBITS; i++)
 			if (!proc || (proc & (1UL << i)))
 				conf->bind_thread[i] |= thread;
 	}
@@ -1005,7 +1055,6 @@
 	sample_register_fetches(&smp_kws);
 	acl_register_keywords(&acl_kws);
 	bind_register_keywords(&bind_kws);
-	HA_SPIN_INIT(&lq_lock);
 }
 
 /*
diff -Nru haproxy-1.8.19/src/log.c haproxy-1.8.20/src/log.c
--- haproxy-1.8.19/src/log.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/log.c	2019-04-25 23:59:27.000000000 +0200
@@ -1380,11 +1380,15 @@
 /* Deinitialize log buffers used for syslog messages */
 void deinit_log_buffers()
 {
+	void *tmp_startup_logs;
+
 	free(logheader);
 	free(logheader_rfc5424);
 	free(logline);
 	free(logline_rfc5424);
-	free(startup_logs);
+	tmp_startup_logs = HA_ATOMIC_XCHG(&startup_logs, NULL);
+	free(tmp_startup_logs);
+
 	logheader         = NULL;
 	logheader_rfc5424 = NULL;
 	logline           = NULL;
@@ -1647,7 +1651,6 @@
 				break;
 
 			case LOG_FMT_TS: // %Ts
-				get_gmtime(s->logs.accept_date.tv_sec, &tm);
 				if (tmp->options & LOG_OPT_HEXA) {
 					iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)s->logs.accept_date.tv_sec);
 					if (iret < 0 || iret > dst + maxsize - tmplog)
diff -Nru haproxy-1.8.19/src/map.c haproxy-1.8.20/src/map.c
--- haproxy-1.8.19/src/map.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/map.c	2019-04-25 23:59:27.000000000 +0200
@@ -142,10 +142,10 @@
 	                            1, err, file, line))
 		return 0;
 
-	/* the maps of type IP have a string as defaultvalue. This
-	 * string canbe anipv4 or an ipv6, we must convert it.
+	/* the maps of type IP support a string as default value. This
+	 * string can be an ipv4 or an ipv6, we must convert it.
 	 */
-	if (desc->conv->out_type == SMP_T_ADDR) {
+	if (arg[1].type != ARGT_STOP && desc->conv->out_type == SMP_T_ADDR) {
 		struct sample_data data;
 		if (!map_parse_ip(arg[1].data.str.str, &data)) {
 			memprintf(err, "map: cannot parse default ip <%s>.", arg[1].data.str.str);
diff -Nru haproxy-1.8.19/src/peers.c haproxy-1.8.20/src/peers.c
--- haproxy-1.8.19/src/peers.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/peers.c	2019-04-25 23:59:27.000000000 +0200
@@ -172,7 +172,7 @@
 #define PEER_DWNGRD_MINOR_VER 0
 
 struct peers *cfg_peers = NULL;
-static void peer_session_forceshutdown(struct appctx *appctx);
+static void peer_session_forceshutdown(struct peer *peer);
 
 /* This function encode an uint64 to 'dynamic' length format.
    The encoded value is written at address *str, and the
@@ -493,14 +493,52 @@
 }
 
 /*
+ * Function to deinit connected peer
+ */
+void __peer_session_deinit(struct peer *peer)
+{
+	struct stream_interface *si;
+	struct stream *s;
+	struct peers *peers;
+
+	if (!peer->appctx)
+		return;
+
+	si = peer->appctx->owner;
+	if (!si)
+		return;
+
+	s = si_strm(si);
+	if (!s)
+		return;
+
+	peers = strm_fe(s)->parent;
+	if (!peers)
+		return;
+
+	/* Re-init current table pointers to force announcement on re-connect */
+	peer->remote_table = peer->last_local_table = NULL;
+	peer->appctx = NULL;
+	if (peer->flags & PEER_F_LEARN_ASSIGN) {
+		/* unassign current peer for learning */
+		peer->flags &= ~(PEER_F_LEARN_ASSIGN);
+		peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
+
+		/* reschedule a resync */
+		peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
+	}
+	/* reset teaching and learning flags to 0 */
+	peer->flags &= PEER_TEACH_RESET;
+	peer->flags &= PEER_LEARN_RESET;
+	task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
+}
+
+/*
  * Callback to release a session with a peer
  */
 static void peer_session_release(struct appctx *appctx)
 {
-	struct stream_interface *si = appctx->owner;
-	struct stream *s = si_strm(si);
 	struct peer *peer = appctx->ctx.peers.ptr;
-	struct peers *peers = strm_fe(s)->parent;
 
 	/* appctx->ctx.peers.ptr is not a peer session */
 	if (appctx->st0 < PEER_SESS_ST_SENDSUCCESS)
@@ -509,24 +547,9 @@
 	/* peer session identified */
 	if (peer) {
 		HA_SPIN_LOCK(PEER_LOCK, &peer->lock);
-		if (peer->appctx == appctx) {
-			/* Re-init current table pointers to force announcement on re-connect */
-			peer->remote_table = peer->last_local_table = NULL;
-			peer->appctx = NULL;
-			if (peer->flags & PEER_F_LEARN_ASSIGN) {
-				/* unassign current peer for learning */
-				peer->flags &= ~(PEER_F_LEARN_ASSIGN);
-				peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
-
-				/* reschedule a resync */
-				peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
-			}
-			/* reset teaching and learning flags to 0 */
-			peer->flags &= PEER_TEACH_RESET;
-			peer->flags &= PEER_LEARN_RESET;
-		}
+		if (peer->appctx == appctx)
+			__peer_session_deinit(peer);
 		HA_SPIN_UNLOCK(PEER_LOCK, &peer->lock);
-		task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
 	}
 }
 
@@ -704,7 +727,7 @@
 					 * for a while.
 					 */
 					curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
-					peer_session_forceshutdown(curpeer->appctx);
+					peer_session_forceshutdown(curpeer);
 				}
 				if (maj_ver != (unsigned int)-1 && min_ver != (unsigned int)-1) {
 					if (min_ver == PEER_DWNGRD_MINOR_VER) {
@@ -1832,11 +1855,14 @@
 	.release = peer_session_release,
 };
 
+
 /*
  * Use this function to force a close of a peer session
  */
-static void peer_session_forceshutdown(struct appctx *appctx)
+static void peer_session_forceshutdown(struct peer *peer)
 {
+	struct appctx *appctx = peer->appctx;
+
 	/* Note that the peer sessions which have just been created
 	 * (->st0 == PEER_SESS_ST_CONNECT) must not
 	 * be shutdown, if not, the TCP session will never be closed
@@ -1849,6 +1875,8 @@
 	if (appctx->applet != &peer_applet)
 		return;
 
+	__peer_session_deinit(peer);
+
 	appctx->st0 = PEER_SESS_ST_END;
 	appctx_wakeup(appctx);
 }
@@ -2094,8 +2122,7 @@
 				 */
 				ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
 				if (ps->appctx) {
-					peer_session_forceshutdown(ps->appctx);
-					ps->appctx = NULL;
+					peer_session_forceshutdown(ps);
 				}
 			}
 		}
diff -Nru haproxy-1.8.19/src/proto_http.c haproxy-1.8.20/src/proto_http.c
--- haproxy-1.8.19/src/proto_http.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/proto_http.c	2019-04-25 23:59:27.000000000 +0200
@@ -2717,12 +2717,14 @@
 			value->str[value->len] = '\0';
 
 			/* perform update */
+			HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
 			if (pat_ref_find_elt(ref, key->str) != NULL)
 				/* update entry if it exists */
 				pat_ref_set(ref, key->str, value->str, NULL);
 			else
 				/* insert a new entry */
 				pat_ref_add(ref, key->str, value->str, NULL);
+			HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
 
 			free_trash_chunk(key);
 			free_trash_chunk(value);
@@ -2978,8 +2980,10 @@
 
 			/* perform update */
 			/* check if the entry already exists */
+			HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
 			if (pat_ref_find_elt(ref, key->str) == NULL)
 				pat_ref_add(ref, key->str, NULL, NULL);
+			HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
 
 			free_trash_chunk(key);
 			break;
@@ -6150,6 +6154,7 @@
 		if (!buffer_pending(res->buf)) {
 			if (!(s->flags & SF_ERR_MASK))
 				s->flags |= SF_ERR_SRVCL;
+			HA_ATOMIC_ADD(&sess->fe->fe_counters.srv_aborts, 1);
 			HA_ATOMIC_ADD(&s->be->be_counters.srv_aborts, 1);
 			if (objt_server(s->target))
 				HA_ATOMIC_ADD(&objt_server(s->target)->counters.srv_aborts, 1);
@@ -9446,6 +9451,8 @@
 /************************************************************************/
 /*        The code below is dedicated to ACL parsing and matching       */
 /************************************************************************/
+#define SMP_REQ_CHN(smp) (smp->strm ? &smp->strm->req : NULL)
+#define SMP_RES_CHN(smp) (smp->strm ? &smp->strm->res : NULL)
 
 
 /* This function ensures that the prerequisites for an L7 fetch are ready,
@@ -9462,7 +9469,7 @@
  *   1 if an HTTP message is ready
  */
 int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt,
-                  const struct arg *args, struct sample *smp, int req_vol)
+                  const struct channel *chn, struct sample *smp, int req_vol)
 {
 	struct http_txn *txn;
 	struct http_msg *msg;
@@ -9471,7 +9478,7 @@
 	 * initialization (eg: tcp-request connection), so this function is the
 	 * one responsible for guarding against this case for all HTTP users.
 	 */
-	if (!s)
+	if (!s || !chn)
 		return 0;
 
 	if (!s->txn) {
@@ -9480,78 +9487,78 @@
 		http_init_txn(s);
 	}
 	txn = s->txn;
-	msg = &txn->req;
 
-	/* Check for a dependency on a request */
 	smp->data.type = SMP_T_BOOL;
 
-	if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-		/* If the buffer does not leave enough free space at the end,
-		 * we must first realign it.
-		 */
-		if (s->req.buf->p > s->req.buf->data &&
-		    s->req.buf->i + s->req.buf->p > s->req.buf->data + s->req.buf->size - global.tune.maxrewrite)
-			buffer_slow_realign(s->req.buf);
+	if (chn->flags & CF_ISRESP) {
+		/* Check for a dependency on a response */
+		if (txn->rsp.msg_state < HTTP_MSG_BODY) {
+			smp->flags |= SMP_F_MAY_CHANGE;
+			return 0;
+		}
+		goto end;
+	}
 
-		if (unlikely(txn->req.msg_state < HTTP_MSG_BODY)) {
-			if (msg->msg_state == HTTP_MSG_ERROR)
-				return 0;
+	/* Check for a dependency on a request */
+	msg = &txn->req;
 
-			/* Try to decode HTTP request */
-			if (likely(msg->next < s->req.buf->i))
-				http_msg_analyzer(msg, &txn->hdr_idx);
-
-			/* Still no valid request ? */
-			if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
-				if ((msg->msg_state == HTTP_MSG_ERROR) ||
-				    buffer_full(s->req.buf, global.tune.maxrewrite)) {
-					return 0;
-				}
-				/* wait for final state */
-				smp->flags |= SMP_F_MAY_CHANGE;
-				return 0;
-			}
+	if (req_vol && (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
+		return 0;  /* data might have moved and indexes changed */
+	}
 
-			/* OK we just got a valid HTTP request. We have some minor
-			 * preparation to perform so that further checks can rely
-			 * on HTTP tests.
-			 */
+	/* If the buffer does not leave enough free space at the end, we must
+	 * first realign it.
+	 */
+	if (chn->buf->p > chn->buf->data &&
+	    chn->buf->i + chn->buf->p > chn->buf->data + chn->buf->size - global.tune.maxrewrite)
+		buffer_slow_realign(chn->buf);
 
-			/* If the request was parsed but was too large, we must absolutely
-			 * return an error so that it is not processed. At the moment this
-			 * cannot happen, but if the parsers are to change in the future,
-			 * we want this check to be maintained.
-			 */
-			if (unlikely(s->req.buf->i + s->req.buf->p >
-				     s->req.buf->data + s->req.buf->size - global.tune.maxrewrite)) {
-				msg->err_state = msg->msg_state;
-				msg->msg_state = HTTP_MSG_ERROR;
-				smp->data.u.sint = 1;
-				return 1;
-			}
+	if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
+		if (msg->msg_state == HTTP_MSG_ERROR)
+			return 0;
 
-			txn->meth = find_http_meth(msg->chn->buf->p, msg->sl.rq.m_l);
-			if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
-				s->flags |= SF_REDIRECTABLE;
+		/* Try to decode HTTP request */
+		if (likely(msg->next < chn->buf->i))
+			http_msg_analyzer(msg, &txn->hdr_idx);
 
-			if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn))
+		/* Still no valid request ? */
+		if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
+			if ((msg->msg_state == HTTP_MSG_ERROR) ||
+			    buffer_full(chn->buf, global.tune.maxrewrite)) {
 				return 0;
+			}
+			/* wait for final state */
+			smp->flags |= SMP_F_MAY_CHANGE;
+			return 0;
 		}
 
-		if (req_vol && txn->rsp.msg_state != HTTP_MSG_RPBEFORE) {
-			return 0;  /* data might have moved and indexes changed */
+		/* OK we just got a valid HTTP message. We have some minor
+		 * preparation to perform so that further checks can rely
+		 * on HTTP tests.
+		 */
+
+		/* If the message was parsed but was too large, we must absolutely
+		 * return an error so that it is not processed. At the moment this
+		 * cannot happen, but if the parsers are to change in the future,
+		 * we want this check to be maintained.
+		 */
+		if (unlikely(chn->buf->i + chn->buf->p >
+			     chn->buf->data + chn->buf->size - global.tune.maxrewrite)) {
+			msg->err_state = msg->msg_state;
+			msg->msg_state = HTTP_MSG_ERROR;
+			smp->data.u.sint = 1;
+			return 1;
 		}
 
-		/* otherwise everything's ready for the request */
-	}
-	else {
-		/* Check for a dependency on a response */
-		if (txn->rsp.msg_state < HTTP_MSG_BODY) {
-			smp->flags |= SMP_F_MAY_CHANGE;
+		txn->meth = find_http_meth(chn->buf->p, msg->sl.rq.m_l);
+		if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
+			s->flags |= SF_REDIRECTABLE;
+
+		if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn))
 			return 0;
-		}
 	}
 
+  end:
 	/* everything's OK */
 	smp->data.u.sint = 1;
 	return 1;
@@ -9591,19 +9598,21 @@
 static int
 smp_fetch_meth(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	int meth;
 	struct http_txn *txn;
 
-	CHECK_HTTP_MESSAGE_FIRST_PERM();
+	CHECK_HTTP_MESSAGE_FIRST_PERM(chn);
 
 	txn = smp->strm->txn;
 	meth = txn->meth;
 	smp->data.type = SMP_T_METH;
 	smp->data.u.meth.meth = meth;
 	if (meth == HTTP_METH_OTHER) {
-		if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
+		if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
 			/* ensure the indexes are not affected */
 			return 0;
+		}
 		smp->flags |= SMP_F_CONST;
 		smp->data.u.meth.str.len = txn->req.sl.rq.m_l;
 		smp->data.u.meth.str.str = txn->req.chn->buf->p;
@@ -9645,15 +9654,16 @@
 static int
 smp_fetch_rqver(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	char *ptr;
 	int len;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
 	len = txn->req.sl.rq.v_l;
-	ptr = txn->req.chn->buf->p + txn->req.sl.rq.v;
+	ptr = chn->buf->p + txn->req.sl.rq.v;
 
 	while ((len-- > 0) && (*ptr++ != '/'));
 	if (len <= 0)
@@ -9670,18 +9680,17 @@
 static int
 smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_RES_CHN(smp);
 	struct http_txn *txn;
 	char *ptr;
 	int len;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
-	if (txn->rsp.msg_state < HTTP_MSG_BODY)
-		return 0;
 
 	len = txn->rsp.sl.st.v_l;
-	ptr = txn->rsp.chn->buf->p;
+	ptr = chn->buf->p;
 
 	while ((len-- > 0) && (*ptr++ != '/'));
 	if (len <= 0)
@@ -9699,18 +9708,19 @@
 static int
 smp_fetch_stcode(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_RES_CHN(smp);
 	struct http_txn *txn;
 	char *ptr;
 	int len;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
 	if (txn->rsp.msg_state < HTTP_MSG_BODY)
 		return 0;
 
 	len = txn->rsp.sl.st.c_l;
-	ptr = txn->rsp.chn->buf->p + txn->rsp.sl.st.c;
+	ptr = chn->buf->p + txn->rsp.sl.st.c;
 
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = __strl2ui(ptr, len);
@@ -9745,20 +9755,21 @@
 static int
 smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_msg *msg;
 	struct hdr_idx *idx;
 	struct http_txn *txn;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
 	idx = &txn->hdr_idx;
 	msg = &txn->req;
 
 	smp->data.type = SMP_T_STR;
-	smp->data.u.str.str = msg->chn->buf->p + hdr_idx_first_pos(idx);
+	smp->data.u.str.str = chn->buf->p + hdr_idx_first_pos(idx);
 	smp->data.u.str.len = msg->eoh - hdr_idx_first_pos(idx) + 1 +
-	                      (msg->chn->buf->p[msg->eoh] == '\r');
+	                      (chn->buf->p[msg->eoh] == '\r');
 
 	return 1;
 }
@@ -9779,7 +9790,7 @@
 static int
 smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct http_msg *msg;
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct chunk *temp;
 	struct hdr_idx *idx;
 	const char *cur_ptr, *cur_next, *p;
@@ -9792,7 +9803,7 @@
 	char *buf;
 	char *end;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	temp = get_trash_chunk();
 	buf = temp->str;
@@ -9800,11 +9811,10 @@
 
 	txn = smp->strm->txn;
 	idx = &txn->hdr_idx;
-	msg = &txn->req;
 
 	/* Build array of headers. */
 	old_idx = 0;
-	cur_next = msg->chn->buf->p + hdr_idx_first_pos(idx);
+	cur_next = chn->buf->p + hdr_idx_first_pos(idx);
 	while (1) {
 		cur_idx = idx->v[old_idx].next;
 		if (!cur_idx)
@@ -9879,25 +9889,23 @@
 static int
 smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_msg *msg;
 	unsigned long len;
 	unsigned long block1;
 	char *body;
 	struct chunk *temp;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
-	if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-		msg = &smp->strm->txn->req;
-	else
-		msg = &smp->strm->txn->rsp;
+	msg = &smp->strm->txn->req;
 
 	len  = http_body_bytes(msg);
-	body = b_ptr(msg->chn->buf, -http_data_rewind(msg));
+	body = b_ptr(chn->buf, -http_data_rewind(msg));
 
 	block1 = len;
-	if (block1 > msg->chn->buf->data + msg->chn->buf->size - body)
-		block1 = msg->chn->buf->data + msg->chn->buf->size - body;
+	if (block1 > chn->buf->data + chn->buf->size - body)
+		block1 = chn->buf->data + chn->buf->size - body;
 
 	if (block1 == len) {
 		/* buffer is not wrapped (or empty) */
@@ -9910,7 +9918,7 @@
 		/* buffer is wrapped, we need to defragment it */
 		temp = get_trash_chunk();
 		memcpy(temp->str, body, block1);
-		memcpy(temp->str + block1, msg->chn->buf->data, len - block1);
+		memcpy(temp->str + block1, chn->buf->data, len - block1);
 		smp->data.type = SMP_T_BIN;
 		smp->data.u.str.str = temp->str;
 		smp->data.u.str.len = len;
@@ -9926,15 +9934,12 @@
 static int
 smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_msg *msg;
 
-	CHECK_HTTP_MESSAGE_FIRST();
-
-	if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-		msg = &smp->strm->txn->req;
-	else
-		msg = &smp->strm->txn->rsp;
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
+	msg = &smp->strm->txn->req;
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = http_body_bytes(msg);
 
@@ -9950,15 +9955,12 @@
 static int
 smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_msg *msg;
 
-	CHECK_HTTP_MESSAGE_FIRST();
-
-	if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-		msg = &smp->strm->txn->req;
-	else
-		msg = &smp->strm->txn->rsp;
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
+	msg = &smp->strm->txn->req;
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = msg->body_len;
 
@@ -9971,14 +9973,15 @@
 static int
 smp_fetch_url(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
 	smp->data.type = SMP_T_STR;
 	smp->data.u.str.len = txn->req.sl.rq.u_l;
-	smp->data.u.str.str = txn->req.chn->buf->p + txn->req.sl.rq.u;
+	smp->data.u.str.str = chn->buf->p + txn->req.sl.rq.u;
 	smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
 	return 1;
 }
@@ -9986,13 +9989,14 @@
 static int
 smp_fetch_url_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	struct sockaddr_storage addr;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
-	url2sa(txn->req.chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
+	url2sa(chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
 	if (((struct sockaddr_in *)&addr)->sin_family != AF_INET)
 		return 0;
 
@@ -10005,13 +10009,14 @@
 static int
 smp_fetch_url_port(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	struct sockaddr_storage addr;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
-	url2sa(txn->req.chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
+	url2sa(chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
 	if (((struct sockaddr_in *)&addr)->sin_family != AF_INET)
 		return 0;
 
@@ -10031,6 +10036,8 @@
 static int
 smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.fhdr, res.fhdr */
+	struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct hdr_idx *idx;
 	struct hdr_ctx *ctx = smp->ctx.a[0];
 	const struct http_msg *msg;
@@ -10055,10 +10062,9 @@
 			occ = args[1].data.sint;
 	}
 
-	CHECK_HTTP_MESSAGE_FIRST();
-
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 	idx = &smp->strm->txn->hdr_idx;
-	msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
+	msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp);
 
 	if (ctx && !(smp->flags & SMP_F_NOT_LAST))
 		/* search for header from the beginning */
@@ -10088,9 +10094,10 @@
 static int
 smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.fhdr_cnt, res.fhdr_cnt */
+	struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct hdr_idx *idx;
 	struct hdr_ctx ctx;
-	const struct http_msg *msg;
 	int cnt;
 	const char *name = NULL;
 	int len = 0;
@@ -10100,14 +10107,12 @@
 		len = args->data.str.len;
 	}
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	idx = &smp->strm->txn->hdr_idx;
-	msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
-
 	ctx.idx = 0;
 	cnt = 0;
-	while (http_find_full_header2(name, len, msg->chn->buf->p, idx, &ctx))
+	while (http_find_full_header2(name, len, chn->buf->p, idx, &ctx))
 		cnt++;
 
 	smp->data.type = SMP_T_SINT;
@@ -10119,24 +10124,24 @@
 static int
 smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.hdr_names, res.hdr_names */
+	struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct hdr_idx *idx;
 	struct hdr_ctx ctx;
-	const struct http_msg *msg;
 	struct chunk *temp;
 	char del = ',';
 
 	if (args && args->type == ARGT_STR)
 		del = *args[0].data.str.str;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	idx = &smp->strm->txn->hdr_idx;
-	msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
 
 	temp = get_trash_chunk();
 
 	ctx.idx = 0;
-	while (http_find_next_header(msg->chn->buf->p, idx, &ctx)) {
+	while (http_find_next_header(chn->buf->p, idx, &ctx)) {
 		if (temp->len)
 			temp->str[temp->len++] = del;
 		memcpy(temp->str + temp->len, ctx.line, ctx.del);
@@ -10159,6 +10164,8 @@
 static int
 smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.hdr / hdr, res.hdr / shdr */
+	struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct hdr_idx *idx;
 	struct hdr_ctx *ctx = smp->ctx.a[0];
 	const struct http_msg *msg;
@@ -10183,10 +10190,10 @@
 			occ = args[1].data.sint;
 	}
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	idx = &smp->strm->txn->hdr_idx;
-	msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
+	msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp);
 
 	if (ctx && !(smp->flags & SMP_F_NOT_LAST))
 		/* search for header from the beginning */
@@ -10215,9 +10222,10 @@
 static int
 smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.hdr_cnt / hdr_cnt, res.hdr_cnt / shdr_cnt */
+	struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct hdr_idx *idx;
 	struct hdr_ctx ctx;
-	const struct http_msg *msg;
 	int cnt;
 	const char *name = NULL;
 	int len = 0;
@@ -10227,14 +10235,13 @@
 		len = args->data.str.len;
 	}
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	idx = &smp->strm->txn->hdr_idx;
-	msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
 
 	ctx.idx = 0;
 	cnt = 0;
-	while (http_find_header2(name, len, msg->chn->buf->p, idx, &ctx))
+	while (http_find_header2(name, len, chn->buf->p, idx, &ctx))
 		cnt++;
 
 	smp->data.type = SMP_T_SINT;
@@ -10299,13 +10306,14 @@
 static int
 smp_fetch_path(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	char *ptr, *end;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
-	end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+	end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 	ptr = http_get_path(txn);
 	if (!ptr)
 		return 0;
@@ -10332,16 +10340,17 @@
 static int
 smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	char *ptr, *end, *beg;
 	struct hdr_ctx ctx;
 	struct chunk *temp;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
 	ctx.idx = 0;
-	if (!http_find_header2("Host", 4, txn->req.chn->buf->p, &txn->hdr_idx, &ctx) || !ctx.vlen)
+	if (!http_find_header2("Host", 4, chn->buf->p, &txn->hdr_idx, &ctx) || !ctx.vlen)
 		return smp_fetch_path(args, smp, kw, private);
 
 	/* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
@@ -10352,7 +10361,7 @@
 	smp->data.u.str.len = ctx.vlen;
 
 	/* now retrieve the path */
-	end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+	end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 	beg = http_get_path(txn);
 	if (!beg)
 		beg = end;
@@ -10379,17 +10388,18 @@
 int
 smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	struct hdr_ctx ctx;
 	unsigned int hash = 0;
 	char *ptr, *beg, *end;
 	int len;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
 	ctx.idx = 0;
-	if (http_find_header2("Host", 4, txn->req.chn->buf->p, &txn->hdr_idx, &ctx)) {
+	if (http_find_header2("Host", 4, chn->buf->p, &txn->hdr_idx, &ctx)) {
 		/* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
 		ptr = ctx.line + ctx.val;
 		len = ctx.vlen;
@@ -10398,7 +10408,7 @@
 	}
 
 	/* now retrieve the path */
-	end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+	end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 	beg = http_get_path(txn);
 	if (!beg)
 		beg = end;
@@ -10465,13 +10475,14 @@
 static int
 smp_fetch_query(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	char *ptr, *end;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
-	ptr = txn->req.chn->buf->p + txn->req.sl.rq.u;
+	ptr = chn->buf->p + txn->req.sl.rq.u;
 	end = ptr + txn->req.sl.rq.u_l;
 
 	/* look up the '?' */
@@ -10490,11 +10501,13 @@
 static int
 smp_fetch_proto_http(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
+
 	/* Note: hdr_idx.v cannot be NULL in this ACL because the ACL is tagged
 	 * as a layer7 ACL, which involves automatic allocation of hdr_idx.
 	 */
 
-	CHECK_HTTP_MESSAGE_FIRST_PERM();
+	CHECK_HTTP_MESSAGE_FIRST_PERM(chn);
 
 	smp->data.type = SMP_T_BOOL;
 	smp->data.u.sint = 1;
@@ -10514,11 +10527,12 @@
 static int
 smp_fetch_http_auth(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 
 	if (!args || args->type != ARGT_USR)
 		return 0;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	if (!get_http_auth(smp->strm))
 		return 0;
@@ -10533,10 +10547,12 @@
 static int
 smp_fetch_http_auth_grp(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
+
 	if (!args || args->type != ARGT_USR)
 		return 0;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	if (!get_http_auth(smp->strm))
 		return 0;
@@ -10827,10 +10843,10 @@
  */
 int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct http_txn *txn;
+	/* possible keywords: req.cookie / cookie / cook, res.cookie / scook / set-cookie */
+	struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct hdr_idx *idx;
 	struct hdr_ctx *ctx = smp->ctx.a[2];
-	const struct http_msg *msg;
 	const char *hdr_name;
 	int hdr_name_len;
 	char *sol;
@@ -10847,17 +10863,14 @@
 		smp->ctx.a[2] = ctx;
 	}
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
-	txn = smp->strm->txn;
 	idx = &smp->strm->txn->hdr_idx;
 
-	if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-		msg = &txn->req;
+	if (!(chn->flags & CF_ISRESP)) {
 		hdr_name = "Cookie";
 		hdr_name_len = 6;
 	} else {
-		msg = &txn->rsp;
 		hdr_name = "Set-Cookie";
 		hdr_name_len = 10;
 	}
@@ -10871,7 +10884,7 @@
 	 * next one.
 	 */
 
-	sol = msg->chn->buf->p;
+	sol = chn->buf->p;
 	if (!(smp->flags & SMP_F_NOT_LAST)) {
 		/* search for the header from the beginning, we must first initialize
 		 * the search parameters.
@@ -10928,10 +10941,10 @@
 static int
 smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct http_txn *txn;
+	/* possible keywords: req.cook_cnt / cook_cnt, res.cook_cnt / scook_cnt */
+	struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct hdr_idx *idx;
 	struct hdr_ctx ctx;
-	const struct http_msg *msg;
 	const char *hdr_name;
 	int hdr_name_len;
 	int cnt;
@@ -10941,22 +10954,19 @@
 	if (!args || args->type != ARGT_STR)
 		return 0;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
-	txn = smp->strm->txn;
 	idx = &smp->strm->txn->hdr_idx;
 
-	if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-		msg = &txn->req;
+	if (!(chn->flags & CF_ISRESP)) {
 		hdr_name = "Cookie";
 		hdr_name_len = 6;
 	} else {
-		msg = &txn->rsp;
 		hdr_name = "Set-Cookie";
 		hdr_name_len = 10;
 	}
 
-	sol = msg->chn->buf->p;
+	sol = chn->buf->p;
 	val_end = val_beg = NULL;
 	ctx.idx = 0;
 	cnt = 0;
@@ -11286,6 +11296,7 @@
 static int
 smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_msg *msg;
 	char delim = '?';
 	const char *name;
@@ -11307,16 +11318,16 @@
 		delim = *args[1].data.str.str;
 
 	if (!smp->ctx.a[0]) { // first call, find the query string
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		msg = &smp->strm->txn->req;
 
-		smp->ctx.a[0] = find_param_list(msg->chn->buf->p + msg->sl.rq.u,
+		smp->ctx.a[0] = find_param_list(chn->buf->p + msg->sl.rq.u,
 		                                msg->sl.rq.u_l, delim);
 		if (!smp->ctx.a[0])
 			return 0;
 
-		smp->ctx.a[1] = msg->chn->buf->p + msg->sl.rq.u + msg->sl.rq.u_l;
+		smp->ctx.a[1] = chn->buf->p + msg->sl.rq.u + msg->sl.rq.u_l;
 
 		/* Assume that the context is filled with NULL pointer
 		 * before the first call.
@@ -11338,6 +11349,7 @@
 static int
 smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_msg *msg;
 	unsigned long len;
 	unsigned long block1;
@@ -11356,19 +11368,15 @@
 	}
 
 	if (!smp->ctx.a[0]) { // first call, find the query string
-		CHECK_HTTP_MESSAGE_FIRST();
-
-		if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-			msg = &smp->strm->txn->req;
-		else
-			msg = &smp->strm->txn->rsp;
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
+		msg = &smp->strm->txn->req;
 		len  = http_body_bytes(msg);
-		body = b_ptr(msg->chn->buf, -http_data_rewind(msg));
+		body = b_ptr(chn->buf, -http_data_rewind(msg));
 
 		block1 = len;
-		if (block1 > msg->chn->buf->data + msg->chn->buf->size - body)
-			block1 = msg->chn->buf->data + msg->chn->buf->size - body;
+		if (block1 > chn->buf->data + chn->buf->size - body)
+			block1 = chn->buf->data + chn->buf->size - body;
 
 		if (block1 == len) {
 			/* buffer is not wrapped (or empty) */
@@ -11385,8 +11393,8 @@
 			/* buffer is wrapped, we need to defragment it */
 			smp->ctx.a[0] = body;
 			smp->ctx.a[1] = body + block1;
-			smp->ctx.a[2] = msg->chn->buf->data;
-			smp->ctx.a[3] = msg->chn->buf->data + ( len - block1 );
+			smp->ctx.a[2] = chn->buf->data;
+			smp->ctx.a[3] = chn->buf->data + ( len - block1 );
 		}
 	}
 	return smp_fetch_param('&', name, name_len, args, smp, kw, private);
@@ -11421,17 +11429,18 @@
 static int
 smp_fetch_url32(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	struct hdr_ctx ctx;
 	unsigned int hash = 0;
 	char *ptr, *beg, *end;
 	int len;
 
-	CHECK_HTTP_MESSAGE_FIRST();
+	CHECK_HTTP_MESSAGE_FIRST(chn);
 
 	txn = smp->strm->txn;
 	ctx.idx = 0;
-	if (http_find_header2("Host", 4, txn->req.chn->buf->p, &txn->hdr_idx, &ctx)) {
+	if (http_find_header2("Host", 4, chn->buf->p, &txn->hdr_idx, &ctx)) {
 		/* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
 		ptr = ctx.line + ctx.val;
 		len = ctx.vlen;
@@ -11440,7 +11449,7 @@
 	}
 
 	/* now retrieve the path */
-	end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+	end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 	beg = http_get_path(txn);
 	if (!beg)
 		beg = end;
diff -Nru haproxy-1.8.19/src/session.c haproxy-1.8.20/src/session.c
--- haproxy-1.8.19/src/session.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/session.c	2019-04-25 23:59:27.000000000 +0200
@@ -53,10 +53,6 @@
 		vars_init(&sess->vars, SCOPE_SESS);
 		sess->task = NULL;
 		sess->t_handshake = -1; /* handshake not done yet */
-		HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.conn_max,
-				     HA_ATOMIC_ADD(&fe->feconn, 1));
-		if (li)
-			proxy_inc_fe_conn_ctr(li, fe);
 		HA_ATOMIC_ADD(&totalconn, 1);
 		HA_ATOMIC_ADD(&jobs, 1);
 	}
@@ -65,7 +61,6 @@
 
 void session_free(struct session *sess)
 {
-	HA_ATOMIC_SUB(&sess->fe->feconn, 1);
 	if (sess->listener)
 		listener_release(sess->listener);
 	session_store_counters(sess);
diff -Nru haproxy-1.8.19/src/sha1.c haproxy-1.8.20/src/sha1.c
--- haproxy-1.8.19/src/sha1.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/sha1.c	2019-04-25 23:59:27.000000000 +0200
@@ -26,7 +26,7 @@
 
 /* this is only to get definitions for memcpy(), ntohl() and htonl() */
 #include <string.h>
-#include <stdint.h>
+#include <inttypes.h>
 #include <arpa/inet.h>
 
 #include <import/sha1.h>
diff -Nru haproxy-1.8.19/src/ssl_sock.c haproxy-1.8.20/src/ssl_sock.c
--- haproxy-1.8.19/src/ssl_sock.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/ssl_sock.c	2019-04-25 23:59:27.000000000 +0200
@@ -4696,7 +4696,7 @@
 
 #if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
 	if (srv->ssl_ctx.ciphersuites &&
-		!SSL_CTX_set_cipher_list(srv->ssl_ctx.ctx, srv->ssl_ctx.ciphersuites)) {
+		!SSL_CTX_set_ciphersuites(srv->ssl_ctx.ctx, srv->ssl_ctx.ciphersuites)) {
 		ha_alert("Proxy '%s', server '%s' [%s:%d] : unable to set TLS 1.3 cipher suites to '%s'.\n",
 			 curproxy->id, srv->id,
 			 srv->conf.file, srv->conf.line, srv->ssl_ctx.ciphersuites);
@@ -7418,7 +7418,7 @@
 
 static int ssl_bind_parse_tls_method_minmax(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
-#if (OPENSSL_VERSION_NUMBER < 0x10101000L) || !defined(OPENSSL_IS_BORINGSSL)
+#if (OPENSSL_VERSION_NUMBER < 0x10101000L) && !defined(OPENSSL_IS_BORINGSSL)
 	ha_warning("crt-list: ssl-min-ver and ssl-max-ver are not supported with this Openssl version (skipped).\n");
 #endif
 	return parse_tls_method_minmax(args, cur_arg, &conf->ssl_methods, err);
diff -Nru haproxy-1.8.19/src/standard.c haproxy-1.8.20/src/standard.c
--- haproxy-1.8.19/src/standard.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/standard.c	2019-04-25 23:59:27.000000000 +0200
@@ -3402,12 +3402,14 @@
 		return NULL;
 
 	do {
+		char buf1;
+
 		/* vsnprintf() will return the required length even when the
 		 * target buffer is NULL. We do this in a loop just in case
 		 * intermediate evaluations get wrong.
 		 */
 		va_copy(args, orig_args);
-		needed = vsnprintf(ret, allocated, format, args);
+		needed = vsnprintf(ret ? ret : &buf1, allocated, format, args);
 		va_end(args);
 		if (needed < allocated) {
 			/* Note: on Solaris 8, the first iteration always
diff -Nru haproxy-1.8.19/src/stats.c haproxy-1.8.20/src/stats.c
--- haproxy-1.8.19/src/stats.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/stats.c	2019-04-25 23:59:27.000000000 +0200
@@ -2441,6 +2441,7 @@
 			              "Action not processed because of invalid parameters."
 			              "<ul>"
 			              "<li>The action is maybe unknown.</li>"
+				      "<li>Invalid key parameter (empty or too long).</li>"
 			              "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
 			              "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
 			              "</ul>"
@@ -2634,17 +2635,20 @@
 	int reql;
 
 	temp = get_trash_chunk();
-	if (temp->size < s->txn->req.body_len) {
-		/* too large request */
-		appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
-		goto out;
-	}
 
+	/* we need more data */
+	if (s->txn->req.msg_state < HTTP_MSG_DONE) {
+		/* check if we can receive more */
+		if (buffer_total_space(s->req.buf) <= global.tune.maxrewrite) {
+			appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
+			goto out;
+		}
+		goto wait;
+	}
 	reql = co_getblk(si_oc(si), temp->str, s->txn->req.body_len, s->txn->req.eoh + 2);
 	if (reql <= 0) {
-		/* we need more data */
-		appctx->ctx.stats.st_code = STAT_STATUS_NONE;
-		return 0;
+		appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
+		goto out;
 	}
 
 	first_param = temp->str;
@@ -2673,7 +2677,7 @@
 				strncpy(key, cur_param + poffset, plen);
 				key[plen - 1] = '\0';
 			} else {
-				appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
+				appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
 				goto out;
 			}
 
@@ -2929,6 +2933,9 @@
 	}
  out:
 	return 1;
+ wait:
+	appctx->ctx.stats.st_code = STAT_STATUS_NONE;
+	return 0;
 }
 
 
diff -Nru haproxy-1.8.19/src/xxhash.c haproxy-1.8.20/src/xxhash.c
--- haproxy-1.8.19/src/xxhash.c	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/src/xxhash.c	2019-04-25 23:59:27.000000000 +0200
@@ -98,7 +98,7 @@
 // Basic Types
 //**************************************
 #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99
-# include <stdint.h>
+# include <inttypes.h>
 typedef uint8_t  BYTE;
 typedef uint16_t U16;
 typedef uint32_t U32;
diff -Nru haproxy-1.8.19/VERDATE haproxy-1.8.20/VERDATE
--- haproxy-1.8.19/VERDATE	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/VERDATE	2019-04-25 23:59:27.000000000 +0200
@@ -1,2 +1,2 @@
 $Format:%ci$
-2019/02/11
+2019/04/25
diff -Nru haproxy-1.8.19/VERSION haproxy-1.8.20/VERSION
--- haproxy-1.8.19/VERSION	2019-02-11 14:16:19.000000000 +0100
+++ haproxy-1.8.20/VERSION	2019-04-25 23:59:27.000000000 +0200
@@ -1 +1 @@
-1.8.19
+1.8.20

Reply to: