Merge tag 'v2.0.17' into dev-2.0

HAProxy 2.0.17
diff --git a/CHANGELOG b/CHANGELOG
index 25a6f0c..73c734f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,365 @@
 ChangeLog :
 ===========
 
+2020/07/31 : 2.0.17
+    - BUILD: ebtree: fix build on libmusl after recent introduction of eb_memcmp()
+    - REGEST: Add reg tests about error files
+    - BUG/MINOR: threads: Don't forget to init each thread toremove_lock.
+    - MINOR: pools: increase MAX_BASE_POOLS to 64
+    - BUILD: thread: add parenthesis around values of locking macros
+    - BUG/MINOR: cfgparse: don't increment linenum on incomplete lines
+    - BUG/MEDIUM: resolve: fix init resolving for ring and peers section.
+    - BUG/MEDIUM: mux-h2: Emit an error if the response chunk formatting is incomplete
+    - BUG/MAJOR: dns: Make the do-resolve action thread-safe
+    - BUG/MEDIUM: dns: Release answer items when a DNS resolution is freed
+    - BUG/MEDIUM: mux-h1: Wakeup the H1C in h1_rcv_buf() if more data are expected
+    - BUG/MEDIUM: mux-h1: Disable the splicing when nothing is received
+    - BUG/MINOR: debug: Don't dump the lua stack if it is not initialized
+    - MEDIUM: lua: Add support for the Lua 5.4
+    - BUG/MEDIUM: dns: Don't yield in do-resolve action on a final evaluation
+    - BUG/MINOR: tcp-rules: Set the inspect-delay when a tcp-response action yields
+    - MINOR: connection: Preinstall the mux for non-ssl connect
+    - MINOR: stream-int: Be sure to have a mux to do sends and receives
+    - SCRIPTS: announce-release: add the link to the wiki in the announce messages
+
+2020/07/17 : 2.0.16
+    - MINOR: http: Add 410 to http-request deny
+    - MINOR: http: Add 404 to http-request deny
+    - BUG/MINOR: tcp-rules: tcp-response must check the buffer's fullness
+    - BUG/MEDIUM: ebtree: use a byte-per-byte memcmp() to compare memory blocks
+    - BUG/MINOR: spoe: add missing key length check before checking key names
+    - BUG/MINOR: cli: allow space escaping on the CLI
+    - BUG/MINOR: mworker/cli: fix the escaping in the master CLI
+    - BUG/MINOR: mworker/cli: fix semicolon escaping in master CLI
+    - REGTEST: http-rules: test spaces in ACLs
+    - REGTEST: http-rules: test spaces in ACLs with master CLI
+    - MEDIUM: map: make the "clear map" operation yield
+    - BUG/MINOR: systemd: Wait for network to be online
+    - REGTEST: Add a simple script to tests errorfile directives in proxy sections
+    - BUG/MINOR: spoe: correction of setting bits for analyzer
+    - BUG/MINOR: http_ana: clarify connection pointer check on L7 retry
+    - MINOR: spoe: Don't systematically create new applets if processing rate is low
+    - REGTEST: ssl: tests the ssl_f_* sample fetches
+    - REGTEST: ssl: add some ssl_c_* sample fetches test
+    - BUG/MEDIUM: fetch: Fix hdr_ip misparsing IPv4 addresses due to missing NUL
+    - MINOR: cli: make "show sess" stop at the last known session
+    - DOC: ssl: add "allow-0rtt" and "ciphersuites" in crt-list
+    - BUG/MEDIUM: pattern: Add a trailing \0 to match strings only if possible
+    - BUG/MINOR: proxy: fix dump_server_state()'s misuse of the trash
+    - BUG/MINOR: proxy: always initialize the trash in show servers state
+    - DOC: configuration: add missing index entries for tune.pool-{low,high}-fd-ratio
+    - DOC: configuration: fix alphabetical ordering for tune.pool-{high,low}-fd-ratio
+    - BUG/MINOR: http_act: don't check capture id in backend (2)
+    - BUG/MINOR: mux-h1: Fix the splicing in TUNNEL mode
+    - BUG/MINOR: mux-h1: Don't read data from a pipe if the mux is unable to receive
+    - BUG/MINOR: mux-h1: Disable splicing only if input data was processed
+    - BUG/MEDIUM: mux-h1: Disable splicing for the conn-stream if read0 is received
+    - BUG/MEDIUM: mux-h1: Subscribe rather than waking up in h1_rcv_buf()
+    - MINOR: connection: move the CO_FL_WAIT_ROOM cleanup to the reader only
+    - BUG/MEDIUM: connection: Continue to recv data to a pipe when the FD is not ready
+    - BUG/MINOR: backend: Remove CO_FL_SESS_IDLE if a client remains on the last server
+    - MINOR: http: Add support for http 413 status
+    - BUG/MAJOR: stream: Mark the server address as unset on new outgoing connection
+    - BUG/MEDIUM: stream-int: Disable connection retries on plain HTTP proxy mode
+    - DOC: configuration: remove obsolete mentions of H2 being converted to HTTP/1.x
+    - BUG/MINOR: sample: Free str.area in smp_check_const_bool
+    - BUG/MINOR: sample: Free str.area in smp_check_const_meth
+    - CONTRIB: da: fix memory leak in dummy function da_atlas_open()
+    - BUG/MEDIUM: mux-h1: Continue to process request when switching in tunnel mode
+    - BUG/MEDIUM: log: issue mixing sampled to not sampled log servers.
+    - BUG/MEDIUM: channel: Be aware of SHUTW_NOW flag when output data are peeked
+
+2020/06/12 : 2.0.15
+    - BUG/MINOR: protocol_buffer: Wrong maximum shifting.
+    - BUG/MINOR: peers: Incomplete peers sections should be validated.
+    - DOC: hashing: update link to hashing functions
+    - DOC: Improve documentation on http-request set-src
+    - BUG/MINOR: ssl: default settings for ssl server options are not used
+    - BUG/MEDIUM: http-ana: Handle NTLM messages correctly.
+    - BUG/MINOR: tools: fix the i386 version of the div64_32 function
+    - BUG/MINOR: http: make url_decode() optionally convert '+' to SP
+    - DOC: option logasap does not depend on mode
+    - BUG/MINOR: check: Update server address and port to execute an external check
+    - MINOR: checks: Add a way to send custom headers and payload during http chekcs
+    - BUG/MINOR: checks: Respect the no-check-ssl option
+    - BUG/MINOR: checks: chained expect will not properly wait for enough data
+    - BUG/MINOR: obj_type: Handle stream object in obj_base_ptr() function
+    - BUG/MEDIUM: capture: capture-req/capture-res converters crash without a stream
+    - BUG/MEDIUM: capture: capture.{req,res}.* crash without a stream
+    - BUG/MEDIUM: http: the "http_first_req" sample fetch could crash without a steeam
+    - BUG/MEDIUM: http: the "unique-id" sample fetch could crash without a steeam
+    - BUG/MEDIUM: sample: make the CPU and latency sample fetches check for a stream
+    - BUG/MEDIUM: listener: mark the thread as not stuck inside the loop
+    - MINOR: threads: export the POSIX thread ID in panic dumps
+    - BUG/MINOR: debug: properly use long long instead of long for the thread ID
+    - BUG/MEDIUM: shctx: really check the lock's value while waiting
+    - BUG/MEDIUM: shctx: bound the number of loops that can happen around the lock
+    - MINOR: stream: report the list of active filters on stream crashes
+    - REGTEST: ssl: test the client certificate authentication
+    - BUG/MEDIUM: backend: don't access a non-existing mux from a previous connection
+    - Revert "BUG/MINOR: connection: make sure to correctly tag local PROXY connections"
+    - BUG/MEDIUM: server/checks: Init server check during config validity check
+    - BUG/MINOR: checks/server: use_ssl member must be signed
+    - BUG/MEDIUM: checks: Always initialize checks before starting them
+    - BUG/MINOR: checks: Compute the right HTTP request length for HTTP health checks
+    - BUG/MINOR: checks: Remove a warning about http health checks
+    - BUG/MEDIUM: streams: Remove SF_ADDR_SET if we're retrying due to L7 retry.
+    - BUG/MEDIUM: stream: Only allow L7 retries when using HTTP.
+    - BUG/MAJOR: stream-int: always detach a faulty endpoint on connect failure
+    - BUG/MEDIUM: connections: force connections cleanup on server changes
+    - BUG/MEDIUM: ssl: fix the id length check within smp_fetch_ssl_fc_session_id()
+    - CLEANUP: connections: align function declaration
+    - BUG/MINOR: sample: Set the correct type when a binary is converted to a string
+    - BUG/MINOR: threads: fix multiple use of argument inside HA_ATOMIC_CAS()
+    - BUG/MINOR: threads: fix multiple use of argument inside HA_ATOMIC_UPDATE_{MIN,MAX}()
+    - BUG/MEDIUM: lua: Fix dumping of stick table entries for STD_T_DICT
+    - BUG/MINOR: config: Make use_backend and use-server post-parsing less obscur
+    - BUG/MINOR: http-ana: fix NTLM response parsing again
+    - BUG/MEDIUM: http_ana: make the detection of NTLM variants safer
+    - BUG/MINOR: cfgparse: Abort parsing the current line if an invalid \x sequence is encountered
+    - BUG/MINOR: pools: use %u not %d to report pool stats in "show pools"
+    - BUG/MINOR: pollers: remove uneeded free in global init
+    - BUG/MINOR: soft-stop: always wake up waiting threads on stopping
+    - BUILD: select: only declare existing local labels to appease clang
+    - BUG/MINOR: cache: Don't needlessly test "cache" keyword in parse_cache_flt()
+    - BUG/MINOR: checks: Respect check-ssl param when a port or an addr is specified
+    - BUG/MINOR: server: Fix server_finalize_init() to avoid unused variable
+    - BUG/MINOR: lua: Add missing string length for lua sticktable lookup
+    - BUG/MINOR: nameservers: fix error handling in parsing of resolv.conf
+    - Revert "BUG/MEDIUM: connections: force connections cleanup on server changes"
+    - SCRIPTS: publish-release: pass -n to gzip to remove timestamp
+    - BUG/MINOR: peers: fix internal/network key type mapping.
+    - BUG/MEDIUM: lua: Reset analyse expiration timeout before executing a lua action
+    - BUG/MEDIUM: hlua: Lock pattern references to perform set/add/del operations
+    - BUG/MEDIUM: contrib/prometheus-exporter: Properly set flags to dump metrics
+    - BUG/MINOR: logs: prevent double line returns in some events.
+    - BUG/MEDIUM: logs: fix trailing zeros on log message.
+    - BUG/MINOR: proto-http: Fix detection of NTLM for the legacy HTTP version
+    - BUILD: makefile: adjust the sed expression of "make help" for solaris
+    - BUG/MEDIUM: mworker: fix the copy of options in copy_argv()
+    - BUG/MINOR: init: -x can have a parameter starting with a dash
+    - BUG/MINOR: init: -S can have a parameter starting with a dash
+    - BUG/MEDIUM: mworker: fix the reload with an -- option
+    - BUG/MINOR: mworker: fix a memleak when execvp() failed
+    - BUG/MEDIUM: log: don't hold the log lock during writev() on a file descriptor
+    - BUG/MEDIUM: pattern: fix thread safety of pattern matching
+    - REGTESTS: Add missing OPENSSL to REQUIRE_OPTIONS for lua/txn_get_priv
+    - REGTESTS: Add missing OPENSSL to REQUIRE_OPTIONS for compression/lua_validation
+    - BUG/MINOR: ssl: fix ssl-{min,max}-ver with openssl < 1.1.0
+    - REGTESTS: checks: Fix tls_health_checks when IPv6 addresses are used
+
+2020/04/02 : 2.0.14
+    - BUG/MINOR: namespace: avoid closing fd when socket failed in my_socketat
+    - BUG/MEDIUM: muxes: Use the right argument when calling the destroy method.
+    - SCRIPTS: announce-release: use mutt -H instead of -i to include the draft
+    - MINOR: http-htx: Add a function to retrieve the headers size of an HTX message
+    - MINOR: filters: Forward data only if the last filter forwards something
+    - BUG/MINOR: filters: Count HTTP headers as filtered data but don't forward them
+    - BUG/MINOR: http-ana: Matching on monitor-uri should be case-sensitive
+    - BUG/MAJOR: http-ana: Always abort the request when a tarpit is triggered
+    - MINOR: ist: add an iststop() function
+    - BUG/MINOR: http: http-request replace-path duplicates the query string
+    - BUG/MEDIUM: shctx: make sure to keep all blocks aligned
+    - MINOR: compiler: move CPU capabilities definition from config.h and complete them
+    - BUG/MEDIUM: ebtree: don't set attribute packed without unaligned access support
+    - BUILD: fix recent build failure on unaligned archs
+    - CLEANUP: cfgparse: Fix type of second calloc() parameter
+    - BUG/MINOR: sample: fix the json converter's endian-sensitivity
+    - BUG/MEDIUM: ssl: fix several bad pointer aliases in a few sample fetch functions
+    - BUG/MINOR: connection: make sure to correctly tag local PROXY connections
+    - MINOR: compiler: add new alignment macros
+    - BUILD: ebtree: improve architecture-specific alignment
+    - BUG/MINOR: sample: Make sure to return stable IDs in the unique-id fetch
+    - BUG/MINOR: dns: ignore trailing dot
+    - MINOR: contrib/prometheus-exporter: Add heathcheck status/code in server metrics
+    - MINOR: contrib/prometheus-exporter: Add the last heathcheck duration metric
+    - BUG/MEDIUM: random: initialize the random pool a bit better
+    - MINOR: tools: add 64-bit rotate operators
+    - BUG/MEDIUM: random: implement a thread-safe and process-safe PRNG
+    - MINOR: backend: use a single call to ha_random32() for the random LB algo
+    - BUG/MINOR: checks/threads: use ha_random() and not rand()
+    - BUG/MAJOR: list: fix invalid element address calculation
+    - MINOR: debug: report the task handler's pointer relative to main
+    - BUG/MEDIUM: debug: make the debug_handler check for the thread in threads_to_dump
+    - MINOR: haproxy: export main to ease access from debugger
+    - BUG/MINOR: wdt: do not return an error when the watchdog couldn't be enabled
+    - DOC: fix incorrect indentation of http_auth_*
+    - OPTIM: startup: fast unique_id allocation for acl.
+    - BUG/MINOR: pattern: Do not pass len = 0 to calloc()
+    - DOC: configuration.txt: fix various typos
+    - DOC: assorted typo fixes in the documentation and Makefile
+    - BUG/MINOR: init: make the automatic maxconn consider the max of soft/hard limits
+    - BUG/MAJOR: proxy_protocol: Properly validate TLV lengths
+    - REGTEST: make the PROXY TLV validation depend on version 2.2
+    - MINOR: htx: Add a function to return a block at a specific offset
+    - BUG/MEDIUM: cache/filters: Fix loop on HTX blocks caching the response payload
+    - BUG/MEDIUM: compression/filters: Fix loop on HTX blocks compressing the payload
+    - BUG/MINOR: http-ana: Reset request analysers on a response side error
+    - BUG/MINOR: lua: Ignore the reserve to know if a channel is full or not
+    - BUG/MINOR: http-rules: Preserve FLT_END analyzers on reject action
+    - BUG/MINOR: http-rules: Fix a typo in the reject action function
+    - BUG/MINOR: rules: Preserve FLT_END analyzers on silent-drop action
+    - BUG/MINOR: rules: Increment be_counters if backend is assigned for a silent-drop
+    - DOC: fix typo about no-tls-tickets
+    - DOC: improve description of no-tls-tickets
+    - DOC: ssl: clarify security implications of TLS tickets
+    - BUILD: wdt: only test for SI_TKILL when compiled with thread support
+    - BUG/MEDIUM: random: align the state on 2*64 bits for ARM64
+    - BUG/MINOR: haproxy: always initialize sleeping_thread_mask
+    - BUG/MINOR: listener/mq: do not dispatch connections to remote threads when stopping
+    - BUG/MINOR: haproxy/threads: try to make all threads leave together
+    - DOC: proxy_protocol: Reserve TLV type 0x05 as PP2_TYPE_UNIQUE_ID
+    - BUILD: on ARM, must be linked to libatomic.
+    - BUILD: makefile: fix regex syntax in ARM platform detection
+    - BUILD: makefile: fix expression again to detect ARM platform
+    - BUG/MEDIUM: peers: resync ended with RESYNC_PARTIAL in wrong cases.
+    - DOC: assorted typo fixes in the documentation
+    - MINOR: wdt: Move the definitions of WDTSIG and DEBUGSIG into types/signal.h.
+    - BUG/MEDIUM: wdt: Don't ignore WDTSIG and DEBUGSIG in __signal_process_queue().
+    - MINOR: memory: Change the flush_lock to a spinlock, and don't get it in alloc.
+    - BUG/MINOR: connections: Make sure we free the connection on failure.
+    - REGTESTS: use "command -v" instead of "which"
+    - REGTEST: increase timeouts on the seamless-reload test
+    - BUG/MINOR: haproxy/threads: close a possible race in soft-stop detection
+    - BUG/MINOR: peers: init bind_proc to 1 if it wasn't initialized
+    - BUG/MINOR: peers: avoid an infinite loop with peers_fe is NULL
+    - BUG/MINOR: peers: Use after free of "peers" section.
+    - MINOR: listener: add so_name sample fetch
+    - BUILD: ssl: only pass unsigned chars to isspace()
+    - BUG/MINOR: stats: Fix color of draining servers on stats page
+    - DOC: internals: Fix spelling errors in filters.txt
+    - MINOR: http-rules: Add a flag on redirect rules to know the rule direction
+    - BUG/MINOR: http_ana: make sure redirect flags don't have overlapping bits
+    - MINOR: http-rules: Handle the rule direction when a redirect is evaluated
+    - BUG/MINOR: filters: Use filter offset to decude the amount of forwarded data
+    - BUG/MINOR: filters: Forward everything if no data filters are called
+    - BUG/MINOR: http-ana: Reset request analysers on error when waiting for response
+    - BUG/CRITICAL: hpack: never index a header into the headroom after wrapping
+
+2020/02/13 : 2.0.13
+    - BUG/MINOR: checks: refine which errno values are really errors.
+    - BUG/MEDIUM: checks: Only attempt to do handshakes if the connection is ready.
+    - BUG/MEDIUM: connections: Hold the lock when wanting to kill a connection.
+    - MINOR: config: disable busy polling on old processes
+    - MINOR: ssl: Remove unused variable "need_out".
+    - BUG/MINOR: h1: Report the right error position when a header value is invalid
+    - BUG/MINOR: proxy: Fix input data copy when an error is captured
+    - BUG/MEDIUM: http-ana: Truncate the response when a redirect rule is applied
+    - BUG/MINOR: channel: inject output data at the end of output
+    - BUG/MEDIUM: session: do not report a failure when rejecting a session
+    - BUG/MINOR: stream-int: Don't trigger L7 retry if max retries is already reached
+    - BUG/MINOR: mux-h2: use a safe list_for_each_entry in h2_send()
+    - BUG/MEDIUM: mux-h2: fix missing test on sending_list in previous patch
+    - BUG/MEDIUM: mux-h2: don't stop sending when crossing a buffer boundary
+    - BUG/MINOR: cli/mworker: can't start haproxy with 2 programs
+    - REGTEST: mcli/mcli_start_progs: start 2 programs
+    - BUG/MEDIUM: mworker: remain in mworker mode during reload
+    - BUG/MEDIUM: mux_h1: Don't call h1_send if we subscribed().
+    - BUG/MAJOR: hashes: fix the signedness of the hash inputs
+    - REGTEST: add sample_fetches/hashes.vtc to validate hashes
+    - BUG/MEDIUM: cli: _getsocks must send the peers sockets
+    - BUG/MINOR: stream: don't mistake match rules for store-request rules
+    - BUG/MEDIUM: connection: add a mux flag to indicate splice usability
+    - BUG/MINOR: pattern: handle errors from fgets when trying to load patterns
+    - BUG/MINOR: cache: Fix leak of cache name in error path
+    - BUG/MINOR: dns: Make dns_query_id_seed unsigned
+    - BUG/MINOR: 51d: Fix bug when HTX is enabled
+    - BUILD: pattern: include errno.h
+    - BUG/MINOR: http-ana/filters: Wait end of the http_end callback for all filters
+    - BUG/MINOR: http-rules: Remove buggy deinit functions for HTTP rules
+    - BUG/MINOR: stick-table: Use MAX_SESS_STKCTR as the max track ID during parsing
+    - BUG/MINOR: tcp-rules: Fix memory releases on error path during action parsing
+    - MINOR: proxy/http-ana: Add support of extra attributes for the cookie directive
+    - BUG/MINOR: http_act: don't check capture id in backend
+    - BUG/MEDIUM: 0rtt: Only consider the SSL handshake.
+    - BUG/MINOR: stktable: report the current proxy name in error messages
+    - BUG/MEDIUM: mux-h2: make sure we don't emit TE headers with anything but "trailers"
+    - BUILD: cfgparse: silence a bogus gcc warning on 32-bit machines
+    - BUG/MINOR: dns: allow srv record weight set to 0
+    - BUG/MEDIUM: ssl: Don't forget to free ctx->ssl on failure.
+    - BUG/MINOR: tcpchecks: fix the connect() flags regarding delayed ack
+    - BUG/MEDIUM: pipe: fix a use-after-free in case of pipe creation error
+    - BUG/MINOR: connection: fix ip6 dst_port copy in make_proxy_line_v2
+    - BUG/MEDIUM: connections: Don't forget to unlock when killing a connection.
+    - BUG/MEDIUM: memory_pool: Update the seq number in pool_flush().
+    - MINOR: memory: Only init the pool spinlock once.
+    - BUG/MEDIUM: memory: Add a rwlock before freeing memory.
+    - BUG/MAJOR: memory: Don't forget to unlock the rwlock if the pool is empty.
+    - BUG/MINOR: ssl: we may only ignore the first 64 errors
+    - CONTRIB: debug: add missing flags SF_HTX and SF_MUX
+    - CONTRIB: debug: add the possibility to decode the value as certain types only
+    - CONTRIB: debug: support reporting multiple values at once
+    - MINOR: acl: Warn when an ACL is named 'or'
+    - CONTRIB: debug: also support reading values from stdin
+    - SCRIPTS: announce-release: place the send command in the mail's header
+    - SCRIPTS: announce-release: allow the user to force to overwrite old files
+    - MINOR: build: add linux-glibc-legacy build TARGET
+    - BUG/MINOR: unix: better catch situations where the unix socket path length is close to the limit
+    - MINOR: http: add a new "replace-path" action
+    - BUG/MINOR: ssl: Possible memleak when allowing the 0RTT data buffer.
+    - BUG/MINOR: dns: allow 63 char in hostname
+    - BUG/MEDIUM: listener: only consider running threads when resuming listeners
+    - BUG/MINOR: listener: enforce all_threads_mask on bind_thread on init
+    - BUG/MINOR: tcp: avoid closing fd when socket failed in tcp_bind_listener
+    - DOC: word converter ignores delimiters at the start or end of input string
+    - BUG/MINOR: tcp: don't try to set defaultmss when value is negative
+    - SCRIPTS: make announce-release executable again
+
+2019/12/21 : 2.0.12
+    - DOC: Improve documentation of http-re(quest|sponse) replace-(header|value|uri)
+    - DOC: clarify the fact that replace-uri works on a full URI
+    - BUG/MINOR: sample: fix the closing bracket and LF in the debug converter
+    - BUG/MINOR: sample: always check converters' arguments
+    - BUG/MEDIUM: ssl: Don't set the max early data we can receive too early.
+    - MINOR: task: only check TASK_WOKEN_ANY to decide to requeue a task
+    - BUG/MAJOR: task: add a new TASK_SHARED_WQ flag to fix foreing requeuing
+    - BUG/MEDIUM: ssl: Revamp the way early data are handled.
+    - MINOR: fd/threads: make _GET_NEXT()/_GET_PREV() use the volatile attribute
+    - BUG/MEDIUM: fd/threads: fix a concurrency issue between add and rm on the same fd
+    - BUG/MINOR: ssl: openssl-compat: Fix getm_ defines
+    - BUG/MEDIUM: stream: Be sure to never assign a TCP backend to an HTX stream
+    - BUILD: ssl: improve SSL_CTX_set_ecdh_auto compatibility
+
+2019/12/11 : 2.0.11
+    - BUG/MINOR: stream: init variables when the list is empty
+    - BUG/MINOR: contrib/prometheus-exporter: Use HTX errors and not legacy ones
+    - BUG/MINOR: contrib/prometheus-exporter: decode parameter and value only
+    - BUG/MINOR: http-htx: Don't make http_find_header() fail if the value is empty
+    - DOC: Clarify behavior of server maxconn in HTTP mode
+    - DOC: clarify matching strings on binary fetches
+    - DOC: move the "group" keyword at the right place
+    - BUG/MEDIUM: stream-int: don't subscribed for recv when we're trying to flush data
+    - BUG/MINOR: stream-int: avoid calling rcv_buf() when splicing is still possible
+    - BUG/MEDIUM: listener/thread: fix a race when pausing a listener
+    - BUG/MINOR: ssl: certificate choice can be unexpected with openssl >= 1.1.1
+    - BUG/MEDIUM: mux-h1: Never reuse H1 connection if a shutw is pending
+    - BUG/MINOR: mux-h1: Don't rely on CO_FL_SOCK_RD_SH to set H1C_F_CS_SHUTDOWN
+    - BUG/MINOR: mux-h1: Fix conditions to know whether or not we may receive data
+    - BUG/MEDIUM: tasks: Make sure we switch wait queues in task_set_affinity().
+    - BUG/MEDIUM: checks: Make sure we set the task affinity just before connecting.
+    - BUG/MINOR: mux-h1: Be sure to set CS_FL_WANT_ROOM when EOM can't be added
+    - BUG/MINOR: proxy: make soft_stop() also close FDs in LI_PAUSED state
+    - BUG/MINOR: listener/threads: always use atomic ops to clear the FD events
+    - BUG/MINOR: listener: also clear the error flag on a paused listener
+    - BUG/MEDIUM: listener/threads: fix a remaining race in the listener's accept()
+    - DOC: document the listener state transitions
+    - BUG/MAJOR: dns: add minimalist error processing on the Rx path
+    - BUG/MEDIUM: proto_udp/threads: recv() and send() must not be exclusive.
+    - BUG/MEDIUM: kqueue: Make sure we report read events even when no data.
+    - DOC: listeners: add a few missing transitions
+    - BUG/MINOR: tasks: only requeue a task if it was already in the queue
+    - DOC: proxies: HAProxy only supports 3 connection modes
+    - BUILD/MINOR: ssl: shut up a build warning about format truncation
+    - BUILD/MINOR: tools: shut up the format truncation warning in get_gmt_offset()
+    - BUILD: do not disable -Wformat-truncation anymore
+    - DOC: remove references to the outdated architecture.txt
+    - BUG/MINOR: log: fix minor resource leaks on logformat error path
+    - BUG/MINOR: mworker: properly pass SIGTTOU/SIGTTIN to workers
+    - BUG/MINOR: listener: do not immediately resume on transient error
+    - BUG/MINOR: server: make "agent-addr" work on default-server line
+    - BUG/MINOR: listener: fix off-by-one in state name check
+    - BUILD/MINOR: unix sockets: silence an absurd gcc warning about strncpy()
+
 2019/11/25 : 2.0.10
     - BUG/MINOR: init: fix set-dumpable when using uid/gid
     - MINOR: peers: Alway show the table info for disconnected peers.
diff --git a/CONTRIBUTING b/CONTRIBUTING
index 201e122..638a646 100644
--- a/CONTRIBUTING
+++ b/CONTRIBUTING
@@ -234,7 +234,7 @@
    indented code, which only proves that the person has no consideration for
    quality and/or has done it in a hurry (probably worse). Please note that most
    bugs were found in low-quality code. Reviewers know this and tend to be much
-   more reluctant to accept poorly formated code because by experience they
+   more reluctant to accept poorly formatted code because by experience they
    won't trust their author's ability to write correct code. It is also worth
    noting that poor quality code is painful to read and may result in nobody
    willing to waste their time even reviewing your work.
@@ -990,7 +990,7 @@
 Among the best ways to quickly lose everyone's respect, there is this small
 selection, which should help you improve the way you work with others, if
 you notice you're already practising some of them:
-  - repeatedly send improperly formated commit messages, with no type or
+  - repeatedly send improperly formatted commit messages, with no type or
     severity, or with no commit message body. These ones require manual
     edition, maintainers will quickly learn to recognize your name.
 
diff --git a/INSTALL b/INSTALL
index d6d786b..f65bc51 100644
--- a/INSTALL
+++ b/INSTALL
@@ -130,7 +130,7 @@
 HAProxy in its basic form does not depend on anything beyond a working libc.
 However a number of options are enabled by default, or are highly recommended,
 and these options will typically involve some external components or libraries,
-depending on the targetted platform.
+depending on the targeted platform.
 
 Optional dependencies may be split into several categories :
 
@@ -288,7 +288,7 @@
 
 4.7) Lua
 --------
-Lua is an embedded programming langage supported by HAProxy to provide more
+Lua is an embedded programming language supported by HAProxy to provide more
 advanced scripting capabilities. Only versions 5.3 and above are supported.
 In order to enable Lua support, please specify "USE_LUA=1" on the command line.
 Some systems provide this library under various names to avoid conflicts with
@@ -372,18 +372,19 @@
 To build haproxy, you have to choose your target OS amongst the following ones
 and assign it to the TARGET variable :
 
-  - linux-glibc for Linux kernel 2.6.28 and above
-  - solaris     for Solaris 8 or 10 (others untested)
-  - freebsd     for FreeBSD 5 to 12 (others untested)
-  - netbsd      for NetBSD
-  - osx         for Mac OS/X
-  - openbsd     for OpenBSD 5.7 and above
-  - aix51       for AIX 5.1
-  - aix52       for AIX 5.2
-  - cygwin      for Cygwin
-  - haiku       for Haiku
-  - generic     for any other OS or version.
-  - custom      to manually adjust every setting
+  - linux-glibc         for Linux kernel 2.6.28 and above
+  - linux-glibc-legacy  for Linux kernel 2.6.28 and above without new features
+  - solaris             for Solaris 8 or 10 (others untested)
+  - freebsd             for FreeBSD 5 to 12 (others untested)
+  - netbsd              for NetBSD
+  - osx                 for Mac OS/X
+  - openbsd             for OpenBSD 5.7 and above
+  - aix51               for AIX 5.1
+  - aix52               for AIX 5.2
+  - cygwin              for Cygwin
+  - haiku               for Haiku
+  - generic             for any other OS or version.
+  - custom              to manually adjust every setting
 
 You may also choose your CPU to benefit from some optimizations. This is
 particularly important on UltraSparc machines. For this, you can assign
diff --git a/Makefile b/Makefile
index a9c65c5..ba4be95 100644
--- a/Makefile
+++ b/Makefile
@@ -41,7 +41,7 @@
 #   USE_LUA              : enable Lua support.
 #   USE_FUTEX            : enable use of futex on kernel 2.6. Automatic.
 #   USE_ACCEPT4          : enable use of accept4() on linux. Automatic.
-#   USE_MY_ACCEPT4       : use own implemention of accept4() if glibc < 2.10.
+#   USE_MY_ACCEPT4       : use own implementation of accept4() if glibc < 2.10.
 #   USE_PRCTL            : enable use of prctl(). Automatic.
 #   USE_ZLIB             : enable zlib library support.
 #   USE_SLZ              : enable slz library instead of zlib (pick at most one).
@@ -139,10 +139,10 @@
 DOCDIR = $(PREFIX)/doc/haproxy
 
 #### TARGET system
-# Use TARGET=<target_name> to optimize for a specifc target OS among the
+# Use TARGET=<target_name> to optimize for a specific target OS among the
 # following list (use the default "generic" if uncertain) :
-#    linux-glibc, solaris, freebsd, openbsd, netbsd, cygwin,
-#    haiku, aix51, aix52, osx, generic, custom
+#    linux-glibc, linux-glibc-legacy, solaris, freebsd, openbsd, netbsd,
+#    cygwin, haiku, aix51, aix52, osx, generic, custom
 TARGET =
 
 #### TARGET CPU
@@ -182,7 +182,6 @@
 # to be sure we get the intended behavior.
 SPEC_CFLAGS := -fno-strict-aliasing -Wdeclaration-after-statement
 SPEC_CFLAGS += $(call cc-opt-alt,-fwrapv,$(call cc-opt,-fno-strict-overflow))
-SPEC_CFLAGS += $(call cc-nowarn,format-truncation)
 SPEC_CFLAGS += $(call cc-nowarn,address-of-packed-member)
 SPEC_CFLAGS += $(call cc-nowarn,unused-label)
 SPEC_CFLAGS += $(call cc-nowarn,sign-compare)
@@ -245,7 +244,7 @@
 # It's automatically appended depending on the targets.
 EXTRA =
 
-#### CPU dependant optimizations
+#### CPU dependent optimizations
 # Some CFLAGS are set by default depending on the target CPU. Those flags only
 # feed CPU_CFLAGS, which in turn feed CFLAGS, so it is not mandatory to use
 # them. You should not have to change these options. Better use CPU_CFLAGS or
@@ -257,7 +256,7 @@
 CPU_CFLAGS.ultrasparc = -O6 -mcpu=v9 -mtune=ultrasparc
 CPU_CFLAGS            = $(CPU_CFLAGS.$(CPU))
 
-#### ARCH dependant flags, may be overridden by CPU flags
+#### ARCH dependent flags, may be overridden by CPU flags
 ARCH_FLAGS.32     = -m32
 ARCH_FLAGS.64     = -m64
 ARCH_FLAGS.i386   = -m32 -march=i386
@@ -325,6 +324,17 @@
     USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_FUTEX USE_LINUX_TPROXY          \
     USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO     \
     USE_GETADDRINFO)
+ifneq ($(shell echo __arm__/__aarch64__ | $(CC) -E -xc - | grep '^[^\#]'),__arm__/__aarch64__)
+  TARGET_LDFLAGS=-latomic
+endif
+endif
+
+# For linux >= 2.6.28, glibc without new features
+ifeq ($(TARGET),linux-glibc-legacy)
+  set_target_defaults = $(call default_opts, \
+    USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER  \
+    USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_FUTEX USE_LINUX_TPROXY          \
+    USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_GETADDRINFO)
 endif
 
 # Solaris 8 and above
@@ -733,8 +743,8 @@
 	@echo
 	@echo "Please choose the target among the following supported list :"
 	@echo
-	@echo "   linux-glibc, solaris, freebsd, openbsd, netbsd, cygwin,"
-	@echo "   haiku, aix51, aix52, osx, generic, custom"
+	@echo "   linux-glibc, linux-glibc-legacy, solaris, freebsd, openbsd, netbsd,"
+	@echo "   cygwin, haiku, aix51, aix52, osx, generic, custom"
 	@echo
 	@echo "Use \"generic\" if you don't want any optimization, \"custom\" if you"
 	@echo "want to precisely tweak every option, or choose the target which"
@@ -800,7 +810,7 @@
 DEP = $(INCLUDES) .build_opts
 
 help:
-	$(Q)sed -ne "/^[^#]*$$/q;s/^# \?\(.*\)/\1/p" Makefile
+	$(Q)sed -ne "/^[^#]*$$/q;s/^# \{0,1\}\(.*\)/\1/;p" Makefile
 	$(Q)echo; \
 	   if [ -n "$(TARGET)" ]; then \
 	     if [ -n "$(set_target_defaults)" ]; then \
@@ -810,8 +820,8 @@
 	     fi; \
 	   else \
 	     echo "TARGET not set, you may pass 'TARGET=xxx' to set one among :";\
-	     echo "  linux-glibc, solaris, freebsd, netbsd, osx, openbsd,"; \
-	     echo "  aix51, aix52, cygwin, haiku, generic, custom"; \
+	     echo "  linux-glibc, linux-glibc-legacy, solaris, freebsd, netbsd, osx,"; \
+	     echo "  openbsd, aix51, aix52, cygwin, haiku, generic, custom"; \
 	   fi
 	$(Q)echo;echo "Enabled features for TARGET '$(TARGET)' (disable with 'USE_xxx=') :"
 	$(Q)set -- $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),$(opt),)); echo "  $$*" | (fmt || cat) 2>/dev/null
diff --git a/VERDATE b/VERDATE
index 2deb4e6..2f14d6d 100644
--- a/VERDATE
+++ b/VERDATE
@@ -1,2 +1,2 @@
 $Format:%ci$
-2019/11/25
+2020/07/31
diff --git a/VERSION b/VERSION
index 0a69206..a6e7bcb 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.10
+2.0.17
diff --git a/contrib/debug/flags.c b/contrib/debug/flags.c
index 92b9c66..0a2fccf 100644
--- a/contrib/debug/flags.c
+++ b/contrib/debug/flags.c
@@ -8,6 +8,21 @@
 #include <types/stream_interface.h>
 #include <types/task.h>
 
+// 1 bit per flag, no hole permitted here
+#define SHOW_AS_ANA   0x00000001
+#define SHOW_AS_CHN   0x00000002
+#define SHOW_AS_CONN  0x00000004
+#define SHOW_AS_CS    0x00000008
+#define SHOW_AS_SI    0x00000010
+#define SHOW_AS_SIET  0x00000020
+#define SHOW_AS_STRM  0x00000040
+#define SHOW_AS_TASK  0x00000080
+#define SHOW_AS_TXN   0x00000100
+
+// command line names, must be in exact same order as the SHOW_AS_* flags above
+// so that show_as_words[i] matches flag 1U<<i.
+const char *show_as_words[] = { "ana", "chn", "conn", "cs", "si", "siet", "strm", "task", "txn", };
+
 #define SHOW_FLAG(f,n)					\
 	do {				 		\
 		if (!((f) & (n))) break; 		\
@@ -15,6 +30,19 @@
 		printf(#n"%s", (f) ? " | " : "");	\
 	} while (0)
 
+unsigned int get_show_as(const char *word)
+{
+	int w = 0;
+
+	while (1) {
+		if (w == sizeof(show_as_words) / sizeof(*show_as_words))
+			return 0;
+		if (strcmp(word, show_as_words[w]) == 0)
+			return 1U << w;
+		w++;
+	}
+}
+
 void show_chn_ana(unsigned int f)
 {
 	printf("chn->ana    = ");
@@ -364,7 +392,9 @@
 	case SF_ERR_CHK_PORT: f &= ~SF_ERR_MASK ; printf("SF_ERR_CHK_PORT%s",       f ? " | " : ""); break;
 	}
 
+	SHOW_FLAG(f, SF_HTX);
 	SHOW_FLAG(f, SF_REDIRECTABLE);
+	SHOW_FLAG(f, SF_IGNORE);
 	SHOW_FLAG(f, SF_REDISP);
 	SHOW_FLAG(f, SF_CURR_SESS);
 	SHOW_FLAG(f, SF_MONITOR);
@@ -380,26 +410,84 @@
 	putchar('\n');
 }
 
+void usage_exit(const char *name)
+{
+	fprintf(stderr, "Usage: %s [ana|chn|conn|cs|si|sierr|strm|task|txn]* { [+-][0x]value* | - }\n", name);
+	exit(1);
+}
+
 int main(int argc, char **argv)
 {
 	unsigned int flags;
+	unsigned int show_as = 0;
+	unsigned int f;
+	const char *name = argv[0];
+	char line[20];
+	char *value;
+	int multi = 0;
+	int use_stdin = 0;
+	char *err;
 
-	if (argc < 2) {
-		fprintf(stderr, "Usage: %s 0x<flags|state>\n", argv[0]);
-		exit(1);
+	while (argc > 0) {
+		argv++; argc--;
+		if (argc < 1)
+			usage_exit(name);
+
+		f = get_show_as(argv[0]);
+		if (!f)
+			break;
+		show_as |= f;
 	}
 
+	if (!show_as)
+		show_as = ~0U;
+
-	flags = strtoul(argv[1], NULL, 0);
+	if (argc > 1)
+		multi = 1;
 
-	show_task_state(flags);
-	show_txn_flags(flags);
-	show_strm_flags(flags);
-	show_si_et(flags);
-	show_si_flags(flags);
-	show_cs_flags(flags);
-	show_conn_flags(flags);
-	show_chn_flags(flags);
-	show_chn_ana(flags);
+	if (strcmp(argv[0], "-") == 0)
+		use_stdin = 1;
 
+	while (argc > 0) {
+		if (use_stdin) {
+			value = fgets(line, sizeof(line), stdin);
+			if (!value)
+				break;
+
+			/* skip common leading delimitors that slip from copy-paste */
+			while (*value == ' ' || *value == '\t' || *value == ':' || *value == '=')
+				value++;
+
+			/* stop at the end of the number and trim any C suffix like "UL" */
+			err = value;
+			while (*err == '-' || *err == '+' ||
+			       (isalnum(*err) && toupper(*err) != 'U' && toupper(*err) != 'L'))
+				err++;
+			if (err)
+				*err = 0;
+		} else {
+			value = argv[0];
+			argv++; argc--;
+		}
+
+		flags = strtoul(value, &err, 0);
+		if (!*value || *err) {
+			fprintf(stderr, "Unparsable value: <%s>\n", value);
+			usage_exit(name);
+		}
+
+		if (multi || use_stdin)
+			printf("### 0x%08x:\n", flags);
+
+		if (show_as & SHOW_AS_ANA)   show_chn_ana(flags);
+		if (show_as & SHOW_AS_CHN)   show_chn_flags(flags);
+		if (show_as & SHOW_AS_CONN)  show_conn_flags(flags);
+		if (show_as & SHOW_AS_CS)    show_cs_flags(flags);
+		if (show_as & SHOW_AS_SI)    show_si_flags(flags);
+		if (show_as & SHOW_AS_SIET)  show_si_et(flags);
+		if (show_as & SHOW_AS_STRM)  show_strm_flags(flags);
+		if (show_as & SHOW_AS_TASK)  show_task_state(flags);
+		if (show_as & SHOW_AS_TXN)   show_txn_flags(flags);
+	}
 	return 0;
 }
diff --git a/contrib/deviceatlas/dac.c b/contrib/deviceatlas/dac.c
index f94fe8d..720dc6a 100644
--- a/contrib/deviceatlas/dac.c
+++ b/contrib/deviceatlas/dac.c
@@ -63,8 +63,9 @@
 da_status_t
 da_atlas_open(da_atlas_t *atlas, da_property_decl_t *extraprops, const void *ptr, size_t len)
 {
-    ptr = malloc(len);
-    return ptr ? DA_OK : DA_NOMEM;
+    void *ptr2 = malloc(len);
+    free(ptr2);
+    return ptr2 ? DA_OK : DA_NOMEM;
 }
 
 void
diff --git a/contrib/modsecurity/README b/contrib/modsecurity/README
index 8031389..8e74016 100644
--- a/contrib/modsecurity/README
+++ b/contrib/modsecurity/README
@@ -1,7 +1,7 @@
 ModSecurity for HAProxy
 -----------------------
 
-This is a third party deamon which speaks SPOE. It gives requests send by HAProxy
+This is a third party daemon which speaks SPOE. It gives requests send by HAProxy
 to ModSecurity and returns the verdict.
 
   Compilation
@@ -24,8 +24,8 @@
 	cp standalone/*.h $PWD/INSTALL/include
 	cp apache2/*.h $PWD/INSTALL/include
 
-Note that this compilation method works, but is a litle bit rustic. I can't
-deal with Lua, I supposed that is a dependecies problem on my computer.
+Note that this compilation method works, but is a little bit rustic. I can't
+deal with Lua, I supposed that is a dependencies problem on my computer.
 
   Start the service
 ---------------------
@@ -113,7 +113,7 @@
    -    rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_DEFAULT, mp);
    +    rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_PROC_PTHREAD, mp);
 
-* Configuration file loaded with wilcard (eg. Include rules/*.conf), are loaded
+* Configuration file loaded with wildcard (eg. Include rules/*.conf), are loaded
   in reverse alphabetical order. You can found a patch below. The ModSecurity
   team ignored this patch.
 
diff --git a/contrib/prometheus-exporter/README b/contrib/prometheus-exporter/README
index b19acc1..379dbc3 100644
--- a/contrib/prometheus-exporter/README
+++ b/contrib/prometheus-exporter/README
@@ -4,14 +4,14 @@
 Prometheus is a monitoring and alerting system. More and more people use it to
 monitor their environment (this is written February 2019). It collects metrics
 from monitored targets by scraping metrics HTTP endpoints on these targets. For
-HAProxy, The Prometheus team offically supports an exporter written in Go
+HAProxy, The Prometheus team officially supports an exporter written in Go
 (https://github.com/prometheus/haproxy_exporter). But it requires an extra
 software to deploy and monitor. PROMEX, on its side, is a built-in Prometheus
 exporter for HAProxy. It was developed as a service and is directly available in
 HAProxy, like the stats applet.
 
 However, PROMEX is not built by default with HAProxy. It is provided as an extra
-component for everyone want to use it. So you need to explicity build HAProxy
+component for everyone want to use it. So you need to explicitly build HAProxy
 with the PROMEX service, using the Makefile variable "EXTRA_OBJS". For instance:
 
     > make TARGET=linux-glibc EXTRA_OBJS="contrib/prometheus-exporter/service-prometheus.o"
@@ -46,7 +46,7 @@
 PROMEX, all lines for a given metric are provided as one single group. So
 instead of collecting all metrics for a proxy before moving to the next one, we
 must loop on all proxies for each metric. Same for the servers. Thus, it will
-spend much more ressources to produce the Prometheus metrics than the CSV export
+spend much more resources to produce the Prometheus metrics than the CSV export
 through the stats page. To give a comparison order, quick benchmarks shown that
 a PROMEX dump is 5x slower and 20x more verbose than a CSV export.
 
@@ -99,7 +99,7 @@
 | haproxy_process_pool_used_bytes                | Total amount of memory used in pools (in bytes).                              |
 | haproxy_process_pool_failures_total            | Total number of failed pool allocations.                                      |
 | haproxy_process_max_fds                        | Maximum number of open file descriptors; 0=unset.                             |
-| haproxy_process_max_sockets                    | Maximum numer of open sockets.                                                |
+| haproxy_process_max_sockets                    | Maximum number of open sockets.                                               |
 | haproxy_process_max_connections                | Maximum number of concurrent connections.                                     |
 | haproxy_process_hard_max_connections           | Initial Maximum number of concurrent connections.                             |
 | haproxy_process_current_connections            | Number of active sessions.                                                    |
@@ -268,6 +268,9 @@
 | haproxy_server_client_aborts_total                 | Total number of data transfers aborted by the client.                     |
 | haproxy_server_server_aborts_total                 | Total number of data transfers aborted by the server.                     |
 | haproxy_server_weight                              | Service weight.                                                           |
+| haproxy_server_check_status                        | Status of last health check, if enabled. (see below for the mapping)      |
+| haproxy_server_check_code                          | layer5-7 code, if available of the last health check.                     |
+| haproxy_server_check_duration_seconds              | Total duration of the latest server health check, in seconds.             |
 | haproxy_server_check_failures_total                | Total number of failed check (Only when the server is up).                |
 | haproxy_server_check_up_down_total                 | Total number of UP->DOWN transitions.                                     |
 | haproxy_server_downtime_seconds_total              | Total downtime (in seconds) for the service.                              |
@@ -278,3 +281,30 @@
 | haproxy_server_idle_connections_current            | Current number of idle connections available for reuse.                   |
 | haproxy_server_idle_connections_limit              | Limit on the number of available idle connections.                        |
 +----------------------------------------------------+---------------------------------------------------------------------------+
+
+Mapping of health check status :
+
+   0 : HCHK_STATUS_UNKNOWN  (Unknown)
+   1 : HCHK_STATUS_INI      (Initializing)
+
+   4 : HCHK_STATUS_HANA     (Health analyze detected enough consecutive errors)
+
+   5 : HCHK_STATUS_SOCKERR  (Socket error)
+
+   6 : HCHK_STATUS_L4OK     (L4 check passed, for example tcp connect)
+   7 : HCHK_STATUS_L4TOUT   (L4 timeout)
+   8 : HCHK_STATUS_L4CON    (L4 connection problem)
+
+   9 : HCHK_STATUS_L6OK     (L6 check passed)
+  10 : HCHK_STATUS_L6TOUT   (L6 (SSL) timeout)
+  11 : HCHK_STATUS_L6RSP    (L6 invalid response - protocol error)
+
+  12 : HCHK_STATUS_L7TOUT   (L7 (HTTP/SMTP) timeout)
+  13 : HCHK_STATUS_L7RSP    (L7 invalid response - protocol error)
+  15 : HCHK_STATUS_L7OKD    (L7 check passed)
+  16 : HCHK_STATUS_L7OKCD   (L7 check conditionally passed)
+  17 : HCHK_STATUS_L7STS    (L7 response error, for example HTTP 5xx)
+
+  18 : HCHK_STATUS_PROCERR  (External process check failure)
+  19 : HCHK_STATUS_PROCTOUT (External process check timeout)
+  20 : HCHK_STATUS_PROCOK   (External process check passed)
diff --git a/contrib/prometheus-exporter/service-prometheus.c b/contrib/prometheus-exporter/service-prometheus.c
index 56d49a3..291155d 100644
--- a/contrib/prometheus-exporter/service-prometheus.c
+++ b/contrib/prometheus-exporter/service-prometheus.c
@@ -367,7 +367,7 @@
 	[ST_F_WRETR]          = ST_F_WREDIS,
 	[ST_F_WREDIS]         = ST_F_WREW,
 	[ST_F_STATUS]         = ST_F_SCUR,
-	[ST_F_WEIGHT]         = ST_F_CHKFAIL,
+	[ST_F_WEIGHT]         = ST_F_CHECK_STATUS,
 	[ST_F_ACT]            = 0,
 	[ST_F_BCK]            = 0,
 	[ST_F_CHKFAIL]        = ST_F_CHKDOWN,
@@ -385,9 +385,9 @@
 	[ST_F_RATE]           = 0,
 	[ST_F_RATE_LIM]       = 0,
 	[ST_F_RATE_MAX]       = ST_F_LASTSESS,
-	[ST_F_CHECK_STATUS]   = 0,
-	[ST_F_CHECK_CODE]     = 0,
-	[ST_F_CHECK_DURATION] = 0,
+	[ST_F_CHECK_STATUS]   = ST_F_CHECK_CODE,
+	[ST_F_CHECK_CODE]     = ST_F_CHECK_DURATION,
+	[ST_F_CHECK_DURATION] = ST_F_CHKFAIL,
 	[ST_F_HRSP_1XX]       = ST_F_HRSP_2XX,
 	[ST_F_HRSP_2XX]       = ST_F_HRSP_3XX,
 	[ST_F_HRSP_3XX]       = ST_F_HRSP_4XX,
@@ -549,7 +549,7 @@
 	[ST_F_RATE_MAX]       = IST("max_session_rate"),
 	[ST_F_CHECK_STATUS]   = IST("check_status"),
 	[ST_F_CHECK_CODE]     = IST("check_code"),
-	[ST_F_CHECK_DURATION] = IST("check_duration_milliseconds"),
+	[ST_F_CHECK_DURATION] = IST("check_duration_seconds"),
 	[ST_F_HRSP_1XX]       = IST("http_responses_total"),
 	[ST_F_HRSP_2XX]       = IST("http_responses_total"),
 	[ST_F_HRSP_3XX]       = IST("http_responses_total"),
@@ -709,9 +709,9 @@
 	[ST_F_RATE]           = IST("Current number of sessions per second over last elapsed second."),
 	[ST_F_RATE_LIM]       = IST("Configured limit on new sessions per second."),
 	[ST_F_RATE_MAX]       = IST("Maximum observed number of sessions per second."),
-	[ST_F_CHECK_STATUS]   = IST("Status of last health check (If a check is running, the status will be reported, prefixed with '* ')."),
+	[ST_F_CHECK_STATUS]   = IST("Status of last health check (HCHK_STATUS_* values)."),
 	[ST_F_CHECK_CODE]     = IST("layer5-7 code, if available of the last health check."),
-	[ST_F_CHECK_DURATION] = IST("Time in ms took to finish last health check."),
+	[ST_F_CHECK_DURATION] = IST("Total duration of the latest server health check, in seconds."),
 	[ST_F_HRSP_1XX]       = IST("Total number of HTTP responses."),
 	[ST_F_HRSP_2XX]       = IST("Total number of HTTP responses."),
 	[ST_F_HRSP_3XX]       = IST("Total number of HTTP responses."),
@@ -1027,8 +1027,8 @@
 	[ST_F_RATE]           = IST("untyped"),
 	[ST_F_RATE_LIM]       = IST("gauge"),
 	[ST_F_RATE_MAX]       = IST("gauge"),
-	[ST_F_CHECK_STATUS]   = IST("untyped"),
-	[ST_F_CHECK_CODE]     = IST("untyped"),
+	[ST_F_CHECK_STATUS]   = IST("gauge"),
+	[ST_F_CHECK_CODE]     = IST("gauge"),
 	[ST_F_CHECK_DURATION] = IST("gauge"),
 	[ST_F_HRSP_1XX]       = IST("counter"),
 	[ST_F_HRSP_2XX]       = IST("counter"),
@@ -2012,6 +2012,22 @@
 						weight = (sv->cur_eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv;
 						metric = mkf_u32(FN_AVG, weight);
 						break;
+					case ST_F_CHECK_STATUS:
+						if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
+							goto next_sv;
+						metric = mkf_u32(FN_OUTPUT, sv->check.status);
+						break;
+					case ST_F_CHECK_CODE:
+						if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
+							goto next_sv;
+						metric = mkf_u32(FN_OUTPUT, (sv->check.status < HCHK_STATUS_L57DATA) ? 0 : sv->check.code);
+						break;
+					case ST_F_CHECK_DURATION:
+						if (sv->check.status < HCHK_STATUS_CHECKED)
+						    goto next_sv;
+						secs = (double)sv->check.duration / 1000.0;
+						metric = mkf_flt(FN_DURATION, secs);
+						break;
 					case ST_F_CHKFAIL:
 						metric = mkf_u64(FN_COUNTER, sv->counters.failed_checks);
 						break;
@@ -2113,13 +2129,12 @@
 static int promex_dump_metrics(struct appctx *appctx, struct stream_interface *si, struct htx *htx)
 {
 	int ret;
-	int flags = appctx->ctx.stats.flags;
 
 	switch (appctx->st1) {
 		case PROMEX_DUMPER_INIT:
 			appctx->ctx.stats.px = NULL;
 			appctx->ctx.stats.sv = NULL;
-			appctx->ctx.stats.flags = (flags|PROMEX_FL_METRIC_HDR|PROMEX_FL_INFO_METRIC);
+			appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_INFO_METRIC);
 			appctx->st2 = promex_global_metrics[INF_NAME];
 			appctx->st1 = PROMEX_DUMPER_GLOBAL;
 			/* fall through */
@@ -2136,7 +2151,8 @@
 
 			appctx->ctx.stats.px = proxies_list;
 			appctx->ctx.stats.sv = NULL;
-			appctx->ctx.stats.flags = (flags|PROMEX_FL_METRIC_HDR|PROMEX_FL_STATS_METRIC);
+			appctx->ctx.stats.flags &= ~PROMEX_FL_INFO_METRIC;
+			appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_STATS_METRIC);
 			appctx->st2 = promex_front_metrics[ST_F_PXNAME];
 			appctx->st1 = PROMEX_DUMPER_FRONT;
 			/* fall through */
@@ -2153,7 +2169,7 @@
 
 			appctx->ctx.stats.px = proxies_list;
 			appctx->ctx.stats.sv = NULL;
-			appctx->ctx.stats.flags = (flags|PROMEX_FL_METRIC_HDR|PROMEX_FL_STATS_METRIC);
+			appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
 			appctx->st2 = promex_back_metrics[ST_F_PXNAME];
 			appctx->st1 = PROMEX_DUMPER_BACK;
 			/* fall through */
@@ -2170,7 +2186,7 @@
 
 			appctx->ctx.stats.px = proxies_list;
 			appctx->ctx.stats.sv = (appctx->ctx.stats.px ? appctx->ctx.stats.px->srv : NULL);
-			appctx->ctx.stats.flags = (flags|PROMEX_FL_METRIC_HDR|PROMEX_FL_STATS_METRIC);
+			appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
 			appctx->st2 = promex_srv_metrics[ST_F_PXNAME];
 			appctx->st1 = PROMEX_DUMPER_SRV;
 			/* fall through */
@@ -2187,7 +2203,7 @@
 
 			appctx->ctx.stats.px = NULL;
 			appctx->ctx.stats.sv = NULL;
-			appctx->ctx.stats.flags = flags;
+			appctx->ctx.stats.flags &= ~(PROMEX_FL_METRIC_HDR|PROMEX_FL_INFO_METRIC|PROMEX_FL_STATS_METRIC);
 			appctx->st2 = 0;
 			appctx->st1 = PROMEX_DUMPER_DONE;
 			/* fall through */
@@ -2220,7 +2236,8 @@
 	struct channel *res = si_ic(si);
 	struct htx *req_htx, *res_htx;
 	struct htx_sl *sl;
-	const char *p, *end;
+	char *p, *key, *value;
+	const char *end;
 	struct buffer *err;
 	int default_scopes = PROMEX_FL_SCOPE_ALL;
 	int len;
@@ -2234,57 +2251,72 @@
 	if (!p)
 		goto end;
 	end = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
-	len = end-p;
 
-	/* Decode the query-string */
+	/* copy the query-string */
+	len = end - p;
 	chunk_reset(&trash);
 	memcpy(trash.area, p, len);
 	trash.area[len] = 0;
-	len = url_decode(trash.area);
-	if (len == -1)
-		goto error;
 	p = trash.area;
-	end = p + len;
+	end = trash.area + len;
 
 	/* Parse the query-string */
-	while (p < end) {
-		if (*p == '&')
+	while (p < end && *p && *p != '#') {
+		value = NULL;
+
+		/* decode parameter name */
+		key = p;
+		while (p < end && *p != '=' && *p != '&' && *p != '#')
 			++p;
-		else if (*p == 's' && (end-p) >= 6 && !memcmp(p, "scope=", 6)) {
-			default_scopes = 0; /* at least a scope defined, unset default scopes */
-			p += 6;             /* now p point on the parameter value */
-			len = 0;            /* len is the value length */
-			while ((p+len) < end && *(p+len) != '&')
-				++len;
+		/* found a value */
+		if (*p == '=') {
+			*(p++) = 0;
+			value = p;
+		}
+		else if (*p == '&')
+			*(p++) = 0;
+		else if (*p == '#')
+			*p = 0;
+		len = url_decode(key, 1);
+		if (len == -1)
+			goto error;
 
-			if (len == 0)
+		/* decode value */
+		if (value) {
+			while (p < end && *p != '=' && *p != '&' && *p != '#')
+				++p;
+			if (*p == '=')
+				goto error;
+			if (*p == '&')
+				*(p++) = 0;
+			else if (*p == '#')
+				*p = 0;
+			len = url_decode(value, 1);
+			if (len == -1)
+				goto error;
+		}
+
+		if (!strcmp(key, "scope")) {
+			default_scopes = 0; /* at least a scope defined, unset default scopes */
+			if (!value)
+				goto error;
+			else if (*value == 0)
 				appctx->ctx.stats.flags &= ~PROMEX_FL_SCOPE_ALL;
-			else if (len == 1 && *p == '*')
+			else if (*value == '*')
 				appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_ALL;
-			else if (len == 6) {
-				if (!memcmp(p, "global", len))
-					appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_GLOBAL;
-				else if (!memcmp(p, "server", len))
-					appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_SERVER;
-			}
-			else if (len == 7 && !memcmp(p, "backend", len))
+			else if (!strcmp(value, "global"))
+				appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_GLOBAL;
+			else if (!strcmp(value, "server"))
+				appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_SERVER;
+			else if (!strcmp(value, "backend"))
 				appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_BACK;
-			else if (len == 8 && !memcmp(p, "frontend", len))
+			else if (!strcmp(value, "frontend"))
 				appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_FRONT;
 			else
 				goto error;
-
-			p += len;
 		}
-		else if (*p == 'n' && (end-p) >= 8 && !memcmp(p, "no-maint", 8)) {
+		else if (!strcmp(key, "no-maint"))
 			appctx->ctx.stats.flags |= PROMEX_FL_NO_MAINT_SRV;
-			p += 8;
-		}
-		else {
-			/* ignore all other params for now */
-			while (p < end && *p != '&')
-				p++;
-		}
 	}
 
   end:
@@ -2292,7 +2324,7 @@
 	return 1;
 
   error:
-	err = &http_err_chunks[HTTP_ERR_400];
+	err = &htx_err_chunks[HTTP_ERR_400];
 	channel_erase(res);
 	res->buf.data = b_data(err);
 	memcpy(res->buf.area, b_head(err), b_data(err));
diff --git a/contrib/spoa_example/include/mini-clist.h b/contrib/spoa_example/include/mini-clist.h
index a89255c..d009704 100644
--- a/contrib/spoa_example/include/mini-clist.h
+++ b/contrib/spoa_example/include/mini-clist.h
@@ -44,7 +44,7 @@
  * since it's used only once.
  * Example: LIST_ELEM(cur_node->args.next, struct node *, args)
  */
-#define LIST_ELEM(lh, pt, el) ((pt)(((void *)(lh)) - ((void *)&((pt)NULL)->el)))
+#define LIST_ELEM(lh, pt, el) ((pt)(((const char *)(lh)) - ((size_t)&((pt)NULL)->el)))
 
 /* checks if the list head <lh> is empty or not */
 #define LIST_ISEMPTY(lh) ((lh)->n == (lh))
diff --git a/contrib/spoa_server/README b/contrib/spoa_server/README
index d820807..6bafcde 100644
--- a/contrib/spoa_server/README
+++ b/contrib/spoa_server/README
@@ -66,7 +66,7 @@
 
 Python:
 
- * Improve repporting: Catch python error message and repport it in the right
+ * Improve reporting: Catch python error message and report it in the right
    place. Today the error are dumped on stdout. How using syslog for logging
    stack traces ?
 
diff --git a/contrib/systemd/haproxy.service.in b/contrib/systemd/haproxy.service.in
index 9b7c3d1..05fc595 100644
--- a/contrib/systemd/haproxy.service.in
+++ b/contrib/systemd/haproxy.service.in
@@ -1,6 +1,7 @@
 [Unit]
 Description=HAProxy Load Balancer
-After=network.target
+After=network-online.target
+Wants=network-online.target
 
 [Service]
 EnvironmentFile=-/etc/default/haproxy
diff --git a/doc/SPOE.txt b/doc/SPOE.txt
index 19f00ad..3661602 100644
--- a/doc/SPOE.txt
+++ b/doc/SPOE.txt
@@ -115,7 +115,7 @@
 scope in the SPOE configuration with the same name. You can have several SPOE
 scope in the same file. In each scope, you must define one and only one
 "spoe-agent" section to configure the SPOA linked to your SPOE and several
-"spoe-message" and "spoe-group" sections to describe, respecively, messages and
+"spoe-message" and "spoe-group" sections to describe, respectively, messages and
 group of messages sent to servers mananged by your SPOA.
 
 A SPOE scope starts with this kind of line :
diff --git a/doc/architecture.txt b/doc/architecture.txt
index 85d5219..8174b5d 100644
--- a/doc/architecture.txt
+++ b/doc/architecture.txt
@@ -257,7 +257,7 @@
  - if a request does not contain a cookie, it will be forwarded to a valid
    server
  - in return, if a JESSIONID cookie is seen, the server name will be prefixed
-   into it, followed by a delimitor ('~')
+   into it, followed by a delimiter ('~')
  - when the client comes again with the cookie "JSESSIONID=A~xxx", LB1 will
    know that it must be forwarded to server A. The server name will then be
    extracted from cookie before it is sent to the server.
@@ -1265,7 +1265,7 @@
 ===================
 
 Sometimes it may reveal useful to access servers from a pool of IP addresses
-instead of only one or two. Some equipments (NAT firewalls, load-balancers)
+instead of only one or two. Some equipment (NAT firewalls, load-balancers)
 are sensible to source address, and often need many sources to distribute the
 load evenly amongst their internal hash buckets.
 
diff --git a/doc/coding-style.txt b/doc/coding-style.txt
index 9f1bd79..24550f1 100644
--- a/doc/coding-style.txt
+++ b/doc/coding-style.txt
@@ -111,7 +111,7 @@
   | [-Tabs-][-Tabs-]ctx->del = len;
   | [-Tabs-]}
 
-It is worth noting that some editors tend to confuse indentations and aligment.
+It is worth noting that some editors tend to confuse indentations and alignment.
 Emacs is notoriously known for this brokenness, and is responsible for almost
 all of the alignment mess. The reason is that Emacs only counts spaces, tries
 to fill as many as possible with tabs and completes with spaces. Once you know
@@ -1218,7 +1218,7 @@
 
 Right use of comments :
 
-  | /* This function returns the positoin of the highest bit set in the lowest
+  | /* This function returns the position of the highest bit set in the lowest
   |  * byte of <x>, between 0 and 7. It only works if <x> is non-null. It uses
   |  * a 32-bit value as a lookup table to return one of 4 values for the
   |  * highest 16 possible 4-bit values.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 568fd77..dd458bd 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4,7 +4,7 @@
                          ----------------------
                               version 2.0
                              willy tarreau
-                              2019/11/25
+                              2020/07/31
 
 
 This document covers the configuration language as implemented in the version
@@ -190,11 +190,6 @@
   - server close  : the server-facing connection is closed after the response.
   - close         : the connection is actively closed after end of response.
 
-For HTTP/2, the connection mode resembles more the "server close" mode : given
-the independence of all streams, there is currently no place to hook the idle
-server connection after a response, so it is closed after the response. HTTP/2
-is only supported for incoming connections, not on connections going to
-servers.
 
 
 1.2. HTTP request
@@ -263,10 +258,6 @@
 
 HTTP/2 doesn't convey a version information with the request, so the version is
 assumed to be the same as the one of the underlying protocol (i.e. "HTTP/2").
-However, haproxy natively processes HTTP/1.x requests and headers, so requests
-received over an HTTP/2 connection are transcoded to HTTP/1.1 before being
-processed. This explains why they still appear as "HTTP/1.1" in haproxy's logs
-as well as in server logs.
 
 
 1.2.2. The request headers
@@ -284,7 +275,10 @@
 Contrary to a common misconception, header names are not case-sensitive, and
 their values are not either if they refer to other header names (such as the
 "Connection:" header). In HTTP/2, header names are always sent in lower case,
-as can be seen when running in debug mode.
+as can be seen when running in debug mode. Internally, all header names are
+normalized to lower case so that HTTP/1.x and HTTP/2 use the exact same
+representation, and they are sent as-is on the other side. This explains why an
+HTTP/1.x request typed with camel case is delivered in lower case.
 
 The end of the headers is indicated by the first empty line. People often say
 that it's a double line feed, which is not exact, even if a double line feed
@@ -365,7 +359,10 @@
    401  when an authentication is required to perform the action (when
         accessing the stats page)
    403  when a request is forbidden by a "block" ACL or "reqdeny" filter
+   404  when the requested resource could not be found
    408  when the request timeout strikes before the request is complete
+   410  when the requested resource is no longer available and will not
+        be available again
    500  when haproxy encounters an unrecoverable internal error, such as a
         memory allocation failure, which should never happen
    502  when the server returns an empty, invalid or incomplete response, or
@@ -668,6 +665,8 @@
    - tune.maxrewrite
    - tune.pattern.cache-size
    - tune.pipesize
+   - tune.pool-high-fd-ratio
+   - tune.pool-low-fd-ratio
    - tune.rcvbuf.client
    - tune.rcvbuf.server
    - tune.recv_enough
@@ -824,6 +823,10 @@
   will only be able to drop these groups if started with superuser privileges.
   See also "group" and "uid".
 
+group <group name>
+  Similar to "gid" but uses the GID of group name <group name> from /etc/group.
+  See also "gid" and "user".
+
 hard-stop-after <time>
   Defines the maximum time allowed to perform a clean soft-stop.
 
@@ -845,7 +848,7 @@
   <from>, to change it to <to> before sending it to HTTP/1 clients or
   servers. <from> must be in lower case, and <from> and <to> must not differ
   except for their case. It may be repeated if several header names need to be
-  ajusted. Duplicate entries are not allowed. If a lot of header names have to
+  adjusted. Duplicate entries are not allowed. If a lot of header names have to
   be adjusted, it might be more convenient to use "h1-case-adjust-file".
   Please note that no transformation will be applied unless "option
   h1-case-adjust-bogus-client" or "option h1-case-adjust-bogus-server" is
@@ -889,10 +892,6 @@
   See "h1-case-adjust", "option h1-case-adjust-bogus-client" and
   "option h1-case-adjust-bogus-server".
 
-group <group name>
-  Similar to "gid" but uses the GID of group name <group name> from /etc/group.
-  See also "gid" and "user".
-
 log <address> [len <length>] [format <format>] [sample <ranges>:<smp_size>]
     <facility> [max level [min level]]
   Adds a global syslog server. Several global servers can be defined. They
@@ -1411,6 +1410,10 @@
   prefixing it with the "no" keyword. It is ignored by the "select" and
   "poll" pollers.
 
+  This option is automatically disabled on old processes in the context of
+  seamless reload; it avoids too much cpu conflicts when multiple processes
+  stay around for some time waiting for the end of their current connections.
+
 max-spread-checks <delay in milliseconds>
   By default, haproxy tries to spread the start of health checks across the
   smallest health check interval of all the servers in a farm. The principle is
@@ -1849,12 +1852,6 @@
   performed. This has an impact on the kernel's memory footprint, so this must
   not be changed if impacts are not understood.
 
-tune.pool-low-fd-ratio <number>
-  This setting sets the max number of file descriptors (in percentage) used by
-  haproxy globally against the maximum number of file descriptors haproxy can
-  use before we stop putting connection into the idle pool for reuse. The
-  default is 20.
-
 tune.pool-high-fd-ratio <number>
   This setting sets the max number of file descriptors (in percentage) used by
   haproxy globally against the maximum number of file descriptors haproxy can
@@ -1864,6 +1861,12 @@
   keep an idle connection behind, anything beyond this probably doesn't make
   much sense in the general case when targeting connection reuse).
 
+tune.pool-low-fd-ratio <number>
+  This setting sets the max number of file descriptors (in percentage) used by
+  haproxy globally against the maximum number of file descriptors haproxy can
+  use before we stop putting connection into the idle pool for reuse. The
+  default is 20.
+
 tune.rcvbuf.client <number>
 tune.rcvbuf.server <number>
   Forces the kernel socket receive buffer size on the client or the server side
@@ -2333,7 +2336,7 @@
 
 In HTTP mode, the processing applied to requests and responses flowing over
 a connection depends in the combination of the frontend's HTTP options and
-the backend's. HAProxy supports 4 connection modes :
+the backend's. HAProxy supports 3 connection modes :
 
   - KAL : keep alive ("option http-keep-alive") which is the default mode : all
     requests and responses are processed, and connections remain open but idle
@@ -3347,7 +3350,7 @@
 cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ]
               [ postonly ] [ preserve ] [ httponly ] [ secure ]
               [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
-              [ dynamic ]
+              [ dynamic ] [ attr <value> ]*
   Enable cookie-based persistence in a backend.
   May be used in sections :   defaults | frontend | listen | backend
                                  yes   |    no    |   yes  |   yes
@@ -3506,6 +3509,11 @@
               The cookie will be regenerated each time the IP address change,
               and is only generated for IPv4/IPv6.
 
+    attr      This option tells haproxy to add an extra attribute when a
+              cookie is inserted. The attribute value can contain any
+              characters except control ones or ";". This option may be
+              repeated.
+
   There can be only one persistence cookie per HTTP backend, and it can be
   declared in a defaults section. The value of the cookie will be the value
   indicated after the "cookie" keyword in a "server" statement. If no cookie
@@ -3657,8 +3665,8 @@
                                  yes   |    yes   |   yes  |   yes
   Arguments :
     <code>    is the HTTP status code. Currently, HAProxy is capable of
-              generating codes 200, 400, 403, 405, 408, 425, 429, 500, 502,
-              503, and 504.
+              generating codes 200, 400, 403, 404, 405, 408, 410, 413, 425, 429,
+              500, 502, 503, and 504.
 
     <file>    designates a file containing the full HTTP response. It is
               recommended to follow the common practice of appending ".http" to
@@ -3706,8 +3714,8 @@
                                  yes   |    yes   |   yes  |   yes
   Arguments :
     <code>    is the HTTP status code. Currently, HAProxy is capable of
-              generating codes 200, 400, 403, 405, 408, 425, 429, 500, 502,
-              503, and 504.
+              generating codes 200, 400, 403, 404, 405, 408, 410, 413, 425, 429,
+              500, 502, 503, and 504.
 
     <url>     it is the exact contents of the "Location" header. It may contain
               either a relative URI to an error page hosted on the same site,
@@ -3738,8 +3746,8 @@
                                  yes   |    yes   |   yes  |   yes
   Arguments :
     <code>    is the HTTP status code. Currently, HAProxy is capable of
-              generating codes 200, 400, 403, 405, 408, 425, 429, 500, 502,
-              503, and 504.
+              generating codes 200, 400, 403, 404, 405, 408, 410, 413, 425, 429,
+              500, 502, 503, and 504.
 
     <url>     it is the exact contents of the "Location" header. It may contain
               either a relative URI to an error page hosted on the same site,
@@ -4203,6 +4211,33 @@
 
   See also : "option httpchk", "http-check disable-on-404"
 
+
+http-check send [hdr <name> <value>]* [body <string>]
+  Add a possible list of headers and/or a body to the request sent during HTTP
+  health checks.
+  May be used in sections :   defaults | frontend | listen | backend
+                                 yes   |    no    |   yes  |   yes
+  Arguments :
+    hdr <name> <value>   adds the HTTP header field whose name is specified in
+                         <name> and whose value is defined by <value> to the
+                         request sent during HTTP health checks.
+
+    body <string>        add the body defined by <string> to the request sent
+                         sent during HTTP health checks. If defined, the
+                         "Content-Length" header is thus automatically added
+                         to the request.
+
+  In addition to the request line defined by the "option httpchk" directive,
+  this one is the valid way to add some headers and optionally a body to the
+  request sent during HTTP health checks. If a body is defined, the associate
+  "Content-Length" header is automatically added. The old trick consisting to
+  add headers after the version string on the "option httpchk" line is now
+  deprecated. Note also the "Connection: close" header is still added if a
+  "http-check expect" direcive is defined independently of this directive, just
+  like the state header if the directive "http-check send-state" is defined.
+
+  See also : "option httpchk", "http-check send-state" and "http-check expect"
+
 
 http-check send-state
   Enable emission of a state header with HTTP health checks
@@ -4375,9 +4410,12 @@
   If the keyword "id" is used instead of "len", the action tries to store the
   captured string in a previously declared capture slot. This is useful to run
   captures in backends. The slot id can be declared by a previous directive
-  "http-request capture" or with the "declare capture" keyword. If the slot
-  <id> doesn't exist, then HAProxy fails parsing the configuration to prevent
-  unexpected behavior at run time.
+  "http-request capture" or with the "declare capture" keyword.
+
+  When using this action in a backend, double check that the relevant
+  frontend(s) have the required capture slots otherwise, this rule will be
+  ignored at run time. This can't be detected at configuration parsing time
+  due to HAProxy's ability to dynamically resolve backend name at runtime.
 
 http-request del-acl(<file-name>) <key fmt> [ { if | unless } <condition> ]
 
@@ -4497,69 +4535,109 @@
 http-request replace-header <name> <match-regex> <replace-fmt>
                             [ { if | unless } <condition> ]
 
-    This  matches the regular expression in all occurrences of header field
-    <name> according to <match-regex>, and replaces them with the
-    <replace-fmt> argument. Format characters are allowed in replace-fmt and
-    work like in <fmt> arguments in "http-request add-header". The match is
-    only case-sensitive. It is important to understand that this action only
-    considers whole header lines, regardless of the number of values they may
-    contain. This usage is suited to headers naturally containing commas in
-    their value, such as If-Modified-Since and so on.
+  This matches the value of all occurrences of header field <name> against
+  <match-regex>. Matching is performed case-sensitively. Matching values are
+  completely replaced by <replace-fmt>. Format characters are allowed in
+  <replace-fmt> and work like <fmt> arguments in "http-request add-header".
+  Standard back-references using the backslash ('\') followed by a number are
+  supported.
 
-    Example:
-      http-request replace-header Cookie foo=([^;]*);(.*) foo=\1;ip=%bi;\2
+  This action acts on whole header lines, regardless of the number of values
+  they may contain. Thus it is well-suited to process headers naturally
+  containing commas in their value, such as If-Modified-Since. Headers that
+  contain a comma-separated list of values, such as Accept, should be processed
+  using "http-request replace-value".
 
-      # applied to:
-      Cookie: foo=foobar; expires=Tue, 14-Jun-2016 01:40:45 GMT;
+  Example:
+    http-request replace-header Cookie foo=([^;]*);(.*) foo=\1;ip=%bi;\2
 
-      # outputs:
-      Cookie: foo=foobar;ip=192.168.1.20; expires=Tue, 14-Jun-2016 01:40:45 GMT;
+    # applied to:
+    Cookie: foo=foobar; expires=Tue, 14-Jun-2016 01:40:45 GMT;
 
-      # assuming the backend IP is 192.168.1.20
+    # outputs:
+    Cookie: foo=foobar;ip=192.168.1.20; expires=Tue, 14-Jun-2016 01:40:45 GMT;
+
+    # assuming the backend IP is 192.168.1.20
+
+    http-request replace-header User-Agent curl foo
+
+    # applied to:
+    User-Agent: curl/7.47.0
+
+    # outputs:
+    User-Agent: foo
+
+http-request replace-path <match-regex> <replace-fmt>
+                           [ { if | unless } <condition> ]
+
+  This works like "replace-header" except that it works on the request's path
+  component instead of a header. The path component starts at the first '/'
+  after an optional scheme+authority. It does contain the query string if any
+  is present. The replacement does not modify the scheme nor authority.
+
+  It is worth noting that regular expressions may be more expensive to evaluate
+  than certain ACLs, so rare replacements may benefit from a condition to avoid
+  performing the evaluation at all if it does not match.
+
+  Example:
+    # prefix /foo : turn /bar?q=1 into /foo/bar?q=1 :
+    http-request replace-path (.*) /foo\1
+
+    # suffix /foo : turn /bar?q=1 into /bar/foo?q=1 :
+    http-request replace-path ([^?]*)(\?(.*))? \1/foo\2
+
+    # strip /foo : turn /foo/bar?q=1 into /bar?q=1
+    http-request replace-path /foo/(.*) /\1
+    # or more efficient if only some requests match :
+    http-request replace-path /foo/(.*) /\1 if { url_beg /foo/ }
 
 http-request replace-uri <match-regex> <replace-fmt>
                            [ { if | unless } <condition> ]
 
+  This works like "replace-header" except that it works on the request's URI part
+  instead of a header. The URI part may contain an optional scheme, authority or
+  query string. These are considered to be part of the value that is matched
+  against.
+
-    This matches the regular expression in the URI part of the request
-    according to <match-regex>, and replaces it with the <replace-fmt>
-    argument. Standard back-references using the backslash ('\') followed by a
-    number are supported. The <fmt> field is interpreted as a log-format string
-    so it may contain special expressions just like the <fmt> argument passed
-    to "http-request set-uri". The match is exclusively case-sensitive. Any
-    optional scheme, authority or query string are considered in the matching
-    part of the URI. It is worth noting that regular expressions may be more
-    expensive to evaluate than certain ACLs, so rare replacements may benefit
-    from a condition to avoid performing the evaluation at all if it does not
-    match.
+  It is worth noting that regular expressions may be more expensive to evaluate
+  than certain ACLs, so rare replacements may benefit from a condition to avoid
+  performing the evaluation at all if it does not match.
 
-    Example:
-        # prefix /foo : turn /bar?q=1 into /foo/bar?q=1 :
-        http-request replace-uri (.*) /foo\1
+  IMPORTANT NOTE: historically in HTTP/1.x, the vast majority of requests sent
+  by browsers use the "origin form", which differs from the "absolute form" in
+  that they do not contain a scheme nor authority in the URI portion. Mostly
+  only requests sent to proxies, those forged by hand and some emitted by
+  certain applications use the absolute form. As such, "replace-uri" usually
+  works fine most of the time in HTTP/1.x with rules starting with a "/". But
+  with HTTP/2, clients are encouraged to send absolute URIs only, which look
+  like the ones HTTP/1 clients use to talk to proxies. Such partial replace-uri
+  rules may then fail in HTTP/2 when they work in HTTP/1. Either the rules need
+  to be adapted to optionally match a scheme and authority, or replace-path
+  should be used.
 
-        # suffix /foo : turn /bar?q=1 into /bar/foo?q=1 :
-        http-request replace-uri ([^?]*)(\?(.*))? \1/foo\2
+  Example:
+    # rewrite all "http" absolute requests to "https":
+    http-request replace-uri ^http://(.*) https://\1
 
-        # strip /foo : turn /foo/bar?q=1 into /bar?q=1
-        http-request replace-uri /foo/(.*) /\1
-        # or more efficient if only some requests match :
-        http-request replace-uri /foo/(.*) /\1 if { url_beg /foo/ }
+    # prefix /foo : turn /bar?q=1 into /foo/bar?q=1 :
+    http-request replace-uri ([^/:]*://[^/]*)?(.*) \1/foo\2
 
 http-request replace-value <name> <match-regex> <replace-fmt>
                            [ { if | unless } <condition> ]
 
-    This works like "replace-header" except that it matches the regex against
-    every comma-delimited value of the header field <name> instead of the
-    entire header. This is suited for all headers which are allowed to carry
-    more than one value. An example could be the Accept header.
+  This works like "replace-header" except that it matches the regex against
+  every comma-delimited value of the header field <name> instead of the
+  entire header. This is suited for all headers which are allowed to carry
+  more than one value. An example could be the Accept header.
 
-    Example:
-      http-request replace-value X-Forwarded-For ^192\.168\.(.*)$ 172.16.\1
+  Example:
+    http-request replace-value X-Forwarded-For ^192\.168\.(.*)$ 172.16.\1
 
-      # applied to:
-      X-Forwarded-For: 192.168.10.1, 192.168.13.24, 10.0.0.37
+    # applied to:
+    X-Forwarded-For: 192.168.10.1, 192.168.13.24, 10.0.0.37
 
-      # outputs:
-      X-Forwarded-For: 172.16.10.1, 172.16.13.24, 10.0.0.37
+    # outputs:
+    X-Forwarded-For: 172.16.10.1, 172.16.13.24, 10.0.0.37
 
 http-request sc-inc-gpc0(<sc-id>) [ { if | unless } <condition> ]
 http-request sc-inc-gpc1(<sc-id>) [ { if | unless } <condition> ]
@@ -4733,16 +4811,23 @@
   This is used to set the source IP address to the value of specified
   expression. Useful when a proxy in front of HAProxy rewrites source IP, but
   provides the correct IP in a HTTP header; or you want to mask source IP for
-  privacy.
+  privacy. All subsequent calls to "src" fetch will return this value
+  (see example).
 
   Arguments :
     <expr>  Is a standard HAProxy expression formed by a sample-fetch followed
             by some converters.
 
+  See also "option forwardfor".
+
   Example:
     http-request set-src hdr(x-forwarded-for)
     http-request set-src src,ipmask(24)
 
+    # After the masking this will track connections
+    # based on the IP address with the last byte zeroed out.
+    http-request track-sc0 src
+
   When possible, set-src preserves the original source port as long as the
   address family allows it, otherwise the source port is set to 0.
 
@@ -5024,8 +5109,11 @@
   This is useful to run captures in backends. The slot id can be declared by a
   previous directive "http-response capture" or with the "declare capture"
   keyword.
-  If the slot <id> doesn't exist, then HAProxy fails parsing the configuration
-  to prevent unexpected behavior at run time.
+
+  When using this action in a backend, double check that the relevant
+  frontend(s) have the required capture slots otherwise, this rule will be
+  ignored at run time. This can't be detected at configuration parsing time
+  due to HAProxy's ability to dynamically resolve backend name at runtime.
 
 http-response del-acl(<file-name>) <key fmt> [ { if | unless } <condition> ]
 
@@ -5066,14 +5154,8 @@
 http-response replace-header <name> <regex-match> <replace-fmt>
                              [ { if | unless } <condition> ]
 
-  This matches the regular expression in all occurrences of header field <name>
-  according to <match-regex>, and replaces them with the <replace-fmt> argument.
-  Format characters are allowed in replace-fmt and work like in <fmt> arguments
-  in "add-header". The match is only case-sensitive. It is important to
-  understand that this action only considers whole header lines, regardless of
-  the number of values they may contain. This usage is suited to headers
-  naturally containing commas in their value, such as Set-Cookie, Expires and
-  so on.
+  This works like "http-request replace-header" except that it works on the
+  server's response instead of the client's request.
 
   Example:
     http-response replace-header Set-Cookie (C=[^;]*);(.*) \1;ip=%bi;\2
@@ -5089,10 +5171,8 @@
 http-response replace-value <name> <regex-match> <replace-fmt>
                             [ { if | unless } <condition> ]
 
-  This works like "replace-header" except that it matches the regex against
-  every comma-delimited value of the header field <name> instead of the entire
-  header. This is suited for all headers which are allowed to carry more than
-  one value. An example could be the Accept header.
+  This works like "http-response replace-value" except that it works on the
+  server's response instead of the client's request.
 
   Example:
     http-response replace-value Cache-control ^public$ private
@@ -6700,8 +6780,7 @@
     <version> is the optional HTTP version string. It defaults to "HTTP/1.0"
               but some servers might behave incorrectly in HTTP 1.0, so turning
               it to HTTP/1.1 may sometimes help. Note that the Host field is
-              mandatory in HTTP/1.1, and as a trick, it is possible to pass it
-              after "\r\n" following the version string.
+              mandatory in HTTP/1.1, use "http-check send" directive to add it.
 
   By default, server health checks only consist in trying to establish a TCP
   connection. When "option httpchk" is specified, a complete HTTP request is
@@ -6715,12 +6794,18 @@
   plain TCP backends. This is particularly useful to check simple scripts bound
   to some dedicated ports using the inetd daemon.
 
+  Note : For a while, there was no way to add headers or body in the request
+         used for HTTP health checks. So a workaround was to hide it at the end
+         of the version string with a "\r\n" after the version. It is now
+	 deprecated. The directive "http-check send" must be used instead.
+
   Examples :
       # Relay HTTPS traffic to Apache instance and check service availability
       # using HTTP request "OPTIONS * HTTP/1.1" on port 80.
       backend https_relay
           mode tcp
-          option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www
+          option httpchk OPTIONS * HTTP/1.1
+          http-check send hdr Host www
           server apache1 192.168.1.1:443 check port 80
 
   See also : "option ssl-hello-chk", "option smtpchk", "option mysql-check",
@@ -6945,21 +7030,27 @@
 
 option logasap
 no option logasap
-  Enable or disable early logging of HTTP requests
+  Enable or disable early logging.
   May be used in sections :   defaults | frontend | listen | backend
                                  yes   |    yes   |   yes  |   no
   Arguments : none
 
-  By default, HTTP requests are logged upon termination so that the total
-  transfer time and the number of bytes appear in the logs. When large objects
-  are being transferred, it may take a while before the request appears in the
-  logs. Using "option logasap", the request gets logged as soon as the server
-  sends the complete headers. The only missing information in the logs will be
-  the total number of bytes which will indicate everything except the amount
-  of data transferred, and the total time which will not take the transfer
-  time into account. In such a situation, it's a good practice to capture the
-  "Content-Length" response header so that the logs at least indicate how many
-  bytes are expected to be transferred.
+  By default, logs are emitted when all the log format variables and sample
+  fetches used in the definition of the log-format string return a value, or
+  when the session is terminated. This allows the built in log-format strings
+  to account for the transfer time, or the number of bytes in log messages.
+
+  When handling long lived connections such as large file transfers or RDP,
+  it may take a while for the request or connection to appear in the logs.
+  Using "option logasap", the log message is created as soon as the server
+  connection is established in mode tcp, or as soon as the server sends the
+  complete headers in mode http. Missing information in the logs will be the
+  total number of bytes which will only indicate the amount of data transfered
+  before the message was created and the total time which will not take the
+  remainder of the connection life or transfer time into account. For the case
+  of HTTP, it is good practice to capture the Content-Length response header
+  so that the logs at least indicate how many bytes are expected to be
+  transfered.
 
   Examples :
       listen http_proxy 0.0.0.0:80
@@ -8537,7 +8628,7 @@
     global
       server-state-file-base /etc/haproxy/states
 
-   backend bk
+    backend bk
       load-server-state-from-file
 
   See also: "server-state-file-base", "load-server-state-from-file", and
@@ -11514,10 +11605,11 @@
 
         <crtfile> [\[<sslbindconf> ...\]] [[!]<snifilter> ...]
 
-  sslbindconf support "npn", "alpn", "verify", "ca-file", "no-ca-names",
-  crl-file", "ecdhe", "curves", "ciphers" configuration. With BoringSSL
-  and Openssl >= 1.1.1 "ssl-min-ver" and "ssl-max-ver" are also supported.
-  It override the configuration set in bind line for the certificate.
+  sslbindconf supports "allow-0rtt", "alpn", "ca-file", "ciphers",
+  "ciphersuites", "crl-file", "curves", "ecdhe", "no-ca-names", "npn",
+  "verify" configuration. With BoringSSL and Openssl >= 1.1.1
+  "ssl-min-ver" and "ssl-max-ver" are also supported. It overrides the
+  configuration set in bind line for the certificate.
 
   Wildcards are supported in the SNI filter. Negative filter are also supported,
   only useful in combination with a wildcard filter to exclude a particular SNI.
@@ -11732,6 +11824,9 @@
   extension) and force to use stateful session resumption. Stateless
   session resumption is more expensive in CPU usage. This option is also
   available on global statement "ssl-default-bind-options".
+  The TLS ticket mechanism is only used up to TLS 1.2.
+  Forward Secrecy is compromised with TLS tickets, unless ticket keys
+  are periodically rotated (via reload or by using "tls-ticket-keys").
 
 no-tlsv10
   This setting is only available when support for OpenSSL was built in. It
@@ -12301,13 +12396,19 @@
 maxconn <maxconn>
   The "maxconn" parameter specifies the maximal number of concurrent
   connections that will be sent to this server. If the number of incoming
-  concurrent requests goes higher than this value, they will be queued, waiting
-  for a connection to be released. This parameter is very important as it can
+  concurrent connections goes higher than this value, they will be queued,
+  waiting for a slot to be released. This parameter is very important as it can
   save fragile servers from going down under extreme loads. If a "minconn"
   parameter is specified, the limit becomes dynamic. The default value is "0"
   which means unlimited. See also the "minconn" and "maxqueue" parameters, and
   the backend's "fullconn" keyword.
 
+  In HTTP mode this parameter limits the number of concurrent requests instead
+  of the number of connections. Multiple requests might be multiplexed over a
+  single TCP connection to the server. As an example if you specify a maxconn
+  of 50 you might see between 1 and 50 actual server connections, but no more
+  than 50 concurrent requests.
+
 maxqueue <maxqueue>
   The "maxqueue" parameter specifies the maximal number of connections which
   will wait in the queue for this server. If this limit is reached, next
@@ -12425,6 +12526,9 @@
   extension) and force to use stateful session resumption. Stateless
   session resumption is more expensive in CPU usage for servers. This option
   is also available on global statement "ssl-default-server-options".
+  The TLS ticket mechanism is only used up to TLS 1.2.
+  Forward Secrecy is compromised with TLS tickets, unless ticket keys
+  are periodically rotated (via reload or by using "tls-ticket-keys").
   See also "tls-tickets".
 
 no-tlsv10
@@ -12849,8 +12953,11 @@
   This option may be used as "server" setting to reset any "no-tls-tickets"
   setting which would have been inherited from "default-server" directive as
   default value.
+  The TLS ticket mechanism is only used up to TLS 1.2.
+  Forward Secrecy is compromised with TLS tickets, unless ticket keys
+  are periodically rotated (via reload or by using "tls-ticket-keys").
   It may also be used as "default-server" setting to reset any previous
-  "default-server" "no-tlsv-tickets" setting.
+  "default-server" "no-tls-tickets" setting.
 
 verify [none|required]
   This setting is only available when support for OpenSSL was built in. If set
@@ -13528,6 +13635,14 @@
 to match the string "-i", either set it second, or pass the "--" flag
 before the first string. Same applies of course to match the string "--".
 
+Do not use string matches for binary fetches which might contain null bytes
+(0x00), as the comparison stops at the occurrence of the first null byte.
+Instead, convert the binary fetch to a hex string with the hex converter first.
+
+Example:
+    # matches if the string <tag> is present in the binary sample
+    acl tag_found req.payload(0,0),hex -m sub 3C7461673E
+
 
 7.1.4. Matching regular expressions (regexes)
 ---------------------------------------------
@@ -14450,9 +14565,13 @@
   sample fetch function or after a transformation keyword returning a string
   type. The result is of type string.
 
-url_dec
-  Takes an url-encoded string provided as input and returns the decoded
-  version as output. The input and the output are of type string.
+url_dec([<in_form>])
+  Takes an url-encoded string provided as input and returns the decoded version
+  as output. The input and the output are of type string. If the <in_form>
+  argument is set to a non-zero integer value, the input string is assumed to
+  be part of a form or query string and the '+' character will be turned into a
+  space (' '). Otherwise this will only happen after a question mark indicating
+  a query string ('?').
 
 ungrpc(<field_number>,[<field_type>])
   This extracts the protocol buffers message field in raw mode of an input binary
@@ -14543,6 +14662,7 @@
   Extracts the nth word counting from the beginning (positive index) or from
   the end (negative index) considering given delimiters from an input string.
   Indexes start at 1 or -1 and delimiters are a string formatted list of chars.
+  Delimiters at the beginning or end of the input string are ignored.
   Optionally you can specify <count> of words to extract (default: 1).
   Value of 0 indicates extraction of all remaining words.
 
@@ -14552,6 +14672,7 @@
       str(f1_f2_f3__f5),word(3,_,2)  # f3__f5
       str(f1_f2_f3__f5),word(-2,_,3) # f1_f2_f3
       str(f1_f2_f3__f5),word(-3,_,0) # f1_f2
+      str(/f1/f2/f3/f4),word(1,/)    # f1
 
 wt6([<avalanche>])
   Hashes a binary input sample into an unsigned 32-bit quantity using the WT6
@@ -15401,6 +15522,11 @@
   in frontends involving many "bind" lines, or to stick all users coming via a
   same socket to the same server.
 
+so_name : string
+  Returns a string containing the current listening socket's name, as defined
+  with name on a "bind" line. It can serve the same purposes as so_id but with
+  strings instead of integers.
+
 src : ip
   This is the source IPv4 address of the client of the session. It is of type
   IP and works on both IPv4 and IPv6 tables. On IPv6 tables, IPv4 addresses are
diff --git a/doc/internals/filters.txt b/doc/internals/filters.txt
index 7705ee2..d1e0c4d 100644
--- a/doc/internals/filters.txt
+++ b/doc/internals/filters.txt
@@ -54,7 +54,7 @@
 be involved in the data processing, from the stream creation/destruction to
 the data forwarding. Depending of what it should do, a filter can implement all
 or part of these callbacks. For now, existing callbacks are focused on
-streams. But futur improvements could enlarge filters scope. For example, it
+streams. But future improvements could enlarge filters scope. For example, it
 could be useful to handle events at the connection level.
 
 In HAProxy configuration file, a filter is declared in a proxy section, except
@@ -84,7 +84,7 @@
 processing is serialized, each filter will bahave as it was alone (unless it was
 developed to be aware of other filters). For all that, some constraints are
 imposed to filters, especially when data exchanged between the client and the
-server are processed. We will dicuss again these contraints when we will tackle
+server are processed. We will discuss again these constraints when we will tackle
 the subject of writing a filter.
 
 
@@ -122,11 +122,11 @@
 Multiple filter lines can be used in a proxy section to chain filters. Filters
 will be called in the declaration order.
 
-Some filters can support implicit declarartions in certain circumstances
-(without the filter line). This is not recommanded for new features but are
+Some filters can support implicit declarations in certain circumstances
+(without the filter line). This is not recommended for new features but are
 useful for existing ones moved in a filter, for backward compatibility
-reasons. Implicit declarartions are supported when there is only one filter used
-on a proxy. When several filters are used, explicit declarartions are mandatory.
+reasons. Implicit declarations are supported when there is only one filter used
+on a proxy. When several filters are used, explicit declarations are mandatory.
 The HTTP compression filter is one of these filters. Alone, using 'compression'
 keywords is enough to use it. But when at least a second filter is used, a
 filter line must be added.
@@ -283,7 +283,7 @@
 instances attached to a stream:
 
     /*
-     * Structure reprensenting the "global" state of filters attached to a
+     * Structure representing the "global" state of filters attached to a
      * stream.
      */
     struct strm_flt {
@@ -302,7 +302,7 @@
 'strm_flt.filters', each instance is of type 'struct filter *':
 
     /*
-     * Structure reprensenting a filter instance attached to a stream
+     * Structure representing a filter instance attached to a stream
      *
      * 2D-Array fields are used to store info per channel. The first index
      * stands for the request channel, and the second one for the response
@@ -659,7 +659,7 @@
 The main purpose of filters is to take part in the channels analyzing. To do so,
 there is 2 callbacks, 'flt_ops.channel_pre_analyze' and
 'flt_ops.channel_post_analyze', called respectively before and after each
-analyzer attached to a channel, execpt analyzers responsible for the data
+analyzer attached to a channel, except analyzers responsible for the data
 parsing/forwarding (TCP or HTTP data). Concretely, on the request channel, these
 callbacks could be called before following analyzers:
 
diff --git a/doc/internals/hashing.txt b/doc/internals/hashing.txt
index 281dcf6..af66de2 100644
--- a/doc/internals/hashing.txt
+++ b/doc/internals/hashing.txt
@@ -2,7 +2,7 @@
 
 This document describes how Haproxy implements hashing both map-based and
 consistent hashing, both prior to versions 1.5 and the motivation and tests
-that were done when providing additional options starting in version 1.5.
+that were done when providing additional options starting in version 2.0
 
 A note on hashing in general, hash functions strive to have little
 correlation between input and output. The heart of a hash function is its
@@ -79,5 +79,5 @@
 applicable and may result in less smooth distribution.
 
 References:
-Mixing Functions/Avalanche: http://home.comcast.net/~bretm/hash/3.html
+Mixing Functions/Avalanche: https://papa.bretmulvey.com/post/124027987928/hash-functions
 Hash Functions: http://www.cse.yorku.ca/~oz/hash.html
diff --git a/doc/internals/listener-states.fig b/doc/internals/listener-states.fig
new file mode 100644
index 0000000..1f78203
--- /dev/null
+++ b/doc/internals/listener-states.fig
@@ -0,0 +1,169 @@
+#FIG 3.2  Produced by xfig version 2.0
+Portrait
+Center
+Metric
+A4
+300.00
+Single
+-2
+1200 2
+0 32 #ff60e0
+0 33 #ff8020
+0 34 #56c5ff
+0 35 #55d941
+0 36 #f8e010
+6 3105 1305 4185 1845
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 3645 1575 495 225 3645 1575 4140 1575
+4 1 0 50 -1 18 10 0.0000 4 120 555 3645 1575 LISTEN\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 3645 1755 5\001
+-6
+6 2160 2205 3240 2745
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 2700 2475 495 225 2700 2475 3195 2475
+4 1 0 50 -1 18 10 0.0000 4 120 630 2700 2475 LIMITED\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 2700 2655 8\001
+-6
+6 2160 3195 3240 3735
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 2700 3471 495 225 2700 3471 3195 3471
+4 1 0 50 -1 18 10 0.0000 4 120 630 2700 3465 PAUSED\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 2700 3645 3\001
+-6
+6 3960 2205 5040 2745
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 4500 2475 495 225 4500 2475 4995 2475
+4 1 0 50 -1 18 10 0.0000 4 120 375 4500 2475 FULL\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 4500 2655 7\001
+-6
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 900 450 495 225 900 450 1395 450
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 2700 450 495 225 2700 450 3195 450
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 4500 450 495 225 4500 450 4995 450
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 900 1305 495 225 900 1305 1395 1305
+1 1 0 3 0 7 51 -1 20 0.000 1 0.0000 900 3465 495 225 900 3465 1395 3465
+2 1 1 3 1 7 52 -1 -1 8.000 1 0 -1 0 0 2
+	 270 1980 5355 1350
+2 2 0 2 32 32 52 -1 20 0.000 1 0 -1 0 0 5
+	 2070 3060 3330 3060 3330 3870 2070 3870 2070 3060
+2 3 0 1 33 33 53 -1 20 0.000 1 0 -1 0 0 5
+	 2070 990 5130 990 5130 2880 2070 2880 2070 990
+2 2 0 2 35 35 52 -1 20 0.000 1 0 -1 0 0 5
+	 270 90 5130 90 5130 855 270 855 270 90
+2 2 0 2 34 34 52 -1 20 0.000 1 0 -1 0 0 5
+	 270 990 1530 990 1530 1665 270 1665 270 990
+2 2 0 2 36 36 52 -1 20 0.000 1 0 -1 0 0 5
+	 270 3060 1530 3060 1530 3870 270 3870 270 3060
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 2
+	1 1 1.00 60.00 120.00
+	 1395 450 2250 450
+	 0.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 2
+	1 1 1.00 60.00 120.00
+	 3195 450 4050 450
+	 0.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 4095 1665 4455 2025 4500 2250
+	 0.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 3195 3510 3600 3465 4140 2655
+	 0.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 3195 1485 2970 1305 1350 1305
+	 0.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 4410 2250 4365 2070 4050 1710
+	 0.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 4
+	1 1 1.00 60.00 120.00
+	 945 3240 936 2142 2961 1917 3240 1710
+	 0.000 1.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 4
+	1 1 1.00 60.00 120.00
+	 3195 1665 2835 1845 855 2115 855 3240
+	 0.000 1.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 5
+	1 1 1.00 60.00 120.00
+	 990 3690 1035 3960 2880 4050 4365 3915 4410 2700
+	 0.000 1.000 1.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 2
+	1 1 1.00 60.00 120.00
+	 2700 2700 2700 3240
+	 0.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 4095 2610 3600 3375 3150 3420
+	 0.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 5
+	1 1 1.00 60.00 120.00
+	 4500 2700 4455 4005 2655 4140 945 4005 900 3690
+	 0.000 1.000 1.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 4
+	1 1 1.00 60.00 120.00
+	 2205 2520 1395 2745 1260 2970 1125 3240
+	 0.000 1.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 4
+	1 1 1.00 60.00 120.00
+	 3510 1800 3330 2025 3330 2835 2970 3285
+	 0.000 1.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 4
+	1 1 1.00 60.00 120.00
+	 1170 3285 1305 3015 1485 2790 2250 2610
+	 0.000 1.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 2205 3420 1710 3420 1395 3465
+	 0.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 1395 3510 1800 3510 2205 3465
+	 0.000 1.000 0.000
+3 0 0 3 0 7 51 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 2925 2295 3060 1980 3330 1755
+	 0.000 1.000 0.000
+3 0 0 3 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 4500 675 4455 990 3960 1395
+	 0.000 1.000 0.000
+4 1 0 50 -1 18 10 0.0000 4 120 375 900 450 NEW\001
+4 1 0 50 -1 18 10 0.0000 4 120 315 2700 450 INIT\001
+4 1 0 50 -1 18 10 0.0000 4 120 810 4500 450 ASSIGNED\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 900 630 0\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 2700 630 1\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 4500 630 2\001
+4 1 0 50 -1 16 7 0.0000 4 120 420 1755 405 create()\001
+4 2 0 50 -1 16 7 0.0000 4 120 660 1215 2160 enable() &&\001
+4 2 0 50 -1 16 7 0.0000 4 90 540 1080 2295 !maxconn\001
+4 2 1 51 -1 16 7 1.5708 4 105 600 5355 1485 transitions\001
+4 0 1 51 -1 16 7 1.5708 4 105 600 5355 1260 transitions\001
+4 2 1 51 -1 16 7 1.5708 4 105 795 5265 1485 multi-threaded\001
+4 0 1 51 -1 16 7 1.5708 4 120 870 5265 1260 single-threaded\001
+4 1 0 50 -1 18 10 0.0000 4 120 615 900 1305 ZOMBIE\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 900 1485 4\001
+4 0 0 52 -1 17 7 0.0000 4 90 345 315 765 no FD\001
+4 0 0 52 -1 17 7 0.0000 4 135 315 315 3825 polled\001
+4 1 0 50 -1 18 10 0.0000 4 120 555 900 3465 READY\001
+4 1 1 50 -1 16 10 0.0000 4 120 90 900 3645 6\001
+4 0 0 50 -1 16 7 0.0000 4 120 255 1170 3825 full()\001
+4 2 0 50 -1 16 7 0.0000 4 90 540 2205 3375 !maxconn\001
+4 2 0 50 -1 16 7 0.0000 4 105 675 2295 3240 resume() &&\001
+4 0 0 50 -1 16 7 0.0000 4 105 405 1395 3645 pause()\001
+4 0 0 52 -1 17 7 0.0000 4 135 585 2115 3825 shut(sock)\001
+4 2 0 50 -1 16 7 0.0000 4 120 480 4320 2205 disable()\001
+4 2 0 50 -1 16 7 0.0000 4 105 405 4005 2655 pause()\001
+4 0 0 50 -1 16 7 0.0000 4 105 465 4545 2835 resume()\001
+4 2 0 50 -1 16 7 0.0000 4 120 480 2925 2160 disable()\001
+4 0 0 50 -1 16 7 0.0000 4 105 405 3465 1980 pause()\001
+4 0 0 50 -1 16 7 0.0000 4 120 660 4230 1710 enable() &&\001
+4 0 0 50 -1 16 7 0.0000 4 75 510 4320 1845 maxconn\001
+4 2 0 50 -1 16 7 0.0000 4 105 405 2655 2835 pause()\001
+4 0 0 50 -1 16 7 0.0000 4 105 675 3375 3555 resume() &&\001
+4 0 0 50 -1 16 7 0.0000 4 75 510 3375 3645 maxconn\001
+4 0 0 50 -1 16 7 0.0000 4 120 480 1080 2655 disable()\001
+4 2 0 50 -1 16 7 0.0000 4 105 465 2160 2475 resume()\001
+4 1 0 50 -1 16 7 0.0000 4 120 330 3555 405 .add()\001
+4 0 0 50 -1 16 7 0.0000 4 120 375 4545 810 .bind()\001
+4 0 0 52 -1 17 7 0.0000 4 135 1080 2115 1125 FD ready, not polled\001
+4 0 0 52 -1 17 7 0.0000 4 135 420 315 1620 stopped\001
+4 0 0 50 -1 16 7 0.0000 4 120 315 1305 3240 limit()\001
+4 2 0 50 -1 16 7 0.0000 4 120 930 3060 1530 zombify_proxy()\001
diff --git a/doc/internals/listener-states.png b/doc/internals/listener-states.png
new file mode 100644
index 0000000..15e65dd
--- /dev/null
+++ b/doc/internals/listener-states.png
Binary files differ
diff --git a/doc/intro.txt b/doc/intro.txt
index a60844c..ff3b3db 100644
--- a/doc/intro.txt
+++ b/doc/intro.txt
@@ -85,9 +85,6 @@
   - configuration.txt : the reference manual details all configuration keywords
     and their options. It is used when a configuration change is needed.
 
-  - architecture.txt : the architecture manual explains how to best architect a
-    load-balanced infrastructure and how to interact with third party products.
-
   - coding-style.txt : this is for developers who want to propose some code to
     the project. It explains the style to adopt for the code. It is not very
     strict and not all the code base completely respects it, but contributions
diff --git a/doc/management.txt b/doc/management.txt
index 2ba460d..fada905 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1507,7 +1507,7 @@
   level "admin".
 
 disable dynamic-cookie backend <backend>
-  Disable the generation of dynamic cookies fot the backend <backend>
+  Disable the generation of dynamic cookies for the backend <backend>
 
 disable frontend <frontend>
   Mark the frontend as temporarily stopped. This corresponds to the mode which
@@ -2266,7 +2266,11 @@
 show sess
   Dump all known sessions. Avoid doing this on slow connections as this can
   be huge. This command is restricted and can only be issued on sockets
-  configured for levels "operator" or "admin".
+  configured for levels "operator" or "admin". Note that on machines with
+  quickly recycled connections, it is possible that this output reports less
+  entries than really exist because it will dump all existing sessions up to
+  the last one that was created before the command was entered; those which
+  die in the mean time will not appear.
 
 show sess <id>
   Display a lot of internal information about the specified session identifier.
diff --git a/doc/peers-v2.0.txt b/doc/peers-v2.0.txt
index 477e7bb..344cb56 100644
--- a/doc/peers-v2.0.txt
+++ b/doc/peers-v2.0.txt
@@ -191,11 +191,11 @@
 
 Table Type present the numeric type of key used to store stick table entries:
 integer
- 0: signed integer
- 1: IPv4 address
- 2: IPv6 address
- 3: string
- 4: binary
+ 2: signed integer
+ 4: IPv4 address
+ 5: IPv6 address
+ 6: string
+ 7: binary
 
 Table Keylen present the key length or max length in case of strings or binary (padded with 0).
 
diff --git a/doc/proxy-protocol.txt b/doc/proxy-protocol.txt
index 52d7bc7..ff64c8b 100644
--- a/doc/proxy-protocol.txt
+++ b/doc/proxy-protocol.txt
@@ -1,4 +1,4 @@
-2017/03/10                                                        Willy Tarreau
+2020/03/05                                                        Willy Tarreau
                                                            HAProxy Technologies
                                The PROXY protocol
                                  Versions 1 & 2
@@ -27,6 +27,7 @@
                 reserved TLV type ranges, added TLV documentation, clarified
                 string encoding. With contributions from Andriy Palamarchuk
                 (Amazon.com).
+   2020/03/05 - added the unique ID TLV type (Tim Düsterhus)
 
 
 1. Background
@@ -538,6 +539,7 @@
         #define PP2_TYPE_AUTHORITY      0x02
         #define PP2_TYPE_CRC32C         0x03
         #define PP2_TYPE_NOOP           0x04
+        #define PP2_TYPE_UNIQUE_ID      0x05
         #define PP2_TYPE_SSL            0x20
         #define PP2_SUBTYPE_SSL_VERSION 0x21
         #define PP2_SUBTYPE_SSL_CN      0x22
@@ -602,7 +604,17 @@
 to align only by 3 or more bytes because a TLV can not be smaller than that.
 
 
-2.2.5. The PP2_TYPE_SSL type and subtypes
+2.2.5. PP2_TYPE_UNIQUE_ID
+
+The value of the type PP2_TYPE_UNIQUE_ID is an opaque byte sequence of up to
+128 bytes generated by the upstream proxy that uniquely identifies the
+connection.
+
+The unique ID can be used to easily correlate connections across multiple
+layers of proxies, without needing to look up IP addresses and port numbers.
+
+
+2.2.6. The PP2_TYPE_SSL type and subtypes
 
 For the type PP2_TYPE_SSL, the value is itself a defined like this :
 
@@ -654,13 +666,13 @@
 using the TLV format and the type PP2_SUBTYPE_SSL_CN. E.g. "example.com".
 
 
-2.2.6. The PP2_TYPE_NETNS type
+2.2.7. The PP2_TYPE_NETNS type
 
 The type PP2_TYPE_NETNS defines the value as the US-ASCII string representation
 of the namespace's name.
 
 
-2.2.7. Reserved type ranges
+2.2.8. Reserved type ranges
 
 The following range of 16 type values is reserved for application-specific
 data and will be never used by the PROXY Protocol. If you need more values
diff --git a/doc/regression-testing.txt b/doc/regression-testing.txt
index 320c51c..1b6c21d 100644
--- a/doc/regression-testing.txt
+++ b/doc/regression-testing.txt
@@ -131,7 +131,7 @@
     #   BUG/MINOR: spoe: Initialize variables used during conf parsing before any check
 
     #   Some initializations must be done at the beginning of parse_spoe_flt to avoid
-    #   segmentaion fault when first errors are caught, when the "filter spoe" line is
+    #   segmentation fault when first errors are caught, when the "filter spoe" line is
     #   parsed.
 
     haproxy h1 -conf-BAD {} {
diff --git a/ebtree/eb32sctree.h b/ebtree/eb32sctree.h
index 51a2664..0ab057b 100644
--- a/ebtree/eb32sctree.h
+++ b/ebtree/eb32sctree.h
@@ -38,13 +38,18 @@
  * have put some sort of transparent union here to reduce the indirection
  * level, but the fact is, the end user is not meant to manipulate internals,
  * so this is pointless.
+ * In case sizeof(void*)>=sizeof(long), we know there will be some padding after
+ * the leaf if it's unaligned. In this case we force the alignment on void* so
+ * that we prefer to have the padding before for more efficient accesses.
  */
 struct eb32sc_node {
 	struct eb_node node; /* the tree node, must be at the beginning */
+	MAYBE_ALIGN(sizeof(u32));
 	u32 key;
+	ALWAYS_ALIGN(sizeof(void*));
 	unsigned long node_s; /* visibility of this node's branches */
 	unsigned long leaf_s; /* visibility of this node's leaf */
-};
+} ALIGNED(sizeof(void*));
 
 /*
  * Exported functions and macros.
diff --git a/ebtree/eb32tree.h b/ebtree/eb32tree.h
index 08ff900..b10a5ed 100644
--- a/ebtree/eb32tree.h
+++ b/ebtree/eb32tree.h
@@ -41,8 +41,9 @@
  */
 struct eb32_node {
 	struct eb_node node; /* the tree node, must be at the beginning */
+	MAYBE_ALIGN(sizeof(u32));
 	u32 key;
-};
+} ALIGNED(sizeof(void*));
 
 /*
  * Exported functions and macros.
diff --git a/ebtree/eb64tree.h b/ebtree/eb64tree.h
index 6d0d039..4653070 100644
--- a/ebtree/eb64tree.h
+++ b/ebtree/eb64tree.h
@@ -38,11 +38,16 @@
  * eb_node so that it can be cast into an eb_node. We could also have put some
  * sort of transparent union here to reduce the indirection level, but the fact
  * is, the end user is not meant to manipulate internals, so this is pointless.
+ * In case sizeof(void*)>=sizeof(u64), we know there will be some padding after
+ * the key if it's unaligned. In this case we force the alignment on void* so
+ * that we prefer to have the padding before for more efficient accesses.
  */
 struct eb64_node {
 	struct eb_node node; /* the tree node, must be at the beginning */
+	MAYBE_ALIGN(sizeof(u64));
+	ALWAYS_ALIGN(sizeof(void*));
 	u64 key;
-};
+} ALIGNED(sizeof(void*));
 
 /*
  * Exported functions and macros.
diff --git a/ebtree/ebimtree.h b/ebtree/ebimtree.h
index 4a98c96..28a9f14 100644
--- a/ebtree/ebimtree.h
+++ b/ebtree/ebimtree.h
@@ -62,7 +62,7 @@
 		if (eb_gettag(troot) == EB_LEAF) {
 			node = container_of(eb_untag(troot, EB_LEAF),
 					    struct ebpt_node, node.branches);
-			if (memcmp(node->key + pos, x, len) != 0)
+			if (eb_memcmp(node->key + pos, x, len) != 0)
 				goto ret_null;
 			else
 				goto ret_node;
@@ -76,7 +76,7 @@
 			 * value, and we walk down left, or it's a different
 			 * one and we don't have our key.
 			 */
-			if (memcmp(node->key + pos, x, len) != 0)
+			if (eb_memcmp(node->key + pos, x, len) != 0)
 				goto ret_null;
 			else
 				goto walk_left;
diff --git a/ebtree/ebmbtree.h b/ebtree/ebmbtree.h
index 6ed7de4..e7dfb0b 100644
--- a/ebtree/ebmbtree.h
+++ b/ebtree/ebmbtree.h
@@ -36,11 +36,16 @@
  * is, the end user is not meant to manipulate internals, so this is pointless.
  * The 'node.bit' value here works differently from scalar types, as it contains
  * the number of identical bits between the two branches.
+ * Note that we take a great care of making sure the key is located exactly at
+ * the end of the struct even if that involves holes before it, so that it
+ * always aliases any external key a user would append after. This is why the
+ * key uses the same alignment as the struct.
  */
 struct ebmb_node {
 	struct eb_node node; /* the tree node, must be at the beginning */
+	ALWAYS_ALIGN(sizeof(void*));
 	unsigned char key[0]; /* the key, its size depends on the application */
-};
+} ALIGNED(sizeof(void*));
 
 /*
  * Exported functions and macros.
@@ -149,7 +154,7 @@
 		if (eb_gettag(troot) == EB_LEAF) {
 			node = container_of(eb_untag(troot, EB_LEAF),
 					    struct ebmb_node, node.branches);
-			if (memcmp(node->key + pos, x, len) != 0)
+			if (eb_memcmp(node->key + pos, x, len) != 0)
 				goto ret_null;
 			else
 				goto ret_node;
@@ -163,7 +168,7 @@
 			 * value, and we walk down left, or it's a different
 			 * one and we don't have our key.
 			 */
-			if (memcmp(node->key + pos, x, len) != 0)
+			if (eb_memcmp(node->key + pos, x, len) != 0)
 				goto ret_null;
 			else
 				goto walk_left;
diff --git a/ebtree/ebpttree.h b/ebtree/ebpttree.h
index a1db03b..6cd6659 100644
--- a/ebtree/ebpttree.h
+++ b/ebtree/ebpttree.h
@@ -44,11 +44,14 @@
  * sort of transparent union here to reduce the indirection level, but the fact
  * is, the end user is not meant to manipulate internals, so this is pointless.
  * Internally, it is automatically cast as an eb32_node or eb64_node.
+ * We always align the key since the struct itself will be padded to the same
+ * size anyway.
  */
 struct ebpt_node {
 	struct eb_node node; /* the tree node, must be at the beginning */
+	ALWAYS_ALIGN(sizeof(void*));
 	void *key;
-};
+} ALIGNED(sizeof(void*));
 
 /*
  * Exported functions and macros.
diff --git a/ebtree/ebtree.c b/ebtree/ebtree.c
index fb266ec..10cc2ff 100644
--- a/ebtree/ebtree.c
+++ b/ebtree/ebtree.c
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include <unistd.h>
 #include "ebtree.h"
 
 void eb_delete(struct eb_node *node)
@@ -30,3 +31,21 @@
 {
 	return __eb_insert_dup(sub, new);
 }
+
+/* compares memory blocks m1 and m2 for up to <len> bytes. Immediately stops at
+ * the first non-matching byte. It returns 0 on full match, non-zero otherwise.
+ * One byte will always be checked so this must not be called with len==0. It
+ * takes 2+5cy/B on x86_64 and is ~29 bytes long.
+ */
+int eb_memcmp(const void *m1, const void *m2, size_t len)
+{
+	const char *p1 = (const char *)m1 + len;
+	const char *p2 = (const char *)m2 + len;
+	ssize_t ofs = -len;
+	char diff;
+
+	do {
+		diff = p1[ofs] - p2[ofs];
+	} while (!diff && ++ofs);
+	return diff;
+}
diff --git a/ebtree/ebtree.h b/ebtree/ebtree.h
index 2e6ba03..cfb7851 100644
--- a/ebtree/ebtree.h
+++ b/ebtree/ebtree.h
@@ -370,6 +370,8 @@
  * and one for the node, which remains unused in the very first node inserted
  * into the tree. This structure is 20 bytes per node on 32-bit machines. Do
  * not change the order, benchmarks have shown that it's optimal this way.
+ * Note: be careful about this struct's alignment if it gets included into
+ * another struct and some atomic ops are expected on the keys or the node.
  */
 struct eb_node {
 	struct eb_root branches; /* branches, must be at the beginning */
@@ -906,6 +908,7 @@
 /* These functions are declared in ebtree.c */
 void eb_delete(struct eb_node *node);
 REGPRM1 struct eb_node *eb_insert_dup(struct eb_node *sub, struct eb_node *new);
+int eb_memcmp(const void *m1, const void *m2, size_t len);
 
 #endif /* _EB_TREE_H */
 
diff --git a/include/common/compiler.h b/include/common/compiler.h
index b6f5af6..e5fcf38 100644
--- a/include/common/compiler.h
+++ b/include/common/compiler.h
@@ -151,5 +151,87 @@
 #endif
 #endif
 
+/* Some architectures have a double-word CAS, sometimes even dual-8 bytes.
+ * Some architectures support unaligned accesses, others are fine with them
+ * but only for non-atomic operations. Also mention those supporting unaligned
+ * accesses and being little endian, and those where unaligned accesses are
+ * known to be fast (almost as fast as aligned ones).
+ */
+#if defined(__x86_64__)
+#define HA_UNALIGNED
+#define HA_UNALIGNED_LE
+#define HA_UNALIGNED_LE64
+#define HA_UNALIGNED_FAST
+#define HA_UNALIGNED_ATOMIC
+#define HA_HAVE_CAS_DW
+#define HA_CAS_IS_8B
+#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
+#define HA_UNALIGNED
+#define HA_UNALIGNED_LE
+#define HA_UNALIGNED_ATOMIC
+#elif defined (__aarch64__) || defined(__ARM_ARCH_8A)
+#define HA_UNALIGNED
+#define HA_UNALIGNED_LE
+#define HA_UNALIGNED_LE64
+#define HA_UNALIGNED_FAST
+#define HA_HAVE_CAS_DW
+#define HA_CAS_IS_8B
+#elif defined(__arm__) && (defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__))
+#define HA_UNALIGNED
+#define HA_UNALIGNED_LE
+#define HA_UNALIGNED_FAST
+#define HA_HAVE_CAS_DW
+#endif
+
+
+/* sets alignment for current field or variable */
+#ifndef ALIGNED
+#define ALIGNED(x) __attribute__((aligned(x)))
+#endif
+
+/* sets alignment only on architectures preventing unaligned atomic accesses */
+#ifndef MAYBE_ALIGNED
+#ifndef HA_UNALIGNED
+#define MAYBE_ALIGNED(x)  ALIGNED(x)
+#else
+#define MAYBE_ALIGNED(x)
+#endif
+#endif
+
+/* sets alignment only on architectures preventing unaligned atomic accesses */
+#ifndef ATOMIC_ALIGNED
+#ifndef HA_UNALIGNED_ATOMIC
+#define ATOMIC_ALIGNED(x)  ALIGNED(x)
+#else
+#define ATOMIC_ALIGNED(x)
+#endif
+#endif
+
+/* add a mandatory alignment for next fields in a structure */
+#ifndef ALWAYS_ALIGN
+#define ALWAYS_ALIGN(x)  union { } ALIGNED(x)
+#endif
+
+/* add an optional alignment for next fields in a structure, only for archs
+ * which do not support unaligned accesses.
+ */
+#ifndef MAYBE_ALIGN
+#ifndef HA_UNALIGNED
+#define MAYBE_ALIGN(x)  union { } ALIGNED(x)
+#else
+#define MAYBE_ALIGN(x)
+#endif
+#endif
+
+/* add an optional alignment for next fields in a structure, only for archs
+ * which do not support unaligned accesses for atomic operations.
+ */
+#ifndef ATOMIC_ALIGN
+#ifndef HA_UNALIGNED_ATOMIC
+#define ATOMIC_ALIGN(x)  union { } ALIGNED(x)
+#else
+#define ATOMIC_ALIGN(x)
+#endif
+#endif
 
 #endif /* _COMMON_COMPILER_H */
diff --git a/include/common/config.h b/include/common/config.h
index 55ecd59..16f47c9 100644
--- a/include/common/config.h
+++ b/include/common/config.h
@@ -40,14 +40,6 @@
 #define THREAD_LOCAL
 #endif
 
-/* Some architectures have a double-word CAS, sometimes even dual-8 bytes */
-#if defined(__x86_64__) || defined (__aarch64__)
-#define HA_HAVE_CAS_DW
-#define HA_CAS_IS_8B
-#elif defined(__arm__) && (defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__))
-#define HA_HAVE_CAS_DW
-#endif
-
 /* On architectures supporting threads and double-word CAS, we can implement
  * lock-less memory pools. This isn't supported for debugging modes however.
  */
diff --git a/include/common/hash.h b/include/common/hash.h
index 78fd87b..c17f8c9 100644
--- a/include/common/hash.h
+++ b/include/common/hash.h
@@ -24,10 +24,10 @@
 
 #include <inttypes.h>
 
-unsigned int hash_djb2(const char *key, int len);
-unsigned int hash_wt6(const char *key, int len);
-unsigned int hash_sdbm(const char *key, int len);
-unsigned int hash_crc32(const char *key, int len);
-uint32_t hash_crc32c(const char *key, int len);
+unsigned int hash_djb2(const void *input, int len);
+unsigned int hash_wt6(const void *input, int len);
+unsigned int hash_sdbm(const void *input, int len);
+unsigned int hash_crc32(const void *input, int len);
+uint32_t hash_crc32c(const void *input, int len);
 
 #endif /* _COMMON_HASH_H_ */
diff --git a/include/common/hathreads.h b/include/common/hathreads.h
index fa6487c..7b94ba1 100644
--- a/include/common/hathreads.h
+++ b/include/common/hathreads.h
@@ -77,7 +77,12 @@
 #define __decl_rwlock(lock)
 #define __decl_aligned_rwlock(lock)
 
-#define HA_ATOMIC_CAS(val, old, new) ({((*val) == (*old)) ? (*(val) = (new) , 1) : (*(old) = *(val), 0);})
+#define HA_ATOMIC_CAS(val, old, new)                                    \
+	({                                                              \
+		typeof(val) _v = (val);                                 \
+		typeof(old) _o = (old);                                 \
+		(*_v == *_o) ? ((*_v = (new)), 1) : ((*_o = *_v), 0);   \
+	})
 
 /* warning, n is a pointer to the double value for dwcas */
 #define HA_ATOMIC_DWCAS(val, o, n)				       \
@@ -130,20 +135,22 @@
 #define HA_ATOMIC_STORE(val, new)    ({*(val) = new;})
 #define HA_ATOMIC_UPDATE_MAX(val, new)					\
 	({								\
+		typeof(val) __val = (val);                              \
 		typeof(*(val)) __new_max = (new);			\
 									\
-		if (*(val) < __new_max)					\
-			*(val) = __new_max;				\
-		*(val);							\
+		if (*__val < __new_max)					\
+			*__val = __new_max;				\
+		*__val;							\
 	})
 
 #define HA_ATOMIC_UPDATE_MIN(val, new)					\
 	({								\
+		typeof(val) __val = (val);                              \
 		typeof(*(val)) __new_min = (new);			\
 									\
-		if (*(val) > __new_min)					\
-			*(val) = __new_min;				\
-		*(val);							\
+		if (*__val > __new_min)					\
+			*__val = __new_min;				\
+		*__val;							\
 	})
 
 #define HA_BARRIER() do { } while (0)
@@ -170,6 +177,11 @@
 	ti = &thread_info[tid];
 }
 
+static inline unsigned long long ha_get_pthread_id(unsigned int thr)
+{
+	return 0;
+}
+
 static inline void ha_thread_relax(void)
 {
 #if _POSIX_PRIORITY_SCHEDULING
@@ -400,21 +412,23 @@
 
 #define HA_ATOMIC_UPDATE_MAX(val, new)					\
 	({								\
-		typeof(*(val)) __old_max = *(val);			\
+		typeof(val) __val = (val);                              \
+		typeof(*(val)) __old_max = *__val;			\
 		typeof(*(val)) __new_max = (new);			\
 									\
 		while (__old_max < __new_max &&				\
-		       !HA_ATOMIC_CAS(val, &__old_max, __new_max));	\
-		*(val);							\
+		       !HA_ATOMIC_CAS(__val, &__old_max, __new_max));	\
+		*__val;							\
 	})
 #define HA_ATOMIC_UPDATE_MIN(val, new)					\
 	({								\
-		typeof(*(val)) __old_min = *(val);			\
+		typeof(val) __val = (val);                              \
+		typeof(*(val)) __old_min = *__val;			\
 		typeof(*(val)) __new_min = (new);			\
 									\
 		while (__old_min > __new_min &&				\
-		       !HA_ATOMIC_CAS(val, &__old_min, __new_min));	\
-		*(val);							\
+		       !HA_ATOMIC_CAS(__val, &__old_min, __new_min));	\
+		*__val;							\
 	})
 
 #define HA_BARRIER() pl_barrier()
@@ -483,6 +497,37 @@
 	ti      = &thread_info[tid];
 }
 
+/* Retrieves the opaque pthread_t of thread <thr> cast to an unsigned long long
+ * since POSIX took great care of not specifying its representation, making it
+ * hard to export for post-mortem analysis. For this reason we copy it into a
+ * union and will use the smallest scalar type at least as large as its size,
+ * which will keep endianness and alignment for all regular sizes. As a last
+ * resort we end up with a long long ligned to the first bytes in memory, which
+ * will be endian-dependent if pthread_t is larger than a long long (not seen
+ * yet).
+ */
+static inline unsigned long long ha_get_pthread_id(unsigned int thr)
+{
+	union {
+		pthread_t t;
+		unsigned long long ll;
+		unsigned int i;
+		unsigned short s;
+		unsigned char c;
+	} u;
+
+	memset(&u, 0, sizeof(u));
+	u.t = thread_info[thr].pthread;
+
+	if (sizeof(u.t) <= sizeof(u.c))
+		return u.c;
+	else if (sizeof(u.t) <= sizeof(u.s))
+		return u.s;
+	else if (sizeof(u.t) <= sizeof(u.i))
+		return u.i;
+	return u.ll;
+}
+
 static inline void ha_thread_relax(void)
 {
 #if _POSIX_PRIORITY_SCHEDULING
@@ -581,7 +626,7 @@
 #define __SPIN_INIT(l)         ({ (*l) = 0; })
 #define __SPIN_DESTROY(l)      ({ (*l) = 0; })
 #define __SPIN_LOCK(l)         pl_take_s(l)
-#define __SPIN_TRYLOCK(l)      !pl_try_s(l)
+#define __SPIN_TRYLOCK(l)      (!pl_try_s(l))
 #define __SPIN_UNLOCK(l)       pl_drop_s(l)
 
 #define __HA_RWLOCK_T		unsigned long
@@ -589,10 +634,10 @@
 #define __RWLOCK_INIT(l)          ({ (*l) = 0; })
 #define __RWLOCK_DESTROY(l)       ({ (*l) = 0; })
 #define __RWLOCK_WRLOCK(l)        pl_take_w(l)
-#define __RWLOCK_TRYWRLOCK(l)     !pl_try_w(l)
+#define __RWLOCK_TRYWRLOCK(l)     (!pl_try_w(l))
 #define __RWLOCK_WRUNLOCK(l)      pl_drop_w(l)
 #define __RWLOCK_RDLOCK(l)        pl_take_r(l)
-#define __RWLOCK_TRYRDLOCK(l)     !pl_try_r(l)
+#define __RWLOCK_TRYRDLOCK(l)     (!pl_try_r(l))
 #define __RWLOCK_RDUNLOCK(l)      pl_drop_r(l)
 
 #define HA_SPINLOCK_T       struct ha_spinlock
@@ -978,7 +1023,7 @@
 #define HA_SPIN_INIT(l)         ({ (*l) = 0; })
 #define HA_SPIN_DESTROY(l)      ({ (*l) = 0; })
 #define HA_SPIN_LOCK(lbl, l)    pl_take_s(l)
-#define HA_SPIN_TRYLOCK(lbl, l) !pl_try_s(l)
+#define HA_SPIN_TRYLOCK(lbl, l) (!pl_try_s(l))
 #define HA_SPIN_UNLOCK(lbl, l)  pl_drop_s(l)
 
 #define HA_RWLOCK_T		unsigned long
@@ -986,10 +1031,10 @@
 #define HA_RWLOCK_INIT(l)          ({ (*l) = 0; })
 #define HA_RWLOCK_DESTROY(l)       ({ (*l) = 0; })
 #define HA_RWLOCK_WRLOCK(lbl,l)    pl_take_w(l)
-#define HA_RWLOCK_TRYWRLOCK(lbl,l) !pl_try_w(l)
+#define HA_RWLOCK_TRYWRLOCK(lbl,l) (!pl_try_w(l))
 #define HA_RWLOCK_WRUNLOCK(lbl,l)  pl_drop_w(l)
 #define HA_RWLOCK_RDLOCK(lbl,l)    pl_take_r(l)
-#define HA_RWLOCK_TRYRDLOCK(lbl,l) !pl_try_r(l)
+#define HA_RWLOCK_TRYRDLOCK(lbl,l) (!pl_try_r(l))
 #define HA_RWLOCK_RDUNLOCK(lbl,l)  pl_drop_r(l)
 
 #endif  /* DEBUG_THREAD */
diff --git a/include/common/http.h b/include/common/http.h
index b0befa5..249cd32 100644
--- a/include/common/http.h
+++ b/include/common/http.h
@@ -83,8 +83,11 @@
 	HTTP_ERR_200 = 0,
 	HTTP_ERR_400,
 	HTTP_ERR_403,
+	HTTP_ERR_404,
 	HTTP_ERR_405,
 	HTTP_ERR_408,
+	HTTP_ERR_410,
+	HTTP_ERR_413,
 	HTTP_ERR_421,
 	HTTP_ERR_425,
 	HTTP_ERR_429,
diff --git a/include/common/htx.h b/include/common/htx.h
index 27f0e5e..0522c83 100644
--- a/include/common/htx.h
+++ b/include/common/htx.h
@@ -225,6 +225,7 @@
 struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk);
 struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t blksz);
 struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk);
+struct htx_ret htx_find_offset(struct htx *htx, uint32_t offset);
 void htx_truncate(struct htx *htx, uint32_t offset);
 struct htx_ret htx_drain(struct htx *htx, uint32_t max);
 
diff --git a/include/common/ist.h b/include/common/ist.h
index 10c0b6c..0d8b457 100644
--- a/include/common/ist.h
+++ b/include/common/ist.h
@@ -708,4 +708,16 @@
 	return ist2(NULL, 0);
 }
 
+/*
+ * looks for the first occurence of <chr> in string <ist> and returns a shorter
+ * ist if char is found.
+ */
+static inline struct ist iststop(const struct ist ist, char chr)
+{
+	size_t len = 0;
+
+	while (len++ < ist.len && ist.ptr[len - 1] != chr)
+		;
+	return ist2(ist.ptr, len - 1);
+}
 #endif
diff --git a/include/common/memory.h b/include/common/memory.h
index d21a176..98f9db2 100644
--- a/include/common/memory.h
+++ b/include/common/memory.h
@@ -50,7 +50,9 @@
 #define POOL_LINK(pool, item) ((void **)(item))
 #endif
 
-#define MAX_BASE_POOLS 32
+#ifndef MAX_BASE_POOLS
+#define MAX_BASE_POOLS 64
+#endif
 
 struct pool_cache_head {
 	struct list list;    /* head of objects in this pool */
@@ -78,6 +80,7 @@
 	void **free_list;
 #ifdef CONFIG_HAP_LOCKLESS_POOLS
 	uintptr_t seq;
+	HA_SPINLOCK_T flush_lock;
 #else
 	__decl_hathreads(HA_SPINLOCK_T lock); /* the spin lock */
 #endif
diff --git a/include/common/mini-clist.h b/include/common/mini-clist.h
index 592bb96..501904b 100644
--- a/include/common/mini-clist.h
+++ b/include/common/mini-clist.h
@@ -106,7 +106,7 @@
  * since it's used only once.
  * Example: LIST_ELEM(cur_node->args.next, struct node *, args)
  */
-#define LIST_ELEM(lh, pt, el) ((pt)(((void *)(lh)) - ((void *)&((pt)NULL)->el)))
+#define LIST_ELEM(lh, pt, el) ((pt)(((const char *)(lh)) - ((size_t)&((pt)NULL)->el)))
 
 /* checks if the list head <lh> is empty or not */
 #define LIST_ISEMPTY(lh) ((lh)->n == (lh))
diff --git a/include/common/openssl-compat.h b/include/common/openssl-compat.h
index 0e05649..aeb8389 100644
--- a/include/common/openssl-compat.h
+++ b/include/common/openssl-compat.h
@@ -217,7 +217,8 @@
 #define TLSEXT_signature_ecdsa      3
 #endif
 
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if ((HA_OPENSSL_VERSION_NUMBER < 0x1010000fL) && (LIBRESSL_VERSION_NUMBER < 0x2070000fL)) ||\
+	defined(OPENSSL_IS_BORINGSSL)
 #define X509_getm_notBefore     X509_get_notBefore
 #define X509_getm_notAfter      X509_get_notAfter
 #endif
@@ -312,5 +313,9 @@
 #define BIO_meth_set_destroy(m, f) do { (m)->destroy = (f); } while (0)
 #endif
 
+#ifndef SSL_CTX_set_ecdh_auto
+#define SSL_CTX_set_ecdh_auto(dummy, onoff)      ((onoff) != 0)
+#endif
+
 #endif /* USE_OPENSSL */
 #endif /* _COMMON_OPENSSL_COMPAT_H */
diff --git a/include/common/standard.h b/include/common/standard.h
index cdefc9f..38e2bb7 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -61,6 +61,26 @@
  * power of 2, and 0 otherwise */
 #define POWEROF2(x) (((x) & ((x)-1)) == 0)
 
+/* rotate left a 64-bit integer by <bits:[0-5]> bits */
+static inline uint64_t rotl64(uint64_t v, uint8_t bits)
+{
+#if !defined(__ARM_ARCH_8A) && !defined(__x86_64__)
+	bits &= 63;
+#endif
+	v = (v << bits) | (v >> (-bits & 63));
+	return v;
+}
+
+/* rotate right a 64-bit integer by <bits:[0-5]> bits */
+static inline uint64_t rotr64(uint64_t v, uint8_t bits)
+{
+#if !defined(__ARM_ARCH_8A) && !defined(__x86_64__)
+	bits &= 63;
+#endif
+	v = (v >> bits) | (v << (-bits & 63));
+	return v;
+}
+
 /* operators to compare values. They're ordered that way so that the lowest bit
  * serves as a negation for the test and contains all tests that are not equal.
  */
@@ -541,8 +561,12 @@
  * be shorter. If some forbidden characters are found, the conversion is
  * aborted, the string is truncated before the issue and non-zero is returned,
  * otherwise the operation returns non-zero indicating success.
+ * If the 'in_form' argument is non-nul the string is assumed to be part of
+ * an "application/x-www-form-urlencoded" encoded string, and the '+' will be
+ * turned to a space. If it's zero, this will only be done after a question
+ * mark ('?').
  */
-int url_decode(char *string);
+int url_decode(char *string, int in_form);
 
 /* This one is 6 times faster than strtoul() on athlon, but does
  * no check at all.
@@ -770,10 +794,10 @@
  */
 static inline unsigned int div64_32(unsigned long long o1, unsigned int o2)
 {
-	unsigned int result;
+	unsigned long long result;
 #ifdef __i386__
 	asm("divl %2"
-	    : "=a" (result)
+	    : "=A" (result)
 	    : "A"(o1), "rm"(o2));
 #else
 	result = o1 / o2;
@@ -1455,6 +1479,21 @@
 
 int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
 
+/* PRNG */
+void ha_random_seed(const unsigned char *seed, size_t len);
+void ha_random_jump96(uint32_t dist);
+uint64_t ha_random64();
+
+static inline uint32_t ha_random32()
+{
+	return ha_random64() >> 32;
+}
+
+static inline int32_t ha_random()
+{
+	return ha_random32() >> 1;
+}
+
 /* HAP_STRING() makes a string from a literal while HAP_XSTRING() first
  * evaluates the argument and is suited to pass macros.
  *
diff --git a/include/proto/checks.h b/include/proto/checks.h
index 2b285f3..695fe84 100644
--- a/include/proto/checks.h
+++ b/include/proto/checks.h
@@ -3,7 +3,7 @@
   Functions prototypes for the checks.
 
   Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
-  
+
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation, version 2.1
@@ -50,6 +50,8 @@
 
 const char *init_check(struct check *check, int type);
 void free_check(struct check *check);
+void deinit_srv_check(struct server *srv);
+void deinit_srv_agent_check(struct server *srv);
 
 int init_email_alert(struct mailers *mailers, struct proxy *p, char **err);
 void send_email_alert(struct server *s, int priority, const char *format, ...)
diff --git a/include/proto/connection.h b/include/proto/connection.h
index 02f3234..fcdbd37 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -178,7 +178,7 @@
 	if (conn_ctrl_ready(conn) && !(conn->flags & CO_FL_WILL_UPDATE)) {
 		unsigned int flags = conn->flags;
 
-		flags &= ~(CO_FL_CURR_RD_ENA | CO_FL_CURR_WR_ENA | CO_FL_WAIT_ROOM);
+		flags &= ~(CO_FL_CURR_RD_ENA | CO_FL_CURR_WR_ENA);
 		if (fd_recv_active(conn->handle.fd))
 			flags |= CO_FL_CURR_RD_ENA;
 		if (fd_send_active(conn->handle.fd))
diff --git a/include/proto/http_htx.h b/include/proto/http_htx.h
index ae22380..27d92f2 100644
--- a/include/proto/http_htx.h
+++ b/include/proto/http_htx.h
@@ -31,6 +31,7 @@
 extern struct buffer htx_err_chunks[HTTP_ERR_SIZE];
 
 struct htx_sl *http_get_stline(struct htx *htx);
+size_t http_get_hdrs_size(struct htx *htx);
 int http_find_header(const struct htx *htx, const struct ist name, struct http_hdr_ctx *ctx, int full);
 int http_add_header(struct htx *htx, const struct ist n, const struct ist v);
 int http_replace_stline(struct htx *htx, const struct ist p1, const struct ist p2, const struct ist p3);
diff --git a/include/proto/http_rules.h b/include/proto/http_rules.h
index 5e03dd8..608ca57 100644
--- a/include/proto/http_rules.h
+++ b/include/proto/http_rules.h
@@ -32,8 +32,6 @@
 
 struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy);
 struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy);
-void free_http_req_rules(struct list *r);
-void free_http_res_rules(struct list *r);
 struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
                                                const char **args, char **errmsg, int use_fmt, int dir);
 
diff --git a/include/proto/listener.h b/include/proto/listener.h
index 24a01b2..1dccb8a 100644
--- a/include/proto/listener.h
+++ b/include/proto/listener.h
@@ -169,7 +169,7 @@
 	};
 	unsigned int st = l->state;
 
-	if (st > sizeof(states) / sizeof(*states))
+	if (st >= sizeof(states) / sizeof(*states))
 		return "INVALID";
 	return states[st];
 }
diff --git a/include/proto/mworker.h b/include/proto/mworker.h
index 0418782..595cc19 100644
--- a/include/proto/mworker.h
+++ b/include/proto/mworker.h
@@ -22,6 +22,7 @@
 void mworker_block_signals();
 void mworker_unblock_signals();
 
+void mworker_broadcast_signal(struct sig_handler *sh);
 void mworker_catch_sighup(struct sig_handler *sh);
 void mworker_catch_sigterm(struct sig_handler *sh);
 void mworker_catch_sigchld(struct sig_handler *sh);
diff --git a/include/proto/obj_type.h b/include/proto/obj_type.h
index cc35d5c..1f59494 100644
--- a/include/proto/obj_type.h
+++ b/include/proto/obj_type.h
@@ -184,6 +184,7 @@
 	case OBJ_TYPE_CONN:     return __objt_conn(t);
 	case OBJ_TYPE_SRVRQ:    return __objt_dns_srvrq(t);
 	case OBJ_TYPE_CS:       return __objt_cs(t);
+	case OBJ_TYPE_STREAM:   return __objt_stream(t);
 	default:                return t; // exact pointer for invalid case
 	}
 }
diff --git a/include/proto/pattern.h b/include/proto/pattern.h
index 5b99296..1480d79 100644
--- a/include/proto/pattern.h
+++ b/include/proto/pattern.h
@@ -37,7 +37,7 @@
 extern struct pattern *(*pat_match_fcts[PAT_MATCH_NUM])(struct sample *, struct pattern_expr *, int);
 extern int pat_match_types[PAT_MATCH_NUM];
 
-void pattern_finalize_config(void);
+int pattern_finalize_config(void);
 
 /* return the PAT_MATCH_* index for match name "name", or < 0 if not found */
 static inline int pat_find_match_name(const char *name)
@@ -191,7 +191,7 @@
 int pat_ref_set_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt, const char *value, char **err);
 int pat_ref_delete(struct pat_ref *ref, const char *key);
 int pat_ref_delete_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt);
-void pat_ref_prune(struct pat_ref *ref);
+int pat_ref_prune(struct pat_ref *ref);
 int pat_ref_load(struct pat_ref *ref, struct pattern_expr *expr, int patflags, int soe, char **err);
 void pat_ref_reload(struct pat_ref *ref, struct pat_ref *replace);
 
diff --git a/include/proto/protocol_buffers.h b/include/proto/protocol_buffers.h
index 69f0bdf..0426d83 100644
--- a/include/proto/protocol_buffers.h
+++ b/include/proto/protocol_buffers.h
@@ -158,7 +158,7 @@
 
 		shift += 7;
 		/* The maximum length in bytes of a 64-bit encoded value is 10. */
-		if (shift > 70)
+		if (shift > 63)
 			return 0;
 	}
 
@@ -194,7 +194,7 @@
 
 		shift += 7;
 		/* The maximum length in bytes of a 64-bit encoded value is 10. */
-		if (shift > 70)
+		if (shift > 63)
 			return 0;
 	}
 
@@ -227,7 +227,7 @@
 
 		shift += 7;
 		/* The maximum length in bytes of a 64-bit encoded value is 10. */
-		if (shift > 70)
+		if (shift > 63)
 			return 0;
 	}
 
@@ -263,7 +263,7 @@
 
 		shift += 7;
 		/* The maximum length in bytes of a 64-bit encoded value is 10. */
-		if (shift > 70)
+		if (shift > 63)
 			return -1;
 	}
 
diff --git a/include/proto/server.h b/include/proto/server.h
index f52b38e..44ac3d4 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -46,7 +46,7 @@
 int srv_downtime(const struct server *s);
 int srv_lastsession(const struct server *s);
 int srv_getinter(const struct check *check);
-int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy, int parse_addr);
+int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy, int parse_addr, int in_peers_section, int initial_resolve);
 int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char *updater);
 const char *update_server_addr_port(struct server *s, const char *addr, const char *port, char *updater);
 struct server *server_find_by_id(struct proxy *bk, int id);
diff --git a/include/proto/shctx.h b/include/proto/shctx.h
index f5448e0..25bf377 100644
--- a/include/proto/shctx.h
+++ b/include/proto/shctx.h
@@ -93,8 +93,10 @@
         for (i = 0; i < *count; i++) {
                 relax();
                 relax();
+		if (*uaddr != value)
+			return;
         }
-        *count = *count << 1;
+        *count = (unsigned char)((*count << 1) + 1);
 }
 
 #define _shctx_awakelocker(a)
@@ -154,7 +156,7 @@
 static inline void _shctx_lock(struct shared_context *shctx)
 {
 	unsigned int x;
-	unsigned int count = 4;
+	unsigned int count = 3;
 
 	x = cmpxchg(&shctx->waiters, 0, 1);
 	if (x) {
diff --git a/include/proto/task.h b/include/proto/task.h
index 4044bd4..5c7aa6a 100644
--- a/include/proto/task.h
+++ b/include/proto/task.h
@@ -103,6 +103,8 @@
 __decl_hathreads(extern HA_SPINLOCK_T rq_lock);  /* spin lock related to run queue */
 __decl_hathreads(extern HA_RWLOCK_T wq_lock);    /* RW lock related to the wait queue */
 
+static inline struct task *task_unlink_wq(struct task *t);
+static inline void task_queue(struct task *task);
 
 /* return 0 if task is in run queue, otherwise non-zero */
 static inline int task_in_rq(struct task *t)
@@ -148,10 +150,22 @@
 	}
 }
 
-/* change the thread affinity of a task to <thread_mask> */
+/* change the thread affinity of a task to <thread_mask>.
+ * This may only be done from within the running task itself or during its
+ * initialization. It will unqueue and requeue the task from the wait queue
+ * if it was in it. This is safe against a concurrent task_queue() call because
+ * task_queue() itself will unlink again if needed after taking into account
+ * the new thread_mask.
+ */
 static inline void task_set_affinity(struct task *t, unsigned long thread_mask)
 {
-	t->thread_mask = thread_mask;
+	if (unlikely(task_in_wq(t))) {
+		task_unlink_wq(t);
+		t->thread_mask = thread_mask;
+		task_queue(t);
+	}
+	else
+		t->thread_mask = thread_mask;
 }
 
 /*
@@ -167,15 +181,15 @@
 }
 
 /* remove a task from its wait queue. It may either be the local wait queue if
- * the task is bound to a single thread (in which case there's no locking
- * involved) or the global queue, with locking.
+ * the task is bound to a single thread or the global queue. If the task uses a
+ * shared wait queue, the global wait queue lock is used.
  */
 static inline struct task *task_unlink_wq(struct task *t)
 {
 	unsigned long locked;
 
 	if (likely(task_in_wq(t))) {
-		locked = atleast2(t->thread_mask);
+		locked = t->state & TASK_SHARED_WQ;
 		if (locked)
 			HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
 		__task_unlink_wq(t);
@@ -261,7 +275,8 @@
 /*
  * Initialize a new task. The bare minimum is performed (queue pointers and
  * state).  The task is returned. This function should not be used outside of
- * task_new().
+ * task_new(). If the thread mask contains more than one thread, TASK_SHARED_WQ
+ * is set.
  */
 static inline struct task *task_init(struct task *t, unsigned long thread_mask)
 {
@@ -269,6 +284,8 @@
 	t->rq.node.leaf_p = NULL;
 	t->state = TASK_SLEEPING;
 	t->thread_mask = thread_mask;
+	if (atleast2(thread_mask))
+		t->state |= TASK_SHARED_WQ;
 	t->nice = 0;
 	t->calls = 0;
 	t->call_date = 0;
@@ -373,9 +390,9 @@
 
 /* Place <task> into the wait queue, where it may already be. If the expiration
  * timer is infinite, do nothing and rely on wake_expired_task to clean up.
- * If the task is bound to a single thread, it's assumed to be bound to the
- * current thread's queue and is queued without locking. Otherwise it's queued
- * into the global wait queue, protected by locks.
+ * If the task uses a shared wait queue, it's queued into the global wait queue,
+ * protected by the global wq_lock, otherwise by it necessarily belongs to the
+ * current thread'sand is queued without locking.
  */
 static inline void task_queue(struct task *task)
 {
@@ -392,7 +409,7 @@
 		return;
 
 #ifdef USE_THREAD
-	if (atleast2(task->thread_mask)) {
+	if (task->state & TASK_SHARED_WQ) {
 		HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
 		if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key))
 			__task_queue(task, &timers);
@@ -400,6 +417,7 @@
 	} else
 #endif
 	{
+		BUG_ON((task->thread_mask & tid_bit) == 0); // should have TASK_SHARED_WQ
 		if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key))
 			__task_queue(task, &task_per_thread[tid].timers);
 	}
@@ -416,7 +434,7 @@
 		return;
 
 #ifdef USE_THREAD
-	if (atleast2(task->thread_mask)) {
+	if (task->state & TASK_SHARED_WQ) {
 		/* FIXME: is it really needed to lock the WQ during the check ? */
 		HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
 		if (task_in_wq(task))
@@ -429,6 +447,7 @@
 	} else
 #endif
 	{
+		BUG_ON((task->thread_mask & tid_bit) == 0); // should have TASK_SHARED_WQ
 		if (task_in_wq(task))
 			when = tick_first(when, task->expire);
 
diff --git a/include/types/checks.h b/include/types/checks.h
index 03d2305..174bdb9 100644
--- a/include/types/checks.h
+++ b/include/types/checks.h
@@ -82,9 +82,11 @@
 };
 
 /* environment variables memory requirement for different types of data */
-#define EXTCHK_SIZE_EVAL_INIT 0		/* size determined during the init phase,
-					 * such environment variables are not updatable. */
-#define EXTCHK_SIZE_ULONG     20	/* max string length for an unsigned long value */
+#define EXTCHK_SIZE_EVAL_INIT 0                  /* size determined during the init phase,
+                                                  * such environment variables are not updatable. */
+#define EXTCHK_SIZE_ULONG     20                 /* max string length for an unsigned long value */
+#define EXTCHK_SIZE_UINT      11                 /* max string length for an unsigned int value */
+#define EXTCHK_SIZE_ADDR      INET6_ADDRSTRLEN+1 /* max string length for an address */
 
 /* external checks environment variables */
 enum {
@@ -165,7 +167,7 @@
 	short status, code;			/* check result, check code */
 	unsigned short port;			/* the port to use for the health checks */
 	char desc[HCHK_DESC_LEN];		/* health check description */
-	int use_ssl;				/* use SSL for health checks */
+	signed char use_ssl;			/* use SSL for health checks (1: on, 0: server mode, -1: off) */
 	int send_proxy;				/* send a PROXY protocol header with checks */
 	struct list *tcpcheck_rules;		/* tcp-check send / expect rules */
 	struct tcpcheck_rule *current_step;     /* current step when using tcpcheck */
diff --git a/include/types/connection.h b/include/types/connection.h
index ecd3377..2f9ab2f 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -95,7 +95,7 @@
 	CS_FL_EOS           = 0x00001000,  /* End of stream delivered to data layer */
 	/* unused: 0x00002000 */
 	CS_FL_EOI           = 0x00004000,  /* end-of-input reached */
-	/* unused: 0x00008000 */
+	CS_FL_MAY_SPLICE    = 0x00008000,  /* caller may use rcv_pipe() only if this flag is set */
 	CS_FL_WAIT_FOR_HS   = 0x00010000,  /* This stream is waiting for handhskae */
 	CS_FL_KILL_CONN     = 0x00020000,  /* must kill the connection when the CS closes */
 
@@ -561,6 +561,7 @@
 #define PP2_TYPE_AUTHORITY      0x02
 #define PP2_TYPE_CRC32C         0x03
 #define PP2_TYPE_NOOP           0x04
+#define PP2_TYPE_UNIQUE_ID      0x05
 #define PP2_TYPE_SSL            0x20
 #define PP2_SUBTYPE_SSL_VERSION 0x21
 #define PP2_SUBTYPE_SSL_CN      0x22
diff --git a/include/types/global.h b/include/types/global.h
index 0cedc95..abc930d 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -251,6 +251,7 @@
 extern unsigned int rlim_fd_cur_at_boot;
 extern unsigned int rlim_fd_max_at_boot;
 extern int atexit_flag;
+extern unsigned char boot_seed[20];  // per-boot random seed (160 bits initially)
 
 /* bit values to go with "warned" above */
 #define WARN_BLOCK_DEPRECATED       0x00000001
@@ -297,6 +298,7 @@
 int tell_old_pids(int sig);
 int delete_oldpid(int pid);
 
+int main(int argc, char **argv);
 void deinit(void);
 void hap_register_build_opts(const char *str, int must_free);
 void hap_register_post_check(int (*fct)());
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index d4fa652..b4d18f9 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -155,6 +155,7 @@
 	REDIRECT_FLAG_NONE = 0,
 	REDIRECT_FLAG_DROP_QS = 1,	/* drop query string */
 	REDIRECT_FLAG_APPEND_SLASH = 2,	/* append a slash if missing at the end */
+	REDIRECT_FLAG_FROM_REQ = 4,     /* redirect rule on the request path */
 };
 
 /* Redirect types (location, prefix, extended ) */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 6aa0de1..06887c3 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -339,6 +339,7 @@
 	int  cookie_len;			/* strlen(cookie_name), computed only once */
 	char *cookie_domain;			/* domain used to insert the cookie */
 	char *cookie_name;			/* name of the cookie to look for */
+	char *cookie_attrs;                     /* list of attributes to add to the cookie */
 	char *dyncookie_key;			/* Secret key used to generate dynamic persistent cookies */
 	unsigned int cookie_maxidle;		/* max idle time for this cookie */
 	unsigned int cookie_maxlife;		/* max life time for this cookie */
@@ -427,6 +428,10 @@
 	int grace;				/* grace time after stop request */
 	int check_len;				/* Length of the HTTP or SSL3 request */
 	char *check_req;			/* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */
+	int check_body_len;                     /* Length of the request body for HTTP checks */
+	char *check_hdrs;                       /* Request headers for HTTP cheks */
+	int check_hdrs_len;                     /* Length of the headers for HTTP checks */
+	char *check_body;                       /* Request body for HTTP cheks */
 	char *check_command;			/* Command to use for external agent checks */
 	char *check_path;			/* PATH environment to use for external agent checks */
 	char *expect_str;			/* http-check expected content : string or text version of the regex */
diff --git a/include/types/server.h b/include/types/server.h
index e053416..99f2653 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -199,7 +199,7 @@
 	enum obj_type obj_type;                 /* object type == OBJ_TYPE_SERVER */
 	enum srv_state next_state, cur_state;   /* server state among SRV_ST_* */
 	enum srv_admin next_admin, cur_admin;   /* server maintenance status : SRV_ADMF_* */
-	unsigned char use_ssl;			/* ssl enabled  */
+	signed char use_ssl;		        /* ssl enabled (1: on, 0: disabled, -1 forced off)  */
 	unsigned int pp_opts;                   /* proxy protocol options (SRV_PP_*) */
 	struct server *next;
 	int cklen;				/* the len of the cookie, to speed up checks */
diff --git a/include/types/signal.h b/include/types/signal.h
index 598001e..ec3dcf2 100644
--- a/include/types/signal.h
+++ b/include/types/signal.h
@@ -25,6 +25,26 @@
 #define SIG_F_TYPE_FCT          0x0002  /* handler is a function + arg */
 #define SIG_F_TYPE_TASK         0x0004  /* handler is a task + reason */
 
+/* Define WDTSIG if available */
+#if defined(USE_RT) && (_POSIX_TIMERS > 0) && defined(_POSIX_THREAD_CPUTIME)
+
+
+/* We'll deliver SIGALRM when we've run out of CPU as it's not intercepted by
+ * gdb by default.
+ */
+#define WDTSIG SIGALRM
+
+#endif
+
+#ifdef USE_THREAD_DUMP
+/* The signal to trigger a debug dump on a thread is SIGURG. It has the benefit
+ * of not stopping gdb by default, so that issuing "show threads" in a process
+ * being debugged has no adverse effect.
+ */
+#define DEBUGSIG SIGURG
+
+#endif
+
 /* those are highly dynamic and stored in pools */
 struct sig_handler {
 	struct list list;
diff --git a/include/types/task.h b/include/types/task.h
index a949058..d02e3c1 100644
--- a/include/types/task.h
+++ b/include/types/task.h
@@ -34,6 +34,8 @@
 #define TASK_RUNNING      0x0001  /* the task is currently running */
 #define TASK_GLOBAL       0x0002  /* The task is currently in the global runqueue */
 #define TASK_QUEUED       0x0004  /* The task has been (re-)added to the run queue */
+#define TASK_SHARED_WQ    0x0008  /* The task's expiration may be updated by other
+                                   * threads, must be set before first queue/wakeup */
 
 #define TASK_WOKEN_INIT   0x0100  /* woken up for initialisation purposes */
 #define TASK_WOKEN_TIMER  0x0200  /* woken up because of expired timer */
diff --git a/reg-tests/checks/http-check-send.vtc b/reg-tests/checks/http-check-send.vtc
new file mode 100644
index 0000000..7e2e37b
--- /dev/null
+++ b/reg-tests/checks/http-check-send.vtc
@@ -0,0 +1,150 @@
+varnishtest "Health-checks: http-check send test"
+
+feature ignore_unknown_macro
+
+# This script tests HTTP health-checks and more particularly the "http-check
+# send" directive.
+
+server s1 {
+    rxreq
+    expect req.method == OPTIONS
+    expect req.url == /
+    expect req.proto == HTTP/1.0
+    txresp
+} -start
+
+server s2 {
+    rxreq
+    expect req.method == GET
+    expect req.url == /test
+    expect req.proto == HTTP/1.1
+    txresp
+} -start
+
+server s3 {
+    rxreq
+    expect req.method == OPTIONS
+    expect req.url == /
+    expect req.proto == HTTP/1.0
+    expect req.http.hdr == <undef>
+    expect req.http.host == <undef>
+    expect req.http.x-test == <undef>
+    expect req.http.content-length == <undef>
+    expect req.bodylen == 0
+    txresp
+} -start
+
+server s4 {
+    rxreq
+    expect req.method == GET
+    expect req.url == /status
+    expect req.proto == HTTP/1.1
+    expect req.http.hdr == <undef>
+    expect req.http.host == "my-www-host"
+    expect req.http.x-test == true
+    expect req.http.content-length == 4
+    expect req.bodylen == 4
+    expect req.body == "test"
+    txresp
+} -start
+
+server s5 {
+    rxreq
+    expect req.method == GET
+    expect req.url == /status
+    expect req.proto == HTTP/1.1
+    expect req.http.hdr == <undef>
+    expect req.http.host == "other-www-host"
+    expect req.http.x-test == <undef>
+    expect req.http.x-new-test == true
+    expect req.http.content-length == 10
+    expect req.bodylen == 10
+    expect req.body == "other test"
+    txresp
+} -start
+
+
+syslog S1 -level notice {
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be1 started."
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded.*code: 200"
+} -start
+
+syslog S2 -level notice {
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be2 started."
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv succeeded.*code: 200"
+} -start
+
+syslog S3 -level notice {
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be3 started."
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be3/srv succeeded.*code: 200"
+} -start
+
+syslog S4 -level notice {
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be4 started."
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv succeeded.*code: 200"
+} -start
+
+syslog S5 -level notice {
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be5 started."
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be5/srv succeeded.*code: 200"
+} -start
+
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        timeout client 1s
+        timeout server 1s
+        timeout connect 200ms
+        option httpchk
+        option log-health-checks
+
+    backend be1
+        log ${S1_addr}:${S1_port} len 2048 local0
+        server srv ${s1_addr}:${s1_port} check inter 200ms rise 1 fall 1
+
+    backend be2
+        log ${S2_addr}:${S2_port} len 2048 local0
+        option httpchk GET /test HTTP/1.1
+        server srv ${s2_addr}:${s2_port} check inter 200ms rise 1 fall 1
+
+    defaults
+        mode http
+        timeout client 1s
+        timeout server 1s
+        timeout connect 200ms
+        option httpchk GET /status HTTP/1.1\r\nHdr:\ must-be-removed
+        option log-health-checks
+        http-check send hdr Host "my-www-host" hdr X-test true body "test"
+
+    backend be3
+        option httpchk
+        log ${S3_addr}:${S3_port} len 2048 local0
+        server srv ${s3_addr}:${s3_port} check inter 200ms rise 1 fall 1
+
+    backend be4
+        log ${S4_addr}:${S4_port} len 2048 local0
+        server srv ${s4_addr}:${s4_port} check inter 200ms rise 1 fall 1
+
+    backend be5
+        log ${S5_addr}:${S5_port} len 2058 local0
+        http-check send hdr Host "other-www-host" hdr X-New-Test true body "other test"
+        server srv ${s5_addr}:${s5_port} check inter 200ms rise 1 fall 1
+
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
+syslog S3 -wait
+syslog S4 -wait
+syslog S5 -wait
diff --git a/reg-tests/checks/tls_health_checks.vtc b/reg-tests/checks/tls_health_checks.vtc
index 4a431af..f6e4d74 100644
--- a/reg-tests/checks/tls_health_checks.vtc
+++ b/reg-tests/checks/tls_health_checks.vtc
@@ -19,13 +19,19 @@
 server s2 {
 } -start
 
+server s3 {
+    rxreq
+    expect req.method == OPTIONS
+    expect req.url == *
+    expect req.proto == HTTP/1.1
+    txresp
+} -start
+
 syslog S1 -level notice {
     recv
     expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy fe1 started."
     recv info
     expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* fe1~ be1/srv1 .* 200 [[:digit:]]+ - - ---- .* \"OPTIONS \\* HTTP/1.1\""
-    recv info
-    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: .* fe1~ be1/srv1 .* 504 [[:digit:]]+ - - sH-- .* \"OPTIONS \\* HTTP/1.1\""
 } -start
 
 haproxy h1 -conf {
@@ -44,6 +50,9 @@
     backend be2
         server srv2 ${s2_addr}:${s2_port}
 
+    backend be3
+        server srv3 ${s3_addr}:${s3_port}
+
     frontend fe1
         option httplog
         log ${S1_addr}:${S1_port} len 2048 local0 debug err
@@ -54,15 +63,18 @@
         option tcplog
         bind "fd@${fe2}" ssl crt ${testdir}/common.pem
         use_backend be2
+
+    frontend fe3
+        option httplog
+        bind "fd@${fe3}" ssl crt ${testdir}/common.pem
+        use_backend be3
 } -start
 
 syslog S2 -level notice {
     recv
     expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Proxy be2 started."
     recv
-    expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be2/srv1 succeeded, reason: Layer7 check passed, code: 200, info: \"OK\", check duration: [[:digit:]]+ms, status: 1/1 UP."
-    recv
-    expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be2/srv1 failed, reason: Layer7 wrong status, code: 504, info: \"Gateway Time-out\", check duration: [[:digit:]]+ms, status: 0/1 DOWN."
+    expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be2/srv1 succeeded, reason: Layer7 check passed.+code: 200.+check duration: [[:digit:]]+ms, status: 1/1 UP."
 } -start
 
 syslog S4 -level notice {
@@ -72,6 +84,13 @@
     expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be4/srv2 succeeded, reason: Layer6 check passed, check duration: [[:digit:]]+ms, status: 1/1 UP."
 } -start
 
+syslog S6 -level notice {
+    recv
+    expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Proxy be6 started."
+    recv
+    expect ~ "[^:\\[ ]\\[${h2_pid}\\]: Health check for server be6/srv3 succeeded, reason: Layer7 check passed.+code: 200.+check duration: [[:digit:]]+ms, status: 1/1 UP."
+} -start
+
 haproxy h2 -conf {
     global
         tune.ssl.default-dh-param 2048
@@ -92,10 +111,16 @@
         option log-health-checks
         log ${S4_addr}:${S4_port} daemon
         server srv2 ${h1_fe2_addr}:${h1_fe2_port} ssl crt ${testdir}/common.pem verify none check-ssl check
+
+    backend be6
+        option log-health-checks
+        option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www
+        log ${S6_addr}:${S6_port} daemon
+        server srv3 127.0.0.1:80 crt ${testdir}/common.pem verify none check check-ssl port ${h1_fe3_port} addr ${h1_fe3_addr}:80
 } -start
 
 syslog S1 -wait
 
 syslog S2 -wait
 syslog S4 -wait
-
+syslog S6 -wait
diff --git a/reg-tests/compression/lua_validation.vtc b/reg-tests/compression/lua_validation.vtc
index 6e8c1e8..d74f2eb 100644
--- a/reg-tests/compression/lua_validation.vtc
+++ b/reg-tests/compression/lua_validation.vtc
@@ -2,7 +2,7 @@
 
 varnishtest "Compression validation"
 #REQUIRE_VERSION=1.6
-#REQUIRE_OPTIONS=ZLIB|SLZ,LUA
+#REQUIRE_OPTIONS=ZLIB|SLZ,LUA,OPENSSL
 #REGTEST_TYPE=slow
 
 feature ignore_unknown_macro
@@ -40,7 +40,7 @@
         HOST="\[::1\]"
     fi
 
-    md5=$(which md5 || which md5sum)
+    md5=$(command -v md5 || command -v md5sum)
 
     if [ -z $md5 ] ; then
         echo "MD5 checksum utility not found"
diff --git a/reg-tests/connection/proxy_protocol_tlv_validation.vtc b/reg-tests/connection/proxy_protocol_tlv_validation.vtc
new file mode 100644
index 0000000..24c548f
--- /dev/null
+++ b/reg-tests/connection/proxy_protocol_tlv_validation.vtc
@@ -0,0 +1,140 @@
+varnishtest "Check that the TLVs are properly validated"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+# We need one HAProxy for each test, because apparently the connection by
+# the client is reused, leading to connection resets.
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that a correct header passes
+client c1 -connect ${h1_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "12345"
+    sendhex "02 00 05 31 32 33 34 35"
+
+    txreq -url "/"
+    rxresp
+    expect resp.http.echo == "3132333435"
+} -run
+
+haproxy h2 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that a TLV after the end of the PROXYv2 header is ignored
+client c2 -connect ${h2_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "12345"
+    sendhex "02 00 05 31 32 33 34 35"
+    # after the end of the PROXYv2 header: PP2_TYPE_AUTHORITY + length of the value + "54321"
+    sendhex "02 00 05 35 34 33 32 31"
+
+    txreq -url "/"
+    rxresp
+    expect resp.http.echo == "3132333435"
+} -run
+
+haproxy h3 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that a TLV length exceeding the PROXYv2 length fails
+client c3 -connect ${h3_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + too small length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "1234512345"
+    sendhex "02 00 0A 31 32 33 34 35 31 32 33 34 35"
+
+    txreq -url "/"
+    expect_close
+} -run
+
+haproxy h4 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that TLVs not ending with the PROXYv2 header fail
+client c4 -connect ${h4_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + too big length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "1234"
+    sendhex "02 00 04 31 32 33 34"
+
+    txreq -url "/"
+    expect_close
+} -run
diff --git a/reg-tests/http-errorfiles/errorfiles.vtc b/reg-tests/http-errorfiles/errorfiles.vtc
new file mode 100644
index 0000000..c8670ae
--- /dev/null
+++ b/reg-tests/http-errorfiles/errorfiles.vtc
@@ -0,0 +1,51 @@
+varnishtest "Test the errofile directive in proxy sections"
+
+# This config tests the errorfile directive in proxy sections (including the
+# defaults section).
+
+feature ignore_unknown_macro
+
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+        errorfile 400  ${testdir}/errors/400.http
+        errorfile 403  ${testdir}/errors/403.http
+        errorfile 408  /dev/null
+
+    frontend fe1
+        bind "fd@${fe1}"
+
+        errorfile 403  ${testdir}/errors/403-1.http
+        errorfile 500 /dev/null
+
+        http-request deny deny_status 400 if { path /400 }
+        http-request deny deny_status 403 if { path /403 }
+        http-request deny deny_status 408 if { path /408 }
+        http-request deny deny_status 500 if { path /500 }
+
+} -start
+
+client c1r1  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /400
+        rxresp
+        expect resp.status == 400
+        expect resp.http.x-err-type == "default"
+} -run
+client c1r2  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /403
+        rxresp
+        expect resp.status == 403
+        expect resp.http.x-err-type == "errors-1"
+} -run
+client c1r3  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /408
+        expect_close
+} -run
+client c1r4  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /500
+        expect_close
+} -run
diff --git a/reg-tests/http-errorfiles/errors/400-1.http b/reg-tests/http-errorfiles/errors/400-1.http
new file mode 100644
index 0000000..86a2e69
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400-1.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-1

+

+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/400-2.http b/reg-tests/http-errorfiles/errors/400-2.http
new file mode 100644
index 0000000..c108510
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400-2.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-2

+

+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/400-3.http b/reg-tests/http-errorfiles/errors/400-3.http
new file mode 100644
index 0000000..1fe1841
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400-3.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-3

+

+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/400.http b/reg-tests/http-errorfiles/errors/400.http
new file mode 100644
index 0000000..ce229aa
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/400.http
@@ -0,0 +1,9 @@
+HTTP/1.1 400 Bad request

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: default

+

+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/403-1.http b/reg-tests/http-errorfiles/errors/403-1.http
new file mode 100644
index 0000000..08bdf02
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/403-1.http
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-1

+

+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/403-2.http b/reg-tests/http-errorfiles/errors/403-2.http
new file mode 100644
index 0000000..9c07e5d
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/403-2.http
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-2

+

+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/403.http b/reg-tests/http-errorfiles/errors/403.http
new file mode 100644
index 0000000..fd969b2
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/403.http
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: default

+

+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404-1.http b/reg-tests/http-errorfiles/errors/404-1.http
new file mode 100644
index 0000000..154ed0b
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404-1.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-1

+

+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404-2.http b/reg-tests/http-errorfiles/errors/404-2.http
new file mode 100644
index 0000000..e26f91d
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404-2.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-2

+

+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404-3.http b/reg-tests/http-errorfiles/errors/404-3.http
new file mode 100644
index 0000000..4bc1661
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404-3.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-3

+

+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/404.http b/reg-tests/http-errorfiles/errors/404.http
new file mode 100644
index 0000000..8dacd95
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/404.http
@@ -0,0 +1,9 @@
+HTTP/1.1 404 Not Found

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: default

+

+<html><body><h1>404 Not Found</h1>
+The resource could not be found.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/500-1.http b/reg-tests/http-errorfiles/errors/500-1.http
new file mode 100644
index 0000000..c5717ad
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/500-1.http
@@ -0,0 +1,9 @@
+HTTP/1.0 500 Internal Server Error

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: errors-1

+

+<html><body><h1>500 Internal Server Error</h1>
+An internal server error occured.
+</body></html>
diff --git a/reg-tests/http-errorfiles/errors/500.http b/reg-tests/http-errorfiles/errors/500.http
new file mode 100644
index 0000000..acd2714
--- /dev/null
+++ b/reg-tests/http-errorfiles/errors/500.http
@@ -0,0 +1,9 @@
+HTTP/1.0 500 Internal Server Error

+Cache-Control: no-cache

+Connection: close

+Content-Type: text/html

+x-err-type: default

+

+<html><body><h1>500 Internal Server Error</h1>
+An internal server error occured.
+</body></html>
diff --git a/reg-tests/http-errorfiles/http_deny_errors.vtc b/reg-tests/http-errorfiles/http_deny_errors.vtc
new file mode 100644
index 0000000..53c4c42
--- /dev/null
+++ b/reg-tests/http-errorfiles/http_deny_errors.vtc
@@ -0,0 +1,57 @@
+varnishtest "Test the custom erorrs for HTTP deny rules"
+#REQUIRE_VERSION=2.2
+
+# This config tests the custom errors for HTTP deny rules.
+
+feature ignore_unknown_macro
+
+
+haproxy h1 -conf {
+    http-errors errors-1
+        errorfile 400  ${testdir}/errors/400-1.http
+        errorfile 403  ${testdir}/errors/403-1.http
+        errorfile 404  ${testdir}/errors/404-1.http
+        errorfile 500  /dev/null
+
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend fe1
+        bind "fd@${fe1}"
+        http-request deny deny_status 400 if { path /400 }
+        http-request deny deny_status 403 errorfile  ${testdir}/errors/403.http if { path /403 }
+        http-request deny deny_status 404 errorfiles errors-1 if { path /404 }
+        http-request deny deny_status 500 errorfile  /dev/null if { path /500-1 }
+        http-request deny deny_status 500 errorfiles errors-1 if { path /500-2 }
+
+} -start
+
+client c1r1  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /400
+        rxresp
+        expect resp.status == 400
+	expect resp.http.x-err-type == <undef>
+} -run
+client c1r2  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /403
+        rxresp
+        expect resp.status == 403
+	expect resp.http.x-err-type == "default"
+} -run
+client c1r3  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /404
+        rxresp
+        expect resp.status == 404
+	expect resp.http.x-err-type == "errors-1"
+} -run
+client c1r4  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /500-1
+        expect_close
+} -run
+client c1r5  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /500-2
+        expect_close
+} -run
diff --git a/reg-tests/http-errorfiles/http_errors.vtc b/reg-tests/http-errorfiles/http_errors.vtc
new file mode 100644
index 0000000..7d9f18c
--- /dev/null
+++ b/reg-tests/http-errorfiles/http_errors.vtc
@@ -0,0 +1,134 @@
+varnishtest "Test the errorfiles directive"
+#REQUIRE_VERSION=2.2
+
+# This config tests the errorfiles directive.
+
+feature ignore_unknown_macro
+
+
+haproxy h1 -conf {
+    http-errors errors-1
+        errorfile 400  ${testdir}/errors/400-1.http
+        errorfile 403  ${testdir}/errors/403-1.http
+        errorfile 404  ${testdir}/errors/404-1.http
+        errorfile 500  ${testdir}/errors/500-1.http
+
+    http-errors errors-2
+        errorfile 400  ${testdir}/errors/400-2.http
+        errorfile 403  ${testdir}/errors/403-2.http
+        errorfile 404  ${testdir}/errors/404-2.http
+
+    http-errors errors-3
+        errorfile 400  ${testdir}/errors/400-3.http
+        errorfile 404  ${testdir}/errors/404-3.http
+
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+	errorfiles errors-2
+        errorfile 400  ${testdir}/errors/400.http
+        errorfile 404  ${testdir}/errors/404.http
+
+    frontend fe1
+        bind "fd@${fe1}"
+        http-request deny deny_status 400 if { path /400 }
+        http-request deny deny_status 403 if { path /403 }
+        http-request deny deny_status 404 if { path /404 }
+        http-request deny deny_status 500 if { path /500 }
+
+    frontend fe2
+        bind "fd@${fe2}"
+        errorfiles errors-1
+        errorfile 500  ${testdir}/errors/500.http
+        http-request deny deny_status 400 if { path /400 }
+        http-request deny deny_status 403 if { path /403 }
+        http-request deny deny_status 404 if { path /404 }
+        http-request deny deny_status 500 if { path /500 }
+
+    frontend fe3
+        bind "fd@${fe3}"
+        errorfile 500  ${testdir}/errors/500.http
+        errorfiles errors-1 500
+        errorfiles errors-3 400
+        http-request deny deny_status 400 if { path /400 }
+        http-request deny deny_status 403 if { path /403 }
+        http-request deny deny_status 404 if { path /404 }
+        http-request deny deny_status 500 if { path /500 }
+} -start
+
+client c1r1  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /400
+        rxresp
+        expect resp.status == 400
+	expect resp.http.x-err-type == "default"
+} -run
+client c1r2  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /403
+        rxresp
+        expect resp.status == 403
+	expect resp.http.x-err-type == "errors-2"
+} -run
+client c1r3  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /404
+        rxresp
+        expect resp.status == 404
+	expect resp.http.x-err-type == "default"
+} -run
+client c1r4  -connect ${h1_fe1_sock} {
+        txreq -req GET -url /500
+        rxresp
+        expect resp.status == 500
+	expect resp.http.x-err-type == <undef>
+} -run
+
+client c2r1  -connect ${h1_fe2_sock} {
+        txreq -req GET -url /400
+        rxresp
+        expect resp.status == 400
+	expect resp.http.x-err-type == "errors-1"
+} -run
+client c2r2  -connect ${h1_fe2_sock} {
+        txreq -req GET -url /403
+        rxresp
+        expect resp.status == 403
+	expect resp.http.x-err-type == "errors-1"
+} -run
+client c2r3  -connect ${h1_fe2_sock} {
+        txreq -req GET -url /404
+        rxresp
+        expect resp.status == 404
+	expect resp.http.x-err-type == "errors-1"
+} -run
+client c2r4  -connect ${h1_fe2_sock} {
+        txreq -req GET -url /500
+        rxresp
+        expect resp.status == 500
+	expect resp.http.x-err-type == "default"
+} -run
+
+client c3r1  -connect ${h1_fe3_sock} {
+        txreq -req GET -url /400
+        rxresp
+        expect resp.status == 400
+	expect resp.http.x-err-type == "errors-3"
+} -run
+client c3r2  -connect ${h1_fe3_sock} {
+        txreq -req GET -url /403
+        rxresp
+        expect resp.status == 403
+	expect resp.http.x-err-type == "errors-2"
+} -run
+client c3r3  -connect ${h1_fe3_sock} {
+        txreq -req GET -url /404
+        rxresp
+        expect resp.status == 404
+	expect resp.http.x-err-type == "default"
+} -run
+client c3r4  -connect ${h1_fe3_sock} {
+        txreq -req GET -url /500
+        rxresp
+        expect resp.status == 500
+	expect resp.http.x-err-type == "errors-1"
+} -run
diff --git a/reg-tests/http-rules/acl_cli_spaces.vtc b/reg-tests/http-rules/acl_cli_spaces.vtc
new file mode 100644
index 0000000..e67f7ce
--- /dev/null
+++ b/reg-tests/http-rules/acl_cli_spaces.vtc
@@ -0,0 +1,80 @@
+varnishtest "haproxy ACL, CLI and mCLI spaces"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.0
+
+server s1 {
+   rxreq
+   expect req.method == "GET"
+   txresp
+} -repeat 2 -start
+
+haproxy h1 -W -S -conf {
+  defaults
+    mode http
+    ${no-htx} option http-use-htx
+    log global
+    option httplog
+    timeout connect         15ms
+    timeout client          20ms
+    timeout server          20ms
+
+  frontend fe1
+    bind "fd@${fe1}"
+
+    http-request deny if { req.hdr(user-agent) -i -m str -f ${testdir}/agents.acl }
+
+    default_backend be1
+
+  backend be1
+    server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+    txreq -hdr "User-Agent: Mon User Agent"
+    rxresp
+    expect resp.status == 200
+} -run
+
+haproxy h1 -cli {
+  send "add acl ${testdir}/agents.acl Mon\\ User\\ Agent\\;"
+  expect ~ .*
+
+  send "show acl ${testdir}/agents.acl"
+  expect ~ ".*Mon User Agent.*"
+}
+
+client c1 -connect ${h1_fe1_sock} {
+    txreq -hdr "User-Agent: Mon User Agent;"
+    rxresp
+    expect resp.status == 403
+} -run
+
+
+haproxy h1 -cli {
+  send "del acl ${testdir}/agents.acl Mon\\ User\\ Agent\\;"
+  expect ~ .*
+
+  send "show acl ${testdir}/agents.acl"
+  expect ~ .*
+}
+
+client c1 -connect ${h1_fe1_sock} {
+    txreq -hdr "User-Agent: Mon User Agent;"
+    rxresp
+    expect resp.status == 200
+} -run
+
+
+# Try it with the master CLI
+haproxy h1 -mcli {
+  send "@1 add acl ${testdir}/agents.acl Mon\\ User\\ Agent\\;;@1 show acl ${testdir}/agents.acl"
+  expect ~ ".*Mon User Agent;.*"
+}
+
+client c1 -connect ${h1_fe1_sock} {
+    txreq -hdr "User-Agent: Mon User Agent;"
+    rxresp
+    expect resp.status == 403
+} -run
diff --git a/reg-tests/http-rules/agents.acl b/reg-tests/http-rules/agents.acl
new file mode 100644
index 0000000..345e6ae
--- /dev/null
+++ b/reg-tests/http-rules/agents.acl
@@ -0,0 +1 @@
+Test
diff --git a/reg-tests/lua/txn_get_priv.vtc b/reg-tests/lua/txn_get_priv.vtc
index 98f98f7..25fb063 100644
--- a/reg-tests/lua/txn_get_priv.vtc
+++ b/reg-tests/lua/txn_get_priv.vtc
@@ -1,5 +1,5 @@
 varnishtest "Lua: txn:get_priv() scope"
-#REQUIRE_OPTIONS=LUA
+#REQUIRE_OPTIONS=LUA,OPENSSL
 #REGTEST_TYPE=bug
 
 feature ignore_unknown_macro
diff --git a/reg-tests/mcli/mcli_start_progs.vtc b/reg-tests/mcli/mcli_start_progs.vtc
new file mode 100644
index 0000000..bda9b96
--- /dev/null
+++ b/reg-tests/mcli/mcli_start_progs.vtc
@@ -0,0 +1,40 @@
+varnishtest "Try to start a master CLI with 2 programs"
+#REGTEST_TYPE=bug
+#REQUIRE_VERSION=2.0
+#REQUIRE_BINARIES=sleep
+
+feature ignore_unknown_macro
+
+# Do nothing. Is there only to create s1_* macros
+server s1 {
+} -start
+
+haproxy h1 -W -S -conf {
+    global
+        nbproc 1
+    defaults
+        mode http
+        ${no-htx} option http-use-htx
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend myfrontend
+        bind "fd@${my_fe}"
+        default_backend test
+
+    backend test
+        server www1 ${s1_addr}:${s1_port}
+
+    program foo
+        command sleep 10
+
+    program bar
+        command sleep 10
+
+} -start
+
+haproxy h1 -mcli {
+    send "show proc"
+    expect ~ ".*foo.*\n.*bar.*\n"
+} -wait
diff --git a/reg-tests/sample_fetches/hashes.vtc b/reg-tests/sample_fetches/hashes.vtc
new file mode 100644
index 0000000..874f81e
--- /dev/null
+++ b/reg-tests/sample_fetches/hashes.vtc
@@ -0,0 +1,97 @@
+varnishtest "Hash validity test"
+
+#REQUIRE_VERSION=1.9
+
+feature ignore_unknown_macro
+
+server s1 {
+    rxreq
+    txresp
+} -start
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend fe
+        bind "fd@${fe}"
+
+        # base64 encoding of \x00\x01\x02...\xFF
+        http-response set-var(res.key) "str(AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==),b64dec"
+
+        # length (start:0, next:255)
+        http-response set-header x-len0 "%[var(res.key),length]"
+        http-response set-header x-len1 "%[var(res.key),bytes(1),length]"
+
+        # text-based encoding
+        http-response set-header x-hex "%[var(res.key),hex]"
+        http-response set-header x-b64 "%[var(res.key),base64]"
+
+        # SHA family
+        http-response set-header x-sha1 "%[var(res.key),sha1,hex]"
+        #http-response set-header x-sha2 "%[var(res.key),sha2,hex]"
+        #http-response set-header x-sha2-224 "%[var(res.key),sha2(224),hex]"
+        #http-response set-header x-sha2-256 "%[var(res.key),sha2(256),hex]"
+        #http-response set-header x-sha2-384 "%[var(res.key),sha2(384),hex]"
+        #http-response set-header x-sha2-512 "%[var(res.key),sha2(512),hex]"
+
+        # 32-bit hashes, and their avalanche variants
+        http-response set-header x-crc32   "%[var(res.key),crc32]"
+        http-response set-header x-crc32-1 "%[var(res.key),crc32(1)]"
+
+        http-response set-header x-crc32c "%[var(res.key),crc32c]"
+        http-response set-header x-crc32c-1 "%[var(res.key),crc32c(1)]"
+
+        http-response set-header x-djb2 "%[var(res.key),djb2]"
+        http-response set-header x-djb2-1 "%[var(res.key),djb2(1)]"
+
+        http-response set-header x-sdbm "%[var(res.key),sdbm]"
+        http-response set-header x-sdbm-1 "%[var(res.key),sdbm(1)]"
+
+        http-response set-header x-wt6 "%[var(res.key),wt6]"
+        http-response set-header x-wt6-1 "%[var(res.key),wt6(1)]"
+
+        # 32/64-bit hashes, with seed variant
+        http-response set-header x-xxh32 "%[var(res.key),xxh32]"
+        http-response set-header x-xxh32-1 "%[var(res.key),xxh32(1)]"
+        http-response set-header x-xxh64 "%[var(res.key),xxh64]"
+        http-response set-header x-xxh64-1 "%[var(res.key),xxh64(1)]"
+        default_backend be
+
+    backend be
+        server srv1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.status == 200
+    expect resp.http.x-len0 == "0"
+    expect resp.http.x-len1 == "255"
+    expect resp.http.x-hex == "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
+    expect resp.http.x-b64 == "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
+
+    expect resp.http.x-sha1 == "4916D6BDB7F78E6803698CAB32D1586EA457DFC8"
+    #expect resp.http.x-sha2 == "40AFF2E9D2D8922E47AFD4648E6967497158785FBD1DA870E7110266BF944880"
+    #expect resp.http.x-sha2-224 == "88702E63237824C4EB0D0FCFE41469A462493E8BEB2A75BBE5981734"
+    #expect resp.http.x-sha2-256 == "40AFF2E9D2D8922E47AFD4648E6967497158785FBD1DA870E7110266BF944880"
+    #expect resp.http.x-sha2-384 == "FFDAEBFF65ED05CF400F0221C4CCFB4B2104FB6A51F87E40BE6C4309386BFDEC2892E9179B34632331A59592737DB5C5"
+    #expect resp.http.x-sha2-512 == "1E7B80BC8EDC552C8FEEB2780E111477E5BC70465FAC1A77B29B35980C3F0CE4A036A6C9462036824BD56801E62AF7E9FEBA5C22ED8A5AF877BF7DE117DCAC6D"
+    expect resp.http.x-crc32 == "688229491"
+    expect resp.http.x-crc32-1 == "4230317029"
+    expect resp.http.x-crc32c == "2621708363"
+    expect resp.http.x-crc32c-1 == "2242979626"
+    expect resp.http.x-djb2 == "2589693061"
+    expect resp.http.x-djb2-1 == "600622701"
+    expect resp.http.x-sdbm == "905707648"
+    expect resp.http.x-sdbm-1 == "3103804144"
+    expect resp.http.x-wt6 == "4090277559"
+    expect resp.http.x-wt6-1 == "1192658767"
+    expect resp.http.x-xxh32 == "1497633363"
+    expect resp.http.x-xxh32-1 == "1070421674"
+    expect resp.http.x-xxh64 == "2282408585429094475"
+    expect resp.http.x-xxh64-1 == "-4689339368900765961"
+} -run
diff --git a/reg-tests/sample_fetches/so_name.vtc b/reg-tests/sample_fetches/so_name.vtc
new file mode 100644
index 0000000..c6211fa
--- /dev/null
+++ b/reg-tests/sample_fetches/so_name.vtc
@@ -0,0 +1,22 @@
+varnishtest "so_name sample fetche Test"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+    defaults
+        mode http
+
+    frontend fe
+        bind "fd@${fe}" name foo
+        http-request return status 200 hdr so-name %[so_name]
+
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.status == 200
+    expect resp.http.so-name == "foo"
+} -run
diff --git a/reg-tests/seamless-reload/abns_socket.vtc b/reg-tests/seamless-reload/abns_socket.vtc
index b1f1aa5..b36dcbe 100644
--- a/reg-tests/seamless-reload/abns_socket.vtc
+++ b/reg-tests/seamless-reload/abns_socket.vtc
@@ -26,9 +26,9 @@
     ${no-htx} option http-use-htx
     log global
     option httplog
-    timeout connect         15ms
-    timeout client          20ms
-    timeout server          20ms
+    timeout connect         1s
+    timeout client          1s
+    timeout server          1s
 
   listen testme
     bind "fd@${testme}"
diff --git a/reg-tests/ssl/ca-auth.crt b/reg-tests/ssl/ca-auth.crt
new file mode 100644
index 0000000..1695af5
--- /dev/null
+++ b/reg-tests/ssl/ca-auth.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFyzCCA7OgAwIBAgIURpSju/jEN7LJUV4vEibyeuJwd5kwDQYJKoZIhvcNAQEL
+BQAwdDELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxHTAbBgNVBAoM
+FEhBUHJveHkgVGVjaG5vbG9naWVzMTEwLwYDVQQDDChIQVByb3h5IFRlY2hub2xv
+Z2llcyBDQSBUZXN0IENsaWVudCBBdXRoMCAXDTIwMDQyODE4NTIwMloYDzIwNTAw
+NDIxMTg1MjAyWjB0MQswCQYDVQQGEwJGUjETMBEGA1UECAwKU29tZS1TdGF0ZTEd
+MBsGA1UECgwUSEFQcm94eSBUZWNobm9sb2dpZXMxMTAvBgNVBAMMKEhBUHJveHkg
+VGVjaG5vbG9naWVzIENBIFRlc3QgQ2xpZW50IEF1dGgwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDGlgKEhw5RLOBgaTsPJJYglozt3SVv94084RoA19kw
+udemrMaJdXV517MsR9qoHoxFVFdYP//W6vx7c5RadPqMZrWT9QXhJSR0Kr5KdHUs
++t8H8pmlDicxIx0cuRtmKmRuAMoDI1E+5EsRspemyq1ExcBm42zM9Oj9QysKF0wc
+FXq56eHgXSmKQGAiGuGB5v6CeVu3DVhZHuGyv3XVeOsf35s2M757hi+N6mqqvtw8
+JiQyw53YxBaB80CRtiIXzLd07S4GZnRCNOWgXLwo6+8K1gId3KRk4DhUIEIMrchy
+aqeZmJVToF+8fbjZ97pREJyQo4EXsgPrLE3Ck5Y+TfYJli3NJNhEWhucu4RQ6XXg
+lTruatM9uj9ZZEvtJreu5KRvAOfRLBj+C3f+VRoDrE9RSEn/XSGek+/D7+n3U0GO
+h2KcrUn7R+Yy6DdwxhGImqDnYaKaZds+vEjtvP4ViOC982eVl5/lFAw3JBHR57iL
+/K0zTRwjSasUvlJFQNUNAG9HktCYTdEj0U3C/xBDPayY04BFvn8piZeMpoCN9dre
+UxuctmMrz1pIvYAdZSseraf4W0psx6oeU/CcFZnkc5lbUDvn7u6Ozk4gnfyo1fxJ
+8a2X7dl3joqUABwaS/FkP/CPoEKBMFf4pcZUhuWbPkhiVNXZIkQYQISY6JOr5qDn
+TwIDAQABo1MwUTAdBgNVHQ4EFgQUW4t2W4MUuBG0EyFdHObYYZbtjEowHwYDVR0j
+BBgwFoAUW4t2W4MUuBG0EyFdHObYYZbtjEowDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAIqQJu2nX0Rn9EUPuVDhCrirQFDwMFb7Ifoqr6rMoD9OT
+pgyQb198TkW550Rhg36LnnmBzifOoPBmHVJQWvMAVnH/BQrkRqXFRk2M3PRoEv44
+twMlUPU/NMLVKnXE+neBlXhBWeyY/bCmVftk/TdLwom0Mer4Nw+rt1JQAXKKNRdj
+3b8EnJHGy7Es4fv/traZQ6ZSHoD0GsxydweCjZEO2hLw9/VVrjdM6rNDZlz7cST5
+rhyUeG3mlwWweGY6ahlMx//Z11m/1JLGyDcwMgunRoBiDep7I3ZMcWH1PjM3CyrL
+ZrDoUvwtMSEAuT/be5SfU/CzS/DTyBtfSpEUbm1dg9cqm1vG7/GFdzJqafv8ppwh
+fZhtxKXcyH4C1BeWlDqA06aNM3ClxWNyrAjdcyI45tosxgDuWyRyUC5IhyK6s81O
+6AP7xQH6s+i0k3mzgOxieV/QRo4E67y31XZHJz6uFKSaHOIdpV7li7mAiswFfhMl
++C3ud2rU79X2vTYLzELR05djzAXHJT9sc5NjbODw3RRKRkcB78IoNM7D0Mcctz+3
+1DHcmk6crsxPRDmvKj9zQTjbG1UpjTogdsbh1afuqJ1atxBgav+/YhefAziXazAy
+P1CHU/OYq/vjfGobIz6RVUjkg50RwkD58TR3LzQPOpSNoM55U/jGd3x4X3kh8tU=
+-----END CERTIFICATE-----
diff --git a/reg-tests/ssl/client1.pem b/reg-tests/ssl/client1.pem
new file mode 100644
index 0000000..3725f79
--- /dev/null
+++ b/reg-tests/ssl/client1.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIIFLTCCAxUCAQIwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCRlIxEzARBgNV
+BAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMTEw
+LwYDVQQDDChIQVByb3h5IFRlY2hub2xvZ2llcyBDQSBUZXN0IENsaWVudCBBdXRo
+MCAXDTIwMDQyODE4NTk0MloYDzIwNTAwNDIxMTg1OTQyWjBDMQswCQYDVQQGEwJG
+UjEiMCAGA1UECgwZSEFQcm94eSBUZWNobm9sb2dpZXMgVGVzdDEQMA4GA1UEAwwH
+Y2xpZW50MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMCUnq4y/rYG
+n2BPYutNd/dQX3KV1qVKKXBsXXqBzE84qjBg6SeQQVwcVN0UYK+l2Rqnkt9m+sV+
+CxAwDAVHts/QmD/4tbjuP8tMQiZcUsl7hxRzLRK2lXGwoX3B7GJgXxjDckCllert
+FgUMylb4ACt8oA1e4c75fY8ppmOcjaNcPBghk09uKVUOKy+UZ/HWkNncF6cO9N82
+Y+bPdL1hg8mr6n7U+jv0bdyBjjN+b/3ggInY8NGyPHpl6ezvmgaI5+stA77YolCY
+NoG7ZexMpBbtv/2M7+PHlx5c5lzd4HbuC5fOtfkMvoosIZJaI8/mM5J6aeu6JpPB
+XvGRRE1Opmmhk1M3aQvU4q9LPYLkXIivuH+sHZnVZHZ32hhpZ4GhTpgayF22n/hI
+fMOzSIMhpao/1YuLVbLgXdWJZx9uOIT//a/3Bd4I/c1/Pt11oNSIhiEAS7beWj0c
+QtsSabeQwEIOOlbxWFA1aRogFNNE3iW4gps4p/4oHmT9Warb5AadE6nzh7N1nCiD
+oO7JoHUzOj8VunLn2RZ8vWuBJI/2fh1TJVjOBmQBl6YGHD8BaRWlzv/VOiq2z8at
+90rXGUb58KYvcfOTOZmYjKK16r/112pEgJuivXXr+N6qJKYxw46m+MAD2eDQ0Bc6
+gFZMlcyBAyJwuxIejUTvWwoddfRnaFajAgMBAAEwDQYJKoZIhvcNAQELBQADggIB
+AFk00NuZDresZ9voh2E9J2GvUbG9x+NSjZR6pQ3MiPPXpLYskV2xAvxFSayGcQhG
+mIfHshsnEhE35WYU80R5Ag1Mxh+XPbZUiNj/oOEFdBj45c0HGorChaVkZtnLilMo
+B0yW+0pnkqKaRkgmVsSrNCgimBtZX1hsZRLDxa2vldJ9lTIg3OuveqBv/uwbMOUC
+eT+il/sdl68K6oNHvAFdY1U34oJnvj4yF6ZZM1jRERK38wY0+2C+mlcXNL648k+2
+lOMeBddaDUKhscWdw1+Ui8Sn6lc6H+iPpGo3xdj9awc0568SCH+D5cpuTMNTREuM
+p3paOMGpLWuQQisltdmz8Ms8lAcJUDeyDmrgE9CPx9DiydB6Z1uP9y9sozqB2SIN
++QqfQLv+lAaUB6cu5xIWfZIFeTxxziABZ2jDF4vVvK+NN2IdBahbI63HQpfeK4tG
+Bkmuny4vlCnHBnzVaAboaQk2xzI9Yp79IN6yhmuO8AjCvd0XlH/nYF6b7WjVy0gU
+LpmkYVHWhADLY4q06PUz8gFGsfDHnx9RQIV01SXbcFxhmAjJBequBbTpucW4UAK4
+sTmg59wlYEeNdomLBPW8f0zkY3Nm/IbyJ8kEofUa4kbwdD/osS5fgWgARiVQXEMW
+W4oMGWpplJan6qe+hInvd+5syZXtO+K/uSOj63H6BwAu
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAwJSerjL+tgafYE9i601391BfcpXWpUopcGxdeoHMTziqMGDp
+J5BBXBxU3RRgr6XZGqeS32b6xX4LEDAMBUe2z9CYP/i1uO4/y0xCJlxSyXuHFHMt
+EraVcbChfcHsYmBfGMNyQKWV6u0WBQzKVvgAK3ygDV7hzvl9jymmY5yNo1w8GCGT
+T24pVQ4rL5Rn8daQ2dwXpw703zZj5s90vWGDyavqftT6O/Rt3IGOM35v/eCAidjw
+0bI8emXp7O+aBojn6y0DvtiiUJg2gbtl7EykFu2//Yzv48eXHlzmXN3gdu4Ll861
++Qy+iiwhklojz+Yzknpp67omk8Fe8ZFETU6maaGTUzdpC9Tir0s9guRciK+4f6wd
+mdVkdnfaGGlngaFOmBrIXbaf+Eh8w7NIgyGlqj/Vi4tVsuBd1YlnH244hP/9r/cF
+3gj9zX8+3XWg1IiGIQBLtt5aPRxC2xJpt5DAQg46VvFYUDVpGiAU00TeJbiCmzin
+/igeZP1ZqtvkBp0TqfOHs3WcKIOg7smgdTM6PxW6cufZFny9a4Ekj/Z+HVMlWM4G
+ZAGXpgYcPwFpFaXO/9U6KrbPxq33StcZRvnwpi9x85M5mZiMorXqv/XXakSAm6K9
+dev43qokpjHDjqb4wAPZ4NDQFzqAVkyVzIEDInC7Eh6NRO9bCh119GdoVqMCAwEA
+AQKCAgEAuDVxE2/z9GmhSZ6mIC2Z8xcONazeBH1L5h3BzM0bgSvSnzQT0aRK4LC4
+/D/hvCIH6VchRlBaz04hhvpuhR5z35TIDWj5akt6+huXqtnk1pUyQH1rP9smV/l8
+f65fTjqgvC83ul6paG4gAfSaF1Zh0zcCYcfAdxpu3+IXJnE5imlPkkWLgw78uj8z
+T+/E/a8gH0RH26SS1nBQXxdRs1TzmpS3WVqfgXntHF9QhjELLuzwButcbzjuYKXb
+fKgzzMxoCqykSIkvuaffe3ilpcIps0T3wLBvRpJEGucB8xLJAvnwXOV9axylpcVQ
+140hdFveON6fMrx86hitmKQ7kTcKNZXEnaduehhQgDn6bqeJoAfHs4EX1JJqPZyw
+ibON1LYeUoFw9yWRy35Wr/XMkBoPwAykCWUwmOm2QEbmwhC7ORdjUVVuzrFdn/c8
+beoBfJTJ19GQjqSNcUv0cevfwYMxvimTh6oC0yPn3prRXCzL5Xd6ssSW9ISlIpu1
+etbhkvP1GNDKiAbH5uTZNIYMANbdOybfFHDUDWXHg0ObvXVLOhjH3OzdAORHKugS
+PPygnW4eXKt5R/uDRW/B0aUWLDtuB2Uj/+YQoA6Bm2AD75e4BkW9tRTqNBXOCCtk
+onvyAVJC4NoBZZQBRaOMBa0FIIxrjPLS9zmlyLehLjg5vjwjbAECggEBAPKmWUhO
+0HmwQ4/167CMwn+lIW/v9U1gQ1fvpBClFeJF/Px1AUQP+foajGxuXoMMnwH0fTrE
++ya2PA5ShZCkV+ajlBd1B8ymCRu1lp4ilCzEgjNU5U7nhJESCXBNRFEGFSjmO26a
+66sny4FiV7d6DeiJ36vPSn2BV0GezHedhbZBuaE+vVWahCXESsAqhAgejRB0A1uf
+sSyxXDaJl365J17jdO3YbS7p8LsovsK/Gfn6tTqxMNDnSJWgfFUPLOBznK64L1bJ
+RhW9HVrOeIrgV/l7mWEN/LFmWNMEcvi1E1oF08ZamjcwCmtBdGADaPcx07xQyJnQ
+6czKf52RMMcQK1ECggEBAMstD3JinyrNb71kb3fL0qlx3kO2HRsqbFVvuItd5SF8
+3/y6GuKmAikhmiZTx/DtGDGUIHD/ioiWkgswyoNKbuKcuDHklc9v3zk2A2Kac35c
+KXg//3keNouPLX/NEsu8haVas6GJhlvA+FxgtuNYQeCRMWzrzyCDBNf9KVnV8uV6
+/DBs7W306Q07im1MGMCc3P9Jl1cA0auyxOPD3mSnoDZfdzJmwNjrELSunM2BfzRn
+6h9Yi6adFkIgcEKg1a6/R6CyCTGGYs3QQ7+a+UoiNCXj99td5KWW+WMpSns6I7pw
+0bmiCb87lXrit1nn7Zgkk7W46jO6H/KyGgMQDs7b/bMCggEAC4S4AkWzgcNLQb8z
+w/q6lOKa1rx2UYj7SWZXzG55vncCDl3jhH6ZqDSwa8lFdUUZGzem5i5JmcnWyftC
+2d3jSlCDjCWDDETpc8ZH8xPDIujlIVirVfaJhXVsu0b/sjTutjVPpu11uHc4Itkv
+Psdtd5dr5bT+XTzRjoziOd9hZIh0LmJTDIg8M6rAvaSHBfelTJ2lNyk6eNume/RG
+G77gTpHPkCbQ8AQs6EWD4Ky/p+0twy58Gb6Q5IFsxYDl6XWzf1vA64a8a/XBOflJ
+IZaKto4WdtP6JdWs679vUb0OwRw7tFPCtFH0fKjrxE8FIY7c9TiEfUC8iHsoWat1
+vE1ZMQKCAQBBLl6TQsJvd+LOLsd39kLKK4az0Fv8GBsTOblJtMDKgoZVTNtNNRbi
+XS5X927ggx/M4AmcVs75zNxjjK6beiiiuMZ68yuAlhJWB1cErio5MpX3RwjNsXys
+Py3In2DVTdDOYI/aVgVtsDW9ZSWnP+w6gDoMCIa1lnLaXBSFBpdbOZ5oZrmxGe4e
+WaiqMcyLFofruo870T5yx/JUY8UYI5LJfsz9tWtO6/K7FH2njFDj3iaFEeITfLfk
+VQXOykxjOGhhTFyYr9VI0/S4Jp6tQtXaBg3BKZkt6oZtYpTLfbZynLkbxbk8yX/G
+Ia/Svw5BThK5LO6t05tmP+8KZn9pq7fzAoIBAQC/JwXRuUelycI7+tIXvcDX1ydu
+xASH6fyYcB1378KWNzWddcEb4NscfdRK4ekMO+oPyd40DNFl9ESXSUrxoILUIERV
+DywvQPMh+2sEblzDXvKO70BmSBSwq0tgfLSXpnu6nv+EHMRARA/qTk9R+Gl/REF/
+mH4ojpv2jHE50ysWFWvxK6whSG1/bMXBsT7YocR1TLBxZpaB1mVxUJ11ESKDMy+A
+lf79rIhGfU41mjzr4fkuYbERQy0yM3+lfG5qShAFAl52Fa2eFVBFso090+1TMhlR
+1ZmG9ZnE31uXoKU6OGcAGyFmvwhBIkjczH0z74CIYkD9gZJ1lW4RohgiZUja
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/client2_expired.pem b/reg-tests/ssl/client2_expired.pem
new file mode 100644
index 0000000..9d0d2e5
--- /dev/null
+++ b/reg-tests/ssl/client2_expired.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIIFKzCCAxMCAQMwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCRlIxEzARBgNV
+BAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMTEw
+LwYDVQQDDChIQVByb3h5IFRlY2hub2xvZ2llcyBDQSBUZXN0IENsaWVudCBBdXRo
+MB4XDTE4MDQyOTE5MDEwMVoXDTE5MDQyOTE5MDEwMVowQzELMAkGA1UEBhMCRlIx
+IjAgBgNVBAoMGUhBUHJveHkgVGVjaG5vbG9naWVzIFRlc3QxEDAOBgNVBAMMB2Ns
+aWVudDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD8JQiW54JVNyFa
+lPvw6skL0W5790n5R5kx10H4RUT3sIErV0K6Hvz/KwVG/jYb8yA7YSHZKYaklNis
+Zjpxj7tnKop7QwyWViXlbW1hRC+imsyO8PLrrc6YkLujKBmB1/z4523/opgNE0+m
+ROEjLIEB/nPHSPy57qdS3RdbCkQoBT/1fG8yyKbhcyHbL1Aj3Hk/553ZSgOo/Xl7
+HJ8wM+MzgkoSvPFGHn4WGckBEtiz9Fvt7v8RQJhMePjOXmDLdoiaRmeyhu0a8drq
+fg55s4LFbM58vW/pXAPyb6KzPFC1htFY+yBk2l5s4JpggNuvXEJIiP+9COY4D/oy
+79mMxZXWY/6VY5NQu54LN6vt24q9pBtaF6OjsaXUz4ZW5pj8Qpej1uXS8N69jgy1
+3CR4kFDb7pa1roe9zXq14h64kpoLA86Y17B3rRAIkIDGf/LdwL1il92Jdcl+K4g4
+YycbWCzgNb4whgokfYGfwsVV01SG1+19h+Nsme5hYROQmYbCbC94lAWJD/U/7EUN
+6KN4A7WgCxTt7Vvz2GSEE+HU/WVO+tfgxOPs40M5R3D2LKC0owEyXqkFxAANstd3
+ky6KZfkVQP0U+iz8m54o5HKvoF6EAzEHR/l2kPNCBj/hhyYGi44SwjXEOdzOcJVM
+buA7Hp2U4eOhoAJ/VoWJhY2gntcQJQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCl
+h7ITBQcBe/Rhc7Q7YE/1Sr9duVrUAUgS5bO1xHzqlBeUxPXqQhBMYBYLnvoVdJUz
+Hk/7JgvuTgQWUHHabSmKiQ5ug/8sRJSJpOavWelJW+gKaBbMUDZ2xiVYsVXJSmCk
+RpvZV+Gb4Q3JRPxkz7+KddB8FnvPYg16LyfoRKk5aVPD4vjT3ePgFZRRLY2w6BH3
+tQFB/xjCTLyX6Bhu+fC37S2N/+a+i7/vEpcOcjKpqkE/Kvb9W5Usjz9kIy5ceq6h
+i0t6FfYVcpwO6ZCSB6DT9OnzbdzPbYILdYhpCua5i64YS4cSaW9ltFvsTMDy1Nvm
+VbRh3kEtrkywXa5XmYbQE1Zm56jc7MIiyQRLBS60/SA5IzFQFZQh/NDzysLlvDMf
+vdExBQ5HJGKje+GN9deYoN3WXKpK+Qik6YZ3cVKMhBD6hYTM/da/4A6XGJEKvARy
+21rreRz/D3YMwac1/b5DPViU/6pXMGKud9/ZtlkEi7NHdzVJHMl/htcVQGlTnZdK
+Q6Yeug24JbnBZxIbhcp5aaJ+rzQeswL2SkWpF4td87Ku0gFEBShxG4tQNtfvewML
+XFybPNAkKOhR84K2rdMKwjva7vxm3tI22wll6LTVP8YUd1SS3/t9yL4jWxHX4Ds8
+gxdxuGWjJe6zm9R6OX63sv0meKNUsesmKQTpdu/gLw==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEA/CUIlueCVTchWpT78OrJC9Fue/dJ+UeZMddB+EVE97CBK1dC
+uh78/ysFRv42G/MgO2Eh2SmGpJTYrGY6cY+7ZyqKe0MMllYl5W1tYUQvoprMjvDy
+663OmJC7oygZgdf8+Odt/6KYDRNPpkThIyyBAf5zx0j8ue6nUt0XWwpEKAU/9Xxv
+Msim4XMh2y9QI9x5P+ed2UoDqP15exyfMDPjM4JKErzxRh5+FhnJARLYs/Rb7e7/
+EUCYTHj4zl5gy3aImkZnsobtGvHa6n4OebOCxWzOfL1v6VwD8m+iszxQtYbRWPsg
+ZNpebOCaYIDbr1xCSIj/vQjmOA/6Mu/ZjMWV1mP+lWOTULueCzer7duKvaQbWhej
+o7Gl1M+GVuaY/EKXo9bl0vDevY4MtdwkeJBQ2+6Wta6Hvc16teIeuJKaCwPOmNew
+d60QCJCAxn/y3cC9YpfdiXXJfiuIOGMnG1gs4DW+MIYKJH2Bn8LFVdNUhtftfYfj
+bJnuYWETkJmGwmwveJQFiQ/1P+xFDeijeAO1oAsU7e1b89hkhBPh1P1lTvrX4MTj
+7ONDOUdw9iygtKMBMl6pBcQADbLXd5MuimX5FUD9FPos/JueKORyr6BehAMxB0f5
+dpDzQgY/4YcmBouOEsI1xDncznCVTG7gOx6dlOHjoaACf1aFiYWNoJ7XECUCAwEA
+AQKCAgEApmQJ9wtvhqyK5ivK1oEZiyL5tfTKbCZDghB7CEst6AYiN2euMQSiEAFj
+yiWXr3oRmx3OKHCu2Y0gLySHDMm88aexGwZ0GAFhoLVFqRpGFRfyRaHbrItV+ngI
+WvLrYjQWTGrsu/WgQYCs3xw1NfD4cUhpPul7XXeQE66y6vEraP2N54HmH60p8zz2
+6p2eVQv5N6KxF+Mv5yTeNc/9fOHA3QzttM/aqFsW+Z6qdnrpZlerEqjUyZ3G4zAx
+gH3ngl0GaEhtxfIkJdPUk0n8Y3OCqKXU3Zxlbam7MRFaXM1AtYnyPLX7+pHgHhlZ
+xrVCQ8auNw+xNB3bTsO8aEC/X5ZD+ZdO/NCbhzEXPdx5XF6LDlB9uthC/i5G79DB
+5DK3GsrPjFjmeY3gmvKm5ikiLNiAvMqghIrKKdLhMJe/AfUTkwVh0Hh5St1o9zPT
+ZZP0sNIw+da5/qW2iB1uBdP8h6sdrZVOsfkY/fynny+wEkkP40FAVRHH42p/evRY
+qLu0/4MVUjHEgkC0G2ZLFw3n2Eq5omwH2/4u2xzN8W83+kMdBj8gB3qNFjFXLba0
+Z9izOc8xcFsvnmjWIIZ1RZsby0DqefVSfYuc1ON4qOA4hiZZNywS8Uk52i/+7MDi
+Q8eGOdUzFPmM6nTPwMLFspzzLTiflTvGDeEITJO5/DFa2ZWf3AECggEBAP/QNzTx
+vxOw3nuEvoa/4yp7TBSwvw1V8KC9298IQ3wIqNs699wNqxADGz3P3Vb37psPyBmk
+oOclX7we8hUpP0F7OIQo5oENcRu20fMY9Lvbygr8T9j6rxUj5CCqO/HqiD07J59w
+4/DE3kuzG6wLV+Tbuo+kV1ywNT3NnjzjFgaZYVjp1P71uUQfsg9ccX8N6jaF5LTn
+UTxMAxxvLJ+7qk+4OmFDLZ5y3LiCyezdsCUQeEcHf3VQKNdafkGSmMlpyIpa7D1b
+CLfJcR/UOYMezNzuHlLc18pxATOzbCLLZFmGfhdgI4SnNFpdqGk/tUAyiQrN8tTv
+JeSDi/usSQaZXlECggEBAPxUIfMCaSPpG8KM8gHgp7yz+KqY+cErD4Oh/8j9do/x
+0JkO2sV8EMSuD/alKzbN9O40KCPPw8unnlvkKE0C4of0ugzRg/e1L8SpOYzKWX5J
+zMjO6g7m+QU2kKwrcrjosmaWH32ANPY16fUiUOyl1Md7U0trYFRt9+4eqNdlej9j
+xMql9KCii1SrGrrh1sTzluvkIedqKYB7S9nv/z5diTm4F+IiBXU536YfJXJ16LpM
+aJE8+yECnV8x5Hr9LEGayjRtxIvLObBrCyeVDCQSXT0fB9sAL+gr5baFO6fq3kS0
+pk2hCkx+r0lqwIOOd8guKSIHIpTFOmd0x2RtoxNOu5UCggEBAKL76yCdYYtPJgD2
+i1lZCof3dHq+PYtmlOAk9uA76Jsu/T5obKDUSIf0IrgYJsKRRoGe5XOJE0cR5dP6
+t2xKElZLBrAVSv8wLD9nFI7Y+Jx0JV8ocEsjNMe4TVGOhJDWR6UTemQ4TdIJ7EO4
+wsmzlHVolY2NPGf+kH1m8wmB+XeM45v9p0omDrrbotvsnxc/K1k/p36m3ngXSegk
+4P6IV7NhAjkTzw3jysL3+WUjvWVv/+HpYgjBYLQMoOJwX038StvzoA5bYMuP2bZY
+xafHyOh+Ae3zbL07kHN7PktQ4Qe1C8Mi6p5K1a05fOJJx9Y2HGA45R1LnQ3hzh80
+HnbI4nECggEAPGu0+WixXnz6PbrcVGDEKaZ6u/cHjx7NhzqqcilnU46W4Z+x+Sn9
+Jet8PRZN48CrjsKEfhbJDqIjhGN81vwC3IVYa6tby1vihVf0ROdLSLdJRyhs2Yar
+SHlJaUC6JtbpqTD3d2jUxcQhMqa19AS9j8rTJjMfDPiMLsO+sF1HSZiNTe0xR6nE
+bVDPhMKBWAXwNKobCDveljpv7k7OstNZAa44Ydi9r9Vc3X2FzQO456tWOrj8dWoX
+3uymhmDLUSZMlwNV1heix8DKGf9Rue1/0Bv3GJTR4+lnBy6eG1ZdRNxxGhOe0LRh
+KtZaJOZfflq3VMOanz8e/hjzifPK4duvhQKCAQB8Mo5dWvs5fCpWAQrNqj+ua8gY
+a8ftp7R+idGGgOLSCUArjY7sS1RvZzCB28I3/5QpAuEEhaLFTABNonhbD5MdB5SL
+xVxfXqcW/WfXkGF+QqB1AMXpE4zLeGSRERWpWJSaD7B2I8UdS/Leo3lVchvA66qx
+SG+Pojcp5DsoZP3hrh54fsPdGorzezoTIwfQtsy3P8DnzPohjzbqDKmloglDbo4Q
+rBuJVs/Gc7UwZGvka+roi6VUaVdRa5PAluCE4GS9HSwf31k74jw3TfYVIlQgL5Yi
+kIHsC0yXfJ0FPXiw62CMEr51ssX3QNGTIKfos24smCjK09eInNZpIZm+p+SL
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/client3_revoked.pem b/reg-tests/ssl/client3_revoked.pem
new file mode 100644
index 0000000..0aba2ce
--- /dev/null
+++ b/reg-tests/ssl/client3_revoked.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIIFLTCCAxUCAQMwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCRlIxEzARBgNV
+BAgMClNvbWUtU3RhdGUxHTAbBgNVBAoMFEhBUHJveHkgVGVjaG5vbG9naWVzMTEw
+LwYDVQQDDChIQVByb3h5IFRlY2hub2xvZ2llcyBDQSBUZXN0IENsaWVudCBBdXRo
+MCAXDTIwMDQyODE5MjkxN1oYDzIwNTAwNDIxMTkyOTE3WjBDMQswCQYDVQQGEwJG
+UjEiMCAGA1UECgwZSEFQcm94eSBUZWNobm9sb2dpZXMgVGVzdDEQMA4GA1UEAwwH
+Y2xpZW50MzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM+8CxcAKMMh
+BILdtSx27Zjlri9ygpI55eW94t9rYb967glYbF+ZGZ2LiqXHzIpabasquXD/oH9l
+fQpaeb498ZUblbVb0OPyVkSSBpt4y0wPBYYiUGU7T94drdMwEySIyMEIRNlfXePB
+EQJLbdksdFBu4QCQEzzdL3aMBCogFfN85zJ6WJhDHnkbtKdUpKJ5irBB/3Hs+pMq
+I3Y4cdeWmFkJ+xQpu9oh0igAhkbSPYXu+asSCzExO4G4ttBnQQh4RYUUe+IqO8z0
+QQ/La+m+OBXWR1ti+/3ImeZWdRlA7xpTNYTOxAg0eO1FuUhwvw6Fpvo5KV7wre4W
+Qkmsjc5vpxubWWrdfSK/YB8jZJsdx2zgk8thdhj31Zhv0PUP64fhX03DKFSF+qNG
+0POpjPthu+f96umHfIFNNKiLPyWBpl0+ppI1FB8uW9xXRZw00iXl89bXNa1lbQqr
+c3cj893HUnEpx3H0Q3piEsKu0mchGXiVVJsoZgbLn6yOXDnkWBQhAFvvRcfrAzki
+w3f/gU+BiT05csRCXtmbL28RaK70fBxD6fDhGRHyMt/0MFhYnJRxmIB3GniQAgC0
+lyqyMOplkHVeHO0LhjrLTZwbbD083A/KRzpsrVLHd8sjCEjojae0tPDj65u0xg1R
+JrszrjO8ZNLQoXr2rl6hjxeLC2Yn08W1AgMBAAEwDQYJKoZIhvcNAQELBQADggIB
+AIzgQBfDxEdowxYsdZ4cb0wySg+xB11XRLeR9k6c1kExDkpTKRyAy+6CNS0X2mAz
+3v/jVoh3G4crlBkL7UJn6ycunuJ2SdiUexsJAOveVgPPml7YnRRfPW9ddM+gn0y/
+TtTB0D52XaXczeIqQKFD67OtjbVvObbrO1cITkh9q+mMtTO8T/V1gBRd1VH1YFdi
+nPqTYYA9QqJ2zAaufhZVCkpJJn5onpT5t+GBpe9O0lKlkQrduLzjr2rrfJCg2Uuw
+xBXwpvFdOK4BY5tDqVLb7BOLkEUUltWKTYd4IFjonRE6OSxtY+1L/RnEYMfSSVIf
+GsTkKugTuVSmmyFmh5H10YjwMMD3j36hjxJcGJDzZIuOQMY+2UKI61eF7StqZTXE
+wRj+JMHHRHIEw0181lHxGSCArWyLEoSn57NSAqJEzdhq7wb6eZwqZzRo8EJUSYIK
+3fLnfjSLHS/XaH9mCbx7VpYfC310UGzQ1QXSOIp1LtKtxUbT1YL7RTwa7GVfvQ0e
+9nsY9/qd9Oo2VJtxKQRsfro6Z/MdP97lUpTaigQEUpB7KICl1ks56oQrunRLXkO7
+EoDNlnDGkp8DghO+tPqx44OogbXBFCRTO7ncYxSE83WcG0UMUvVfGoMJKqF8V0n3
+LmKLNCvzLQ2Gt21Cp/zNiwHSjMNIIqybjAe+nVT4+sSI
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAz7wLFwAowyEEgt21LHbtmOWuL3KCkjnl5b3i32thv3ruCVhs
+X5kZnYuKpcfMilptqyq5cP+gf2V9Clp5vj3xlRuVtVvQ4/JWRJIGm3jLTA8FhiJQ
+ZTtP3h2t0zATJIjIwQhE2V9d48ERAktt2Sx0UG7hAJATPN0vdowEKiAV83znMnpY
+mEMeeRu0p1SkonmKsEH/cez6kyojdjhx15aYWQn7FCm72iHSKACGRtI9he75qxIL
+MTE7gbi20GdBCHhFhRR74io7zPRBD8tr6b44FdZHW2L7/ciZ5lZ1GUDvGlM1hM7E
+CDR47UW5SHC/DoWm+jkpXvCt7hZCSayNzm+nG5tZat19Ir9gHyNkmx3HbOCTy2F2
+GPfVmG/Q9Q/rh+FfTcMoVIX6o0bQ86mM+2G75/3q6Yd8gU00qIs/JYGmXT6mkjUU
+Hy5b3FdFnDTSJeXz1tc1rWVtCqtzdyPz3cdScSnHcfRDemISwq7SZyEZeJVUmyhm
+BsufrI5cOeRYFCEAW+9Fx+sDOSLDd/+BT4GJPTlyxEJe2ZsvbxForvR8HEPp8OEZ
+EfIy3/QwWFiclHGYgHcaeJACALSXKrIw6mWQdV4c7QuGOstNnBtsPTzcD8pHOmyt
+Usd3yyMISOiNp7S08OPrm7TGDVEmuzOuM7xk0tChevauXqGPF4sLZifTxbUCAwEA
+AQKCAgEAr48B6ExQJqhOwbJRHOTdY6woCx1BUAbyTbiudZawozxm0ysRW7FtvoFh
+iT1TlVFbAZ833VGL+F5y0D8qSCbddeA8I2sXHK1/TrACOX5agUropjV1sDfyBYsb
+jjFA3E1lLA2q8fHwzTwq/b91dGZnXlp2eR1JxNRA+nTWSCuZoY9bbIONQBDpPfy2
+LfwQrub82HzOPx/BnIGuOoj1XPd+hTE0KhQjF/QhQYE/+bZQHFKcWYEESGyNF9Jd
+Xb8FbP0H87IeCEMdCtcZ7RlDd+U0TPAsmgULZa0Us1850z/XUm4j+rsrXKvLzupv
+7dKrBMDbHvkUB5Jry5ywJMdZiK8/j0QW5bw9Hw8tEyXxT2gzXFze4DrEHC1cPLod
+3bcMOnp8axtdfm23tlFQuq4fGsERABFWByylF2Pu9KQ1AgH8/53IcVnNjd7Z4ZlA
+eBrZynEDg67sggFNRa6EnjAYFS0Zqgmfo/160awcGSLPLSkE5FhtByQOEzyAumXn
+UmDO4zlP3dc54WzCnxdS8GpbbmjniXGSDe9D16D0uWQeo+LCoDDTExT3wDjY3tDt
+R4VjIBVs1vWXH2oG/oC/ulgXwcSKSSvtqftcGnPj9EE6exNzanTQwCFHosYWl5Tn
+MkRsxNRFITKksNH5sXAkzwogWlyG4PK4ink0IixpYh2N0WgncDkCggEBAPqZ7z45
+F/YusR+eJdMrloECYhP1BId7zUzZglAUof/KJH86TVnQ/wGBcl6x9SXGzy5Mix8S
+q0qUILJ3nXkyRqFjlch9c8NlMR5P/IPyQWupSGNFPTIBvNefCfqosEFD9635P4ND
+sN19gwrx9IqMYgyPzw05G2CJPafemjF5NLIHKyhUnjGUij+D/WZYCKvHIrq085ac
+0dLncRvlnzloa8PCGXDXcTuFacVGcj6QZJvn7ZrprMOwpwh7RT02U9cqlLYKfjEg
+9xnTSmxbb8esRtWDlJPoj3+P732Cj7BynSpvLFSsvHAAFS4j/g3XIjdY/yocd0xG
+UfYDEcilgPiaEnMCggEBANQ1st6aaf/7oH4hJ/mgOc2RhGYxpjHgXs+ux77llHks
+o4VlAILV9CLQyQ9/3PU/4vidw/rseZwPmONDlBeU4319MQzjZeihaqfaTQXnQRBj
+xX2sJ/7EeBoq6Xlkvc+lVbiWcA7i97dYEumV/q7ozYRyAhP/D/VAdmT9ZWK3qJ+l
+/L7h43ch9PWGDOjiUIfWx/xAodyDUEM1iforv/S7D4+3j9BriRnhpZRLZT5bG24h
+vVN344ETxaoVWwjw9yzmJHF/4ooqJgdTsjJ8ujPPLvJKXAD7ZVerhcUpZWmyMACL
+Dj8IuNZeB7IN9LOCr1xPenR0jdltz7+LU6WTK0F4NTcCggEBALvxvOL+sMDaTc63
+rgiM1ShWIDZ1ePsfV15+dmQWxVRwRyUAFcj5nHaFnb/1WTUGwJUppOEeAEdDhq4y
+VXDyytP5OvmNVMfDWa4xMOHIS1YyNG73G6kocneH+FT8NIwOLHBW0VJh7wB+RExu
+IAfUtyhSpmd1X9nrs8j1gtD95Q5rn+t0YtwuWey+0cny1jX2eE5srY9Ud2zkVQkm
+El9cuA2twaTGf06zhRsF9WKEql/e9m1LOV3eW7dZtBjvaLujXLqWbgPshEXjGBri
+DJhE1S8GquSu8wgpa+TGiXs8yjBsBmRO1FhvR3M+XSgGI8w8u8naZYJX7tpBfRHt
+RiesbY8CggEBAKMcm14rBmryOIx6Y8Wl+Igf38rFQt93fKjZyULcKGFzhEUWO2xV
+lA/mt4SoXWhNMOK0MV1/woHII1YcLYpqsOlV/wvPBhfnapmWXDm7ZPF6HuTYHO3g
+ighjD451dshSZy84wu1OW+WbVv4gguBipQW2tA42sUdrwxUhCHr2fDAqX7lA25xI
+h4bpAKdIvWmMF6+25jMe4+SlFGcslaFA31cyWuJypbE1FhaEVU+2q8DdQi8UKdSf
+JAH15EFdJkBmrYBoMfLNLunW1VOlN2J3T7iAm3NNyLm4Z1wC06aIhgkE2XBt/dUX
+9YZQ39PTEYM8u/0jUZzcoSCzsRnFoyvxf8UCggEAblaNsi8/Nx8FId/aW3klrIxY
+UfSVXL3InIkrr2hJs7GYMpdWRwQZpo+Nv45cBnGoRwWWIsYkcWEbrs1vxvkNg0Ro
+pTa0Pt5gb1u7BfvpSqC/VyFBd66BcTQeJqTUHzWPKhMyCrP/eRYfFFQtpy5EZ+/O
+cjEVO1Tv5VhqM1PtANHdsS6o0jKMWFQ/Ofu4sOp6hQl4E1oOAzjLdtyBgJzSk1Jg
+M1lKPzSpYgRWcMB9CFTE2JO/4b+iMhxQjvGtD5nkeA6ZD7DSDItH6lhAQsho1pMi
+uoFlxDSFYHt0KcFp9zMrB2810mmNvjiEaqVXkA7QRH3XCA0BTkIXxzQe5QgTNg==
+-----END RSA PRIVATE KEY-----
diff --git a/reg-tests/ssl/crl-auth.pem b/reg-tests/ssl/crl-auth.pem
new file mode 100644
index 0000000..af59d1d
--- /dev/null
+++ b/reg-tests/ssl/crl-auth.pem
@@ -0,0 +1,18 @@
+-----BEGIN X509 CRL-----
+MIIC0jCBuzANBgkqhkiG9w0BAQUFADB0MQswCQYDVQQGEwJGUjETMBEGA1UECAwK
+U29tZS1TdGF0ZTEdMBsGA1UECgwUSEFQcm94eSBUZWNobm9sb2dpZXMxMTAvBgNV
+BAMMKEhBUHJveHkgVGVjaG5vbG9naWVzIENBIFRlc3QgQ2xpZW50IEF1dGgXDTIw
+MDQyODE5MjkyNloYDzIwNTAwNDIxMTkyOTI2WjAUMBICAQMXDTIwMDQyODE5MDE1
+MVowDQYJKoZIhvcNAQEFBQADggIBAMPJgdU6bsFMFKBop0dngtAG1DXSrHo1XlYY
+J1uWEuVcNnimH1EHQXMmL5C26ALrHlQILLzq3RVcNZIT0tVF6jvcf8tzcaGeybS1
+tpDloE2A2jPz3Pf/uS4MB7eTPiMvY7cUl7fk4Oif/PjGPxdu+E5SP6HWVdjCvBHb
+2yye/KjN/vj3g5uI6z2l1Hxh2yzYmMVS8cTRG5SfUXgH+IXJOS8zE7CsMB/IRctQ
+TXD0q0iZLn7Q0liA/wxxJHYg2m3RdFa82THdWaqsIM4ao2KLz324ycQpWT0eRWpv
+6gyVXbEU/sX8HdZdNpfgQADiU8eK4XlnEmXehSE3TwyM1ysnoFRtOqDvaQrHbAMh
+Av0/9JLOPGDqCjof4lLfAW6JDtU55J4SxCYlaRj152939eXwDkb70WefZMssfqcw
+ZPDK6afY358kb7Yb0U2pE73+Z3VDcczBF085nc6q/2m5lvA+XwZYr4xBkVzHbdP3
+USEFd06FHlh2i2rpaiihR7sQx9KJ75ko3TjDbeg/QryMBKsS2CeJoHPDcFjjzFZF
+RW1HYReV1MZT8UEuskMvl+w57OYbfqf/pwhQcJTL8XE9PRtzntmLMofmiN/X5PQV
+YS6JvGVAIC7HFDiZ8Wn8B+WT93ecCNQL1FpIpo1JxuRfx6jTtGqGg65R3CzwbqUH
+dBkieO8E
+-----END X509 CRL-----
diff --git a/reg-tests/ssl/ssl_client_auth.vtc b/reg-tests/ssl/ssl_client_auth.vtc
new file mode 100644
index 0000000..849e878
--- /dev/null
+++ b/reg-tests/ssl/ssl_client_auth.vtc
@@ -0,0 +1,85 @@
+#REGTEST_TYPE=devel
+
+# This reg-test tests the client auth feature of HAProxy for both the backend
+# and frontend section with a CRL list
+#
+# This reg-test uses 2 chained listeners because vtest does not handle the SSL.
+# Test the frontend client auth and the backend side at the same time.
+#
+# The sends 3 requests one with a correct certificate, one with an expired one and one which was revoked.
+# The client then check if we received the right one with the right error.
+#
+# Certificates, CA and CRL are expiring in 2050 so it should be fine for the CI.
+#
+# Detail about configuration is explained there:
+# https://www.haproxy.com/blog/ssl-client-certificate-management-at-application-level/
+
+varnishtest "Test the client auth"
+#REQUIRE_VERSION=1.6
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+  rxreq
+  txresp
+} -start
+
+haproxy h1 -conf {
+    global
+        tune.ssl.default-dh-param 2048
+
+    defaults
+        mode http
+        option httplog
+        ${no-htx} option http-use-htx
+        log stderr local0 debug err
+        option logasap
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    listen clear-lst
+        bind "fd@${clearlst}"
+        balance roundrobin
+        # crt: certificate sent for a client certificate request
+        server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client1.pem
+        server s2 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client2_expired.pem # expired
+        server s3 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client3_revoked.pem # revoked
+
+    listen ssl-lst
+        # crt: certificate of the server
+        # ca-file: CA used for client authentication request
+        # crl-file: revocation list for client auth: the client1 certificate is revoked
+        bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err all crl-file ${testdir}/crl-auth.pem
+
+        acl cert_expired ssl_c_verify 10
+        acl cert_revoked ssl_c_verify 23
+        acl cert_ok ssl_c_verify 0
+
+        http-response add-header X-SSL Ok if cert_ok
+        http-response add-header X-SSL Expired if cert_expired
+        http-response add-header X-SSL Revoked if cert_revoked
+
+        server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_clearlst_sock} {
+  txreq
+  rxresp
+  expect resp.status == 200
+  expect resp.http.x-ssl == "Ok"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+  txreq
+  rxresp
+  expect resp.status == 200
+  expect resp.http.x-ssl == "Expired"
+} -run
+
+client c1 -connect ${h1_clearlst_sock} {
+  txreq
+  rxresp
+  expect resp.status == 200
+  expect resp.http.x-ssl == "Revoked"
+} -run
diff --git a/reg-tests/ssl/ssl_client_samples.vtc b/reg-tests/ssl/ssl_client_samples.vtc
new file mode 100644
index 0000000..db5305a
--- /dev/null
+++ b/reg-tests/ssl/ssl_client_samples.vtc
@@ -0,0 +1,72 @@
+#REGTEST_TYPE=devel
+
+varnishtest "Test the ssl_c_* sample fetches"
+#REQUIRE_VERSION=1.6
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+    rxreq
+    txresp
+} -start
+
+haproxy h1 -conf {
+    global
+        tune.ssl.default-dh-param 2048
+        tune.ssl.capture-cipherlist-size 1
+        crt-base ${testdir}
+
+    defaults
+        mode http
+        option httplog
+        ${no-htx} option http-use-htx
+        log stderr local0 debug err
+        option logasap
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+
+    listen clear-lst
+        bind "fd@${clearlst}"
+        balance roundrobin
+        server s1 "${tmpdir}/ssl.sock" ssl verify none crt ${testdir}/client1.pem
+
+    listen ssl-lst
+        mode http
+        ${no-htx} option http-use-htx
+
+        http-response add-header x-ssl-der %[ssl_c_der,hex]
+        http-response add-header x-ssl-sha1 %[ssl_c_sha1,hex]
+        http-response add-header x-ssl-notafter %[ssl_c_notafter]
+        http-response add-header x-ssl-notbefore %[ssl_c_notbefore]
+        http-response add-header x-ssl-sig_alg %[ssl_c_sig_alg]
+        http-response add-header x-ssl-i_dn %[ssl_c_i_dn]
+        http-response add-header x-ssl-s_dn %[ssl_c_s_dn]
+        http-response add-header x-ssl-s_serial %[ssl_c_serial,hex]
+        http-response add-header x-ssl-key_alg %[ssl_c_key_alg]
+        http-response add-header x-ssl-version %[ssl_c_version]
+
+        bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err all crl-file ${testdir}/crl-auth.pem
+
+        server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+client c1 -connect ${h1_clearlst_sock} {
+    txreq
+    rxresp
+    expect resp.status == 200
+    expect resp.http.x-ssl-der ~ 3082052D30820315020102300D0.*995ED3BE2BFB923A3EB71FA07002E
+    expect resp.http.x-ssl-sha1 == "D9C3BAE37EA5A7EDB7B3C9BDD4DCB2FE58A412E4"
+    expect resp.http.x-ssl-notafter == "500421185942Z"
+    expect resp.http.x-ssl-notbefore == "200428185942Z"
+    expect resp.http.x-ssl-sig_alg == "RSA-SHA256"
+    expect resp.http.x-ssl-i_dn == "/C=FR/ST=Some-State/O=HAProxy Technologies/CN=HAProxy Technologies CA Test Client Auth"
+    expect resp.http.x-ssl-s_dn  == "/C=FR/O=HAProxy Technologies Test/CN=client1"
+    expect resp.http.x-ssl-s_serial == "02"
+    expect resp.http.x-ssl-key_alg == "rsaEncryption"
+    expect resp.http.x-ssl-version == "1"
+} -run
+
+
diff --git a/reg-tests/ssl/ssl_frontend_samples.vtc b/reg-tests/ssl/ssl_frontend_samples.vtc
new file mode 100644
index 0000000..5a7a0ee
--- /dev/null
+++ b/reg-tests/ssl/ssl_frontend_samples.vtc
@@ -0,0 +1,72 @@
+#REGTEST_TYPE=devel
+
+varnishtest "Test the ssl_f_* sample fetches"
+#REQUIRE_VERSION=1.5
+#REQUIRE_OPTIONS=OPENSSL
+feature ignore_unknown_macro
+
+server s1 -repeat 3 {
+    rxreq
+    txresp
+} -start
+
+haproxy h1 -conf {
+    global
+        tune.ssl.default-dh-param 2048
+        tune.ssl.capture-cipherlist-size 1
+        crt-base ${testdir}
+
+    defaults
+        mode http
+        option httplog
+        ${no-htx} option http-use-htx
+        log stderr local0 debug err
+        option logasap
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+
+    listen clear-lst
+        bind "fd@${clearlst}"
+        balance roundrobin
+        server s1 "${tmpdir}/ssl.sock" ssl verify none
+
+    listen ssl-lst
+        mode http
+        ${no-htx} option http-use-htx
+
+        http-response add-header x-ssl-der %[ssl_f_der,hex]
+        http-response add-header x-ssl-sha1 %[ssl_f_sha1,hex]
+        http-response add-header x-ssl-notafter %[ssl_f_notafter]
+        http-response add-header x-ssl-notbefore %[ssl_f_notbefore]
+        http-response add-header x-ssl-sig_alg %[ssl_f_sig_alg]
+        http-response add-header x-ssl-i_dn %[ssl_f_i_dn]
+        http-response add-header x-ssl-s_dn %[ssl_f_s_dn]
+        http-response add-header x-ssl-s_serial %[ssl_f_serial,hex]
+        http-response add-header x-ssl-key_alg %[ssl_f_key_alg]
+        http-response add-header x-ssl-version %[ssl_f_version]
+
+        bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem
+
+        server s1 ${s1_addr}:${s1_port}
+} -start
+
+
+client c1 -connect ${h1_clearlst_sock} {
+    txreq
+    rxresp
+    expect resp.status == 200
+    expect resp.http.x-ssl-der ~ 3082067930820461A0030201020201.*207B5E3D4498BB847BC4DE093F9AD1AD3661C93EE43EB
+    expect resp.http.x-ssl-sha1 == "2195C9F0FD58470313013FC27C1B9CF9864BD1C6"
+    expect resp.http.x-ssl-notafter == "180116230238Z"
+    expect resp.http.x-ssl-notbefore == "160117230238Z"
+    expect resp.http.x-ssl-sig_alg == "RSA-SHA256"
+    expect resp.http.x-ssl-i_dn == "/C=FR/ST=Ile-de-France/L=Paris/O=ozon.io/CN=Ozon Test CA/emailAddress=support@ozon.io"
+    expect resp.http.x-ssl-s_dn  == "/C=FR/ST=Ile-de-France/L=Neuilly-sur-Seine/O=TOAD Consulting/OU=eParapher Team/CN=www.test1.com/emailAddress=arnault.michel@toad-consulting.fr"
+    expect resp.http.x-ssl-s_serial == "02"
+    expect resp.http.x-ssl-key_alg == "rsaEncryption"
+    expect resp.http.x-ssl-version == "3"
+} -run
+
+
diff --git a/scripts/announce-release b/scripts/announce-release
index 972d6ea..aa0dd83 100755
--- a/scripts/announce-release
+++ b/scripts/announce-release
@@ -10,7 +10,8 @@
 #   - creates web-$version.html
 #   - indicates how to edit the mail and how to send it
 
-USAGE="Usage: ${0##*/} [-b branch] [-d date] [-o oldver] [-n newver]"
+USAGE="Usage: ${0##*/} [-f] [-b branch] [-d date] [-o oldver] [-n newver]"
+FORCE=
 OUTPUT=
 BRANCH=
 HTML=
@@ -38,6 +39,7 @@
 	case "$1" in
 		-d)        DATE="$2"      ; shift 2 ;;
 		-b)        BRANCH="$2"    ; shift 2 ;;
+		-f)        FORCE=1        ; shift   ;;
 		-o)        OLD="$2"       ; shift 2 ;;
 		-n)        NEW="$2"       ; shift 2 ;;
 		-h|--help) quit "$USAGE" ;;
@@ -108,18 +110,24 @@
 YEAR="${DATE%%/*}"
 
 OUTPUT="$DIR/mail-haproxy-$NEW.txt"
+HTML="$DIR/web-haproxy-$NEW.html"
+
+[ -z "$FORCE" ] || rm -f "${OUTPUT}" "${HTML}"
+
 if [ -e "$OUTPUT" ]; then
-	die "$OUTPUT already exists, please remove it."
+	die "${OUTPUT##*/} already exists, please remove it or retry with -f."
 fi
 
-HTML="$DIR/web-haproxy-$NEW.html"
 if [ -e "$HTML" ]; then
-	die "$HTML already exists, please remove it."
+	die "$HTML already exists, please remove it or retry with -f."
 fi
 
-(echo "Subject: [ANNOUNCE] haproxy-$NEW"
- echo "To: haproxy@formilux.org"
- echo
+(
+ echo "# Send this using:"
+ echo "#   mutt -H <(tail -n +4 ${OUTPUT##*/}) -s \"[ANNOUNCE] haproxy-$NEW\" haproxy@formilux.org"
+) >> "$OUTPUT"
+
+(echo
  echo "Hi,"
  echo
  echo -n "HAProxy $NEW was released on $DATE. It added "
@@ -157,6 +165,7 @@
  echo "   Discourse        : http://discourse.haproxy.org/"
  echo "   Slack channel    : https://slack.haproxy.org/"
  echo "   Issue tracker    : https://github.com/haproxy/haproxy/issues"
+ echo "   Wiki             : https://github.com/haproxy/wiki/wiki"
  echo "   Sources          : http://www.haproxy.org/download/${BRANCH}/src/"
  echo "   Git repository   : http://git.haproxy.org/git/${gitdir}/"
  echo "   Git Web browsing : http://git.haproxy.org/?p=${gitdir}"
@@ -210,7 +219,7 @@
 echo "The announce was emitted into file $OUTPUT."
 echo "You can edit it and send it this way :"
 echo
-echo "   mutt -i ${OUTPUT##*/} -s \"[ANNOUNCE] haproxy-$NEW\" haproxy@formilux.org"
+echo "   mutt -H <(tail -n +4 ${OUTPUT##*/}) -s \"[ANNOUNCE] haproxy-$NEW\" haproxy@formilux.org"
 echo
 echo "The HTML block was emitted into $HTML and needs to be finished by hand."
 echo
diff --git a/scripts/publish-release b/scripts/publish-release
index 6a615a6..abde56f 100755
--- a/scripts/publish-release
+++ b/scripts/publish-release
@@ -9,6 +9,7 @@
 #   - shows a listing of the final file
 
 USAGE="Usage: ${0##*/} [-a] [-q] [-y] [-b branch] [-n newver] DIR"
+CMD_GZIP="${CMD_GZIP:-gzip -nc9}"
 TARGET_DIR=
 OUTPUT=
 SAYYES=
@@ -161,7 +162,7 @@
 echo "Archiving sources for version $NEW ..."
 rm -f "${TARGET_DIR}/src${DEVEL}/haproxy-${NEW}.tar.gz"{,.md5,.sha256}
 if ! git archive --format=tar --prefix="haproxy-${NEW}/" "v$NEW" | \
-     gzip -9 > "${TARGET_DIR}/src${DEVEL}/haproxy-${NEW}.tar.gz"; then
+     $CMD_GZIP > "${TARGET_DIR}/src${DEVEL}/haproxy-${NEW}.tar.gz"; then
 	die "Failed to produce the tar.gz archive"
 fi
 
@@ -174,7 +175,7 @@
 
 for i in "${DOC[@]}"; do
 	git show "v$NEW:$i" > "$TARGET_DIR/doc/${i#doc/}"
-	gzip -c9 < "$TARGET_DIR/doc/${i#doc/}" > "$TARGET_DIR/doc/${i#doc/}.gz"
+	$CMD_GZIP < "$TARGET_DIR/doc/${i#doc/}" > "$TARGET_DIR/doc/${i#doc/}.gz"
 done
 
 echo "Done : ls -l ${TARGET_DIR}"
diff --git a/src/51d.c b/src/51d.c
index 344be41..dc1acba 100644
--- a/src/51d.c
+++ b/src/51d.c
@@ -461,6 +461,21 @@
 	smp->data.u.str.data = temp->data;
 }
 
+/* Sets the sample data as a constant string. This ensures that the
+ * string will be processed correctly.
+ */
+static void _51d_set_smp(struct sample *smp)
+{
+	/*
+	 * Data type has to be set to ensure the string output is processed
+	 * correctly.
+	 */
+	smp->data.type = SMP_T_STR;
+
+	/* Flags the sample to show it uses constant memory. */
+	smp->flags |= SMP_F_CONST;
+}
+
 static int _51d_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
 #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
@@ -484,14 +499,6 @@
 		/* Legacy version */
 		CHECK_HTTP_MESSAGE_FIRST(chn);
 	}
-	/*
-	 * Data type has to be reset to ensure the string output is processed
-	 * correctly.
-	 */
-	smp->data.type = SMP_T_STR;
-
-	/* Flags the sample to show it uses constant memory*/
-	smp->flags |= SMP_F_CONST;
 
 #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
 
@@ -519,6 +526,8 @@
 			fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws);
 			_51d_retrieve_cache_entry(smp, lru);
 			HA_SPIN_UNLOCK(OTHER_LOCK, &_51d_lru_lock);
+
+			_51d_set_smp(smp);
 			return 1;
 		}
 		HA_SPIN_UNLOCK(OTHER_LOCK, &_51d_lru_lock);
@@ -556,6 +565,7 @@
 		_51d_insert_cache_entry(smp, lru, (void*)args);
 #endif
 
+	_51d_set_smp(smp);
 	return 1;
 }
 
@@ -568,8 +578,6 @@
 #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
 	fiftyoneDegreesDeviceOffsets *offsets; /* Offsets for detection */
 #endif
-	/* Flags the sample to show it uses constant memory*/
-	smp->flags |= SMP_F_CONST;
 
 #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
 
@@ -631,6 +639,7 @@
 #endif
 #endif
 
+	_51d_set_smp(smp);
 	return 1;
 }
 
@@ -762,7 +771,7 @@
 	free(_51d_property_list);
 
 #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
-	_51d_lru_seed = random();
+	_51d_lru_seed = ha_random();
 	if (global_51degrees.cache_size) {
 		_51d_lru_tree = lru64_new(global_51degrees.cache_size);
 	}
@@ -840,4 +849,4 @@
 #else
 	REGISTER_BUILD_OPTS("Built with 51Degrees Trie support (dummy library).");
 #endif
-#endif
\ No newline at end of file
+#endif
diff --git a/src/backend.c b/src/backend.c
index 9fe9c62..92d1016 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -676,8 +676,7 @@
 	curr = NULL;
 	do {
 		prev = curr;
-		/* ensure all 32 bits are covered as long as RAND_MAX >= 65535 */
-		hash = ((uint64_t)random() * ((uint64_t)RAND_MAX + 1)) ^ random();
+		hash = ha_random32();
 		curr = chash_get_server_hash(px, hash, avoid);
 		if (!curr)
 			break;
@@ -766,6 +765,10 @@
 
 						srv = tmpsrv;
 						s->target = &srv->obj_type;
+						if (conn->flags & CO_FL_SESS_IDLE) {
+							conn->flags &= ~CO_FL_SESS_IDLE;
+							s->sess->idle_conns--;
+						}
 						goto out_ok;
 					}
 				}
@@ -1464,6 +1467,7 @@
 				// see it possibly larger.
 				ALREADY_CHECKED(i);
 
+				HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[tid]);
 				tokill_conn = LIST_POP_LOCKED(&srv->idle_orphan_conns[i],
 				    struct connection *, list);
 				if (tokill_conn) {
@@ -1472,8 +1476,10 @@
 					LIST_ADDQ_LOCKED(&toremove_connections[i],
 					    &tokill_conn->list);
 					task_wakeup(idle_conn_cleanup[i], TASK_WOKEN_OTHER);
+					HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
 					break;
 				}
+				HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
 			}
 		}
 
@@ -1553,6 +1559,7 @@
 
 	/* no reuse or failed to reuse the connection above, pick a new one */
 	if (!srv_conn) {
+		s->flags &= ~SF_ADDR_SET;
 		srv_conn = conn_new();
 		if (srv_conn)
 			srv_conn->target = s->target;
@@ -1577,13 +1584,18 @@
 		}
 	}
 
-	if (!srv_conn)
+	if (!srv_conn) {
+		if (srv_conn)
+			conn_free(srv_conn);
 		return SF_ERR_RESOURCE;
+	}
 
 	if (!(s->flags & SF_ADDR_SET)) {
 		err = assign_server_address(s, srv_conn);
-		if (err != SRV_STATUS_OK)
+		if (err != SRV_STATUS_OK) {
+			conn_free(srv_conn);
 			return SF_ERR_INTERNAL;
+		}
 	}
 
 	if (!conn_xprt_ready(srv_conn) && !srv_conn->mux) {
@@ -1593,16 +1605,20 @@
 		else if (obj_type(s->target) == OBJ_TYPE_PROXY) {
 			/* proxies exclusively run on raw_sock right now */
 			conn_prepare(srv_conn, protocol_by_family(srv_conn->addr.to.ss_family), xprt_get(XPRT_RAW));
-			if (!(srv_conn->ctrl))
+			if (!(srv_conn->ctrl)) {
+				conn_free(srv_conn);
 				return SF_ERR_INTERNAL;
+			}
 		}
-		else
+		else {
+			conn_free(srv_conn);
 			return SF_ERR_INTERNAL;  /* how did we get there ? */
+		}
 
 #if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
 		if (!srv ||
-		    ((!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
-		    srv->mux_proto || s->be->mode != PR_MODE_HTTP))
+		    (srv->use_ssl != 1 || (!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
+		     srv->mux_proto || s->be->mode != PR_MODE_HTTP))
 #endif
 		{
 			srv_cs = objt_cs(s->si[1].end);
@@ -1649,6 +1665,12 @@
 			srv_conn->flags |= CO_FL_SOCKS4;
 		}
 	}
+	else if (!srv_conn->mux) {
+		/* No mux? We possibly asked for ALPN during a first failed
+		 * attempt and are trying to start again from this connection,
+		 * thus we have nothing to do.
+		 */
+	}
 	else if (!conn_xprt_ready(srv_conn)) {
 		if (srv_conn->mux->reset)
 			srv_conn->mux->reset(srv_conn);
diff --git a/src/cache.c b/src/cache.c
index 54e3340..e6b802b 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -336,6 +336,7 @@
 	struct htx *htx = htxbuf(&msg->chn->buf);
 	struct htx_blk *blk;
 	struct shared_block *fb;
+	struct htx_ret htxret;
 	unsigned int orig_len, to_forward;
 	int ret;
 
@@ -350,16 +351,15 @@
 	chunk_reset(&trash);
 	orig_len = len;
 	to_forward = 0;
-	for (blk = htx_get_first_blk(htx); blk && len; blk = htx_get_next_blk(htx, blk)) {
+
+	htxret = htx_find_offset(htx, offset);
+	blk = htxret.blk;
+	offset = htxret.ret;
+	for (; blk && len; blk = htx_get_next_blk(htx, blk)) {
 		enum htx_blk_type type = htx_get_blk_type(blk);
 		uint32_t info, sz = htx_get_blksz(blk);
 		struct ist v;
 
-		if (offset >= sz) {
-			offset -= sz;
-			continue;
-		}
-
 		switch (type) {
 			case HTX_BLK_UNUSED:
 				break;
@@ -1767,19 +1767,17 @@
 	char *name = NULL;
 	int pos = *cur_arg;
 
-	/* Get the cache filter name*/
-	if (!strcmp(args[pos], "cache")) {
-		if (!*args[pos + 1]) {
-			memprintf(err, "%s : expects an <id> argument", args[pos]);
-			goto error;
-		}
-		name = strdup(args[pos + 1]);
-		if (!name) {
-			memprintf(err, "%s '%s' : out of memory", args[pos], args[pos + 1]);
-			goto error;
-		}
-		pos += 2;
+	/* Get the cache filter name. <pos> point on "cache" keyword */
+	if (!*args[pos + 1]) {
+		memprintf(err, "%s : expects an <id> argument", args[pos]);
+		goto error;
 	}
+	name = strdup(args[pos + 1]);
+	if (!name) {
+		memprintf(err, "%s '%s' : out of memory", args[pos], args[pos + 1]);
+		goto error;
+	}
+	pos += 2;
 
 	/* Check if an implicit filter with the same name already exists. If so,
 	 * we remove the implicit filter to use the explicit one. */
@@ -1797,7 +1795,7 @@
 			cconf = NULL;
 			memprintf(err, "%s: multiple explicit declarations of the cache filter '%s'",
 				  px->id, name);
-			return -1;
+			goto error;
 		}
 
 		/* Remove the implicit filter. <cconf> is kept for the explicit one */
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index db70f26..1328430 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -438,6 +438,18 @@
 			}
 			curproxy->check_len = defproxy.check_len;
 
+			if (defproxy.check_hdrs) {
+				curproxy->check_hdrs = calloc(1, defproxy.check_hdrs_len);
+				memcpy(curproxy->check_hdrs, defproxy.check_hdrs, defproxy.check_hdrs_len);
+			}
+			curproxy->check_hdrs_len = defproxy.check_hdrs_len;
+
+			if (defproxy.check_body) {
+				curproxy->check_body = calloc(1, defproxy.check_body_len);
+				memcpy(curproxy->check_body, defproxy.check_body, defproxy.check_body_len);
+			}
+			curproxy->check_body_len = defproxy.check_body_len;
+
 			if (defproxy.expect_str) {
 				curproxy->expect_str = strdup(defproxy.expect_str);
 				if (defproxy.expect_regex) {
@@ -473,6 +485,8 @@
 				 curproxy->rdp_cookie_name = strdup(defproxy.rdp_cookie_name);
 			curproxy->rdp_cookie_len = defproxy.rdp_cookie_len;
 
+			if (defproxy.cookie_attrs)
+				curproxy->cookie_attrs = strdup(defproxy.cookie_attrs);
 
 			if (defproxy.lbprm.arg_str)
 				curproxy->lbprm.arg_str = strdup(defproxy.lbprm.arg_str);
@@ -624,6 +638,7 @@
 		free(defproxy.rdp_cookie_name);
 		free(defproxy.dyncookie_key);
 		free(defproxy.cookie_domain);
+		free(defproxy.cookie_attrs);
 		free(defproxy.lbprm.arg_str);
 		free(defproxy.capture_name);
 		free(defproxy.monitor_uri);
@@ -679,7 +694,7 @@
 	if (!strcmp(args[0], "server")         ||
 	    !strcmp(args[0], "default-server") ||
 	    !strcmp(args[0], "server-template")) {
-		err_code |= parse_server(file, linenum, args, curproxy, &defproxy, 1);
+		err_code |= parse_server(file, linenum, args, curproxy, &defproxy, 1, 0, 0);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
@@ -953,6 +968,13 @@
 			goto out;
 		}
 
+		if (strcasecmp(args[1], "or") == 0) {
+			ha_warning("parsing [%s:%d] : acl name '%s' will never match. 'or' is used to express a "
+				   "logical disjunction within a condition.\n",
+				   file, linenum, args[1]);
+			err_code |= ERR_WARN;
+		}
+
 		if (parse_acl((const char **)args + 1, &curproxy->acl, &errmsg, &curproxy->conf.args, file, linenum) == NULL) {
 			ha_alert("parsing [%s:%d] : error detected while parsing ACL '%s' : %s.\n",
 				 file, linenum, args[1], errmsg);
@@ -1137,9 +1159,34 @@
 					err_code |= ERR_WARN;
 				curproxy->ck_opts |= PR_CK_DYNAMIC;
 			}
+			else if (!strcmp(args[cur_arg], "attr")) {
+				char *val;
+				if (!*args[cur_arg + 1]) {
+					ha_alert("parsing [%s:%d]: '%s' expects <value> as argument.\n",
+						 file, linenum, args[cur_arg]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+				val = args[cur_arg + 1];
+				while (*val) {
+					if (iscntrl(*val) || *val == ';') {
+						ha_alert("parsing [%s:%d]: character '%%x%02X' is not permitted in attribute value.\n",
+							 file, linenum, *val);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					val++;
+				}
+				/* don't add ';' for the first attribute */
+				if (!curproxy->cookie_attrs)
+					curproxy->cookie_attrs = strdup(args[cur_arg + 1]);
+				else
+					memprintf(&curproxy->cookie_attrs, "%s; %s", curproxy->cookie_attrs, args[cur_arg + 1]);
+				cur_arg++;
+			}
 
 			else {
-				ha_alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'prefix', 'indirect', 'nocache', 'postonly', 'domain', 'maxidle', 'dynamic' and 'maxlife' options.\n",
+				ha_alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'prefix', 'indirect', 'nocache', 'postonly', 'domain', 'maxidle', 'dynamic', 'maxlife' and 'attr' options.\n",
 					 file, linenum, args[0]);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
@@ -2409,7 +2456,10 @@
 
 			/* use HTTP request to check servers' health */
 			free(curproxy->check_req);
-			curproxy->check_req = NULL;
+			free(curproxy->check_hdrs);
+			free(curproxy->check_body);
+			curproxy->check_req = curproxy->check_hdrs = curproxy->check_body = NULL;
+			curproxy->check_len = curproxy->check_hdrs_len = curproxy->check_body_len = 0;
 			curproxy->options2 &= ~PR_O2_CHK_ANY;
 			curproxy->options2 |= PR_O2_HTTP_CHK;
 			if (!*args[2]) { /* no argument */
@@ -2420,16 +2470,42 @@
 				curproxy->check_req = malloc(reqlen);
 				curproxy->check_len = snprintf(curproxy->check_req, reqlen,
 							       "OPTIONS %s HTTP/1.0\r\n", args[2]); /* URI to use */
-			} else { /* more arguments : METHOD URI [HTTP_VER] */
-				int reqlen = strlen(args[2]) + strlen(args[3]) + 3 + strlen("\r\n");
-				if (*args[4])
-					reqlen += strlen(args[4]);
-				else
-					reqlen += strlen("HTTP/1.0");
+			} else if (!*args[4]) { /* two arguments : METHOD URI */
+				int reqlen = strlen(args[2]) + strlen(args[3]) + strlen("HTTP/1.0\r\n") + 3;
 
 				curproxy->check_req = malloc(reqlen);
 				curproxy->check_len = snprintf(curproxy->check_req, reqlen,
-							       "%s %s %s\r\n", args[2], args[3], *args[4]?args[4]:"HTTP/1.0");
+							       "%s %s HTTP/1.0\r\n", args[2], args[3]);
+			} else { /* 3 arguments : METHOD URI HTTP_VER */
+				char *vsn = args[4];
+				char *hdrs = strstr(vsn, "\r\n");
+				char *body = strstr(vsn, "\r\n\r\n");
+
+				if (hdrs == body)
+					hdrs = NULL;
+				if (hdrs) {
+					*hdrs = '\0';
+					hdrs += 2;
+				}
+				if (body) {
+					*body = '\0';
+					body += 4;
+				}
+
+				curproxy->check_len = strlen(args[2]) + strlen(args[3]) + strlen(vsn) + 4;
+				curproxy->check_req = malloc(curproxy->check_len+1);
+				snprintf(curproxy->check_req, curproxy->check_len+1, "%s %s %s\r\n", args[2], args[3], vsn);
+
+				if (hdrs) {
+					curproxy->check_hdrs_len = strlen(hdrs) + 2;
+					curproxy->check_hdrs = malloc(curproxy->check_hdrs_len+1);
+					snprintf(curproxy->check_hdrs, curproxy->check_hdrs_len+1, "%s\r\n", hdrs);
+				}
+
+				if (body) {
+					curproxy->check_body_len = strlen(body);
+					curproxy->check_body = strdup(body);
+				}
 			}
 			if (alertif_too_many_args_idx(3, 1, file, linenum, args, &err_code))
 				goto out;
@@ -2919,6 +2995,63 @@
 			if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
 				goto out;
 		}
+		else if (strcmp(args[1], "send") == 0) {
+			int cur_arg = 2;
+
+			free(curproxy->check_hdrs);
+			free(curproxy->check_body);
+			curproxy->check_hdrs = curproxy->check_body = NULL;
+			curproxy->check_hdrs_len = curproxy->check_body_len = 0;
+			while (*(args[cur_arg])) {
+				if (strcmp(args[cur_arg], "hdr") == 0) {
+					int hdr_len;
+					if (!*(args[cur_arg+1]) || !*(args[cur_arg+2])) {
+						ha_alert("parsing [%s:%d] : '%s %s' : %s expects a name and a value as parameter.\n",
+							 file, linenum, args[0], args[1], args[cur_arg]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+
+					cur_arg++;
+					hdr_len = strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 4;
+					curproxy->check_hdrs = my_realloc2(curproxy->check_hdrs, curproxy->check_hdrs_len+hdr_len+1);
+					if (curproxy->check_hdrs == NULL) {
+						ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					snprintf(curproxy->check_hdrs + curproxy->check_hdrs_len, hdr_len+1, "%s: %s\r\n", args[cur_arg], args[cur_arg+1]);
+					curproxy->check_hdrs_len += hdr_len;
+
+					cur_arg++;
+				}
+				else if (strcmp(args[cur_arg], "body") == 0) {
+					if (!*(args[cur_arg+1])) {
+						ha_alert("parsing [%s:%d] : '%s %s' : %s expects a string as parameter.\n",
+							 file, linenum, args[0], args[1], args[cur_arg]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					cur_arg++;
+					free(curproxy->check_body);
+					curproxy->check_body = strdup(args[cur_arg]);
+					curproxy->check_body_len = strlen(args[cur_arg]);
+					if (curproxy->check_body == NULL) {
+						ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+				}
+				else {
+					ha_alert("parsing [%s:%d] : '%s %s' only supports 'hdr' and 'body', found '%s'.\n",
+						 file, linenum, args[0], args[1], args[cur_arg]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+				cur_arg++;
+			}
+
+		}
 		else if (strcmp(args[1], "expect") == 0) {
 			const char *ptr_arg;
 			int cur_arg;
@@ -3875,7 +4008,7 @@
 	}
 	else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
 		if (!already_warned(WARN_REQREP_DEPRECATED))
-			ha_warning("parsing [%s:%d] : The '%s' directive is deprecated in favor of 'http-request replace-uri' and 'http-request replace-header' and will be removed in next version.\n", file, linenum, args[0]);
+			ha_warning("parsing [%s:%d] : The '%s' directive is deprecated in favor of 'http-request replace-uri', 'http-request replace-path', and 'http-request replace-header' and will be removed in next version.\n", file, linenum, args[0]);
 
 		if (*(args[2]) == 0) {
 			ha_alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 3bbb802..6f453b5 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -713,7 +713,7 @@
 			err_code |= ERR_ALERT | ERR_ABORT;
 			goto out;
 		}
-		err_code |= parse_server(file, linenum, args, curpeers->peers_fe, NULL, 0);
+		err_code |= parse_server(file, linenum, args, curpeers->peers_fe, NULL, 0, 1, 1);
 	}
 	else if (strcmp(args[0], "peers") == 0) { /* new peers section */
 		/* Initialize these static variables when entering a new "peers" section*/
@@ -811,9 +811,19 @@
 		 * The server address is parsed only if we are parsing a "peer" line,
 		 * or if we are parsing a "server" line and the current peer is not the local one.
 		 */
-		err_code |= parse_server(file, linenum, args, curpeers->peers_fe, NULL, peer || !local_peer);
-		if (!curpeers->peers_fe->srv)
+		err_code |= parse_server(file, linenum, args, curpeers->peers_fe, NULL, peer || !local_peer, 1, 1);
+		if (!curpeers->peers_fe->srv) {
+			/* Remove the newly allocated peer. */
+			if (newpeer != curpeers->local) {
+				struct peer *p;
+
+				p = curpeers->remote;
+				curpeers->remote = curpeers->remote->next;
+				free(p->id);
+				free(p);
+			}
 			goto out;
+		}
 
 		/* If the peer address has just been parsed, let's copy it to <newpeer>
 		 * and initializes ->proto.
@@ -1140,9 +1150,8 @@
 				continue;
 
 			memset(sk, 0, sizeof(*sk));
-			sk = str2ip2(address, sk, 1);
-			if (!sk) {
-				ha_warning("parsing [/etc/resolv.conf:%d] : address '%s' could not be recognized, namerserver will be excluded.\n",
+			if (!str2ip2(address, sk, 1)) {
+				ha_warning("parsing [/etc/resolv.conf:%d] : address '%s' could not be recognized, nameserver will be excluded.\n",
 					   resolv_linenum, address);
 				err_code |= ERR_WARN;
 				continue;
@@ -1902,12 +1911,14 @@
 				ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
 					 file, linenum);
 				err_code |= ERR_ALERT | ERR_FATAL;
+				linenum--;
 				continue;
 			}
 
 			readbytes = linesize - 1;
 			linesize = newlinesize;
 			thisline = newline;
+			linenum--;
 			continue;
 		}
 
@@ -1977,6 +1988,7 @@
 					else {
 						ha_alert("parsing [%s:%d] : invalid or incomplete '\\x' sequence in '%s'.\n", file, linenum, args[0]);
 						err_code |= ERR_ALERT | ERR_FATAL;
+						goto next_line;
 					}
 				} else if (line[1] == '"') {
 					*line = '"';
@@ -2661,7 +2673,10 @@
 					free(pxname);
 					continue;
 				}
-				/* simple string: free the expression and fall back to static rule */
+				/* Only one element in the list, a simple string: free the expression and
+				 * fall back to static rule
+				 */
+				LIST_DEL(&node->list);
 				free(node->arg);
 				free(node);
 			}
@@ -2729,7 +2744,7 @@
 
 			if (!target) {
 				ha_alert("Proxy '%s': unable to find stick-table '%s'.\n",
-					 curproxy->id, mrule->table.name);
+					 curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id);
 				cfgerr++;
 			}
 			else if (!stktable_compatible_sample(mrule->expr,  target->type)) {
@@ -2767,7 +2782,7 @@
 
 			if (!target) {
 				ha_alert("Proxy '%s': unable to find store table '%s'.\n",
-					 curproxy->id, mrule->table.name);
+					 curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id);
 				cfgerr++;
 			}
 			else if (!stktable_compatible_sample(mrule->expr, target->type)) {
@@ -3211,7 +3226,7 @@
 			}
 
 			/* this will also properly set the transport layer for prod and checks */
-			if (newsrv->use_ssl || newsrv->check.use_ssl) {
+			if (newsrv->use_ssl == 1 || newsrv->check.use_ssl == 1 || (newsrv->proxy->options & PR_O_TCPCHK_SSL)) {
 				if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
 					cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
 			}
@@ -3224,9 +3239,6 @@
 				    proxy_type_str(curproxy), curproxy->id,
 				    newsrv->id);
 
-			/* set the check type on the server */
-			newsrv->check.type = curproxy->options2 & PR_O2_CHK_ANY;
-
 			if (newsrv->trackit) {
 				struct proxy *px;
 				struct server *srv, *loop;
@@ -3263,9 +3275,7 @@
 					goto next_srv;
 				}
 
-				if (!(srv->check.state & CHK_ST_CONFIGURED) &&
-				    !(srv->agent.state & CHK_ST_CONFIGURED) &&
-				    !srv->track && !srv->trackit) {
+				if (!srv->do_check && !srv->do_agent && !srv->track && !srv->trackit) {
 					ha_alert("config : %s '%s', server '%s': unable to use %s/%s for "
 						 "tracking as it does not have any check nor agent enabled.\n",
 						 proxy_type_str(curproxy), curproxy->id,
@@ -3695,15 +3705,16 @@
 							goto err;
 						idle_conn_cleanup[i]->process = srv_cleanup_toremove_connections;
 						idle_conn_cleanup[i]->context = NULL;
+						HA_SPIN_INIT(&toremove_lock[i]);
 						LIST_INIT(&toremove_connections[i]);
 					}
 				}
-				newsrv->idle_orphan_conns = calloc((unsigned int)global.nbthread, sizeof(*newsrv->idle_orphan_conns));
+				newsrv->idle_orphan_conns = calloc((unsigned short)global.nbthread, sizeof(*newsrv->idle_orphan_conns));
 				if (!newsrv->idle_orphan_conns)
 					goto err;
 				for (i = 0; i < global.nbthread; i++)
 					LIST_INIT(&newsrv->idle_orphan_conns[i]);
-				newsrv->curr_idle_thr = calloc(global.nbthread, sizeof(int));
+				newsrv->curr_idle_thr = calloc(global.nbthread, sizeof(*newsrv->curr_idle_thr));
 				if (!newsrv->curr_idle_thr)
 					goto err;
 				continue;
@@ -3944,12 +3955,25 @@
 		struct peers *curpeers = cfg_peers, **last;
 		struct peer *p, *pb;
 
+		/* In the case the peers frontend was not initialized by a
+		 stick-table used in the configuration, set its bind_proc
+		 by default to the first process. */
+		while (curpeers) {
+			if (curpeers->peers_fe) {
+				if (curpeers->peers_fe->bind_proc == 0)
+					curpeers->peers_fe->bind_proc = 1;
+			}
+			curpeers = curpeers->next;
+		}
+
+		curpeers = cfg_peers;
 		/* Remove all peers sections which don't have a valid listener,
 		 * which are not used by any table, or which are bound to more
 		 * than one process.
 		 */
 		last = &cfg_peers;
 		while (*last) {
+			struct stktable *t;
 			curpeers = *last;
 
 			if (curpeers->state == PR_STSTOPPED) {
@@ -3961,6 +3985,9 @@
 			else if (!curpeers->peers_fe || !curpeers->peers_fe->id) {
 				ha_warning("Removing incomplete section 'peers %s' (no peer named '%s').\n",
 					   curpeers->id, localpeer);
+				if (curpeers->peers_fe)
+					stop_proxy(curpeers->peers_fe);
+				curpeers->peers_fe = NULL;
 			}
 			else if (atleast2(curpeers->peers_fe->bind_proc)) {
 				/* either it's totally stopped or too much used */
@@ -3983,7 +4010,7 @@
 				p = curpeers->remote;
 				while (p) {
 					if (p->srv) {
-						if (p->srv->use_ssl && xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
+						if (p->srv->use_ssl == 1 && xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
 							cfgerr += xprt_get(XPRT_SSL)->prepare_srv(p->srv);
 					}
 					p = p->next;
@@ -4023,6 +4050,11 @@
 			 */
 			free(curpeers->id);
 			curpeers = curpeers->next;
+			/* Reset any refereance to this peers section in the list of stick-tables */
+			for (t = stktables_list; t; t = t->next) {
+				if (t->peers.p && t->peers.p == *last)
+					t->peers.p = NULL;
+			}
 			free(*last);
 			*last = curpeers;
 		}
diff --git a/src/channel.c b/src/channel.c
index d4a46ff..09ea0e3 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -96,7 +96,7 @@
 	if (len > max)
 		return max;
 
-	memcpy(ci_tail(chn), msg, len);
+	memcpy(co_tail(chn), msg, len);
 	b_add(&chn->buf, len);
 	c_adv(chn, len);
 	chn->total += len;
@@ -260,7 +260,7 @@
 int co_getblk_nc(const struct channel *chn, const char **blk1, size_t *len1, const char **blk2, size_t *len2)
 {
 	if (unlikely(co_data(chn) == 0)) {
-		if (chn->flags & CF_SHUTW)
+		if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
 			return -1;
 		return 0;
 	}
@@ -302,7 +302,7 @@
 		}
 	}
 
-	if (chn->flags & CF_SHUTW) {
+	if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
 		/* If we have found no LF and the buffer is shut, then
 		 * the resulting string is made of the concatenation of
 		 * the pending blocks (1 or 2).
diff --git a/src/checks.c b/src/checks.c
index 06f47ad..a4dbbbd 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -115,8 +115,8 @@
 	[EXTCHK_HAPROXY_PROXY_PORT]     = { "HAPROXY_PROXY_PORT",     EXTCHK_SIZE_EVAL_INIT },
 	[EXTCHK_HAPROXY_SERVER_NAME]    = { "HAPROXY_SERVER_NAME",    EXTCHK_SIZE_EVAL_INIT },
 	[EXTCHK_HAPROXY_SERVER_ID]      = { "HAPROXY_SERVER_ID",      EXTCHK_SIZE_EVAL_INIT },
-	[EXTCHK_HAPROXY_SERVER_ADDR]    = { "HAPROXY_SERVER_ADDR",    EXTCHK_SIZE_EVAL_INIT },
-	[EXTCHK_HAPROXY_SERVER_PORT]    = { "HAPROXY_SERVER_PORT",    EXTCHK_SIZE_EVAL_INIT },
+	[EXTCHK_HAPROXY_SERVER_ADDR]    = { "HAPROXY_SERVER_ADDR",    EXTCHK_SIZE_ADDR },
+	[EXTCHK_HAPROXY_SERVER_PORT]    = { "HAPROXY_SERVER_PORT",    EXTCHK_SIZE_UINT },
 	[EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
 	[EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
 };
@@ -137,6 +137,17 @@
 	[HANA_STATUS_HTTP_BROKEN_PIPE]	= { "Close from server (http)",        { 0, 1 }},
 };
 
+/* checks if <err> is a real error for errno or one that can be ignored, and
+ * return 0 for these ones or <err> for real ones.
+ */
+static inline int unclean_errno(int err)
+{
+	if (err == EAGAIN || err == EINPROGRESS ||
+	    err == EISCONN || err == EALREADY)
+		return 0;
+	return err;
+}
+
 /*
  * Convert check_status code to description
  */
@@ -548,7 +559,7 @@
 	int skerr;
 	socklen_t lskerr = sizeof(skerr);
 
-	if (conn->flags & CO_FL_ERROR && ((errno && errno != EAGAIN) || !conn->ctrl))
+	if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
 		return 1;
 
 	if (!conn_ctrl_ready(conn))
@@ -557,8 +568,7 @@
 	if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
 		errno = skerr;
 
-	if (errno == EAGAIN)
-		errno = 0;
+	errno = unclean_errno(errno);
 
 	if (!errno) {
 		/* we could not retrieve an error, that does not mean there is
@@ -599,8 +609,8 @@
 	if (check->result != CHK_RES_UNKNOWN)
 		return;
 
-	errno = errno_bck;
-	if (conn && (!errno || errno == EAGAIN))
+	errno = unclean_errno(errno_bck);
+	if (conn && errno)
 		retrieve_errno_from_socket(conn);
 
 	if (conn && !(conn->flags & CO_FL_ERROR) &&
@@ -644,7 +654,7 @@
 	}
 
 	if (conn && conn->err_code) {
-		if (errno && errno != EAGAIN)
+		if (unclean_errno(errno))
 			chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
 				     chk->area);
 		else
@@ -653,7 +663,7 @@
 		err_msg = trash.area;
 	}
 	else {
-		if (errno && errno != EAGAIN) {
+		if (unclean_errno(errno)) {
 			chunk_printf(&trash, "%s%s", strerror(errno),
 				     chk->area);
 			err_msg = trash.area;
@@ -1383,7 +1393,7 @@
 	default:
 		/* good connection is enough for pure TCP check */
 		if ((conn->flags & CO_FL_CONNECTED) && !check->type) {
-			if (check->use_ssl)
+			if (check->use_ssl == 1)
 				set_server_check_status(check, HCHK_STATUS_L6OK, NULL);
 			else
 				set_server_check_status(check, HCHK_STATUS_L4OK, NULL);
@@ -1611,13 +1621,30 @@
 			memcpy(b_head(&check->bo) + 11, &gmt_time, 4);
 		}
 		else if ((check->type) == PR_O2_HTTP_CHK) {
-			if (s->proxy->options2 & PR_O2_CHK_SNDST)
-				b_putblk(&check->bo, trash.area,
-					 httpchk_build_status_header(s, trash.area, trash.size));
 			/* prevent HTTP keep-alive when "http-check expect" is used */
 			if (s->proxy->options2 & PR_O2_EXP_TYPE)
 				b_putist(&check->bo, ist("Connection: close\r\n"));
+
+			/* If there is a body, add its content-length */
+			if (s->proxy->check_body_len)
+				chunk_appendf(&check->bo, "Content-Length: %s\r\n", ultoa(s->proxy->check_body_len));
+
+			/* Add configured headers */
+			if (s->proxy->check_hdrs)
+				b_putblk(&check->bo, s->proxy->check_hdrs, s->proxy->check_hdrs_len);
+
+			/* Add send-state header */
+			if (s->proxy->options2 & PR_O2_CHK_SNDST)
+				b_putblk(&check->bo, trash.area,
+					 httpchk_build_status_header(s, trash.area, trash.size));
+
+			/* end-of-header */
 			b_putist(&check->bo, ist("\r\n"));
+
+			/* Add the body */
+			if (s->proxy->check_body)
+				b_putblk(&check->bo, s->proxy->check_body, s->proxy->check_body_len);
+
 			*b_tail(&check->bo) = '\0'; /* to make gdb output easier to read */
 		}
 	}
@@ -1701,6 +1728,9 @@
 	if (s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
 		conn->send_proxy_ofs = 1;
 		conn->flags |= CO_FL_SEND_PROXY;
+	}
+	if (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4) &&
+	    conn_ctrl_ready(conn)) {
 		if (xprt_add_hs(conn) < 0)
 			ret = SF_ERR_RESOURCE;
 	}
@@ -1930,14 +1960,21 @@
 		goto err;
 	}
 
+	if (!check->argv[1] || !check->argv[2]) {
+		ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
+		goto err;
+	}
+
-	addr_to_str(&s->addr, buf, sizeof(buf));
-	check->argv[3] = strdup(buf);
+	check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
+	check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
+	if (!check->argv[3] || !check->argv[4]) {
+		ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
+		goto err;
+	}
 
+	addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
 	if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
-		snprintf(buf, sizeof(buf), "%u", s->svc_port);
-	else
-		*buf = 0;
-	check->argv[4] = strdup(buf);
+		snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
 
 	for (i = 0; i < 5; i++) {
 		if (!check->argv[i]) {
@@ -2035,7 +2072,18 @@
 		}
 
 		environ = check->envp;
+
+		/* Update some environment variables and command args: curconn, server addr and server port */
 		extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
+
+		addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
+		extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
+
+		*check->argv[4] = 0;
+		if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
+			snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
+		extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
+
 		haproxy_unblock_signals();
 		execvp(px->check_command, check->argv);
 		ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
@@ -2186,7 +2234,7 @@
 		rv = 0;
 		if (global.spread_checks > 0) {
 			rv = srv_getinter(check) * global.spread_checks / 100;
-			rv -= (int) (2 * rv * (rand() / (RAND_MAX + 1.0)));
+			rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
 		}
 		t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
 	}
@@ -2239,6 +2287,7 @@
 		b_reset(&check->bi);
 		b_reset(&check->bo);
 
+		task_set_affinity(t, tid_bit);
 		ret = connect_conn_chk(t);
 		cs = check->cs;
 		conn = cs_conn(cs);
@@ -2270,7 +2319,6 @@
 					__event_srv_chk_r(cs);
 			}
 
-			task_set_affinity(t, tid_bit);
 			goto reschedule;
 
 		case SF_ERR_SRVTO: /* ETIMEDOUT */
@@ -2292,6 +2340,7 @@
 		}
 
 		/* here, we have seen a synchronous error, no fd was allocated */
+		task_set_affinity(t, MAX_THREADS_MASK);
 		if (cs) {
 			if (check->wait_list.events)
 				cs->conn->xprt->unsubscribe(cs->conn,
@@ -2333,7 +2382,7 @@
 		if (check->result == CHK_RES_UNKNOWN) {
 			/* good connection is enough for pure TCP check */
 			if ((conn->flags & CO_FL_CONNECTED) && !check->type) {
-				if (check->use_ssl)
+				if (check->use_ssl == 1)
 					set_server_check_status(check, HCHK_STATUS_L6OK, NULL);
 				else
 					set_server_check_status(check, HCHK_STATUS_L4OK, NULL);
@@ -2393,7 +2442,7 @@
 			rv = 0;
 			if (global.spread_checks > 0) {
 				rv = srv_getinter(check) * global.spread_checks / 100;
-				rv -= (int) (2 * rv * (rand() / (RAND_MAX + 1.0)));
+				rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
 			}
 			t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
 		}
@@ -2938,8 +2987,9 @@
 			ret = SF_ERR_INTERNAL;
 			if (proto && proto->connect)
 				ret = proto->connect(conn,
-						     CONNECT_HAS_DATA /* I/O polling is always needed */ | (next && next->action == TCPCHK_ACT_EXPECT) ? 0 : CONNECT_DELACK_ALWAYS);
-			if (check->current_step->conn_opts & TCPCHK_OPT_SEND_PROXY) {
+						     CONNECT_HAS_DATA /* I/O polling is always needed */ | ((next && next->action == TCPCHK_ACT_EXPECT) ? 0 : CONNECT_DELACK_ALWAYS));
+			if (conn_ctrl_ready(conn) &&
+				check->current_step->conn_opts & TCPCHK_OPT_SEND_PROXY) {
 				conn->send_proxy_ofs = 1;
 				conn->flags |= CO_FL_SEND_PROXY;
 				if (xprt_add_hs(conn) < 0)
@@ -3114,10 +3164,10 @@
 				goto out_end_tcpcheck;
 			}
 
+		tcpcheck_expect:
 			if (!done && (check->current_step->string != NULL) && (b_data(&check->bi) < check->current_step->string_len) )
 				continue; /* try to read more */
 
-		tcpcheck_expect:
 			if (check->current_step->string != NULL)
 				ret = my_memmem(contentptr, b_data(&check->bi), check->current_step->string, check->current_step->string_len) != NULL;
 			else if (check->current_step->expect_regex != NULL)
@@ -3276,6 +3326,10 @@
 
 void free_check(struct check *check)
 {
+	task_destroy(check->task);
+	if (check->wait_list.tasklet)
+		tasklet_free(check->wait_list.tasklet);
+
 	free(check->bi.area);
 	free(check->bo.area);
 	if (check->cs) {
@@ -3407,7 +3461,6 @@
 		struct email_alertq *q     = &queues[i];
 		struct check        *check = &q->check;
 
-		task_destroy(check->task);
 		free_check(check);
 	}
 	free(queues);
@@ -3630,16 +3683,6 @@
 
 	srv = chk->server;
 
-	/* If neither a port nor an addr was specified and no check transport
-	 * layer is forced, then the transport layer used by the checks is the
-	 * same as for the production traffic. Otherwise we use raw_sock by
-	 * default, unless one is specified.
-	 */
-	if (!chk->port && !is_addr(&chk->addr)) {
-		chk->use_ssl |= (srv->use_ssl || (srv->proxy->options & PR_O_TCPCHK_SSL));
-		chk->send_proxy |= (srv->pp_opts);
-	}
-
 	/* by default, we use the health check port ocnfigured */
 	if (chk->port > 0)
 		return chk->port;
@@ -3664,7 +3707,148 @@
 	return 0;
 }
 
+static int init_srv_check(struct server *srv)
+{
+	const char *err;
+	struct tcpcheck_rule *r;
+	int ret = 0;
+
+	if (!srv->do_check)
+		goto out;
+
+
+	/* If neither a port nor an addr was specified and no check transport
+	 * layer is forced, then the transport layer used by the checks is the
+	 * same as for the production traffic. Otherwise we use raw_sock by
+	 * default, unless one is specified.
+	 */
+	if (!srv->check.port && !is_addr(&srv->check.addr)) {
+		if (!srv->check.use_ssl && srv->use_ssl != -1) {
+			srv->check.use_ssl = srv->use_ssl;
+			srv->check.xprt    = srv->xprt;
+		}
+		else if (srv->check.use_ssl == 1)
+			srv->check.xprt = xprt_get(XPRT_SSL);
+
+		srv->check.send_proxy |= (srv->pp_opts);
+	}
+	else if (srv->check.use_ssl == 1)
+		srv->check.xprt = xprt_get(XPRT_SSL);
+
+	/* validate <srv> server health-check settings */
+
+	/* We need at least a service port, a check port or the first tcp-check
+	 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
+	 */
+	if ((srv_check_healthcheck_port(&srv->check) != 0) ||
+	    (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
+		goto init;
+
+	if (!LIST_ISEMPTY(&srv->proxy->tcpcheck_rules)) {
+		ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
+			 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
+		ret |= ERR_ALERT | ERR_ABORT;
+		goto out;
+	}
+
+	/* search the first action (connect / send / expect) in the list */
+	r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
+	if (!r || (r->action != TCPCHK_ACT_CONNECT) || !r->port) {
+		ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
+			 "nor tcp_check rule 'connect' with port information.\n",
+			 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
+		ret |= ERR_ALERT | ERR_ABORT;
+		goto out;
+	}
+
+	/* scan the tcp-check ruleset to ensure a port has been configured */
+	list_for_each_entry(r, &srv->proxy->tcpcheck_rules, list) {
+		if ((r->action == TCPCHK_ACT_CONNECT) && (!r->port)) {
+			ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
+				 "and a tcp_check rule 'connect' with no port information.\n",
+				 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
+			ret |= ERR_ALERT | ERR_ABORT;
+			goto out;
+		}
+	}
+
+  init:
+	err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
+	if (err) {
+		ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
+			 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
+		ret |= ERR_ALERT | ERR_ABORT;
+		goto out;
+	}
+	srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
+	global.maxsock++;
+
+  out:
+	return ret;
+}
+
+static int init_srv_agent_check(struct server *srv)
+{
+	const char *err;
+	int ret = 0;
+
+	if (!srv->do_agent)
+		goto out;
+
+	err = init_check(&srv->agent, PR_O2_LB_AGENT_CHK);
+	if (err) {
+		ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
+			 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
+		ret |= ERR_ALERT | ERR_ABORT;
+		goto out;
+	}
+
+	if (!srv->agent.inter)
+		srv->agent.inter = srv->check.inter;
+
+	srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
+	global.maxsock++;
+
+  out:
+	return ret;
+}
+
+void deinit_srv_check(struct server *srv)
+{
+	if (srv->do_check)
+		free_check(&srv->check);
+}
+
+
+void deinit_srv_agent_check(struct server *srv)
+{
+	if (srv->do_agent)
+		free_check(&srv->agent);
+	free(srv->agent.send_string);
+}
+
+static int init_servers_checks()
+{
+	struct proxy *px;
+	struct server *s;
+	int ret = 0;
+
+	for (px = proxies_list; px; px = px->next) {
+		for (s = px->srv; s; s = s->next) {
+			ret |= init_srv_check(s);
+			if (ret & (ERR_ABORT|ERR_FATAL))
+				goto end;
+			ret |= init_srv_agent_check(s);
+			if (ret & (ERR_ABORT|ERR_FATAL))
+				goto end;
+		}
+	}
+	ret = start_checks();
+  end:
+	return ret;
+}
+
-REGISTER_POST_CHECK(start_checks);
+REGISTER_POST_CHECK(init_servers_checks);
 
 /*
  * Local variables:
diff --git a/src/cli.c b/src/cli.c
index 3612577..f4478ff 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -533,7 +533,18 @@
 			break;
 
 		args[i] = p;
-		p += strcspn(p, " \t");
+		while (1) {
+			p += strcspn(p, " \t\\");
+			/* escaped chars using backlashes (\) */
+			if (*p == '\\') {
+				if (!*++p)
+					break;
+				if (!*++p)
+					break;
+			} else {
+				break;
+			}
+		}
 		*p++ = 0;
 
 		/* unescape backslashes (\) */
@@ -1617,6 +1628,7 @@
 	int *tmpfd;
 	int tot_fd_nb = 0;
 	struct proxy *px;
+	struct peers *prs;
 	int i = 0;
 	int fd = -1;
 	int curoff = 0;
@@ -1669,6 +1681,22 @@
 		}
 		px = px->next;
 	}
+	prs = cfg_peers;
+	while (prs) {
+		if (prs->peers_fe) {
+			struct listener *l;
+
+			list_for_each_entry(l, &prs->peers_fe->conf.listeners, by_fe) {
+				/* Only transfer IPv4/IPv6/UNIX sockets */
+				if (l->state >= LI_ZOMBIE &&
+				    (l->proto->sock_family == AF_INET ||
+				     l->proto->sock_family == AF_INET6 ||
+				     l->proto->sock_family == AF_UNIX))
+					tot_fd_nb++;
+			}
+		}
+		prs = prs->next;
+	}
 	if (tot_fd_nb == 0)
 		goto out;
 
@@ -1692,7 +1720,6 @@
 	cmsg->cmsg_type = SCM_RIGHTS;
 	tmpfd = (int *)CMSG_DATA(cmsg);
 
-	px = proxies_list;
 	/* For each socket, e message is sent, containing the following :
 	 *  Size of the namespace name (or 0 if none), as an unsigned char.
 	 *  The namespace name, if any
@@ -1709,6 +1736,7 @@
 		goto out;
 	}
 	iov.iov_base = tmpbuf;
+	px = proxies_list;
 	while (px) {
 		struct listener *l;
 
@@ -1742,7 +1770,6 @@
 				    sizeof(l->options));
 				curoff += sizeof(l->options);
 
-
 				i++;
 			} else
 				continue;
@@ -1763,10 +1790,70 @@
 				}
 				curoff = 0;
 			}
-
 		}
 		px = px->next;
 	}
+	/* should be done for peers too */
+	prs = cfg_peers;
+	while (prs) {
+		if (prs->peers_fe) {
+			struct listener *l;
+
+			list_for_each_entry(l, &prs->peers_fe->conf.listeners, by_fe) {
+				int ret;
+				/* Only transfer IPv4/IPv6 sockets */
+				if (l->state >= LI_ZOMBIE &&
+				    (l->proto->sock_family == AF_INET ||
+				     l->proto->sock_family == AF_INET6 ||
+				     l->proto->sock_family == AF_UNIX)) {
+					memcpy(&tmpfd[i % MAX_SEND_FD], &l->fd, sizeof(l->fd));
+					if (!l->netns)
+						tmpbuf[curoff++] = 0;
+#ifdef USE_NS
+					else {
+						char *name = l->netns->node.key;
+						unsigned char len = l->netns->name_len;
+						tmpbuf[curoff++] = len;
+						memcpy(tmpbuf + curoff, name, len);
+						curoff += len;
+					}
+#endif
+					if (l->interface) {
+						unsigned char len = strlen(l->interface);
+						tmpbuf[curoff++] = len;
+						memcpy(tmpbuf + curoff, l->interface, len);
+						curoff += len;
+					} else
+						tmpbuf[curoff++] = 0;
+					memcpy(tmpbuf + curoff, &l->options,
+					       sizeof(l->options));
+					curoff += sizeof(l->options);
+
+					i++;
+				} else
+					continue;
+				if ((!(i % MAX_SEND_FD))) {
+					iov.iov_len = curoff;
+					if (sendmsg(fd, &msghdr, 0) != curoff) {
+						ha_warning("Failed to transfer sockets\n");
+						goto out;
+					}
+					/* Wait for an ack */
+					do {
+						ret = recv(fd, &tot_fd_nb,
+							   sizeof(tot_fd_nb), 0);
+					} while (ret == -1 && errno == EINTR);
+					if (ret <= 0) {
+						ha_warning("Unexpected error while transferring sockets\n");
+						goto out;
+					}
+					curoff = 0;
+				}
+			}
+		}
+		prs = prs->next;
+	}
+
 	if (i % MAX_SEND_FD) {
 		iov.iov_len = curoff;
 		cmsg->cmsg_len = CMSG_LEN((i % MAX_SEND_FD) * sizeof(int));
@@ -2001,7 +2088,7 @@
 		while (p+reql < end) {
 			/* handle escaping */
 			if (p[reql] == '\\') {
-				reql++;
+				reql+=2;
 				continue;
 			}
 			if (p[reql] == ';' || p[reql] == '\n') {
@@ -2041,29 +2128,25 @@
 
 	/* splits the command in words */
 	while (i < MAX_STATS_ARGS && p < end) {
-		int j, k;
-
 		/* skip leading spaces/tabs */
 		p += strspn(p, " \t");
 		if (!*p)
 			break;
 
 		args[i] = p;
-		p += strcspn(p, " \t");
-		*p++ = 0;
-
-		/* unescape backslashes (\) */
-		for (j = 0, k = 0; args[i][k]; k++) {
-			if (args[i][k] == '\\') {
-				if (args[i][k + 1] == '\\')
-					k++;
-				else
-					continue;
+		while (1) {
+			p += strcspn(p, " \t\\");
+			/* escaped chars using backlashes (\) */
+			if (*p == '\\') {
+				if (!*++p)
+					break;
+				if (!*++p)
+					break;
+			} else {
+				break;
 			}
-			args[i][j] = args[i][k];
-			j++;
 		}
-		args[i][j] = 0;
+		*p++ = 0;
 		i++;
 	}
 
@@ -2458,6 +2541,10 @@
 		int port1, port2, port;
 		struct protocol *proto;
 
+		/* only the workers support the master CLI */
+		if (!(child->options & PROC_O_TYPE_WORKER))
+			continue;
+
 		newsrv = new_server(mworker_proxy);
 		if (!newsrv)
 			goto error;
diff --git a/src/connection.c b/src/connection.c
index 4d6d4d5..5f058e4 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -390,7 +390,7 @@
 	char *line, *end;
 	struct proxy_hdr_v2 *hdr_v2;
 	const char v2sig[] = PP2_SIGNATURE;
-	int tlv_length = 0;
+	size_t total_v2_len;
 	int tlv_offset = 0;
 	int ret;
 
@@ -566,7 +566,8 @@
 		goto fail;
 	}
 
-	if (trash.data < PP2_HEADER_LEN + ntohs(hdr_v2->len))
+	total_v2_len = PP2_HEADER_LEN + ntohs(hdr_v2->len);
+	if (trash.data < total_v2_len)
 		goto missing;
 
 	switch (hdr_v2->ver_cmd & PP2_CMD_MASK) {
@@ -584,7 +585,6 @@
 			((struct sockaddr_in *)&conn->addr.to)->sin_port = hdr_v2->addr.ip4.dst_port;
 			conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET;
 			tlv_offset = PP2_HEADER_LEN + PP2_ADDR_LEN_INET;
-			tlv_length = ntohs(hdr_v2->len) - PP2_ADDR_LEN_INET;
 			break;
 		case 0x21:  /* TCPv6 */
 			if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET6)
@@ -598,41 +598,64 @@
 			((struct sockaddr_in6 *)&conn->addr.to)->sin6_port = hdr_v2->addr.ip6.dst_port;
 			conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET;
 			tlv_offset = PP2_HEADER_LEN + PP2_ADDR_LEN_INET6;
-			tlv_length = ntohs(hdr_v2->len) - PP2_ADDR_LEN_INET6;
 			break;
 		}
 
 		/* TLV parsing */
-		if (tlv_length > 0) {
-			while (tlv_offset + TLV_HEADER_SIZE <= trash.data) {
-				const struct tlv *tlv_packet = (struct tlv *) &trash.area[tlv_offset];
-				const int tlv_len = get_tlv_length(tlv_packet);
-				tlv_offset += tlv_len + TLV_HEADER_SIZE;
+		while (tlv_offset < total_v2_len) {
+			struct tlv *tlv_packet;
+			int tlv_len;
 
-				switch (tlv_packet->type) {
-				case PP2_TYPE_CRC32C: {
-					void *tlv_crc32c_p = (void *)tlv_packet->value;
-					uint32_t n_crc32c = ntohl(read_u32(tlv_crc32c_p));
-					write_u32(tlv_crc32c_p, 0);
-					if (hash_crc32c(trash.area, PP2_HEADER_LEN + ntohs(hdr_v2->len)) != n_crc32c)
-						goto bad_header;
-					break;
-				}
+			/* Verify that we have at least TLV_HEADER_SIZE bytes left */
+			if (tlv_offset + TLV_HEADER_SIZE > total_v2_len)
+				goto bad_header;
+
+			tlv_packet = (struct tlv *) &trash.area[tlv_offset];
+			tlv_len = get_tlv_length(tlv_packet);
+			tlv_offset += tlv_len + TLV_HEADER_SIZE;
+
+			/* Verify that the TLV length does not exceed the total PROXYv2 length */
+			if (tlv_offset > total_v2_len)
+				goto bad_header;
+
+			switch (tlv_packet->type) {
+			case PP2_TYPE_CRC32C: {
+				uint32_t n_crc32c;
+
+				/* Verify that this TLV is exactly 4 bytes long */
+				if (tlv_len != 4)
+					goto bad_header;
+
+				n_crc32c = read_n32(tlv_packet->value);
+				write_n32(tlv_packet->value, 0); // compute with CRC==0
+
+				if (hash_crc32c(trash.area, total_v2_len) != n_crc32c)
+					goto bad_header;
+				break;
+			}
 #ifdef USE_NS
-				case PP2_TYPE_NETNS: {
-					const struct netns_entry *ns;
-					ns = netns_store_lookup((char*)tlv_packet->value, tlv_len);
-					if (ns)
-						conn->proxy_netns = ns;
-					break;
-				}
+			case PP2_TYPE_NETNS: {
+				const struct netns_entry *ns;
+
+				ns = netns_store_lookup((char*)tlv_packet->value, tlv_len);
+				if (ns)
+					conn->proxy_netns = ns;
+				break;
+			}
 #endif
-				default:
-					break;
-				}
+			default:
+				break;
 			}
 		}
 
+		/* Verify that the PROXYv2 header ends at a TLV boundary.
+		 * This is technically unreachable, because the TLV parsing already
+		 * verifies that a TLV does not exceed the total length and also
+		 * that there is space for a TLV header.
+		 */
+		if (tlv_offset != total_v2_len)
+			goto bad_header;
+
 		/* unsupported protocol, keep local connection address */
 		break;
 	case 0x00: /* LOCAL command */
@@ -642,7 +665,7 @@
 		goto bad_header; /* not a supported command */
 	}
 
-	trash.data = PP2_HEADER_LEN + ntohs(hdr_v2->len);
+	trash.data = total_v2_len;
 	goto eat_header;
 
  eat_header:
@@ -1265,7 +1288,7 @@
 			if (dst->ss_family == AF_INET) {
 				v4tov6(&tmp, &((struct sockaddr_in *)dst)->sin_addr);
 				memcpy(hdr->addr.ip6.dst_addr, &tmp, 16);
-				hdr->addr.ip6.src_port = ((struct sockaddr_in *)src)->sin_port;
+				hdr->addr.ip6.dst_port = ((struct sockaddr_in *)dst)->sin_port;
 			}
 			else {
 				memcpy(hdr->addr.ip6.dst_addr, &((struct sockaddr_in6 *)dst)->sin6_addr, 16);
diff --git a/src/debug.c b/src/debug.c
index 79bea88..58b5583 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -23,6 +23,7 @@
 #include <common/standard.h>
 
 #include <types/global.h>
+#include <types/signal.h>
 
 #include <proto/cli.h>
 #include <proto/fd.h>
@@ -50,9 +51,10 @@
 	int stuck = !!(thread_info[thr].flags & TI_FL_STUCK);
 
 	chunk_appendf(buf,
-	              "%c%cThread %-2u: act=%d glob=%d wq=%d rq=%d tl=%d tlsz=%d rqsz=%d\n"
+	              "%c%cThread %-2u: id=0x%llx act=%d glob=%d wq=%d rq=%d tl=%d tlsz=%d rqsz=%d\n"
 	              "             stuck=%d fdcache=%d prof=%d",
 	              (thr == calling_tid) ? '*' : ' ', stuck ? '>' : ' ', thr + 1,
+		      ha_get_pthread_id(thr),
 		      thread_has_tasks(),
 	              !!(global_tasks_mask & thr_bit),
 	              !eb_is_empty(&task_per_thread[thr].timers),
@@ -113,9 +115,11 @@
 		              task->call_date ? " ns ago" : "");
 
 	chunk_appendf(buf, "%s"
-	              "  fct=%p (%s) ctx=%p",
+	              "  fct=%p=main%s%ld (%s) ctx=%p",
 	              pfx,
 	              task->process,
+		      ((void *)task->process - (void *)main) < 0 ? "" : "+",
+		      (long)((void *)task->process - (void *)main),
 	              task->process == process_stream ? "process_stream" :
 	              task->process == task_run_applet ? "task_run_applet" :
 	              task->process == si_cs_io_cb ? "si_cs_io_cb" :
@@ -157,11 +161,13 @@
 		chunk_appendf(buf, "%sCurrent executing a Lua HTTP service -- ", pfx);
 	}
 
-	if (hlua) {
+	if (hlua && hlua->T) {
 		luaL_traceback(hlua->T, hlua->T, NULL, 0);
 		if (!append_prefixed_str(buf, lua_tostring(hlua->T, -1), pfx, '\n', 1))
 			b_putchr(buf, '\n');
 	}
+	else
+		b_putchr(buf, '\n');
 #endif
 }
 
@@ -439,12 +445,6 @@
 
 #else /* below USE_THREAD_DUMP is set */
 
-/* The signal to trigger a debug dump on a thread is SIGURG. It has the benefit
- * of not stopping gdb by default, so that issuing "show threads" in a process
- * being debugged has no adverse effect.
- */
-#define DEBUGSIG SIGURG
-
 /* ID of the thread requesting the dump */
 static unsigned int thread_dump_tid;
 
@@ -473,6 +473,12 @@
 /* handles DEBUGSIG to dump the state of the thread it's working on */
 void debug_handler(int sig, siginfo_t *si, void *arg)
 {
+	/* first, let's check it's really for us and that we didn't just get
+	 * a spurious DEBUGSIG.
+	 */
+	if (!(threads_to_dump & tid_bit))
+		return;
+
 	/* There are 4 phases in the dump process:
 	 *   1- wait for our turn, i.e. when all lower bits are gone.
 	 *   2- perform the action if our bit is set
diff --git a/src/dns.c b/src/dns.c
index 9fabdfd..095040e 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -54,7 +54,7 @@
 struct list dns_resolvers  = LIST_HEAD_INIT(dns_resolvers);
 struct list dns_srvrq_list = LIST_HEAD_INIT(dns_srvrq_list);
 
-static THREAD_LOCAL int64_t dns_query_id_seed = 0; /* random seed */
+static THREAD_LOCAL uint64_t dns_query_id_seed = 0; /* random seed */
 
 DECLARE_STATIC_POOL(dns_answer_item_pool, "dns_answer_item", sizeof(struct dns_answer_item));
 DECLARE_STATIC_POOL(dns_resolution_pool,  "dns_resolution",  sizeof(struct dns_resolution));
@@ -540,10 +540,12 @@
 				    !memcmp(srv->hostname_dn, item->target, item->data_len)) {
 					int ha_weight;
 
-					/* Make sure weight is at least 1, so
-					 * that the server will be used.
+					/* DNS weight range if from 0 to 65535
+					 * HAProxy weight is from 0 to 256
+					 * The rule below ensures that weight 0 is well respected
+					 * while allowing a "mapping" from DNS weight into HAProxy's one.
 					 */
-					ha_weight = item->weight / 256 + 1;
+					ha_weight = (item->weight + 255) / 256;
 					if (srv->uweight != ha_weight) {
 						char weight[9];
 
@@ -587,10 +589,12 @@
 				    !(srv->flags & SRV_F_CHECKPORT))
 					srv->check.port = item->port;
 
-				/* Make sure weight is at least 1, so
-				 * that the server will be used.
+				/* DNS weight range if from 0 to 65535
+				 * HAProxy weight is from 0 to 256
+				 * The rule below ensures that weight 0 is well respected
+				 * while allowing a "mapping" from DNS weight into HAProxy's one.
 				 */
-				ha_weight = item->weight / 256 + 1;
+				ha_weight = (item->weight + 255) / 256;
 
 				snprintf(weight, sizeof(weight), "%d", ha_weight);
 				server_parse_weight_change_request(srv, weight);
@@ -1201,6 +1205,12 @@
 			if (i == offset)
 				return -1;
 
+			/* ignore trailing dot */
+			if (i + 2 == str_len) {
+				i++;
+				break;
+			}
+
 			dn[offset] = (i - offset);
 			offset = i+1;
 			continue;
@@ -1221,7 +1231,6 @@
  */
 int dns_hostname_validation(const char *string, char **err)
 {
-	const char *c, *d;
 	int i;
 
 	if (strlen(string) > DNS_MAX_NAME_SIZE) {
@@ -1230,36 +1239,32 @@
 		return 0;
 	}
 
-	c = string;
-	while (*c) {
-		d = c;
-
+	while (*string) {
 		i = 0;
-		while (*d != '.' && *d && i <= DNS_MAX_LABEL_SIZE) {
-			i++;
-			if (!((*d == '-') || (*d == '_') ||
-			      ((*d >= 'a') && (*d <= 'z')) ||
-			      ((*d >= 'A') && (*d <= 'Z')) ||
-			      ((*d >= '0') && (*d <= '9')))) {
+		while (*string && *string != '.' && i < DNS_MAX_LABEL_SIZE) {
+			if (!(*string == '-' || *string == '_' ||
+			      (*string >= 'a' && *string <= 'z') ||
+			      (*string >= 'A' && *string <= 'Z') ||
+			      (*string >= '0' && *string <= '9'))) {
 				if (err)
 					*err = DNS_INVALID_CHARACTER;
 				return 0;
 			}
-			d++;
+			i++;
+			string++;
 		}
 
+		if (!(*string))
+			break;
+
-		if ((i >= DNS_MAX_LABEL_SIZE) && (d[i] != '.')) {
+		if (*string != '.' && i >= DNS_MAX_LABEL_SIZE) {
 			if (err)
 				*err = DNS_LABEL_TOO_LONG;
 			return 0;
 		}
 
-		if (*d == '\0')
-			goto out;
-
-		c = ++d;
+		string++;
 	}
- out:
 	return 1;
 }
 
@@ -1331,6 +1336,7 @@
 static void dns_free_resolution(struct dns_resolution *resolution)
 {
 	struct dns_requester *req, *reqback;
+	struct dns_answer_item *item, *itemback;
 
 	/* clean up configuration */
 	dns_reset_resolution(resolution);
@@ -1342,6 +1348,11 @@
 		req->resolution = NULL;
 	}
 
+	list_for_each_entry_safe(item, itemback, &resolution->response.answer_list, list) {
+		LIST_DEL(&item->list);
+		pool_free(dns_answer_item_pool, item);
+	}
+
 	LIST_DEL(&resolution->list);
 	pool_free(dns_resolution_pool, resolution);
 }
@@ -1530,18 +1541,25 @@
 		return;
 
 	/* no need to go further if we can't retrieve the nameserver */
-	if ((ns = dgram->owner) == NULL)
+	if ((ns = dgram->owner) == NULL) {
+		_HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
+		fd_stop_recv(fd);
 		return;
+	}
 
 	resolvers = ns->resolvers;
 	HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
 
 	/* process all pending input messages */
-	while (1) {
+	while (fd_recv_ready(fd)) {
 		/* read message received */
 		memset(buf, '\0', resolvers->accepted_payload_size + 1);
 		if ((buflen = recv(fd, (char*)buf , resolvers->accepted_payload_size + 1, 0)) < 0) {
-			/* FIXME : for now we consider EAGAIN only */
+			/* FIXME : for now we consider EAGAIN only, but at
+			 * least we purge sticky errors that would cause us to
+			 * be called in loops.
+			 */
+			_HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
 			fd_cant_recv(fd);
 			break;
 		}
@@ -2150,15 +2168,22 @@
 	struct dns_requester *req;
 	struct dns_resolvers  *resolvers;
 	struct dns_resolution *res;
-	int exp;
+	int exp, locked = 0;
+	enum act_return ret = ACT_RET_CONT;
+
+	resolvers = rule->arg.dns.resolvers;
 
 	/* we have a response to our DNS resolution */
  use_cache:
 	if (s->dns_ctx.dns_requester && s->dns_ctx.dns_requester->resolution != NULL) {
 		resolution = s->dns_ctx.dns_requester->resolution;
-		if (resolution->step == RSLV_STEP_RUNNING) {
-			return ACT_RET_YIELD;
+		if (!locked) {
+			HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
+			locked = 1;
 		}
+
+		if (resolution->step == RSLV_STEP_RUNNING)
+			goto yield;
 		if (resolution->step == RSLV_STEP_NONE) {
 			/* We update the variable only if we have a valid response. */
 			if (resolution->status == RSLV_STATUS_VALID) {
@@ -2192,45 +2217,59 @@
 			}
 		}
 
-		free(s->dns_ctx.hostname_dn); s->dns_ctx.hostname_dn = NULL;
-		s->dns_ctx.hostname_dn_len = 0;
-		dns_unlink_resolution(s->dns_ctx.dns_requester);
-
-		pool_free(dns_requester_pool, s->dns_ctx.dns_requester);
-		s->dns_ctx.dns_requester = NULL;
-
-		return ACT_RET_CONT;
+		goto release_requester;
 	}
 
 	/* need to configure and start a new DNS resolution */
 	smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.dns.expr, SMP_T_STR);
 	if (smp == NULL)
-		return ACT_RET_CONT;
+		goto end;
 
 	fqdn = smp->data.u.str.area;
 	if (action_prepare_for_resolution(s, fqdn) == -1)
-		return ACT_RET_ERR;
+		goto end; /* on error, ignore the action */
 
 	s->dns_ctx.parent = rule;
+
+	HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
+	locked = 1;
+
 	dns_link_resolution(s, OBJ_TYPE_STREAM, 0);
 
 	/* Check if there is a fresh enough response in the cache of our associated resolution */
 	req = s->dns_ctx.dns_requester;
-	if (!req || !req->resolution) {
-		dns_trigger_resolution(s->dns_ctx.dns_requester);
-		return ACT_RET_YIELD;
-	}
-	res       = req->resolution;
-	resolvers = res->resolvers;
+	if (!req || !req->resolution)
+		goto release_requester; /* on error, ignore the action */
+	res = req->resolution;
 
 	exp = tick_add(res->last_resolution, resolvers->hold.valid);
 	if (resolvers->t && res->status == RSLV_STATUS_VALID && tick_isset(res->last_resolution)
-		       && !tick_is_expired(exp, now_ms)) {
+	    && !tick_is_expired(exp, now_ms)) {
 		goto use_cache;
 	}
 
 	dns_trigger_resolution(s->dns_ctx.dns_requester);
-	return ACT_RET_YIELD;
+
+  yield:
+	if (flags & ACT_FLAG_FINAL)
+		goto release_requester;
+	ret = ACT_RET_YIELD;
+
+  end:
+	if (locked)
+		HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
+	return ret;
+
+  release_requester:
+	free(s->dns_ctx.hostname_dn);
+	s->dns_ctx.hostname_dn = NULL;
+	s->dns_ctx.hostname_dn_len = 0;
+	if (s->dns_ctx.dns_requester) {
+		dns_unlink_resolution(s->dns_ctx.dns_requester);
+		pool_free(dns_requester_pool, s->dns_ctx.dns_requester);
+		s->dns_ctx.dns_requester = NULL;
+	}
+	goto end;
 }
 
 
diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c
index aea2ab7..56e7147 100644
--- a/src/ev_kqueue.c
+++ b/src/ev_kqueue.c
@@ -180,7 +180,7 @@
 		}
 
 		if (kev[count].filter ==  EVFILT_READ) {
-			if (kev[count].data)
+			if (kev[count].data || !(kev[count].flags & EV_EOF))
 				n |= FD_POLL_IN;
 			if (kev[count].flags & EV_EOF)
 				n |= FD_POLL_HUP;
diff --git a/src/ev_poll.c b/src/ev_poll.c
index 54812f5..86bf8e8 100644
--- a/src/ev_poll.c
+++ b/src/ev_poll.c
@@ -284,7 +284,6 @@
  fail_swevt:
 	free(fd_evts[DIR_RD]);
  fail_srevt:
-	free(poll_events);
 	p->pref = 0;
 	return 0;
 }
diff --git a/src/ev_select.c b/src/ev_select.c
index 0ccf2f1..378d1e4 100644
--- a/src/ev_select.c
+++ b/src/ev_select.c
@@ -237,13 +237,12 @@
  */
 REGPRM1 static int _do_init(struct poller *p)
 {
-	__label__ fail_swevt, fail_srevt, fail_revt;
 	int fd_set_bytes;
 
 	p->private = NULL;
 
 	if (global.maxsock > FD_SETSIZE)
-		goto fail_revt;
+		goto fail_srevt;
 
 	fd_set_bytes = sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE;
 
@@ -260,9 +259,6 @@
  fail_swevt:
 	free(fd_evts[DIR_RD]);
  fail_srevt:
-	free(tmp_evts[DIR_WR]);
-	free(tmp_evts[DIR_RD]);
- fail_revt:
 	p->pref = 0;
 	return 0;
 }
diff --git a/src/fd.c b/src/fd.c
index 7c90691..3d361e8 100644
--- a/src/fd.c
+++ b/src/fd.c
@@ -188,8 +188,8 @@
 
 volatile int ha_used_fds = 0; // Number of FD we're currently using
 
-#define _GET_NEXT(fd, off) ((struct fdlist_entry *)(void *)((char *)(&fdtab[fd]) + off))->next
-#define _GET_PREV(fd, off) ((struct fdlist_entry *)(void *)((char *)(&fdtab[fd]) + off))->prev
+#define _GET_NEXT(fd, off) ((volatile struct fdlist_entry *)(void *)((char *)(&fdtab[fd]) + off))->next
+#define _GET_PREV(fd, off) ((volatile struct fdlist_entry *)(void *)((char *)(&fdtab[fd]) + off))->prev
 /* adds fd <fd> to fd list <list> if it was not yet in it */
 void fd_add_to_fd_list(volatile struct fdlist *list, int fd, int off)
 {
@@ -201,8 +201,10 @@
 redo_next:
 	next = _GET_NEXT(fd, off);
 	/* Check that we're not already in the cache, and if not, lock us. */
-	if (next >= -2)
+	if (next > -2)
 		goto done;
+	if (next == -2)
+		goto redo_next;
 	if (!_HA_ATOMIC_CAS(&_GET_NEXT(fd, off), &next, -2))
 		goto redo_next;
 	__ha_barrier_atomic_store();
@@ -280,7 +282,7 @@
 
 #else
 lock_self_next:
-	next = ({ volatile int *next = &_GET_NEXT(fd, off); *next; });
+	next = _GET_NEXT(fd, off);
 	if (next == -2)
 		goto lock_self_next;
 	if (next <= -3)
@@ -288,7 +290,7 @@
 	if (unlikely(!_HA_ATOMIC_CAS(&_GET_NEXT(fd, off), &next, -2)))
 		goto lock_self_next;
 lock_self_prev:
-	prev = ({ volatile int *prev = &_GET_PREV(fd, off); *prev; });
+	prev = _GET_PREV(fd, off);
 	if (prev == -2)
 		goto lock_self_prev;
 	if (unlikely(!_HA_ATOMIC_CAS(&_GET_PREV(fd, off), &prev, -2)))
diff --git a/src/filters.c b/src/filters.c
index 45da440..3cb78bd 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -783,8 +783,9 @@
 	struct filter *filter;
 	unsigned long long *strm_off = &FLT_STRM_OFF(s, msg->chn);
 	unsigned int out = co_data(msg->chn);
-	int ret = len - out;
+	int ret, data;
 
+	ret = data = len - out;
 	list_for_each_entry(filter, &strm_flt(s)->filters, list) {
 		/* Call "data" filters only */
 		if (!IS_DATA_FILTER(filter, msg->chn))
@@ -793,14 +794,19 @@
 			unsigned long long *flt_off = &FLT_OFF(filter, msg->chn);
 			unsigned int offset = *flt_off - *strm_off;
 
-			ret = FLT_OPS(filter)->http_payload(s, filter, msg, out + offset, ret - offset);
+			ret = FLT_OPS(filter)->http_payload(s, filter, msg, out + offset, data - offset);
 			if (ret < 0)
 				goto end;
+			data = ret + *flt_off - *strm_off;
 			*flt_off += ret;
-			ret += offset;
 		}
 	}
-	*strm_off += ret;
+
+	/* Only forward data if the last filter decides to forward something */
+	if (ret > 0) {
+		ret = data;
+		*strm_off += ret;
+	}
  end:
 	return ret;
 }
@@ -928,8 +934,17 @@
 		}
 	} RESUME_FILTER_END;
 
-	if (IS_HTX_STRM(s))
-		channel_htx_fwd_headers(chn, htxbuf(&chn->buf));
+	if (IS_HTX_STRM(s)) {
+		if (HAS_DATA_FILTERS(s, chn)) {
+			size_t data = http_get_hdrs_size(htxbuf(&chn->buf));
+			struct filter *f;
+
+			list_for_each_entry(f, &strm_flt(s)->filters, list) {
+				if (IS_DATA_FILTER(f, chn))
+					FLT_OFF(f, chn) = data;
+			}
+		}
+	}
 	else {
 		/* We increase next offset of all "data" filters after all processing on
 		 * headers because any filter can alter them. So the definitive size of
diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c
index 37f237f..ac3bd08 100644
--- a/src/flt_http_comp.c
+++ b/src/flt_http_comp.c
@@ -201,19 +201,17 @@
 {
 	struct comp_state *st = filter->ctx;
 	struct htx *htx = htxbuf(&msg->chn->buf);
+	struct htx_ret htxret = htx_find_offset(htx, offset);
 	struct htx_blk *blk;
 	int ret, consumed = 0, to_forward = 0;
 
-	for (blk = htx_get_first_blk(htx); blk && len; blk = htx_get_next_blk(htx, blk)) {
+	blk = htxret.blk;
+	offset = htxret.ret;
+	for (; blk && len; blk = htx_get_next_blk(htx, blk)) {
 		enum htx_blk_type type = htx_get_blk_type(blk);
 		uint32_t sz = htx_get_blksz(blk);
 		struct ist v;
 
-		if (offset >= sz) {
-			offset -= sz;
-			continue;
-		}
-
 		switch (type) {
 			case HTX_BLK_UNUSED:
 				break;
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index 47352aa..0a1eab6 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -269,7 +269,7 @@
 
 	while (byte < 4) {
 		while (bits < 32) {
-			last |= (uint64_t)random() << bits;
+			last |= (uint64_t)ha_random() << bits;
 			bits += rand_max_bits;
 		}
 		rnd[byte++] = last;
@@ -715,7 +715,7 @@
 		}
 
 		/* Check "version" K/V item */
-		if (!memcmp(str, VERSION_KEY, sz)) {
+		if (sz >= strlen(VERSION_KEY) && !memcmp(str, VERSION_KEY, strlen(VERSION_KEY))) {
 			int i, type = *p++;
 
 			/* The value must be a string */
@@ -744,7 +744,7 @@
 			}
 		}
 		/* Check "max-frame-size" K/V item */
-		else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
+		else if (sz >= strlen(MAX_FRAME_SIZE_KEY) && !memcmp(str, MAX_FRAME_SIZE_KEY, strlen(MAX_FRAME_SIZE_KEY))) {
 			int type = *p++;
 
 			/* The value must be integer */
@@ -767,7 +767,7 @@
 			max_frame_size = sz;
 		}
 		/* Check "capabilities" K/V item */
-		else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
+		else if (sz >= strlen(CAPABILITIES_KEY) && !memcmp(str, CAPABILITIES_KEY, strlen(CAPABILITIES_KEY))) {
 			int type = *p++;
 
 			/* The value must be a string */
@@ -901,7 +901,7 @@
 		}
 
 		/* Check "status-code" K/V item */
-		if (!memcmp(str, STATUS_CODE_KEY, sz)) {
+		if (sz >= strlen(STATUS_CODE_KEY) && !memcmp(str, STATUS_CODE_KEY, strlen(STATUS_CODE_KEY))) {
 			int type = *p++;
 
 			/* The value must be an integer */
@@ -920,7 +920,7 @@
 		}
 
 		/* Check "message" K/V item */
-		else if (!memcmp(str, MSG_KEY, sz)) {
+		else if (sz >= strlen(MSG_KEY) && !memcmp(str, MSG_KEY, strlen(MSG_KEY))) {
 			int type = *p++;
 
 			/* The value must be a string */
@@ -2043,7 +2043,7 @@
 
 	/* Check if we need to create a new SPOE applet or not. */
 	if (!eb_is_empty(&agent->rt[tid].idle_applets) &&
-	    agent->rt[tid].processing < read_freq_ctr(&agent->rt[tid].processing_per_sec))
+	    (agent->rt[tid].processing == 1 || agent->rt[tid].processing < read_freq_ctr(&agent->rt[tid].processing_per_sec)))
 		goto end;
 
 	SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
@@ -3119,10 +3119,6 @@
 	struct spoe_config *conf = fconf->conf;
 	struct spoe_agent *agent = conf->agent;
 
-	/* Use a != seed per process */
-	if (relative_pid > 1 && tid == 0)
-		srandom(now_ms * pid);
-
 	agent->rt[tid].engine_id = generate_pseudo_uuid();
 	if (agent->rt[tid].engine_id == NULL)
 		return -1;
@@ -3234,7 +3230,7 @@
 		ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
 	}
 	else {
-		if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
+		if (filter->pre_analyzers & AN_RES_INSPECT)
 			chn->analysers |= AN_RES_INSPECT;
 
 		if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
@@ -3991,6 +3987,12 @@
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
+		if (strcasecmp(args[1], "or") == 0) {
+			ha_warning("parsing [%s:%d] : acl name '%s' will never match. 'or' is used to express a "
+				   "logical disjunction within a condition.\n",
+				   file, linenum, args[1]);
+			err_code |= ERR_WARN;
+		}
 		if (parse_acl((const char **)args + 1, &curmsg->acls, &errmsg, &curproxy->conf.args, file, linenum) == NULL) {
 			ha_alert("parsing [%s:%d] : error detected while parsing ACL '%s' : %s.\n",
 				 file, linenum, args[1], errmsg);
diff --git a/src/flt_trace.c b/src/flt_trace.c
index c0660ac..223a3ff 100644
--- a/src/flt_trace.c
+++ b/src/flt_trace.c
@@ -487,7 +487,7 @@
 			}
 		}
 		if (data)  {
-			ret = random() % (ret+1);
+			ret = ha_random() % (ret+1);
 			if (!ret || ret >= data)
 				ret = len;
 		}
@@ -516,7 +516,7 @@
 	int ret   = avail;
 
 	if (ret && conf->rand_parsing)
-		ret = random() % (ret+1);
+		ret = ha_random() % (ret+1);
 
 	STRM_TRACE(conf, s, "%-25s: channel=%-10s - mode=%-5s (%s) - "
 		   "chunk_len=%llu - next=%u - fwd=%u - avail=%d - consume=%d",
@@ -582,7 +582,7 @@
 	int                  ret  = len;
 
 	if (ret && conf->rand_forwarding)
-		ret = random() % (ret+1);
+		ret = ha_random() % (ret+1);
 
 	STRM_TRACE(conf, s, "%-25s: channel=%-10s - mode=%-5s (%s) - "
 		   "len=%u - nxt=%u - fwd=%u - forward=%d",
@@ -613,7 +613,7 @@
 	int                  ret  = avail;
 
 	if (ret && conf->rand_parsing)
-		ret = random() % (ret+1);
+		ret = ha_random() % (ret+1);
 
 	STRM_TRACE(conf, s, "%-25s: channel=%-10s - mode=%-5s (%s) - next=%u - avail=%u - consume=%d",
 		   __FUNCTION__,
@@ -633,7 +633,7 @@
 	int                  ret  = len;
 
 	if (ret && conf->rand_forwarding)
-		ret = random() % (ret+1);
+		ret = ha_random() % (ret+1);
 
 	STRM_TRACE(conf, s, "%-25s: channel=%-10s - mode=%-5s (%s) - len=%u - fwd=%u - forward=%d",
 		   __FUNCTION__,
diff --git a/src/h1.c b/src/h1.c
index 5868e02..3839fbe 100644
--- a/src/h1.c
+++ b/src/h1.c
@@ -818,6 +818,7 @@
 
 					if (ret < 0) {
 						state = H1_MSG_HDR_L2_LWS;
+						ptr = v.ptr; /* Set ptr on the error */
 						goto http_msg_invalid;
 					}
 					else if (ret == 0) {
diff --git a/src/haproxy.c b/src/haproxy.c
index 1d4771e..bea6db6 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1,6 +1,6 @@
 /*
  * HA-Proxy : High Availability-enabled HTTP/TCP proxy
- * Copyright 2000-2019 Willy Tarreau <willy@haproxy.org>.
+ * Copyright 2000-2020 Willy Tarreau <willy@haproxy.org>.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -72,6 +72,8 @@
 #include <systemd/sd-daemon.h>
 #endif
 
+#include <import/sha1.h>
+
 #include <common/base64.h>
 #include <common/cfgparse.h>
 #include <common/chunk.h>
@@ -83,6 +85,7 @@
 #include <common/memory.h>
 #include <common/mini-clist.h>
 #include <common/namespace.h>
+#include <common/net_helper.h>
 #include <common/openssl-compat.h>
 #include <common/regex.h>
 #include <common/standard.h>
@@ -104,6 +107,7 @@
 #include <proto/auth.h>
 #include <proto/backend.h>
 #include <proto/channel.h>
+#include <proto/checks.h>
 #include <proto/cli.h>
 #include <proto/connection.h>
 #include <proto/fd.h>
@@ -138,7 +142,9 @@
 unsigned long pid_bit = 1;      /* bit corresponding to the process id */
 unsigned long all_proc_mask = 1; /* mask of all processes */
 
-volatile unsigned long sleeping_thread_mask; /* Threads that are about to sleep in poll() */
+volatile unsigned long sleeping_thread_mask = 0; /* Threads that are about to sleep in poll() */
+volatile unsigned long stopping_thread_mask = 0; /* Threads acknowledged stopping */
+
 /* global options */
 struct global global = {
 	.hard_stop_after = TICK_ETERNITY,
@@ -220,7 +226,7 @@
  */
 int shut_your_big_mouth_gcc_int = 0;
 
-static char **next_argv = NULL;
+static char **old_argv = NULL; /* previous argv but cleaned up */
 
 struct list proc_list = LIST_HEAD_INIT(proc_list);
 
@@ -228,6 +234,9 @@
 unsigned int rlim_fd_cur_at_boot = 0;
 unsigned int rlim_fd_max_at_boot = 0;
 
+/* per-boot randomness */
+unsigned char boot_seed[20];        /* per-boot random seed (160 bits initially) */
+
 struct mworker_proc *proc_self = NULL;
 
 /* list of the temporarily limited listeners because of lack of resource */
@@ -614,7 +623,10 @@
  */
 void mworker_reload()
 {
+	char **next_argv = NULL;
+	int old_argc = 0; /* previous number of argument */
 	int next_argc = 0;
+	int i = 0;
 	char *msg = NULL;
 	struct rlimit limit;
 	struct per_thread_deinit_fct *ptdf;
@@ -658,14 +670,19 @@
 	}
 
 	/* compute length  */
-	while (next_argv[next_argc])
-		next_argc++;
+	while (old_argv[old_argc])
+		old_argc++;
 
 	/* 1 for haproxy -sf, 2 for -x /socket */
-	next_argv = realloc(next_argv, (next_argc + 1 + 2 + mworker_child_nb() + nb_oldpids + 1) * sizeof(char *));
+	next_argv = calloc(old_argc + 1 + 2 + mworker_child_nb() + nb_oldpids + 1, sizeof(char *));
 	if (next_argv == NULL)
 		goto alloc_error;
 
+	/* copy the program name */
+	next_argv[next_argc++] = old_argv[0];
+
+	/* insert the new options just after argv[0] in case we have a -- */
+
 	/* add -sf <PID>*  to argv */
 	if (mworker_child_nb() > 0) {
 		struct mworker_proc *child;
@@ -675,32 +692,33 @@
 		list_for_each_entry(child, &proc_list, list) {
 			if (!(child->options & (PROC_O_TYPE_WORKER|PROC_O_TYPE_PROG)) || child->pid <= -1 )
 				continue;
-			next_argv[next_argc] = memprintf(&msg, "%d", child->pid);
-			if (next_argv[next_argc] == NULL)
+			if ((next_argv[next_argc++] = memprintf(&msg, "%d", child->pid)) == NULL)
 				goto alloc_error;
 			msg = NULL;
-			next_argc++;
 		}
 	}
-
-	next_argv[next_argc] = NULL;
-
 	/* add the -x option with the stat socket */
 	if (cur_unixsocket) {
-
 		next_argv[next_argc++] = "-x";
 		next_argv[next_argc++] = (char *)cur_unixsocket;
-		next_argv[next_argc++] = NULL;
 	}
 
+	/* copy the previous options */
+	for (i = 1; i < old_argc; i++)
+		next_argv[next_argc++] = old_argv[i];
+
 	ha_warning("Reexecuting Master process\n");
 	signal(SIGPROF, SIG_IGN);
 	execvp(next_argv[0], next_argv);
 
 	ha_warning("Failed to reexecute the master process [%d]: %s\n", pid, strerror(errno));
+	free(next_argv);
+	next_argv = NULL;
 	return;
 
 alloc_error:
+	free(next_argv);
+	next_argv = NULL;
 	ha_warning("Failed to reexecute the master process [%d]: Cannot allocate memory\n", pid);
 	return;
 }
@@ -717,12 +735,16 @@
 
 	master = 1;
 
+	signal_unregister(SIGTTIN);
+	signal_unregister(SIGTTOU);
 	signal_unregister(SIGUSR1);
 	signal_unregister(SIGHUP);
 	signal_unregister(SIGQUIT);
 
 	signal_register_fct(SIGTERM, mworker_catch_sigterm, SIGTERM);
 	signal_register_fct(SIGUSR1, mworker_catch_sigterm, SIGUSR1);
+	signal_register_fct(SIGTTIN, mworker_broadcast_signal, SIGTTIN);
+	signal_register_fct(SIGTTOU, mworker_broadcast_signal, SIGTTOU);
 	signal_register_fct(SIGINT, mworker_catch_sigterm, SIGINT);
 	signal_register_fct(SIGHUP, mworker_catch_sighup, SIGHUP);
 	signal_register_fct(SIGUSR2, mworker_catch_sighup, SIGUSR2);
@@ -1006,7 +1028,7 @@
 			   unixsocket);
 		goto out;
 	}
-	strncpy(addr.sun_path, unixsocket, sizeof(addr.sun_path));
+	strncpy(addr.sun_path, unixsocket, sizeof(addr.sun_path) - 1);
 	addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
 	addr.sun_family = PF_UNIX;
 	ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
@@ -1189,37 +1211,194 @@
 
 /*
  * copy and cleanup the current argv
- * Remove the -sf /-st parameters
+ * Remove the -sf /-st / -x parameters
  * Return an allocated copy of argv
  */
 
 static char **copy_argv(int argc, char **argv)
 {
-	char **newargv;
-	int i = 0, j = 0;
+	char **newargv, **retargv;
 
 	newargv = calloc(argc + 2, sizeof(char *));
 	if (newargv == NULL) {
 		ha_warning("Cannot allocate memory\n");
 		return NULL;
 	}
+	retargv = newargv;
+
+	/* first copy argv[0] */
+	*newargv++ = *argv++;
+	argc--;
+
+	while (argc > 0) {
+		if (**argv != '-') {
+			/* non options are copied but will fail in the argument parser */
+			*newargv++ = *argv++;
+			argc--;
+
+		} else  {
+			char *flag;
+
+			flag = *argv + 1;
+
+			if (flag[0] == '-' && flag[1] == 0) {
+				/* "--\0" copy every arguments till the end of argv */
+				*newargv++ = *argv++;
+				argc--;
+
+				while (argc > 0) {
+					*newargv++ = *argv++;
+					argc--;
+				}
+			} else {
+				switch (*flag) {
+					case 's':
+						/* -sf / -st and their parameters are ignored */
+						if (flag[1] == 'f' || flag[1] == 't') {
+							argc--;
+							argv++;
+							/* The list can't contain a negative value since the only
+							way to know the end of this list is by looking for the
+							next option or the end of the options */
+							while (argc > 0 && argv[0][0] != '-') {
+								argc--;
+								argv++;
+							}
+						}
+						break;
+
+					case 'x':
+						/* this option and its parameter are ignored */
+						argc--;
+						argv++;
+						if (argc > 0) {
+							argc--;
+							argv++;
+						}
+						break;
 
-	while (i < argc) {
-		/* -sf or -st or -x */
-		if (i > 0 && argv[i][0] == '-' &&
-		    ((argv[i][1] == 's' && (argv[i][2] == 'f' || argv[i][2] == 't')) || argv[i][1] == 'x' )) {
-			/* list of pids to finish ('f') or terminate ('t') or unix socket (-x) */
-			i++;
-			while (i < argc && argv[i][0] != '-') {
-				i++;
+					case 'C':
+					case 'n':
+					case 'm':
+					case 'N':
+					case 'L':
+					case 'f':
+					case 'p':
+					case 'S':
+						/* these options have only 1 parameter which must be copied and can start with a '-' */
+						*newargv++ = *argv++;
+						argc--;
+						if (argc == 0)
+							goto error;
+						*newargv++ = *argv++;
+						argc--;
+						break;
+					default:
+						/* for other options just copy them without parameters, this is also done
+						 * for options like "--foo", but this  will fail in the argument parser.
+						 * */
+						*newargv++ = *argv++;
+						argc--;
+						break;
+				}
 			}
-			continue;
 		}
+	}
+
+	return retargv;
+
+error:
+	free(retargv);
+	return NULL;
+}
+
+
+/* Performs basic random seed initialization. The main issue with this is that
+ * srandom_r() only takes 32 bits and purposely provides a reproducible sequence,
+ * which means that there will only be 4 billion possible random sequences once
+ * srandom() is called, regardless of the internal state. Not calling it is
+ * even worse as we'll always produce the same randoms sequences. What we do
+ * here is to create an initial sequence from various entropy sources, hash it
+ * using SHA1 and keep the resulting 160 bits available globally.
+ *
+ * We initialize the current process with the first 32 bits before starting the
+ * polling loop, where all this will be changed to have process specific and
+ * thread specific sequences.
+ *
+ * Before starting threads, it's still possible to call random() as srandom()
+ * is initialized from this, but after threads and/or processes are started,
+ * only ha_random() is expected to be used to guarantee distinct sequences.
+ */
+static void ha_random_boot(char *const *argv)
+{
+	unsigned char message[256];
+	unsigned char *m = message;
+	struct timeval tv;
+	blk_SHA_CTX ctx;
+	unsigned long l;
+	int fd;
+	int i;
 
-		newargv[j++] = argv[i++];
+	/* start with current time as pseudo-random seed */
+	gettimeofday(&tv, NULL);
+	write_u32(m, tv.tv_sec);  m += 4;
+	write_u32(m, tv.tv_usec); m += 4;
+
+	/* PID and PPID add some OS-based randomness */
+	write_u16(m, getpid());   m += 2;
+	write_u16(m, getppid());  m += 2;
+
+	/* take up to 160 bits bytes from /dev/urandom if available (non-blocking) */
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd >= 0) {
+		i = read(fd, m, 20);
+		if (i > 0)
+			m += i;
+		close(fd);
+	}
+
+	/* take up to 160 bits bytes from openssl (non-blocking) */
+#ifdef USE_OPENSSL
+	if (RAND_bytes(m, 20) == 1)
+		m += 20;
+#endif
+
+	/* take 160 bits from existing random in case it was already initialized */
+	for (i = 0; i < 5; i++) {
+		write_u32(m, random());
+		m += 4;
 	}
 
+	/* stack address (benefit form operating system's ASLR) */
+	l = (unsigned long)&m;
+	memcpy(m, &l, sizeof(l)); m += sizeof(l);
+
-	return newargv;
+	/* argv address (benefit form operating system's ASLR) */
+	l = (unsigned long)&argv;
+	memcpy(m, &l, sizeof(l)); m += sizeof(l);
+
+	/* use tv_usec again after all the operations above */
+	gettimeofday(&tv, NULL);
+	write_u32(m, tv.tv_usec); m += 4;
+
+	/*
+	 * At this point, ~84-92 bytes have been used
+	 */
+
+	/* finish with the hostname */
+	strncpy((char *)m, hostname, message + sizeof(message) - m);
+	m += strlen(hostname);
+
+	/* total message length */
+	l = m - message;
+
+	memset(&ctx, 0, sizeof(ctx));
+	blk_SHA1_Init(&ctx);
+	blk_SHA1_Update(&ctx, message, l);
+	blk_SHA1_Final(boot_seed, &ctx);
+
+	srandom(read_u32(boot_seed));
+	ha_random_seed(boot_seed, sizeof(boot_seed));
 }
 
 /* considers splicing proxies' maxconn, computes the ideal global.maxpipes
@@ -1283,7 +1462,7 @@
 	int ssl_sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
 	int engine_fds = global.ssl_used_async_engines * ssl_sides;
 	int pipes = compute_ideal_maxpipes();
-	int remain = rlim_fd_cur_at_boot;
+	int remain = MAX(rlim_fd_cur_at_boot, rlim_fd_max_at_boot);
 	int maxconn;
 
 	/* we have to take into account these elements :
@@ -1345,7 +1524,11 @@
 	int ideal_maxconn;
 
 	global.mode = MODE_STARTING;
-	next_argv = copy_argv(argc, argv);
+	old_argv = copy_argv(argc, argv);
+	if (!old_argv) {
+		ha_alert("failed to copy argv.\n");
+		exit(1);
+	}
 
 	if (!init_trash_buffers(1)) {
 		ha_alert("failed to initialize trash buffers.\n");
@@ -1362,6 +1545,10 @@
 	memcpy(localpeer, hostname, (sizeof(hostname) > sizeof(localpeer) ? sizeof(localpeer) : sizeof(hostname)) - 1);
 	setenv("HAPROXY_LOCALPEER", localpeer, 1);
 
+	/* we were in mworker mode, we should restart in mworker mode */
+	if (getenv("HAPROXY_MWORKER_REEXEC") != NULL)
+		global.mode |= MODE_MWORKER;
+
 	/*
 	 * Initialize the previously static variables.
 	 */
@@ -1378,7 +1565,7 @@
 	tv_update_date(-1,-1);
 	start_date = now;
 
-	srandom(now_ms - getpid());
+	ha_random_boot(argv);
 
 	if (init_acl() != 0)
 		exit(1);
@@ -1499,7 +1686,7 @@
 			else if (*flag == 'q')
 				arg_mode |= MODE_QUIET;
 			else if (*flag == 'x') {
-				if (argc <= 1 || argv[1][0] == '-') {
+				if (argc <= 1) {
 					ha_alert("Unix socket path expected with the -x flag\n\n");
 					usage(progname);
 				}
@@ -1513,7 +1700,7 @@
 			else if (*flag == 'S') {
 				struct wordlist *c;
 
-				if (argc <= 1 || argv[1][0] == '-') {
+				if (argc <= 1) {
 					ha_alert("Socket and optional bind parameters expected with the -S flag\n");
 					usage(progname);
 				}
@@ -1755,7 +1942,11 @@
 		exit(1);
 	}
 
-	pattern_finalize_config();
+	err_code |= pattern_finalize_config();
+	if (err_code & (ERR_ABORT|ERR_FATAL)) {
+		ha_alert("Failed to finalize pattern config.\n");
+		exit(1);
+	}
 
 	/* recompute the amount of per-process memory depending on nbproc and
 	 * the shared SSL cache size (allowed to exist in all processes).
@@ -2152,14 +2343,14 @@
 	free(cond);
 }
 
-static void deinit_tcp_rules(struct list *rules)
+static void deinit_act_rules(struct list *rules)
 {
-	struct act_rule *trule, *truleb;
+	struct act_rule *rule, *ruleb;
 
-	list_for_each_entry_safe(trule, truleb, rules, list) {
-		LIST_DEL(&trule->list);
-		deinit_acl_cond(trule->cond);
-		free(trule);
+	list_for_each_entry_safe(rule, ruleb, rules, list) {
+		LIST_DEL(&rule->list);
+		deinit_acl_cond(rule->cond);
+		free(rule);
 	}
 }
 
@@ -2202,8 +2393,11 @@
 		free(p->conf.file);
 		free(p->id);
 		free(p->check_req);
+		free(p->check_hdrs);
+		free(p->check_body);
 		free(p->cookie_name);
 		free(p->cookie_domain);
+		free(p->cookie_attrs);
 		free(p->lbprm.arg_str);
 		free(p->capture_name);
 		free(p->monitor_uri);
@@ -2332,9 +2526,12 @@
 			free(lf);
 		}
 
-		deinit_tcp_rules(&p->tcp_req.inspect_rules);
-		deinit_tcp_rules(&p->tcp_rep.inspect_rules);
-		deinit_tcp_rules(&p->tcp_req.l4_rules);
+		deinit_act_rules(&p->tcp_req.inspect_rules);
+		deinit_act_rules(&p->tcp_rep.inspect_rules);
+		deinit_act_rules(&p->tcp_req.l4_rules);
+		deinit_act_rules(&p->tcp_req.l5_rules);
+		deinit_act_rules(&p->http_req_rules);
+		deinit_act_rules(&p->http_res_rules);
 
 		deinit_stick_rules(&p->storersp_rules);
 		deinit_stick_rules(&p->sticking_rules);
@@ -2361,23 +2558,11 @@
 		while (s) {
 			s_next = s->next;
 
-			task_destroy(s->check.task);
-			task_destroy(s->agent.task);
-
-			if (s->check.wait_list.tasklet)
-				tasklet_free(s->check.wait_list.tasklet);
-			if (s->agent.wait_list.tasklet)
-				tasklet_free(s->agent.wait_list.tasklet);
 
 			task_destroy(s->warmup);
 
 			free(s->id);
 			free(s->cookie);
-			free(s->check.bi.area);
-			free(s->check.bo.area);
-			free(s->agent.bi.area);
-			free(s->agent.bo.area);
-			free(s->agent.send_string);
 			free(s->hostname_dn);
 			free((char*)s->conf.file);
 			free(s->idle_conns);
@@ -2385,8 +2570,10 @@
 			free(s->safe_conns);
 			free(s->idle_orphan_conns);
 			free(s->curr_idle_thr);
+			deinit_srv_check(s);
+			deinit_srv_agent_check(s);
 
-			if (s->use_ssl || s->check.use_ssl) {
+			if (s->use_ssl == 1 || s->check.use_ssl == 1 || (s->proxy->options & PR_O_TCPCHK_SSL)) {
 				if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->destroy_srv)
 					xprt_get(XPRT_SSL)->destroy_srv(s);
 			}
@@ -2429,8 +2616,6 @@
 		free(p->desc);
 		free(p->fwdfor_hdr_name);
 
-		free_http_req_rules(&p->http_req_rules);
-		free_http_res_rules(&p->http_res_rules);
 		task_destroy(p->task);
 
 		pool_destroy(p->req_cap_pool);
@@ -2455,7 +2640,7 @@
 		free(uap->desc);
 
 		userlist_free(uap->userlist);
-		free_http_req_rules(&uap->http_req_rules);
+		deinit_act_rules(&uap->http_req_rules);
 
 		free(uap);
 	}
@@ -2523,10 +2708,6 @@
 		/* Check if we can expire some tasks */
 		next = wake_expired_tasks();
 
-		/* stop when there's nothing left to do */
-		if ((jobs - unstoppable_jobs) == 0)
-			break;
-
 		/* also stop  if we failed to cleanly stop all tasks */
 		if (killed > 1)
 			break;
@@ -2549,6 +2730,28 @@
 				wake = 0;
 		}
 
+		if (!wake) {
+			int i;
+
+			if (stopping) {
+				_HA_ATOMIC_OR(&stopping_thread_mask, tid_bit);
+				/* notify all threads that stopping was just set */
+				for (i = 0; i < global.nbthread; i++)
+					if (((all_threads_mask & ~stopping_thread_mask) >> i) & 1)
+						wake_thread(i);
+			}
+
+			/* stop when there's nothing left to do */
+			if ((jobs - unstoppable_jobs) == 0 &&
+			    (stopping_thread_mask & all_threads_mask) == all_threads_mask) {
+				/* wake all threads waiting on jobs==0 */
+				for (i = 0; i < global.nbthread; i++)
+					if (((all_threads_mask & ~tid_bit) >> i) & 1)
+						wake_thread(i);
+				break;
+			}
+		}
+
 		/* The poller will ensure it returns around <next> */
 		cur_poller.poll(&cur_poller, next, wake);
 		if (sleeping_thread_mask & tid_bit)
@@ -3000,8 +3203,10 @@
 					protocol_unbind_all();
 					exit(1); /* there has been an error */
 				}
-				else if (ret == 0) /* child breaks here */
+				else if (ret == 0) { /* child breaks here */
+					ha_random_jump96(relative_pid);
 					break;
+				}
 				if (pidfd >= 0 && !(global.mode & MODE_MWORKER)) {
 					char pidstr[100];
 					snprintf(pidstr, sizeof(pidstr), "%d\n", ret);
diff --git a/src/hash.c b/src/hash.c
index 7045157..8984ef3 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -17,8 +17,9 @@
 #include <common/hash.h>
 
 
-unsigned int hash_wt6(const char *key, int len)
+unsigned int hash_wt6(const void *input, int len)
 {
+	const unsigned char *key = input;
 	unsigned h0 = 0xa53c965aUL;
 	unsigned h1 = 0x5ca6953aUL;
 	unsigned step0 = 6;
@@ -27,7 +28,7 @@
 	for (; len > 0; len--) {
 		unsigned int t;
 
-		t = ((unsigned int)*key);
+		t = *key;
 		key++;
 
 		h0 = ~(h0 ^ t);
@@ -44,8 +45,9 @@
 	return h0 ^ h1;
 }
 
-unsigned int hash_djb2(const char *key, int len)
+unsigned int hash_djb2(const void *input, int len)
 {
+	const unsigned char *key = input;
 	unsigned int hash = 5381;
 
 	/* the hash unrolled eight times */
@@ -72,8 +74,9 @@
 	return hash;
 }
 
-unsigned int hash_sdbm(const char *key, int len)
+unsigned int hash_sdbm(const void *input, int len)
 {
+	const unsigned char *key = input;
 	unsigned int hash = 0;
 	int c;
 
@@ -92,8 +95,9 @@
  * this hash already sustains gigabit speed which is far faster than what
  * we'd ever need. Better preserve the CPU's cache instead.
  */
-unsigned int hash_crc32(const char *key, int len)
+unsigned int hash_crc32(const void *input, int len)
 {
+	const unsigned char *key = input;
 	unsigned int hash;
 	int bit;
 
@@ -174,8 +178,9 @@
   0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
 };
 
-uint32_t hash_crc32c(const char *buf, int len)
+uint32_t hash_crc32c(const void *input, int len)
 {
+	const unsigned char *buf = input;
 	uint32_t crc = 0xffffffff;
 	while (len-- > 0) {
 		crc = (crc >> 8) ^ crctable[(crc ^ (*buf++)) & 0xff];
diff --git a/src/hlua.c b/src/hlua.c
index 5f5b2c3..c2d045e 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -1080,6 +1080,9 @@
  */
 static enum hlua_exec hlua_ctx_resume(struct hlua *lua, int yield_allowed)
 {
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
+	int nres;
+#endif
 	int ret;
 	const char *msg;
 	const char *trace;
@@ -1110,7 +1113,11 @@
 	lua->start_time = now_ms;
 
 	/* Call the function. */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
+	ret = lua_resume(lua->T, gL.T, lua->nargs, &nres);
+#else
 	ret = lua_resume(lua->T, gL.T, lua->nargs);
+#endif
 	switch (ret) {
 
 	case LUA_OK:
@@ -1269,7 +1276,9 @@
 	if (!ref)
 		WILL_LJMP(luaL_error(L, "'del_acl': unknown acl file '%s'", name));
 
+	HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
 	pat_ref_delete(ref, key);
+	HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
 	return 0;
 }
 
@@ -1291,7 +1300,9 @@
 	if (!ref)
 		WILL_LJMP(luaL_error(L, "'del_map': unknown acl file '%s'", name));
 
+	HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
 	pat_ref_delete(ref, key);
+	HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
 	return 0;
 }
 
@@ -1313,8 +1324,10 @@
 	if (!ref)
 		WILL_LJMP(luaL_error(L, "'add_acl': unknown acl file '%s'", name));
 
+	HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
 	if (pat_ref_find_elt(ref, key) == NULL)
 		pat_ref_add(ref, key, NULL, NULL);
+	HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
 	return 0;
 }
 
@@ -1339,10 +1352,12 @@
 	if (!ref)
 		WILL_LJMP(luaL_error(L, "'set_map': unknown map file '%s'", name));
 
+	HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
 	if (pat_ref_find_elt(ref, key) != NULL)
 		pat_ref_set(ref, key, value, NULL);
 	else
 		pat_ref_add(ref, key, value, NULL);
+	HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
 	return 0;
 }
 
@@ -3213,22 +3228,13 @@
 __LJMP static int hlua_channel_is_full(lua_State *L)
 {
 	struct channel *chn;
-	int rem;
 
 	MAY_LJMP(check_args(L, 1, "is_full"));
 	chn = MAY_LJMP(hlua_checkchannel(L, 1));
-
-	if (IS_HTX_STRM(chn_strm(chn))) {
-		struct htx *htx = htxbuf(&chn->buf);
-
-		rem = htx_free_data_space(htx);
-	}
-	else
-		rem = b_room(&chn->buf);
-
-	rem -= global.tune.maxrewrite; /* Rewrite reserved size */
-
-	lua_pushboolean(L, rem <= 0);
+	/* ignore the reserve, we are not on a producer side (ie in an
+	 * applet).
+	 */
+	lua_pushboolean(L, channel_full(chn, 0));
 	return 1;
 }
 
@@ -6861,6 +6867,15 @@
 		s->hlua->max_time = hlua_timeout_session;
 	}
 
+	/* Always reset the analyse expiration timeout for the corresponding
+	 * channel in case the lua script yield, to be sure to not keep an
+	 * expired timeout.
+	 */
+	if (dir == SMP_OPT_DIR_REQ)
+		s->req.analyse_exp = TICK_ETERNITY;
+	else
+		s->res.analyse_exp = TICK_ETERNITY;
+
 	/* Execute the function. */
 	switch (hlua_ctx_resume(s->hlua, !(flags & ACT_FLAG_FINAL))) {
 	/* finished. */
@@ -8270,10 +8285,12 @@
 		memprintf(err, "Lua message handler error: %s\n", lua_tostring(gL.T, -1));
 		lua_pop(gL.T, 1);
 		return -1;
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 503
 	case LUA_ERRGCMM:
 		memprintf(err, "Lua garbage collector error: %s\n", lua_tostring(gL.T, -1));
 		lua_pop(gL.T, 1);
 		return -1;
+#endif
 	default:
 		memprintf(err, "Lua unknonwn error: %s\n", lua_tostring(gL.T, -1));
 		lua_pop(gL.T, 1);
diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c
index 3b71ce1..450d49d 100644
--- a/src/hlua_fcn.c
+++ b/src/hlua_fcn.c
@@ -588,6 +588,12 @@
 			lua_pushinteger(L, read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
 			                t->data_arg[dt].u));
 			break;
+		case STD_T_DICT: {
+			struct dict_entry *de;
+			de = stktable_data_cast(ptr, std_t_dict);
+			lua_pushstring(L, de ? (char *)de->value.key : "-");
+			break;
+		}
 		}
 
 		lua_settable(L, -3);
@@ -611,7 +617,7 @@
 	t = hlua_check_stktable(L, 1);
 	smp.data.type = SMP_T_STR;
 	smp.flags = SMP_F_CONST;
-	smp.data.u.str.area = (char *)luaL_checkstring(L, 2);
+	smp.data.u.str.area = (char *)lua_tolstring(L, 2, &smp.data.u.str.data);
 
 	skey = smp_to_stkey(&smp, t);
 	if (!skey) {
diff --git a/src/hpack-tbl.c b/src/hpack-tbl.c
index 70d7f35..727ff7a 100644
--- a/src/hpack-tbl.c
+++ b/src/hpack-tbl.c
@@ -346,9 +346,9 @@
 	 * room left in the tail to suit the protocol, but tests show that in
 	 * practice it almost never happens in other situations so the extra
 	 * test is useless and we simply fill the headroom as long as it's
-	 * available.
+	 * available and we don't wrap.
 	 */
-	if (headroom >= name.len + value.len) {
+	if (prev == dht->front && headroom >= name.len + value.len) {
 		/* install upfront and update ->front */
 		dht->dte[head].addr = dht->dte[dht->front].addr - (name.len + value.len);
 		dht->front = head;
diff --git a/src/http.c b/src/http.c
index 66a4282..255b487 100644
--- a/src/http.c
+++ b/src/http.c
@@ -221,8 +221,11 @@
 	[HTTP_ERR_200] = 200,  /* used by "monitor-uri" */
 	[HTTP_ERR_400] = 400,
 	[HTTP_ERR_403] = 403,
+	[HTTP_ERR_404] = 404,
 	[HTTP_ERR_405] = 405,
 	[HTTP_ERR_408] = 408,
+	[HTTP_ERR_410] = 410,
+	[HTTP_ERR_413] = 413,
 	[HTTP_ERR_421] = 421,
 	[HTTP_ERR_425] = 425,
 	[HTTP_ERR_429] = 429,
@@ -260,6 +263,15 @@
 	"\r\n"
 	"<html><body><h1>403 Forbidden</h1>\nRequest forbidden by administrative rules.\n</body></html>\n",
 
+	[HTTP_ERR_404] =
+	"HTTP/1.1 404 Not Found\r\n"
+	"Content-length: 83\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>404 Not Found</h1>\nThe resource could not be found.\n</body></html>\n",
+
 	[HTTP_ERR_405] =
 	"HTTP/1.1 405 Method Not Allowed\r\n"
 	"Content-length: 146\r\n"
@@ -278,6 +290,24 @@
 	"\r\n"
 	"<html><body><h1>408 Request Time-out</h1>\nYour browser didn't send a complete request in time.\n</body></html>\n",
 
+	[HTTP_ERR_410] =
+	"HTTP/1.1 410 Gone\r\n"
+	"Content-length: 114\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>410 Gone</h1>\nThe resource is no longer available and will not be available again.\n</body></html>\n",
+
+	[HTTP_ERR_413] =
+	"HTTP/1.1 413 Payload Too Large\r\n"
+	"Content-length: 106\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>413 Payload Too Large</h1>\nThe request entity exceeds the maximum allowed.\n</body></html>\n",
+
 	[HTTP_ERR_421] =
 	"HTTP/1.1 421 Misdirected Request\r\n"
 	"Content-length: 104\r\n"
@@ -382,8 +412,11 @@
 	case 200: return HTTP_ERR_200;
 	case 400: return HTTP_ERR_400;
 	case 403: return HTTP_ERR_403;
+	case 404: return HTTP_ERR_404;
 	case 405: return HTTP_ERR_405;
 	case 408: return HTTP_ERR_408;
+	case 410: return HTTP_ERR_410;
+	case 413: return HTTP_ERR_413;
 	case 421: return HTTP_ERR_421;
 	case 425: return HTTP_ERR_425;
 	case 429: return HTTP_ERR_429;
diff --git a/src/http_act.c b/src/http_act.c
index 0653dd5..4ea2720 100644
--- a/src/http_act.c
+++ b/src/http_act.c
@@ -133,6 +133,8 @@
  * <rule>.arg.act.p[]. It builds a string in the trash from the format string
  * previously filled by function parse_replace_uri() and will execute the regex
  * in p[1] to replace the URI. It uses the format string present in act.p[2..3].
+ * The component to act on (path/uri) is taken from act.p[0] which contains 1
+ * for the path or 3 for the URI (values used by http_req_replace_stline()).
  * It always returns ACT_RET_CONT. If an error occurs, the action is canceled,
  * but the rule processing continues.
  */
@@ -154,6 +156,9 @@
 	else
 		uri = ist2(ci_head(&s->req) + s->txn->req.sl.rq.u, s->txn->req.sl.rq.u_l);
 
+	if (rule->arg.act.p[0] == (void *)1)
+		uri = iststop(http_get_path(uri), '?');
+
 	if (!regex_exec_match2(rule->arg.act.p[1], uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
 		goto leave;
 
@@ -166,8 +171,7 @@
 	if (len == -1)
 		goto leave;
 
-	/* 3 is the set-uri action */
-	http_replace_req_line(3, output->area, len, px, s);
+	http_replace_req_line((long)rule->arg.act.p[0], output->area, len, px, s);
 
 	ret = ACT_RET_CONT;
 
@@ -177,9 +181,9 @@
 	return ret;
 }
 
-/* parse a "replace-uri" http-request action.
+/* parse a "replace-uri" or "replace-path" http-request action.
  * This action takes 2 arguments (a regex and a replacement format string).
- * The resulting rule makes use of arg->act.p[0] to store the action (0 for now),
+ * The resulting rule makes use of arg->act.p[0] to store the action (1/3 for now),
  * p[1] to store the compiled regex, and arg->act.p[2..3] to store the log-format
  * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
  */
@@ -190,7 +194,11 @@
 	char *error = NULL;
 
 	rule->action = ACT_CUSTOM;
-	rule->arg.act.p[0] = (void *)0; // replace-uri
+	if (strcmp(args[cur_arg-1], "replace-path") == 0)
+		rule->arg.act.p[0] = (void *)1; // replace-path
+	else
+		rule->arg.act.p[0] = (void *)3; // replace-uri
+
 	rule->action_ptr = http_action_replace_uri;
 
 	if (!*args[cur_arg] || !*args[cur_arg+1] ||
@@ -277,8 +285,8 @@
 	si_must_kill_conn(chn_prod(&s->req));
 	channel_abort(&s->req);
 	channel_abort(&s->res);
-	s->req.analysers = 0;
-	s->res.analysers = 0;
+	s->req.analysers &= AN_REQ_FLT_END;
+	s->res.analysers &= AN_RES_FLT_END;
 
 	_HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
 	_HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
@@ -420,7 +428,10 @@
 	if (rule->action_ptr != http_action_req_capture_by_id)
 		return 1;
 
-	if (rule->arg.capid.idx >= px->nb_req_cap) {
+	/* capture slots can only be declared in frontends, so we can't check their
+	 * existence in backends at configuration parsing step
+	 */
+	if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
 		memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
 			  rule->arg.capid.idx);
 		return 0;
@@ -602,7 +613,10 @@
 	if (rule->action_ptr != http_action_res_capture_by_id)
 		return 1;
 
-	if (rule->arg.capid.idx >= px->nb_rsp_cap) {
+	/* capture slots can only be declared in frontends, so we can't check their
+	 * existence in backends at configuration parsing step
+	 */
+	if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_rsp_cap) {
 		memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
 			  rule->arg.capid.idx);
 		return 0;
@@ -696,6 +710,7 @@
 		{ "capture",    parse_http_req_capture },
 		{ "reject",     parse_http_action_reject },
 		{ "disable-l7-retry", parse_http_req_disable_l7_retry },
+		{ "replace-path", parse_replace_uri },
 		{ "replace-uri", parse_replace_uri },
 		{ "set-method", parse_set_req_line },
 		{ "set-path",   parse_set_req_line },
diff --git a/src/http_conv.c b/src/http_conv.c
index 93b748c..cef5254 100644
--- a/src/http_conv.c
+++ b/src/http_conv.c
@@ -217,6 +217,7 @@
 /* This fetch url-decode any input string. */
 static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void *private)
 {
+	int in_form = 0;
 	int len;
 
 	/* If the constant flag is set or if not size is available at
@@ -233,7 +234,11 @@
 
 	/* Add final \0 required by url_decode(), and convert the input string. */
 	smp->data.u.str.area[smp->data.u.str.data] = '\0';
-	len = url_decode(smp->data.u.str.area);
+
+	if (args && (args[0].type == ARGT_SINT))
+		in_form = !!args[0].data.sint;
+
+	len = url_decode(smp->data.u.str.area, in_form);
 	if (len < 0)
 		return 0;
 	smp->data.u.str.data = len;
@@ -242,7 +247,7 @@
 
 static int smp_conv_req_capture(const struct arg *args, struct sample *smp, void *private)
 {
-	struct proxy *fe = strm_fe(smp->strm);
+	struct proxy *fe;
 	int idx, i;
 	struct cap_hdr *hdr;
 	int len;
@@ -250,6 +255,10 @@
 	if (!args || args->type != ARGT_SINT)
 		return 0;
 
+	if (!smp->strm)
+		return 0;
+
+	fe = strm_fe(smp->strm);
 	idx = args->data.sint;
 
 	/* Check the availibity of the capture id. */
@@ -283,7 +292,7 @@
 
 static int smp_conv_res_capture(const struct arg *args, struct sample *smp, void *private)
 {
-	struct proxy *fe = strm_fe(smp->strm);
+	struct proxy *fe;
 	int idx, i;
 	struct cap_hdr *hdr;
 	int len;
@@ -291,6 +300,10 @@
 	if (!args || args->type != ARGT_SINT)
 		return 0;
 
+	if (!smp->strm)
+		return 0;
+
+	fe = strm_fe(smp->strm);
 	idx = args->data.sint;
 
 	/* Check the availibity of the capture id. */
@@ -332,7 +345,7 @@
 	{ "language",       sample_conv_q_preferred,  ARG2(1,STR,STR),  NULL,   SMP_T_STR,  SMP_T_STR},
 	{ "capture-req",    smp_conv_req_capture,     ARG1(1,SINT),     NULL,   SMP_T_STR,  SMP_T_STR},
 	{ "capture-res",    smp_conv_res_capture,     ARG1(1,SINT),     NULL,   SMP_T_STR,  SMP_T_STR},
-	{ "url_dec",        sample_conv_url_dec,      0,                NULL,   SMP_T_STR,  SMP_T_STR},
+	{ "url_dec",        sample_conv_url_dec,      ARG1(0,SINT),     NULL,   SMP_T_STR,  SMP_T_STR},
 	{ NULL, NULL, 0, 0, 0 },
 }};
 
diff --git a/src/http_fetch.c b/src/http_fetch.c
index 6448bde..9e6113b 100644
--- a/src/http_fetch.c
+++ b/src/http_fetch.c
@@ -612,14 +612,17 @@
 	if (LIST_ISEMPTY(&smp->sess->fe->format_unique_id))
 		return 0;
 
+	if (!smp->strm)
+		return 0;
+
 	if (!smp->strm->unique_id) {
 		if ((smp->strm->unique_id = pool_alloc(pool_head_uniqueid)) == NULL)
 			return 0;
 		smp->strm->unique_id[0] = '\0';
+		build_logline(smp->strm, smp->strm->unique_id,
+		              UNIQUEID_LEN, &smp->sess->fe->format_unique_id);
 	}
-	smp->data.u.str.data = build_logline(smp->strm, smp->strm->unique_id,
-	                                    UNIQUEID_LEN, &smp->sess->fe->format_unique_id);
-
+	smp->data.u.str.data = strlen(smp->strm->unique_id);
 	smp->data.type = SMP_T_STR;
 	smp->data.u.str.area = smp->strm->unique_id;
 	smp->flags = SMP_F_CONST;
@@ -1560,21 +1563,19 @@
 static int smp_fetch_hdr_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
 	int ret;
+	struct buffer *temp = get_trash_chunk();
 
 	while ((ret = smp_fetch_hdr(args, smp, kw, private)) > 0) {
-		if (url2ipv4((char *) smp->data.u.str.area, &smp->data.u.ipv4)) {
-			smp->data.type = SMP_T_IPV4;
-			break;
-		} else {
-			struct buffer *temp = get_trash_chunk();
-			if (smp->data.u.str.data < temp->size - 1) {
-				memcpy(temp->area, smp->data.u.str.area,
-				       smp->data.u.str.data);
-				temp->area[smp->data.u.str.data] = '\0';
-				if (inet_pton(AF_INET6, temp->area, &smp->data.u.ipv6)) {
-					smp->data.type = SMP_T_IPV6;
-					break;
-				}
+		if (smp->data.u.str.data < temp->size - 1) {
+			memcpy(temp->area, smp->data.u.str.area,
+			       smp->data.u.str.data);
+			temp->area[smp->data.u.str.data] = '\0';
+			if (url2ipv4((char *) temp->area, &smp->data.u.ipv4)) {
+				smp->data.type = SMP_T_IPV4;
+				break;
+			} else if (inet_pton(AF_INET6, temp->area, &smp->data.u.ipv6)) {
+				smp->data.type = SMP_T_IPV6;
+				break;
 			}
 		}
 
@@ -1928,6 +1929,9 @@
 /* return a valid test if the current request is the first one on the connection */
 static int smp_fetch_http_first_req(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	if (!smp->strm)
+		return 0;
+
 	smp->data.type = SMP_T_BOOL;
 	smp->data.u.sint = !(smp->strm->txn->flags & TX_NOT_FIRST);
 	return 1;
@@ -2011,12 +2015,16 @@
  */
 static int smp_fetch_capture_req_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct proxy *fe = strm_fe(smp->strm);
+	struct proxy *fe;
 	int idx;
 
 	if (!args || args->type != ARGT_SINT)
 		return 0;
 
+	if (!smp->strm)
+		return 0;
+
+	fe = strm_fe(smp->strm);
 	idx = args->data.sint;
 
 	if (idx > (fe->nb_req_cap - 1) || smp->strm->req_cap == NULL || smp->strm->req_cap[idx] == NULL)
@@ -2035,12 +2043,16 @@
  */
 static int smp_fetch_capture_res_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct proxy *fe = strm_fe(smp->strm);
+	struct proxy *fe;
 	int idx;
 
 	if (!args || args->type != ARGT_SINT)
 		return 0;
 
+	if (!smp->strm)
+		return 0;
+
+	fe = strm_fe(smp->strm);
 	idx = args->data.sint;
 
 	if (idx > (fe->nb_rsp_cap - 1) || smp->strm->res_cap == NULL || smp->strm->res_cap[idx] == NULL)
@@ -2058,9 +2070,13 @@
 static int smp_fetch_capture_req_method(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
 	struct buffer *temp;
-	struct http_txn *txn = smp->strm->txn;
+	struct http_txn *txn;
 	char *ptr;
 
+	if (!smp->strm)
+		return 0;
+
+	txn = smp->strm->txn;
 	if (!txn || !txn->uri)
 		return 0;
 
@@ -2083,10 +2099,14 @@
 /* Extracts the path in the HTTP request, the txn->uri should be filled before the call  */
 static int smp_fetch_capture_req_uri(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct http_txn *txn = smp->strm->txn;
+	struct http_txn *txn;
 	struct ist path;
 	const char *ptr;
 
+	if (!smp->strm)
+		return 0;
+
+	txn = smp->strm->txn;
 	if (!txn || !txn->uri)
 		return 0;
 
@@ -2121,8 +2141,12 @@
  */
 static int smp_fetch_capture_req_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct http_txn *txn = smp->strm->txn;
+	struct http_txn *txn;
+
+	if (!smp->strm)
+		return 0;
 
+	txn = smp->strm->txn;
 	if (!txn || txn->req.msg_state < HTTP_MSG_HDR_FIRST)
 		return 0;
 
@@ -2143,8 +2167,12 @@
  */
 static int smp_fetch_capture_res_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct http_txn *txn = smp->strm->txn;
+	struct http_txn *txn;
+
+	if (!smp->strm)
+		return 0;
 
+	txn = smp->strm->txn;
 	if (!txn || txn->rsp.msg_state < HTTP_MSG_HDR_FIRST)
 		return 0;
 
diff --git a/src/http_htx.c b/src/http_htx.c
index e537936..98d594b 100644
--- a/src/http_htx.c
+++ b/src/http_htx.c
@@ -37,6 +37,24 @@
 	return htx_get_blk_ptr(htx, blk);
 }
 
+/* Returns the headers size in the HTX message */
+size_t http_get_hdrs_size(struct htx *htx)
+{
+	struct htx_blk *blk;
+	size_t sz = 0;
+
+	blk = htx_get_first_blk(htx);
+	if (!blk || htx_get_blk_type(blk) > HTX_BLK_EOH)
+		return sz;
+
+	for (; blk; blk = htx_get_next_blk(htx, blk)) {
+		sz += htx_get_blksz(blk);
+		if (htx_get_blk_type(blk) == HTX_BLK_EOH)
+			break;
+	}
+	return sz;
+}
+
 /* Finds the first or next occurrence of header <name> in the HTX message <htx>
  * using the context <ctx>. This structure holds everything necessary to use the
  * header and find next occurrence. If its <blk> member is NULL, the header is
@@ -109,8 +127,6 @@
 			v.len--;
 			ctx->lws_after++;
 		}
-		if (!v.len)
-			continue;
 		ctx->blk   = blk;
 		ctx->value = v;
 		return 1;
diff --git a/src/http_rules.c b/src/http_rules.c
index 5089200..2ac11fe 100644
--- a/src/http_rules.c
+++ b/src/http_rules.c
@@ -1037,7 +1037,7 @@
 	const char *destination = NULL;
 	const char *cookie = NULL;
 	int cookie_set = 0;
-	unsigned int flags = REDIRECT_FLAG_NONE;
+	unsigned int flags = (!dir ? REDIRECT_FLAG_FROM_REQ : REDIRECT_FLAG_NONE);
 	struct acl_cond *cond = NULL;
 
 	cur_arg = 0;
@@ -1186,31 +1186,6 @@
 	return NULL;
 }
 
-void free_http_res_rules(struct list *r)
-{
-	struct act_rule *tr, *pr;
-
-	list_for_each_entry_safe(pr, tr, r, list) {
-		LIST_DEL(&pr->list);
-		regex_free(pr->arg.hdr_add.re);
-		free(pr);
-	}
-}
-
-void free_http_req_rules(struct list *r)
-{
-	struct act_rule *tr, *pr;
-
-	list_for_each_entry_safe(pr, tr, r, list) {
-		LIST_DEL(&pr->list);
-		if (pr->action == ACT_HTTP_REQ_AUTH)
-			free(pr->arg.auth.realm);
-
-		regex_free(pr->arg.hdr_add.re);
-		free(pr);
-	}
-}
-
 __attribute__((constructor))
 static void __http_rules_init(void)
 {
diff --git a/src/htx.c b/src/htx.c
index ed28deb..cb389c2 100644
--- a/src/htx.c
+++ b/src/htx.c
@@ -397,6 +397,31 @@
 	return blk;
 }
 
+/* Looks for the HTX block containing the offset <offset>, starting at the HTX
+ * message's head. The function returns an htx_ret with the found HTX block and
+ * the position inside this block where the offset is. If the offset <offset> is
+ * ouside of the HTX message, htx_ret.blk is set to NULL.
+ */
+struct htx_ret htx_find_offset(struct htx *htx, uint32_t offset)
+{
+	struct htx_blk *blk;
+	struct htx_ret htxret = { .blk = NULL, .ret = 0 };
+
+	if (offset >= htx->data)
+		return htxret;
+
+	for (blk = htx_get_head_blk(htx); blk && offset; blk = htx_get_next_blk(htx, blk)) {
+		uint32_t sz = htx_get_blksz(blk);
+
+		if (offset < sz)
+			break;
+		offset -= sz;
+	}
+	htxret.blk = blk;
+	htxret.ret = offset;
+	return htxret;
+}
+
 /* Removes all blocks after the one containing the offset <offset>. This last
  * one may be truncated if it is a DATA block.
  */
diff --git a/src/listener.c b/src/listener.c
index aceb4cd..b68eb8c 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -386,7 +386,7 @@
 		/* we're not allowed to touch this listener's FD, let's requeue
 		 * the listener into one of its owning thread's queue instead.
 		 */
-		int first_thread = my_flsl(thread_mask(l->bind_conf->bind_thread)) - 1;
+		int first_thread = my_flsl(thread_mask(l->bind_conf->bind_thread) & all_threads_mask) - 1;
 		work_list_add(&local_listener_queue[first_thread], &l->wait_queue);
 		goto end;
 	}
@@ -793,16 +793,15 @@
 		if (unlikely(cfd == -1)) {
 			switch (errno) {
 			case EAGAIN:
-				if (fdtab[fd].ev & FD_POLL_HUP) {
+				if (fdtab[fd].ev & (FD_POLL_HUP|FD_POLL_ERR)) {
 					/* the listening socket might have been disabled in a shared
 					 * process and we're a collateral victim. We'll just pause for
 					 * a while in case it comes back. In the mean time, we need to
 					 * clear this sticky flag.
 					 */
-					fdtab[fd].ev &= ~FD_POLL_HUP;
+					_HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
 					goto transient_error;
 				}
-				fd_cant_recv(fd);
 				goto end; /* nothing more to accept */
 			case EINVAL:
 				/* might be trying to accept on a shut fd (eg: soft stop) */
@@ -836,7 +835,8 @@
 				goto transient_error;
 			default:
 				/* unexpected result, let's give up and let other tasks run */
-				goto stop;
+				max_accept = 0;
+				goto end;
 			}
 		}
 
@@ -881,7 +881,7 @@
 
 #if defined(USE_THREAD)
 		mask = thread_mask(l->bind_conf->bind_thread) & all_threads_mask;
-		if (atleast2(mask) && (global.tune.options & GTUNE_LISTENER_MQ)) {
+		if (atleast2(mask) && (global.tune.options & GTUNE_LISTENER_MQ) && !stopping) {
 			struct accept_queue_ring *ring;
 			unsigned int t, t0, t1, t2;
 
@@ -1021,18 +1021,18 @@
 		}
 #endif
 
+		ti->flags &= ~TI_FL_STUCK; // this thread is still running
 	} /* end of for (max_accept--) */
 
 	/* we've exhausted max_accept, so there is no need to poll again */
- stop:
-	fd_done_recv(fd);
 	goto end;
 
  transient_error:
-	/* pause the listener and try again in 100 ms */
+	/* pause the listener for up to 100 ms */
 	expire = tick_add(now_ms, 100);
 
  wait_expire:
+	/* switch the listener to LI_LIMITED and wait until up to <expire> in the global queue */
 	limit_listener(l, &global_listener_queue);
 	task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
  end:
@@ -1046,7 +1046,10 @@
 		_HA_ATOMIC_SUB(&actconn, 1);
 
 	if ((l->state == LI_FULL && (!l->maxconn || l->nbconn < l->maxconn)) ||
-	    (l->state == LI_LIMITED && ((!p || p->feconn < p->maxconn) && (actconn < global.maxconn)))) {
+	    (l->state == LI_LIMITED &&
+	     ((!p || p->feconn < p->maxconn) && (actconn < global.maxconn) &&
+	      (!tick_isset(global_listener_queue_task->expire) ||
+	       tick_is_expired(global_listener_queue_task->expire, now_ms))))) {
 		/* at least one thread has to this when quitting */
 		resume_listener(l);
 
@@ -1058,6 +1061,25 @@
 		    (!p->fe_sps_lim || freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0) > 0))
 			dequeue_all_listeners(&p->listener_queue);
 	}
+
+	/* Now it's getting tricky. The listener was supposed to be in LI_READY
+	 * state but in the mean time we might have changed it to LI_FULL or
+	 * LI_LIMITED, and another thread might also have turned it to
+	 * LI_PAUSED, LI_LISTEN or even LI_INI when stopping a proxy. We must
+	 * be certain to keep the FD enabled when in the READY state but we
+	 * must also stop it for other states that we might have switched to
+	 * while others re-enabled polling.
+	 */
+	HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
+	if (l->state == LI_READY) {
+		if (max_accept > 0)
+			fd_cant_recv(fd);
+		else
+			fd_done_recv(fd);
+	} else if (l->state > LI_ASSIGNED) {
+		fd_stop_recv(l->fd);
+	}
+	HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
 }
 
 /* Notify the listener that a connection initiated from it was released. This
@@ -1205,6 +1227,18 @@
 	smp->data.u.sint = smp->sess->listener->luid;
 	return 1;
 }
+static int
+smp_fetch_so_name(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+	smp->data.u.str.area = smp->sess->listener->name;
+	if (!smp->data.u.str.area)
+		return 0;
+
+	smp->data.type = SMP_T_STR;
+	smp->flags = SMP_F_CONST;
+	smp->data.u.str.data = strlen(smp->data.u.str.area);
+	return 1;
+}
 
 /* parse the "accept-proxy" bind keyword */
 static int bind_parse_accept_proxy(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
@@ -1441,6 +1475,7 @@
 static struct sample_fetch_kw_list smp_kws = {ILH, {
 	{ "dst_conn", smp_fetch_dconn, 0, NULL, SMP_T_SINT, SMP_USE_FTEND, },
 	{ "so_id",    smp_fetch_so_id, 0, NULL, SMP_T_SINT, SMP_USE_FTEND, },
+	{ "so_name",  smp_fetch_so_name, 0, NULL, SMP_T_STR, SMP_USE_FTEND, },
 	{ /* END */ },
 }};
 
diff --git a/src/log.c b/src/log.c
index 7746e9f..53ec4bb 100644
--- a/src/log.c
+++ b/src/log.c
@@ -615,7 +615,7 @@
 				sp = str - 1; /* send both the '%' and the current char */
 				memprintf(err, "unexpected variable name near '%c' at position %d line : '%s'. Maybe you want to write a single '%%', use the syntax '%%%%'",
 				          *str, (int)(str - backfmt), fmt);
-				return 0;
+				goto fail;
 
 			}
 			else
@@ -642,7 +642,7 @@
 				break;
 			}
 			memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
-			return 0;
+			goto fail;
 
 		case LF_STEXPR:                        // text immediately following '%['
 			if (*str == ']') {             // end of arg
@@ -675,16 +675,16 @@
 			switch (pformat) {
 			case LF_VAR:
 				if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
-					return 0;
+					goto fail;
 				break;
 			case LF_STEXPR:
 				if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err))
-					return 0;
+					goto fail;
 				break;
 			case LF_TEXT:
 			case LF_SEPARATOR:
 				if (!add_to_logformat_list(sp, str, pformat, list_format, err))
-					return 0;
+					goto fail;
 				break;
 			}
 			sp = str; /* new start of text at every state switch and at every separator */
@@ -693,11 +693,14 @@
 
 	if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
 		memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
-		return 0;
+		goto fail;
 	}
 	free(backfmt);
 
 	return 1;
+ fail:
+	free(backfmt);
+	return 0;
 }
 
 /*
@@ -1565,6 +1568,12 @@
 
 	dataptr = message;
 
+	/* historically some messages used to already contain the trailing LF
+	 * or Zero. Let's remove all trailing LF or Zero
+	 */
+	while (size && ((dataptr[size-1] == '\n' || (dataptr[size-1] == 0))))
+		size--;
+
 	if (logsrv->addr.ss_family == AF_UNSPEC) {
 		/* the socket's address is a file descriptor */
 		plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
@@ -1624,7 +1633,7 @@
 		hdr_ptr = hdr;
 		hdr_max = 3;
 		maxlen = logsrv->maxlen - hdr_max;
-		max = MIN(size, maxlen) - 1;
+		max = MIN(size, maxlen - 1);
 		goto send;
 
 	case LOG_FORMAT_RAW:
@@ -1632,7 +1641,7 @@
 		hdr_ptr = hdr = "";
 		hdr_max = 0;
 		maxlen = logsrv->maxlen;
-		max = MIN(size, maxlen) - 1;
+		max = MIN(size, maxlen - 1);
 		goto send;
 
 	default:
@@ -1716,7 +1725,7 @@
 		goto send;
 	}
 
-	max = MIN(size, maxlen - sd_max) - 1;
+	max = MIN(size, maxlen - sd_max - 1);
 send:
 	iovec[0].iov_base = hdr_ptr;
 	iovec[0].iov_len  = hdr_max;
@@ -1736,13 +1745,30 @@
 	iovec[7].iov_len  = 1;
 
 	if (logsrv->addr.ss_family == AF_UNSPEC) {
+		int attempts = 0;
+		/* make sure we never interleave writes and we never block. This means
+		 * we prefer to fail on collision than to block. But we don't want to
+		 * lose too many logs so we just perform a few lock attempts then give
+		 * up.
+		 */
+
+		while (HA_SPIN_TRYLOCK(LOGSRV_LOCK, &logsrv->lock) != 0) {
+			if (++attempts >= 200) {
+				/* so that the caller knows the message couldn't be delivered */
+				sent = -1;
+				errno = EAGAIN;
+				goto leave;
+			}
+			ha_thread_relax();
+		}
+
 		/* the target is a direct file descriptor. While writev() guarantees
 		 * to write everything, it doesn't guarantee that it will not be
 		 * interrupted while doing so. This occasionally results in interleaved
 		 * messages when the output is a tty, hence the lock. There's no real
 		 * performance concern here for such type of output.
 		 */
-		HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
+
 		sent = writev(*plogfd, iovec, 8);
 		HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
 	}
@@ -1753,6 +1779,7 @@
 		sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
 	}
 
+leave:
 	if (sent < 0) {
 		static char once;
 
@@ -1807,7 +1834,7 @@
 	/* Send log messages to syslog server. */
 	nblogger = 0;
 	list_for_each_entry(logsrv, logsrvs, list) {
-		static THREAD_LOCAL int in_range = 1;
+		int in_range = 1;
 
 		/* we can filter the level of the messages that are sent to each logger */
 		if (level > logsrv->level)
diff --git a/src/map.c b/src/map.c
index 1a2190d..16b1c5f 100644
--- a/src/map.c
+++ b/src/map.c
@@ -1041,6 +1041,24 @@
 }
 
 
+/* continue to clear a map which was started in the parser */
+static int cli_io_handler_clear_map(struct appctx *appctx)
+{
+	struct stream_interface *si = appctx->owner;
+	int finished;
+
+	HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
+	finished = pat_ref_prune(appctx->ctx.map.ref);
+	HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
+
+	if (!finished) {
+		/* let's come back later */
+		si_rx_endp_more(si);
+		return 0;
+	}
+	return 1;
+}
+
 static int cli_parse_clear_map(char **args, char *payload, struct appctx *appctx, void *private)
 {
 	if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
@@ -1080,28 +1098,22 @@
 			return 1;
 		}
 
-		/* Clear all. */
-		HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
-		pat_ref_prune(appctx->ctx.map.ref);
-		HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
-
-		/* return response */
-		appctx->st0 = CLI_ST_PROMPT;
-		return 1;
+		/* delegate the clearing to the I/O handler which can yield */
+		return 0;
 	}
-	return 0;
+	return 1;
 }
 
 /* register cli keywords */
 
 static struct cli_kw_list cli_kws = {{ },{
 	{ { "add",   "acl", NULL }, "add acl        : add acl entry", cli_parse_add_map, NULL },
-	{ { "clear", "acl", NULL }, "clear acl <id> : clear the content of this acl", cli_parse_clear_map, NULL },
+	{ { "clear", "acl", NULL }, "clear acl <id> : clear the content of this acl", cli_parse_clear_map, cli_io_handler_clear_map, NULL },
 	{ { "del",   "acl", NULL }, "del acl        : delete acl entry", cli_parse_del_map, NULL },
 	{ { "get",   "acl", NULL }, "get acl        : report the patterns matching a sample for an ACL", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook },
 	{ { "show",  "acl", NULL }, "show acl [id]  : report available acls or dump an acl's contents", cli_parse_show_map, NULL },
 	{ { "add",   "map", NULL }, "add map        : add map entry", cli_parse_add_map, NULL },
-	{ { "clear", "map", NULL }, "clear map <id> : clear the content of this map", cli_parse_clear_map, NULL },
+	{ { "clear", "map", NULL }, "clear map <id> : clear the content of this map", cli_parse_clear_map, cli_io_handler_clear_map, NULL },
 	{ { "del",   "map", NULL }, "del map        : delete map entry", cli_parse_del_map, NULL },
 	{ { "get",   "map", NULL }, "get map        : report the keys and values matching a sample for a map", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook },
 	{ { "set",   "map", NULL }, "set map        : modify map entry", cli_parse_set_map, NULL },
diff --git a/src/memory.c b/src/memory.c
index edacb00..6ef9f61 100644
--- a/src/memory.c
+++ b/src/memory.c
@@ -139,11 +139,13 @@
 			for (thr = 0; thr < MAX_THREADS; thr++)
 				pool_cache[thr][idx].size = size;
 		}
-	}
-	pool->users++;
 #ifndef CONFIG_HAP_LOCKLESS_POOLS
-	HA_SPIN_INIT(&pool->lock);
+		HA_SPIN_INIT(&pool->lock);
+#else
+		HA_SPIN_INIT(&pool->flush_lock);
 #endif
+	}
+	pool->users++;
 	return pool;
 }
 
@@ -217,15 +219,22 @@
  */
 void pool_flush(struct pool_head *pool)
 {
+	struct pool_free_list cmp, new;
 	void **next, *temp;
 	int removed = 0;
 
 	if (!pool)
 		return;
+	HA_SPIN_LOCK(POOL_LOCK, &pool->flush_lock);
 	do {
-		next = pool->free_list;
-	} while (!_HA_ATOMIC_CAS(&pool->free_list, &next, NULL));
+		cmp.free_list = pool->free_list;
+		cmp.seq = pool->seq;
+		new.free_list = NULL;
+		new.seq = cmp.seq + 1;
+	} while (!_HA_ATOMIC_DWCAS(&pool->free_list, &cmp, &new));
 	__ha_barrier_atomic_store();
+	HA_SPIN_UNLOCK(POOL_LOCK, &pool->flush_lock);
+	next = cmp.free_list;
 	while (next) {
 		temp = next;
 		next = *POOL_LINK(pool, temp);
@@ -254,6 +263,7 @@
 		return;
 
 	list_for_each_entry(entry, &pools, list) {
+		HA_SPIN_LOCK(POOL_LOCK, &entry->flush_lock);
 		while ((int)((volatile int)entry->allocated - (volatile int)entry->used) > (int)entry->minavail) {
 			struct pool_free_list cmp, new;
 
@@ -270,6 +280,7 @@
 			free(cmp.free_list);
 			_HA_ATOMIC_SUB(&entry->allocated, 1);
 		}
+		HA_SPIN_UNLOCK(POOL_LOCK, &entry->flush_lock);
 	}
 
 	_HA_ATOMIC_STORE(&recurse, 0);
@@ -480,7 +491,7 @@
 #ifndef CONFIG_HAP_LOCKLESS_POOLS
 		HA_SPIN_LOCK(POOL_LOCK, &entry->lock);
 #endif
-		chunk_appendf(&trash, "  - Pool %s (%d bytes) : %d allocated (%u bytes), %d used, %d failures, %d users, @%p=%02d%s\n",
+		chunk_appendf(&trash, "  - Pool %s (%u bytes) : %u allocated (%u bytes), %u used, %u failures, %u users, @%p=%02d%s\n",
 			 entry->name, entry->size, entry->allocated,
 		         entry->size * entry->allocated, entry->used, entry->failed,
 			 entry->users, entry, (int)pool_get_index(entry),
@@ -604,14 +615,14 @@
 	int n;
 
 	if (mem_fail_rate > 0 && !(global.mode & MODE_STARTING)) {
-		int randnb = random() % 100;
+		int randnb = ha_random() % 100;
 
 		if (mem_fail_rate > randnb)
 			ret = 1;
 		else
 			ret = 0;
 	}
-	HA_SPIN_LOCK(OTHER_LOCK, &mem_fail_lock);
+	HA_SPIN_LOCK(POOL_LOCK, &mem_fail_lock);
 	n = snprintf(&mem_fail_str[mem_fail_cur_idx * MEM_FAIL_MAX_CHAR],
 	    MEM_FAIL_MAX_CHAR - 2,
 	    "%d %.18s %d %d", mem_fail_cur_idx, pool->name, ret, tid);
@@ -624,7 +635,7 @@
 	mem_fail_cur_idx++;
 	if (mem_fail_cur_idx == MEM_FAIL_MAX_STR)
 		mem_fail_cur_idx = 0;
-	HA_SPIN_UNLOCK(OTHER_LOCK, &mem_fail_lock);
+	HA_SPIN_UNLOCK(POOL_LOCK, &mem_fail_lock);
 	return ret;
 
 }
diff --git a/src/mux_h1.c b/src/mux_h1.c
index 5ab1fea..7f2d914 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -42,13 +42,15 @@
 /* Flags indicating why reading input data are blocked. */
 #define H1C_F_IN_ALLOC       0x00000010 /* mux is blocked on lack of input buffer */
 #define H1C_F_IN_FULL        0x00000020 /* mux is blocked on input buffer full */
-#define H1C_F_IN_BUSY        0x00000040
-/* 0x00000040 - 0x00000800 unused */
+#define H1C_F_IN_BUSY        0x00000040 /* mux is blocked on input waiting the other side */
+/* 0x00000040 - 0x00000400 unused */
 
+#define H1C_F_CS_WAIT_CONN   0x00000800 /* waiting for the connection establishment */
 #define H1C_F_CS_ERROR       0x00001000 /* connection must be closed ASAP because an error occurred */
 #define H1C_F_CS_SHUTW_NOW   0x00002000 /* connection must be shut down for writes ASAP */
-#define H1C_F_CS_SHUTDOWN    0x00004000 /* connection is shut down for read and writes */
-#define H1C_F_CS_WAIT_CONN   0x00008000 /* waiting for the connection establishment */
+#define H1C_F_CS_SHUTDOWN    0x00004000 /* connection is shut down */
+#define H1C_F_CS_IDLE        0x00008000 /* connection is idle and may be reused
+					 * (exclusive to all H1C_F_CS flags and never set when an h1s is attached) */
 
 #define H1C_F_WAIT_NEXT_REQ  0x00010000 /*  waiting for the next request to start, use keep-alive timeout */
 #define H1C_F_UPG_H2C        0x00020000 /* set if an upgrade to h2 should be done */
@@ -140,29 +142,21 @@
 /* functions below are for dynamic buffer management */
 /*****************************************************/
 /*
- * Indicates whether or not the we may call the h1_recv() function to
- * attempt to receive data into the buffer and/or parse pending data. The
- * condition is a bit complex due to some API limits for now. The rules are the
- * following :
- *   - if an error or a shutdown was detected on the connection and the buffer
- *     is empty, we must not attempt to receive
- *   - if the input buffer failed to be allocated, we must not try to receive
- *      and we know there is nothing pending
- *   - if no flag indicates a blocking condition, we may attempt to receive,
- *     regardless of whether the input buffer is full or not, so that only de
- *     receiving part decides whether or not to block. This is needed because
- *     the connection API indeed prevents us from re-enabling receipt that is
- *     already enabled in a polled state, so we must always immediately stop as
- *     soon as the mux can't proceed so as never to hit an end of read with data
- *     pending in the buffers.
+ * Indicates whether or not we may receive data. The rules are the following :
+ *   - if an error or a shutdown for reads was detected on the connection we
+       must not attempt to receive
+ *   - if the input buffer failed to be allocated or is full , we must not try
+ *     to receive
+ *   - if he input processing is busy waiting for the output side, we may
+ *     attemp to receive
  *   - otherwise must may not attempt to receive
  */
 static inline int h1_recv_allowed(const struct h1c *h1c)
 {
-	if (b_data(&h1c->ibuf) == 0 && (h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTDOWN)))
+	if (h1c->flags & H1C_F_CS_ERROR)
 		return 0;
 
-	if (h1c->conn->flags & CO_FL_ERROR || conn_xprt_read0_pending(h1c->conn))
+	if (h1c->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH))
 		return 0;
 
 	if (!(h1c->flags & (H1C_F_IN_ALLOC|H1C_F_IN_FULL|H1C_F_IN_BUSY)))
@@ -228,15 +222,16 @@
 	}
 }
 
-/* returns the number of streams in use on a connection to figure if it's
- * idle or not. We can't have an h1s without a CS so checking h1s is fine,
- * as the caller will want to know if it was the last one after a detach().
+/* returns the number of streams in use on a connection to figure if it's idle
+ * or not. We rely on H1C_F_CS_IDLE to know if the connection is in-use or
+ * not. This flag is only set when no H1S is attached and when the previous
+ * stream, if any, was fully terminated without any error and in K/A mode.
  */
 static int h1_used_streams(struct connection *conn)
 {
 	struct h1c *h1c = conn->ctx;
 
-	return h1c->h1s ? 1 : 0;
+	return ((h1c->flags & H1C_F_CS_IDLE) ? 0 : 1);
 }
 
 /* returns the number of streams still available on a connection */
@@ -275,6 +270,9 @@
 	if (h1s->flags & H1S_F_NOT_FIRST)
 		cs->flags |= CS_FL_NOT_FIRST;
 
+	if (global.tune.options & GTUNE_USE_SPLICE)
+		cs->flags |= CS_FL_MAY_SPLICE;
+
 	if (stream_create_from_cs(cs) < 0)
 		goto err;
 	return cs;
@@ -315,7 +313,7 @@
 
 	if (h1c->flags & H1C_F_WAIT_NEXT_REQ)
 		h1s->flags |= H1S_F_NOT_FIRST;
-	h1c->flags &= ~H1C_F_WAIT_NEXT_REQ;
+	h1c->flags &= ~(H1C_F_CS_IDLE|H1C_F_WAIT_NEXT_REQ);
 
 	if (!conn_is_back(h1c->conn)) {
 		if (h1c->px->options2 & PR_O2_REQBUG_OK)
@@ -382,9 +380,15 @@
 			h1s->send_wait->events &= ~SUB_RETRY_SEND;
 
 		h1c->flags &= ~H1C_F_IN_BUSY;
-		h1c->flags |= H1C_F_WAIT_NEXT_REQ;
 		if (h1s->flags & (H1S_F_REQ_ERROR|H1S_F_RES_ERROR))
 			h1c->flags |= H1C_F_CS_ERROR;
+
+		if (!(h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTW_NOW|H1C_F_CS_SHUTDOWN)) && /* No error/shutdown on h1c */
+		    !(h1c->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH)) && /* No error/shutdown on conn */
+		    (h1s->flags & H1S_F_WANT_KAL) &&                                         /* K/A possible */
+		    h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE) {        /* req/res in DONE state */
+			h1c->flags |= (H1C_F_CS_IDLE|H1C_F_WAIT_NEXT_REQ);
+		}
 		pool_free(pool_head_h1s, h1s);
 	}
 }
@@ -418,7 +422,7 @@
 	h1c->conn = conn;
 	h1c->px   = proxy;
 
-	h1c->flags = H1C_F_NONE;
+	h1c->flags = H1C_F_CS_IDLE;
 	h1c->ibuf  = *input;
 	h1c->obuf  = BUF_NULL;
 	h1c->h1s   = NULL;
@@ -1378,6 +1382,13 @@
 		h1_capture_bad_message(h1s->h1c, h1s, h1m, buf);
 		return 0;
 	}
+
+	if (h1s->cs && !(h1m->flags & H1_MF_CHNK) &&
+	    ((h1m->state == H1_MSG_DATA && h1m->curr_len) || (h1m->state == H1_MSG_TUNNEL)))
+		h1s->cs->flags |= CS_FL_MAY_SPLICE;
+	else if (h1s->cs)
+		h1s->cs->flags &= ~CS_FL_MAY_SPLICE;
+
 	/* update htx->extra, only when the body length is known */
 	if (h1m->flags & H1_MF_XFER_LEN)
 		htx->extra = h1m->curr_len;
@@ -1504,9 +1515,12 @@
 		else if (h1m->state == H1_MSG_DONE) {
 			if (!(h1m->flags & H1_MF_RESP) && h1s->status == 101)
 				h1_set_req_tunnel_mode(h1s);
-			else if (h1s->req.state < H1_MSG_DONE || h1s->res.state < H1_MSG_DONE)
+			else if (h1s->req.state < H1_MSG_DONE || h1s->res.state < H1_MSG_DONE) {
 				h1c->flags |= H1C_F_IN_BUSY;
-			break;
+				break;
+			}
+			else
+				break;
 		}
 		else if (h1m->state == H1_MSG_TUNNEL) {
 			ret = h1_process_data(h1s, h1m, htx, &h1c->ibuf, &total, count, buf);
@@ -1539,7 +1553,8 @@
 
 	if (!b_data(&h1c->ibuf))
 		h1_release_buf(h1c, &h1c->ibuf);
-	else if (h1s_data_pending(h1s) && !htx_is_empty(htx))
+
+	if ((h1s_data_pending(h1s) && !htx_is_empty(htx)) || (h1s->flags & H1S_F_APPEND_EOM))
 		h1s->cs->flags |= CS_FL_RCV_MORE | CS_FL_WANT_ROOM;
 
 	if (((h1s->flags & (H1S_F_REOS|H1S_F_APPEND_EOM)) == H1S_F_REOS) &&
@@ -2082,10 +2097,9 @@
 
 	if (!h1s) {
 		if (h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTDOWN) ||
-		    conn->flags & (CO_FL_ERROR | CO_FL_SOCK_WR_SH) ||
-		    conn_xprt_read0_pending(conn))
+		    conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH))
 			goto release;
-		if (!conn_is_back(conn) && !(h1c->flags & (H1C_F_CS_SHUTW_NOW|H1C_F_CS_SHUTDOWN))) {
+		if (!conn_is_back(conn) && (h1c->flags & H1C_F_CS_IDLE)) {
 			if (!h1s_create(h1c, NULL, NULL))
 				goto release;
 		}
@@ -2253,7 +2267,6 @@
 	struct h1s *h1s = cs->ctx;
 	struct h1c *h1c;
 	struct session *sess;
-	int has_keepalive;
 	int is_not_first;
 
 	cs->ctx = NULL;
@@ -2264,12 +2277,10 @@
 	h1c = h1s->h1c;
 	h1s->cs = NULL;
 
-	has_keepalive = h1s->flags & H1S_F_WANT_KAL;
 	is_not_first = h1s->flags & H1S_F_NOT_FIRST;
 	h1s_destroy(h1s);
 
-	if (conn_is_back(h1c->conn) && has_keepalive &&
-	    !(h1c->conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) {
+	if (conn_is_back(h1c->conn) && (h1c->flags & H1C_F_CS_IDLE)) {
 		/* If there are any excess server data in the input buffer,
 		 * release it and close the connection ASAP (some data may
 		 * remain in the output buffer). This happens if a server sends
@@ -2278,7 +2289,7 @@
 		 */
 		if (b_data(&h1c->ibuf)) {
 			h1_release_buf(h1c, &h1c->ibuf);
-			h1c->flags |= H1C_F_CS_SHUTW_NOW;
+			h1c->flags = (h1c->flags & ~H1C_F_CS_IDLE) | H1C_F_CS_SHUTW_NOW;
 			goto release;
 		}
 
@@ -2292,11 +2303,10 @@
 				h1c->conn->owner = NULL;
 				if (!srv_add_to_idle_list(objt_server(h1c->conn->target), h1c->conn))
 					/* The server doesn't want it, let's kill the connection right away */
-					h1c->conn->mux->destroy(h1c->conn);
+					h1c->conn->mux->destroy(h1c);
 				else
 					tasklet_wakeup(h1c->wait_event.tasklet);
 				return;
-
 			}
 		}
 		if (h1c->conn->owner == sess) {
@@ -2370,9 +2380,7 @@
 		return;
 	if (conn_xprt_ready(cs->conn) && cs->conn->xprt->shutr)
 		cs->conn->xprt->shutr(cs->conn, cs->conn->xprt_ctx,
-		    (mode == CS_SHR_DRAIN));
-	if ((cs->conn->flags & (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH)) == (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH))
-		h1c->flags = (h1c->flags & ~H1C_F_CS_SHUTW_NOW) | H1C_F_CS_SHUTDOWN;
+				      (mode == CS_SHR_DRAIN));
 }
 
 static void h1_shutw(struct conn_stream *cs, enum cs_shw_mode mode)
@@ -2405,8 +2413,7 @@
 
 	conn_xprt_shutw(conn);
 	conn_sock_shutw(conn, (mode == CS_SHW_NORMAL));
-	if ((conn->flags & (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH)) == (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH))
-		h1c->flags = (h1c->flags & ~H1C_F_CS_SHUTW_NOW) | H1C_F_CS_SHUTDOWN;
+	h1c->flags = (h1c->flags & ~H1C_F_CS_SHUTW_NOW) | H1C_F_CS_SHUTDOWN;
 }
 
 /* Called from the upper layer, to unsubscribe to events */
@@ -2474,11 +2481,12 @@
 	if (flags & CO_RFL_BUF_FLUSH) {
 		struct h1m *h1m = (!conn_is_back(cs->conn) ? &h1s->req : &h1s->res);
 
-		if (h1m->state != H1_MSG_TUNNEL || (h1m->state == H1_MSG_DATA && h1m->curr_len))
+		if (h1m->state == H1_MSG_TUNNEL || (h1m->state == H1_MSG_DATA && h1m->curr_len))
 			h1s->flags |= H1S_F_BUF_FLUSH;
 	}
 	else if (ret > 0 || (h1s->flags & H1S_F_SPLICED_DATA)) {
-		h1s->flags &= ~H1S_F_SPLICED_DATA;
+		if (ret)
+			h1s->flags &= ~H1S_F_SPLICED_DATA;
 		if (!(h1c->wait_event.events & SUB_RETRY_RECV))
 			tasklet_wakeup(h1c->wait_event.tasklet);
 	}
@@ -2509,7 +2517,7 @@
 			break;
 		total += ret;
 		count -= ret;
-		if (!h1_send(h1c))
+		if ((h1c->wait_event.events & SUB_RETRY_SEND) || !h1_send(h1c))
 			break;
 	}
 
@@ -2538,10 +2546,16 @@
 
 	h1s->flags &= ~H1S_F_BUF_FLUSH;
 	h1s->flags |= H1S_F_SPLICED_DATA;
+
+	if (!h1_recv_allowed(h1s->h1c))
+		goto end;
+
 	if (h1m->state == H1_MSG_DATA && count > h1m->curr_len)
 		count = h1m->curr_len;
 	ret = cs->conn->xprt->rcv_pipe(cs->conn, cs->conn->xprt_ctx, pipe, count);
-	if (h1m->state == H1_MSG_DATA && ret >= 0) {
+	if (ret <= 0)
+		h1s->flags &= ~(H1S_F_BUF_FLUSH|H1S_F_SPLICED_DATA);
+	else if (h1m->state == H1_MSG_DATA) {
 		h1m->curr_len -= ret;
 		if (!h1m->curr_len)
 			h1s->flags &= ~(H1S_F_BUF_FLUSH|H1S_F_SPLICED_DATA);
@@ -2552,6 +2566,12 @@
 		h1s->flags |= H1S_F_REOS;
 		h1s->flags &= ~(H1S_F_BUF_FLUSH|H1S_F_SPLICED_DATA);
 	}
+
+	if ((h1s->flags & H1S_F_REOS) ||
+	    (h1m->state != H1_MSG_TUNNEL && h1m->state != H1_MSG_DATA) ||
+	    (h1m->state == H1_MSG_DATA && !h1m->curr_len))
+		cs->flags &= ~CS_FL_MAY_SPLICE;
+
 	return ret;
 }
 
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 74fffbf..9e2746d 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -2720,13 +2720,41 @@
 	h2c_restart_reading(h2c, 0);
 }
 
+/* resume each h2s eligible for sending in list head <head> */
+static void h2_resume_each_sending_h2s(struct h2c *h2c, struct list *head)
+{
+	struct h2s *h2s, *h2s_back;
+
+	list_for_each_entry_safe(h2s, h2s_back, head, list) {
+		if (h2c->mws <= 0 ||
+		    h2c->flags & H2_CF_MUX_BLOCK_ANY ||
+		    h2c->st0 >= H2_CS_ERROR)
+			break;
+
+		h2s->flags &= ~H2_SF_BLK_ANY;
+
+		if (LIST_ADDED(&h2s->sending_list))
+			continue;
+
+		/* For some reason, the upper layer failed to subscribe again,
+		 * so remove it from the send_list
+		 */
+		if (!h2s->send_wait) {
+			LIST_DEL_INIT(&h2s->list);
+			continue;
+		}
+
+		h2s->send_wait->events &= ~SUB_RETRY_SEND;
+		LIST_ADDQ(&h2c->sending_list, &h2s->sending_list);
+		tasklet_wakeup(h2s->send_wait->tasklet);
+	}
+}
+
 /* process Tx frames from streams to be multiplexed. Returns > 0 if it reached
  * the end.
  */
 static int h2_process_mux(struct h2c *h2c)
 {
-	struct h2s *h2s, *h2s_back;
-
 	if (unlikely(h2c->st0 < H2_CS_FRAME_H)) {
 		if (unlikely(h2c->st0 == H2_CS_PREFACE && (h2c->flags & H2_CF_IS_BACK))) {
 			if (unlikely(h2c_bck_send_preface(h2c) <= 0)) {
@@ -2757,47 +2785,8 @@
 	 * waiting there were already elected for immediate emission but were
 	 * blocked just on this.
 	 */
-
-	list_for_each_entry_safe(h2s, h2s_back, &h2c->fctl_list, list) {
-		if (h2c->mws <= 0 || h2c->flags & H2_CF_MUX_BLOCK_ANY ||
-		    h2c->st0 >= H2_CS_ERROR)
-			break;
-
-		if (LIST_ADDED(&h2s->sending_list))
-			continue;
-
-		h2s->flags &= ~H2_SF_BLK_ANY;
-		/* For some reason, the upper layer failed to subsribe again,
-		 * so remove it from the send_list
-		 */
-		if (!h2s->send_wait) {
-			LIST_DEL_INIT(&h2s->list);
-			continue;
-		}
-		h2s->send_wait->events &= ~SUB_RETRY_SEND;
-		LIST_ADDQ(&h2c->sending_list, &h2s->sending_list);
-		tasklet_wakeup(h2s->send_wait->tasklet);
-	}
-
-	list_for_each_entry_safe(h2s, h2s_back, &h2c->send_list, list) {
-		if (h2c->st0 >= H2_CS_ERROR || h2c->flags & H2_CF_MUX_BLOCK_ANY)
-			break;
-
-		if (LIST_ADDED(&h2s->sending_list))
-			continue;
-
-		/* For some reason, the upper layer failed to subsribe again,
-		 * so remove it from the send_list
-		 */
-		if (!h2s->send_wait) {
-			LIST_DEL_INIT(&h2s->list);
-			continue;
-		}
-		h2s->flags &= ~H2_SF_BLK_ANY;
-		h2s->send_wait->events &= ~SUB_RETRY_SEND;
-		LIST_ADDQ(&h2c->sending_list, &h2s->sending_list);
-		tasklet_wakeup(h2s->send_wait->tasklet);
-	}
+	h2_resume_each_sending_h2s(h2c, &h2c->fctl_list);
+	h2_resume_each_sending_h2s(h2c, &h2c->send_list);
 
  fail:
 	if (unlikely(h2c->st0 >= H2_CS_ERROR)) {
@@ -2963,29 +2952,9 @@
 	/* We're not full anymore, so we can wake any task that are waiting
 	 * for us.
 	 */
-	if (!(h2c->flags & (H2_CF_MUX_MFULL | H2_CF_DEM_MROOM)) && h2c->st0 >= H2_CS_FRAME_H) {
-		struct h2s *h2s;
+	if (!(h2c->flags & (H2_CF_MUX_MFULL | H2_CF_DEM_MROOM)) && h2c->st0 >= H2_CS_FRAME_H)
+		h2_resume_each_sending_h2s(h2c, &h2c->send_list);
 
-		list_for_each_entry(h2s, &h2c->send_list, list) {
-			if (h2c->st0 >= H2_CS_ERROR || h2c->flags & H2_CF_MUX_BLOCK_ANY)
-				break;
-
-			if (LIST_ADDED(&h2s->sending_list))
-				continue;
-
-			/* For some reason, the upper layer failed to subsribe again,
-			 * so remove it from the send_list
-			 */
-			if (!h2s->send_wait) {
-				LIST_DEL_INIT(&h2s->list);
-				continue;
-			}
-			h2s->flags &= ~H2_SF_BLK_ANY;
-			h2s->send_wait->events &= ~SUB_RETRY_SEND;
-			tasklet_wakeup(h2s->send_wait->tasklet);
-			LIST_ADDQ(&h2c->sending_list, &h2s->sending_list);
-		}
-	}
 	/* We're done, no more to send */
 	if (!br_data(h2c->mbuf))
 		return sent;
@@ -3346,7 +3315,7 @@
 					if (eb_is_empty(&h2c->streams_by_id)) {
 						if (!srv_add_to_idle_list(objt_server(h2c->conn->target), h2c->conn))
 							/* The server doesn't want it, let's kill the connection right away */
-							h2c->conn->mux->destroy(h2c->conn);
+							h2c->conn->mux->destroy(h2c);
 						return;
 					}
 				}
@@ -4354,11 +4323,8 @@
 	default:          /* te:chunked : parse chunks */
 		if (h1m->state == H1_MSG_CHUNK_CRLF) {
 			ret = h1_skip_chunk_crlf(buf, ofs, ofs + max);
-			if (!ret)
-				goto end;
-
-			if (ret < 0) {
-				/* FIXME: bad contents. how to proceed here when we're in H2 ? */
+			if (ret <= 0) {
+				/* FIXME: bad contents or truncated response. how to proceed here when we're in H2 ? */
 				h1m->err_pos = ofs + max + ret;
 				h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
 				goto end;
@@ -4372,11 +4338,8 @@
 		if (h1m->state == H1_MSG_CHUNK_SIZE) {
 			unsigned int chunk;
 			ret = h1_parse_chunk_size(buf, ofs, ofs + max, &chunk);
-			if (!ret)
-				goto end;
-
-			if (ret < 0) {
-				/* FIXME: bad contents. how to proceed here when we're in H2 ? */
+			if (ret <= 0) {
+				/* FIXME: bad contents or truncated response. how to proceed here when we're in H2 ? */
 				h1m->err_pos = ofs + max + ret;
 				h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
 				goto end;
@@ -4920,19 +4883,32 @@
 
 	/* encode all headers, stop at empty name */
 	for (hdr = 0; hdr < sizeof(list)/sizeof(list[0]); hdr++) {
+		struct ist n = list[hdr].n;
+		struct ist v = list[hdr].v;
+
 		/* these ones do not exist in H2 and must be dropped. */
-		if (isteq(list[hdr].n, ist("connection")) ||
-		    isteq(list[hdr].n, ist("host")) ||
-		    isteq(list[hdr].n, ist("proxy-connection")) ||
-		    isteq(list[hdr].n, ist("keep-alive")) ||
-		    isteq(list[hdr].n, ist("upgrade")) ||
-		    isteq(list[hdr].n, ist("transfer-encoding")))
+		if (isteq(n, ist("connection")) ||
+		    isteq(n, ist("host")) ||
+		    isteq(n, ist("proxy-connection")) ||
+		    isteq(n, ist("keep-alive")) ||
+		    isteq(n, ist("upgrade")) ||
+		    isteq(n, ist("transfer-encoding")))
 			continue;
 
-		if (isteq(list[hdr].n, ist("")))
+		if (isteq(n, ist("te"))) {
+			/* "te" may only be sent with "trailers" if this value
+			 * is present, otherwise it must be deleted.
+			 */
+			v = istist(v, ist("trailers"));
+			if (!v.ptr || (v.len > 8 && v.ptr[8] != ','))
+				continue;
+			v = ist("trailers");
+		}
+
+		if (isteq(n, ist("")))
 			break; // end
 
-		if (!hpack_encode_header(&outbuf, list[hdr].n, list[hdr].v)) {
+		if (!hpack_encode_header(&outbuf, n, v)) {
 			/* output full */
 			if (b_space_wraps(mbuf))
 				goto realign_again;
@@ -5027,6 +5003,7 @@
 	struct htx_blk *blk;
 	enum htx_blk_type type;
 	int idx;
+	int trunc_out; /* non-zero if truncated on out buf */
 
 	if (h2c_mux_busy(h2c, h2s)) {
 		h2s->flags |= H2_SF_BLK_MBUSY;
@@ -5049,6 +5026,7 @@
 	type  = htx_get_blk_type(blk); // DATA or EOM
 	bsize = htx_get_blksz(blk);
 	fsize = bsize;
+	trunc_out = 0;
 
 	if (type == HTX_BLK_EOM) {
 		if (h2s->flags & H2_SF_ES_SENT) {
@@ -5203,6 +5181,7 @@
 		    b_data(mbuf) <= MAX_DATA_REALIGN)
 			goto realign_again;
 		fsize = outbuf.size - 9;
+		trunc_out = 1;
 
 		if (fsize <= 0) {
 			/* no need to send an empty frame here */
@@ -5256,6 +5235,8 @@
 	} else {
 		/* we've truncated this block */
 		htx_cut_data_blk(htx, blk, fsize);
+		if (trunc_out)
+			goto new_frame;
 	}
 
 	if (es_now) {
@@ -5745,10 +5726,10 @@
 			ret = h1_measure_trailers(buf, total, count);
 
 			if (unlikely((int)ret <= 0)) {
-				if ((int)ret < 0)
-					h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
+				h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
 				break;
 			}
+
 			// trim any possibly pending data (eg: extra CR-LF, ...)
 			total += count;
 			count  = 0;
diff --git a/src/mux_pt.c b/src/mux_pt.c
index c66b746..41e8d59 100644
--- a/src/mux_pt.c
+++ b/src/mux_pt.c
@@ -111,6 +111,8 @@
 	conn->ctx = ctx;
 	ctx->cs = cs;
 	cs->flags |= CS_FL_RCV_MORE;
+	if (global.tune.options & GTUNE_USE_SPLICE)
+		cs->flags |= CS_FL_MAY_SPLICE;
 	return 0;
 
  fail_free:
diff --git a/src/mworker.c b/src/mworker.c
index 511d961..ff3e16b 100644
--- a/src/mworker.c
+++ b/src/mworker.c
@@ -204,6 +204,8 @@
 	sigemptyset(&set);
 	sigaddset(&set, SIGUSR1);
 	sigaddset(&set, SIGUSR2);
+	sigaddset(&set, SIGTTIN);
+	sigaddset(&set, SIGTTOU);
 	sigaddset(&set, SIGHUP);
 	sigaddset(&set, SIGCHLD);
 	ha_sigmask(SIG_SETMASK, &set, NULL);
@@ -216,6 +218,12 @@
 
 /* ----- mworker signal handlers ----- */
 
+/* broadcast the configured signal to the workers */
+void mworker_broadcast_signal(struct sig_handler *sh)
+{
+	mworker_kill(sh->arg);
+}
+
 /*
  * When called, this function reexec haproxy with -sf followed by current
  * children PIDs and possibly old children PIDs if they didn't leave yet.
diff --git a/src/namespace.c b/src/namespace.c
index f23da48..89a968e 100644
--- a/src/namespace.c
+++ b/src/namespace.c
@@ -121,7 +121,8 @@
 	sock = socket(domain, type, protocol);
 
 	if (default_namespace >= 0 && ns && setns(default_namespace, CLONE_NEWNET) == -1) {
-		close(sock);
+		if (sock >= 0)
+			close(sock);
 		return -1;
 	}
 	return sock;
diff --git a/src/pattern.c b/src/pattern.c
index ec7e955..9132bc3 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -12,6 +12,7 @@
 
 #include <ctype.h>
 #include <stdio.h>
+#include <errno.h>
 
 #include <common/config.h>
 #include <common/standard.h>
@@ -452,7 +453,6 @@
 {
 	int icase;
 	struct ebmb_node *node;
-	char prev;
 	struct pattern_tree *elt;
 	struct pattern_list *lst;
 	struct pattern *pattern;
@@ -461,14 +461,27 @@
 
 	/* Lookup a string in the expression's pattern tree. */
 	if (!eb_is_empty(&expr->pattern_tree)) {
-		/* we may have to force a trailing zero on the test pattern */
-		prev = smp->data.u.str.area[smp->data.u.str.data];
-		if (prev)
-			smp->data.u.str.area[smp->data.u.str.data] = '\0';
+		char prev = 0;
+
+		if (smp->data.u.str.data < smp->data.u.str.size) {
+			/* we may have to force a trailing zero on the test pattern and
+			 * the buffer is large enough to accommodate it.
+			 */
+			prev = smp->data.u.str.area[smp->data.u.str.data];
+			if (prev)
+				smp->data.u.str.area[smp->data.u.str.data] = '\0';
+		}
+		else {
+			/* Otherwise, the sample is duplicated. A trailing zero
+			 * is automatically added to the string.
+			 */
+			if (!smp_dup(smp))
+				return NULL;
+		}
+
 		node = ebst_lookup(&expr->pattern_tree, smp->data.u.str.area);
 		if (prev)
 			smp->data.u.str.area[smp->data.u.str.data] = prev;
-
 		if (node) {
 			if (fill) {
 				elt = ebmb_entry(node, struct pattern_tree, node);
@@ -617,7 +630,6 @@
 {
 	int icase;
 	struct ebmb_node *node;
-	char prev;
 	struct pattern_tree *elt;
 	struct pattern_list *lst;
 	struct pattern *pattern;
@@ -626,10 +638,24 @@
 
 	/* Lookup a string in the expression's pattern tree. */
 	if (!eb_is_empty(&expr->pattern_tree)) {
-		/* we may have to force a trailing zero on the test pattern */
-		prev = smp->data.u.str.area[smp->data.u.str.data];
-		if (prev)
-			smp->data.u.str.area[smp->data.u.str.data] = '\0';
+		char prev = 0;
+
+		if (smp->data.u.str.data < smp->data.u.str.size) {
+			/* we may have to force a trailing zero on the test pattern and
+			 * the buffer is large enough to accommodate it.
+			 */
+			prev = smp->data.u.str.area[smp->data.u.str.data];
+			if (prev)
+				smp->data.u.str.area[smp->data.u.str.data] = '\0';
+		}
+		else {
+			/* Otherwise, the sample is duplicated. A trailing zero
+			 * is automatically added to the string.
+			 */
+			if (!smp_dup(smp))
+				return NULL;
+		}
+
 		node = ebmb_lookup_longest(&expr->pattern_tree,
 					   smp->data.u.str.area);
 		if (prev)
@@ -2099,18 +2125,27 @@
 }
 
 /* This function prune all entries of <ref>. This function
- * prune the associated pattern_expr.
+ * prunes the associated pattern_expr. It may return before the end of
+ * the list is reached, returning 0, to yield. The caller must call it
+ * again. Otherwise it returns 1 once done.
  */
-void pat_ref_prune(struct pat_ref *ref)
+int pat_ref_prune(struct pat_ref *ref)
 {
 	struct pat_ref_elt *elt, *safe;
 	struct pattern_expr *expr;
 	struct bref *bref, *back;
+	int loops = 0;
 
 	list_for_each_entry(expr, &ref->pat, list) {
 		HA_RWLOCK_WRLOCK(PATEXP_LOCK, &expr->lock);
 		expr->pat_head->prune(expr);
 		HA_RWLOCK_WRUNLOCK(PATEXP_LOCK, &expr->lock);
+		loops++;
+		/* yield often, some lists may be huge, especially those
+		 * having to be freed through free_pattern_tree()
+		 */
+		if (loops > 10)
+			return 0;
 	}
 
 	/* we trash pat_ref_elt in a second time to ensure that data is
@@ -2131,9 +2166,11 @@
 		free(elt->pattern);
 		free(elt->sample);
 		free(elt);
+		loops++;
+		if (loops > 100000)
+			return 0;
 	}
-
-
+	return 1;
 }
 
 /* This function lookup for existing reference <ref> in pattern_head <head>. */
@@ -2328,6 +2365,11 @@
 		}
 	}
 
+	if (ferror(file)) {
+		memprintf(err, "error encountered while reading  <%s> : %s",
+				filename, strerror(errno));
+		goto out_close;
+	}
 	/* succes */
 	ret = 1;
 
@@ -2385,6 +2427,11 @@
 		}
 	}
 
+	if (ferror(file)) {
+		memprintf(err, "error encountered while reading  <%s> : %s",
+				filename, strerror(errno));
+		goto out_close;
+	}
 	ret = 1; /* success */
 
  out_close:
@@ -2548,17 +2595,22 @@
 						if (static_sample_data.u.str.data >= static_sample_data.u.str.size)
 							static_sample_data.u.str.data = static_sample_data.u.str.size - 1;
 						memcpy(static_sample_data.u.str.area,
-						       pat->data->u.str.area,
-						       static_sample_data.u.str.data);
+						       pat->data->u.str.area, static_sample_data.u.str.data);
 						static_sample_data.u.str.area[static_sample_data.u.str.data] = 0;
+						pat->data = &static_sample_data;
+						break;
+
 					case SMP_T_IPV4:
 					case SMP_T_IPV6:
 					case SMP_T_SINT:
 						memcpy(&static_sample_data, pat->data, sizeof(struct sample_data));
+						pat->data = &static_sample_data;
+						break;
 					default:
+						/* unimplemented pattern type */
 						pat->data = NULL;
+						break;
 				}
-				pat->data = &static_sample_data;
 			}
 			HA_RWLOCK_RDUNLOCK(PATEXP_LOCK, &list->expr->lock);
 			return pat;
@@ -2632,53 +2684,84 @@
 	return 1;
 }
 
-/* This function finalize the configuration parsing. Its set all the
+/* This function compares two pat_ref** on unique_id */
+static int cmp_pat_ref(const void *_a, const void *_b)
+{
+	struct pat_ref * const *a = _a;
+	struct pat_ref * const *b = _b;
+
+	if ((*a)->unique_id < (*b)->unique_id)
+		return -1;
+	else if ((*a)->unique_id > (*b)->unique_id)
+		return 1;
+	return 0;
+}
+
+/* This function finalize the configuration parsing. It sets all the
  * automatic ids
  */
-void pattern_finalize_config(void)
+int pattern_finalize_config(void)
 {
-	int i = 0;
-	struct pat_ref *ref, *ref2, *ref3;
+	size_t len = 0;
+	size_t unassigned_pos = 0;
+	int next_unique_id = 0;
+	size_t i, j;
+	struct pat_ref *ref, **arr;
 	struct list pr = LIST_HEAD_INIT(pr);
 
-	pat_lru_seed = random();
+	pat_lru_seed = ha_random();
 
+	/* Count pat_refs with user defined unique_id and totalt count */
 	list_for_each_entry(ref, &pattern_reference, list) {
-		if (ref->unique_id == -1) {
-			/* Look for the first free id. */
-			while (1) {
-				list_for_each_entry(ref2, &pattern_reference, list) {
-					if (ref2->unique_id == i) {
-						i++;
-						break;
-					}
-				}
-				if (&ref2->list == &pattern_reference)
-					break;
-			}
+		len++;
+		if (ref->unique_id != -1)
+			unassigned_pos++;
+	}
 
-			/* Uses the unique id and increment it for the next entry. */
-			ref->unique_id = i;
-			i++;
-		}
+	if (len == 0) {
+		return 0;
 	}
 
-	/* This sort the reference list by id. */
-	list_for_each_entry_safe(ref, ref2, &pattern_reference, list) {
-		LIST_DEL(&ref->list);
-		list_for_each_entry(ref3, &pr, list) {
-			if (ref->unique_id < ref3->unique_id) {
-				LIST_ADDQ(&ref3->list, &ref->list);
-				break;
-			}
-		}
-		if (&ref3->list == &pr)
-			LIST_ADDQ(&pr, &ref->list);
+	arr = calloc(len, sizeof(*arr));
+	if (arr == NULL) {
+		ha_alert("Out of memory error.\n");
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	i = 0;
+	j = unassigned_pos;
+	list_for_each_entry(ref, &pattern_reference, list) {
+		if (ref->unique_id != -1)
+			arr[i++] = ref;
+		else
+			arr[j++] = ref;
+	}
+
+	/* Sort first segment of array with user-defined unique ids for
+	 * fast lookup when generating unique ids
+	 */
+	qsort(arr, unassigned_pos, sizeof(*arr), cmp_pat_ref);
+
+	/* Assign unique ids to the rest of the elements */
+	for (i = unassigned_pos; i < len; i++) {
+		do {
+			arr[i]->unique_id = next_unique_id++;
+		} while (bsearch(&arr[i], arr, unassigned_pos, sizeof(*arr), cmp_pat_ref));
 	}
 
+	/* Sort complete array */
+	qsort(arr, len, sizeof(*arr), cmp_pat_ref);
+
+	/* Convert back to linked list */
+	for (i = 0; i < len; i++)
+		LIST_ADDQ(&pr, &arr[i]->list);
+
 	/* swap root */
 	LIST_ADD(&pr, &pattern_reference);
 	LIST_DEL(&pr);
+
+	free(arr);
+	return 0;
 }
 
 static int pattern_per_thread_lru_alloc()
diff --git a/src/peers.c b/src/peers.c
index 4a28db4..2b3fd79 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -125,6 +125,48 @@
 	PEER_MSG_ERR_SIZELIMIT,
 };
 
+/* network key types;
+ * network types were directly and mistakenly
+ * mapped on sample types, to keep backward
+ * compatiblitiy we keep those values but
+ * we now use a internal/network mapping
+ * to avoid further mistakes adding or
+ * modifying internals types
+ */
+enum {
+        PEER_KT_ANY = 0,  /* any type */
+        PEER_KT_RESV1,    /* UNUSED */
+        PEER_KT_SINT,     /* signed 64bits integer type */
+        PEER_KT_RESV3,    /* UNUSED */
+        PEER_KT_IPV4,     /* ipv4 type */
+        PEER_KT_IPV6,     /* ipv6 type */
+        PEER_KT_STR,      /* char string type */
+        PEER_KT_BIN,      /* buffer type */
+        PEER_KT_TYPES     /* number of types, must always be last */
+};
+
+/* Map used to retrieve network type from internal type
+ * Note: Undeclared mapping maps entry to PEER_KT_ANY == 0
+ */
+static int peer_net_key_type[SMP_TYPES] = {
+	[SMP_T_SINT] = PEER_KT_SINT,
+	[SMP_T_IPV4] = PEER_KT_IPV4,
+	[SMP_T_IPV6] = PEER_KT_IPV6,
+	[SMP_T_STR]  = PEER_KT_STR,
+	[SMP_T_BIN]  = PEER_KT_BIN,
+};
+
+/* Map used to retrieve internal type from external type
+ * Note: Undeclared mapping maps entry to SMP_T_ANY == 0
+ */
+static int peer_int_key_type[PEER_KT_TYPES] = {
+	[PEER_KT_SINT] = SMP_T_SINT,
+	[PEER_KT_IPV4] = SMP_T_IPV4,
+	[PEER_KT_IPV6] = SMP_T_IPV6,
+	[PEER_KT_STR]  = SMP_T_STR,
+	[PEER_KT_BIN]  = SMP_T_BIN,
+};
+
 /*
  * Parameters used by functions to build peer protocol messages. */
 struct peer_prep_params {
@@ -620,7 +662,7 @@
 
 	/* encode table type */
 
-	intencode(st->table->type, &cursor);
+	intencode(peer_net_key_type[st->table->type], &cursor);
 
 	/* encode table key size */
 	intencode(st->table->key_size, &cursor);
@@ -1027,13 +1069,13 @@
  * any other negative returned value must  be considered as an error with an appctx st0
  * returned value equal to PEER_SESS_ST_END.
  */
-static inline int peer_send_resync_finishedmsg(struct appctx *appctx, struct peer *peer)
+static inline int peer_send_resync_finishedmsg(struct appctx *appctx, struct peers *peers)
 {
 	struct peer_prep_params p = {
 		.control.head = { PEER_MSG_CLASS_CONTROL, },
 	};
 
-	p.control.head[1] = (peer->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED ?
+	p.control.head[1] = (peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED ?
 		PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL;
 
 	return peer_send_msg(appctx, peer_prepare_control_msg, &p);
@@ -1655,7 +1697,7 @@
 	if (!*msg_cur)
 		goto malformed_exit;
 
-	if (p->remote_table->table->type != table_type
+	if (p->remote_table->table->type != peer_int_key_type[table_type]
 		|| p->remote_table->table->key_size != table_keylen) {
 		p->remote_table = NULL;
 		goto ignore_msg;
@@ -1950,7 +1992,7 @@
 	}
 
 	if ((peer->flags & PEER_F_TEACH_PROCESS) && !(peer->flags & PEER_F_TEACH_FINISHED)) {
-		repl = peer_send_resync_finishedmsg(appctx, peer);
+		repl = peer_send_resync_finishedmsg(appctx, peers);
 		if (repl <= 0)
 			return repl;
 
@@ -2232,7 +2274,7 @@
 					 * retrying otherwise the other end will do the same and we can loop
 					 * for a while.
 					 */
-					curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
+					curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
 					peer_session_forceshutdown(curpeer);
 				}
 				if (maj_ver != (unsigned int)-1 && min_ver != (unsigned int)-1) {
@@ -2709,7 +2751,7 @@
 									ps->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
 								}
 								else  {
-									ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
+									ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
 									peer_session_forceshutdown(ps);
 									ps->no_hbt++;
 								}
@@ -2765,7 +2807,7 @@
 				 * retrying otherwise the other end will do the same and we can loop
 				 * for a while.
 				 */
-				ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
+				ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
 				if (ps->appctx) {
 					peer_session_forceshutdown(ps);
 				}
diff --git a/src/pipe.c b/src/pipe.c
index 9190ba9..8941f89 100644
--- a/src/pipe.c
+++ b/src/pipe.c
@@ -55,6 +55,7 @@
 
 	if (pipe(pipefd) < 0) {
 		pool_free(pool_head_pipe, ret);
+		ret = NULL;
 		goto out;
 	}
 #ifdef F_SETPIPE_SZ
diff --git a/src/proto_http.c b/src/proto_http.c
index 3c16776..4d26bb8 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2447,8 +2447,6 @@
 
 	/* add end of headers and the keep-alive/close status. */
 	txn->status = rule->code;
-	/* let's log the request time */
-	s->logs.tv_request = now;
 
 	if (((!(req->flags & HTTP_MSGF_TE_CHNK) && !req->body_len) || (req->msg_state == HTTP_MSG_DONE)) &&
 	    ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL ||
@@ -2483,6 +2481,11 @@
 		/* let the server side turn to SI_ST_CLO */
 		channel_shutw_now(req->chn);
 		channel_dont_connect(req->chn);
+
+		if (rule->flags & REDIRECT_FLAG_FROM_REQ) {
+			/* let's log the request time */
+			s->logs.tv_request = now;
+		}
 	} else {
 		/* keep-alive not possible */
 		if (unlikely(txn->flags & TX_USE_PX_CONN)) {
@@ -2495,13 +2498,21 @@
 			chunk->data += 23;
 		}
 		http_reply_and_close(s, txn->status, chunk);
-		req->chn->analysers &= AN_REQ_FLT_END;
+
+		if (rule->flags & REDIRECT_FLAG_FROM_REQ) {
+			/* let's log the request time */
+			s->logs.tv_request = now;
+			req->chn->analysers &= AN_REQ_FLT_END;
+			if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
+				HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
+
+		}
 	}
 
 	if (!(s->flags & SF_ERR_MASK))
 		s->flags |= SF_ERR_LOCAL;
 	if (!(s->flags & SF_FINST_MASK))
-		s->flags |= SF_FINST_R;
+		s->flags |= ((rule->flags & REDIRECT_FLAG_FROM_REQ) ? SF_FINST_R : SF_FINST_H);
 
 	ret = 1;
  leave:
@@ -3164,8 +3175,7 @@
 	 */
 	s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
 
-	if (!(req->flags & CF_READ_ERROR))
-		http_reply_and_close(s, txn->status, http_error_message(s));
+	http_reply_and_close(s, txn->status, (!(req->flags & CF_READ_ERROR) ? http_error_message(s) : NULL));
 
 	req->analysers &= AN_REQ_FLT_END;
 	req->analyse_exp = TICK_ETERNITY;
@@ -4320,6 +4330,8 @@
 		abort_response:
 			channel_auto_close(rep);
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 502;
 			s->si[1].flags |= SI_FL_NOLINGER;
 			channel_truncate(rep);
@@ -4355,6 +4367,8 @@
 
 			channel_auto_close(rep);
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 502;
 
 			/* Check to see if the server refused the early data.
@@ -4391,6 +4405,8 @@
 
 			channel_auto_close(rep);
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 504;
 			s->si[1].flags |= SI_FL_NOLINGER;
 			channel_truncate(rep);
@@ -4411,6 +4427,8 @@
 				_HA_ATOMIC_ADD(&objt_server(s->target)->counters.cli_aborts, 1);
 
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			channel_auto_close(rep);
 
 			txn->status = 400;
@@ -4441,6 +4459,8 @@
 
 			channel_auto_close(rep);
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 502;
 			s->si[1].flags |= SI_FL_NOLINGER;
 			channel_truncate(rep);
@@ -4462,6 +4482,8 @@
 
 			_HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			channel_auto_close(rep);
 
 			if (!(s->flags & SF_ERR_MASK))
@@ -4762,16 +4784,22 @@
 	    /* check for Negotiate/NTLM WWW-Authenticate headers */
 	    ctx.idx = 0;
 	    while (http_find_header2("WWW-Authenticate", 16, ci_head(rep), &txn->hdr_idx, &ctx)) {
-	            if ((ctx.vlen >= 9 && word_match(ctx.line + ctx.val, ctx.vlen, "Negotiate", 9)) ||
-	                  (ctx.vlen >= 4 && word_match(ctx.line + ctx.val, ctx.vlen, "NTLM", 4)))
+		    /* If www-authenticate contains "Negotiate", "Nego2", or "NTLM",
+		     * possibly followed by blanks and a base64 string, the connection
+		     * is private. Since it's a mess to deal with, we only check for
+		     * values starting with "NTLM" or "Nego". Note that often multiple
+		     * headers are sent by the server there.
+		     */
+	            if ((ctx.vlen >= 4 && strncasecmp(ctx.line + ctx.val, "Nego", 4)) ||
+	                  (ctx.vlen >= 4 && strncasecmp(ctx.line + ctx.val, "NTLM", 4)))
 				srv_conn->flags |= CO_FL_PRIVATE;
 	    }
 	} else if (srv_conn && txn->status == 407) {
 	    /* check for Negotiate/NTLM Proxy-Authenticate headers */
 	    ctx.idx = 0;
 	    while (http_find_header2("Proxy-Authenticate", 18, ci_head(rep), &txn->hdr_idx, &ctx)) {
-	            if ((ctx.vlen >= 9 && word_match(ctx.line + ctx.val, ctx.vlen, "Negotiate", 9)) ||
-	                  (ctx.vlen >= 4 && word_match(ctx.line + ctx.val, ctx.vlen, "NTLM", 4)))
+	            if ((ctx.vlen >= 4 && strncasecmp(ctx.line + ctx.val, "Nego", 4)) ||
+	                  (ctx.vlen >= 4 && strncasecmp(ctx.line + ctx.val, "NTLM", 4)))
 				srv_conn->flags |= CO_FL_PRIVATE;
 	    }
 	}
@@ -4844,6 +4872,7 @@
 	txn->status = 0;
 	rep->analysers   &= AN_RES_FLT_END;
 	s->req.analysers &= AN_REQ_FLT_END;
+	rep->analyse_exp = TICK_ETERNITY;
 	channel_auto_close(rep);
 	s->logs.logwait = 0;
 	s->logs.level = 0;
@@ -4947,6 +4976,8 @@
 				_HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
 			return_srv_prx_502:
 				rep->analysers &= AN_RES_FLT_END;
+				s->req.analysers &= AN_REQ_FLT_END;
+				rep->analyse_exp = TICK_ETERNITY;
 				txn->status = 502;
 				s->logs.t_data = -1; /* was not a valid response */
 				s->si[1].flags |= SI_FL_NOLINGER;
@@ -5077,6 +5108,9 @@
 		if (s->be->ck_opts & PR_CK_SECURE)
 			chunk_appendf(&trash, "; Secure");
 
+		if (s->be->cookie_attrs)
+			chunk_appendf(&trash, "; %s", s->be->cookie_attrs);
+
 		if (unlikely(http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.area, trash.data) < 0))
 			goto return_bad_resp;
 
diff --git a/src/proto_htx.c b/src/proto_htx.c
index 3de33b5..35925c4 100644
--- a/src/proto_htx.c
+++ b/src/proto_htx.c
@@ -329,7 +329,7 @@
 	 * the monitor-uri is defined by the frontend.
 	 */
 	if (unlikely((sess->fe->monitor_uri_len != 0) &&
-		     isteqi(htx_sl_req_uri(sl), ist2(sess->fe->monitor_uri, sess->fe->monitor_uri_len)))) {
+		     isteq(htx_sl_req_uri(sl), ist2(sess->fe->monitor_uri, sess->fe->monitor_uri_len)))) {
 		/*
 		 * We have found the monitor URI
 		 */
@@ -524,7 +524,7 @@
 	}
 
 	if (conn && (conn->flags & CO_FL_EARLY_DATA) &&
-	    (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_HANDSHAKE))) {
+	    (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_SSL_WAIT_HS))) {
 		struct http_hdr_ctx ctx;
 
 		ctx.blk = NULL;
@@ -1010,8 +1010,7 @@
 	 */
 	s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
 
-	if (!(req->flags & CF_READ_ERROR))
-		htx_reply_and_close(s, txn->status, htx_error_message(s));
+	htx_reply_and_close(s, txn->status, (!(req->flags & CF_READ_ERROR) ? htx_error_message(s) : NULL));
 
 	req->analysers &= AN_REQ_FLT_END;
 	req->analyse_exp = TICK_ETERNITY;
@@ -1204,11 +1203,8 @@
 
 	if (req->to_forward) {
 		if (req->to_forward == CHN_INFINITE_FORWARD) {
-			if (req->flags & CF_EOI) {
-				msg->msg_state = HTTP_MSG_DONE;
-				req->to_forward = 0;
-				goto done;
-			}
+			if (req->flags & CF_EOI)
+				msg->msg_state = HTTP_MSG_ENDING;
 		}
 		else {
 			/* We can't process the buffer's contents yet */
@@ -1217,8 +1213,14 @@
 		}
 	}
 
-	if (msg->msg_state >= HTTP_MSG_DONE)
-		goto done;
+	if (msg->msg_state >= HTTP_MSG_ENDING)
+		goto ending;
+
+	if (txn->meth == HTTP_METH_CONNECT) {
+		msg->msg_state = HTTP_MSG_ENDING;
+		goto ending;
+	}
+
 	/* Forward input data. We get it by removing all outgoing data not
 	 * forwarded yet from HTX data size. If there are some data filters, we
 	 * let them decide the amount of data to forward.
@@ -1235,11 +1237,8 @@
 			channel_htx_forward_forever(req, htx);
 	}
 
-	if (txn->meth == HTTP_METH_CONNECT) {
-		msg->msg_state = HTTP_MSG_TUNNEL;
-		goto done;
-	}
-
+	if (htx->data != co_data(req))
+		goto missing_data_or_waiting;
 
 	/* Check if the end-of-message is reached and if so, switch the message
 	 * in HTTP_MSG_ENDING state. Then if all data was marked to be
@@ -1249,16 +1248,11 @@
 		goto missing_data_or_waiting;
 
 	msg->msg_state = HTTP_MSG_ENDING;
-	if (htx->data != co_data(req))
-		goto missing_data_or_waiting;
-	msg->msg_state = HTTP_MSG_DONE;
-	req->to_forward = 0;
 
-  done:
-	/* other states, DONE...TUNNEL */
-	/* we don't want to forward closes on DONE except in tunnel mode. */
-	if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
-		channel_dont_close(req);
+  ending:
+	/* other states, ENDING...TUNNEL */
+	if (msg->msg_state >= HTTP_MSG_DONE)
+		goto done;
 
 	if (HAS_REQ_DATA_FILTERS(s)) {
 		ret = flt_http_end(s, msg);
@@ -1269,6 +1263,18 @@
 		}
 	}
 
+	if (txn->meth == HTTP_METH_CONNECT)
+		msg->msg_state = HTTP_MSG_TUNNEL;
+	else {
+		msg->msg_state = HTTP_MSG_DONE;
+		req->to_forward = 0;
+	}
+
+  done:
+	/* we don't want to forward closes on DONE except in tunnel mode. */
+	if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
+		channel_dont_close(req);
+
 	htx_end_request(s);
 	if (!(req->analysers & an_bit)) {
 		htx_end_response(s);
@@ -1407,6 +1413,7 @@
 	res->flags &= ~(CF_READ_ERROR | CF_READ_TIMEOUT | CF_SHUTR | CF_EOI | CF_READ_NULL | CF_SHUTR_NOW);
 	res->analysers = 0;
 	si->flags &= ~(SI_FL_ERR | SI_FL_EXP | SI_FL_RXBLK_SHUT);
+	s->flags &= ~SF_ADDR_SET;
 	stream_choose_redispatch(s);
 	si->exp = TICK_ETERNITY;
 	res->rex = TICK_ETERNITY;
@@ -1511,12 +1518,14 @@
 			}
 
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 502;
 
 			/* Check to see if the server refused the early data.
 			 * If so, just send a 425
 			 */
-			if (conn->err_code == CO_ER_SSL_EARLY_FAILED) {
+			if (conn && conn->err_code == CO_ER_SSL_EARLY_FAILED) {
 				if ((s->be->retry_type & PR_RE_EARLY_ERROR) &&
 				    (si_b->flags & SI_FL_L7_RETRY) &&
 				    do_l7_retry(s, si_b) == 0)
@@ -1548,6 +1557,8 @@
 			}
 
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 504;
 			s->si[1].flags |= SI_FL_NOLINGER;
 			htx_reply_and_close(s, txn->status, htx_error_message(s));
@@ -1567,6 +1578,8 @@
 				_HA_ATOMIC_ADD(&__objt_server(s->target)->counters.cli_aborts, 1);
 
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 400;
 			htx_reply_and_close(s, txn->status, htx_error_message(s));
 
@@ -1597,6 +1610,8 @@
 			}
 
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 			txn->status = 502;
 			s->si[1].flags |= SI_FL_NOLINGER;
 			htx_reply_and_close(s, txn->status, htx_error_message(s));
@@ -1615,6 +1630,8 @@
 
 			_HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
 			rep->analysers &= AN_RES_FLT_END;
+			s->req.analysers &= AN_REQ_FLT_END;
+			rep->analyse_exp = TICK_ETERNITY;
 
 			if (!(s->flags & SF_ERR_MASK))
 				s->flags |= SF_ERR_CLICL;
@@ -1786,10 +1803,17 @@
 
 		ctx.blk = NULL;
 		while (http_find_header(htx, hdr, &ctx, 0)) {
-			if ((ctx.value.len >= 9 && word_match(ctx.value.ptr, ctx.value.len, "Negotiate", 9)) ||
-			    (ctx.value.len >= 4 && word_match(ctx.value.ptr, ctx.value.len, "NTLM", 4))) {
+			/* If www-authenticate contains "Negotiate", "Nego2", or "NTLM",
+			 * possibly followed by blanks and a base64 string, the connection
+			 * is private. Since it's a mess to deal with, we only check for
+			 * values starting with "NTLM" or "Nego". Note that often multiple
+			 * headers are sent by the server there.
+			 */
+			if ((ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "Nego", 4) == 0) ||
+			    (ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "NTLM", 4) == 0)) {
 				sess->flags |= SESS_FL_PREFER_LAST;
 				srv_conn->flags |= CO_FL_PRIVATE;
+				break;
 			}
 		}
 	}
@@ -1818,6 +1842,8 @@
 	s->si[1].flags |= SI_FL_NOLINGER;
 	htx_reply_and_close(s, txn->status, htx_error_message(s));
 	rep->analysers &= AN_RES_FLT_END;
+	s->req.analysers &= AN_REQ_FLT_END;
+	rep->analyse_exp = TICK_ETERNITY;
 
 	if (!(s->flags & SF_ERR_MASK))
 		s->flags |= SF_ERR_PRXCOND;
@@ -2048,6 +2074,9 @@
 		if (s->be->ck_opts & PR_CK_SECURE)
 			chunk_appendf(&trash, "; Secure");
 
+		if (s->be->cookie_attrs)
+			chunk_appendf(&trash, "; %s", s->be->cookie_attrs);
+
 		if (unlikely(!http_add_header(htx, ist("Set-Cookie"), ist2(trash.area, trash.data))))
 			goto return_bad_resp;
 
@@ -2134,6 +2163,9 @@
 		s->flags |= SF_ERR_PRXCOND;
 	if (!(s->flags & SF_FINST_MASK))
 		s->flags |= SF_FINST_H;
+
+	s->req.analysers &= AN_REQ_FLT_END;
+	rep->analyse_exp = TICK_ETERNITY;
 	return 0;
 }
 
@@ -2205,11 +2237,8 @@
 
 	if (res->to_forward) {
 		if (res->to_forward == CHN_INFINITE_FORWARD) {
-			if (res->flags & CF_EOI) {
-				msg->msg_state = HTTP_MSG_DONE;
-				res->to_forward = 0;
-				goto done;
-			}
+			if (res->flags & CF_EOI)
+				msg->msg_state = HTTP_MSG_ENDING;
 		}
 		else {
 			/* We can't process the buffer's contents yet */
@@ -2218,8 +2247,14 @@
 		}
 	}
 
-	if (msg->msg_state >= HTTP_MSG_DONE)
-		goto done;
+	if (msg->msg_state >= HTTP_MSG_ENDING)
+		goto ending;
+
+	if ((txn->meth == HTTP_METH_CONNECT && txn->status == 200) || txn->status == 101 ||
+	    (!(msg->flags & HTTP_MSGF_XFER_LEN) && !HAS_RSP_DATA_FILTERS(s))) {
+		msg->msg_state = HTTP_MSG_ENDING;
+		goto ending;
+	}
 
 	/* Forward input data. We get it by removing all outgoing data not
 	 * forwarded yet from HTX data size. If there are some data filters, we
@@ -2237,10 +2272,12 @@
 			channel_htx_forward_forever(res, htx);
 	}
 
-	if ((txn->meth == HTTP_METH_CONNECT && txn->status == 200) || txn->status == 101 ||
-	    (!(msg->flags & HTTP_MSGF_XFER_LEN) && (res->flags & CF_SHUTR || !HAS_RSP_DATA_FILTERS(s)))) {
-		msg->msg_state = HTTP_MSG_TUNNEL;
-		goto done;
+	if (htx->data != co_data(res))
+		goto missing_data_or_waiting;
+
+	if (!(msg->flags & HTTP_MSGF_XFER_LEN) && res->flags & CF_SHUTR) {
+		msg->msg_state = HTTP_MSG_ENDING;
+		goto ending;
 	}
 
 	/* Check if the end-of-message is reached and if so, switch the message
@@ -2251,14 +2288,11 @@
 		goto missing_data_or_waiting;
 
 	msg->msg_state = HTTP_MSG_ENDING;
-	if (htx->data != co_data(res))
-		goto missing_data_or_waiting;
-	msg->msg_state = HTTP_MSG_DONE;
-	res->to_forward = 0;
 
-  done:
-	/* other states, DONE...TUNNEL */
-	channel_dont_close(res);
+  ending:
+	/* other states, ENDING...TUNNEL */
+	if (msg->msg_state >= HTTP_MSG_DONE)
+		goto done;
 
 	if (HAS_RSP_DATA_FILTERS(s)) {
 		ret = flt_http_end(s, msg);
@@ -2269,6 +2303,20 @@
 		}
 	}
 
+	if ((txn->meth == HTTP_METH_CONNECT && txn->status == 200) || txn->status == 101 ||
+	    !(msg->flags & HTTP_MSGF_XFER_LEN)) {
+		msg->msg_state = HTTP_MSG_TUNNEL;
+		goto ending;
+	}
+	else {
+		msg->msg_state = HTTP_MSG_DONE;
+		res->to_forward = 0;
+	}
+
+  done:
+
+	channel_dont_close(res);
+
 	htx_end_response(s);
 	if (!(res->analysers & an_bit)) {
 		htx_end_request(s);
@@ -2544,6 +2592,8 @@
 		close = 1;
 
 	htx = htx_from_buf(&res->buf);
+	/* Trim any possible response */
+	channel_htx_truncate(&s->res, htx);
 	flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
 	sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), status, reason);
 	if (!sl)
@@ -2571,8 +2621,7 @@
 	if (!htx_add_endof(htx, HTX_BLK_EOH) || !htx_add_endof(htx, HTX_BLK_EOM))
 		goto fail;
 
-	/* let's log the request time */
-	s->logs.tv_request = now;
+	htx_to_buf(htx, &res->buf);
 
 	data = htx->data - co_data(res);
 	c_adv(res, data);
@@ -2587,13 +2636,19 @@
 	channel_auto_read(res);
 	channel_auto_close(res);
 	channel_shutr_now(res);
+	if (rule->flags & REDIRECT_FLAG_FROM_REQ) {
+		/* let's log the request time */
+		s->logs.tv_request = now;
+		req->analysers &= AN_REQ_FLT_END;
 
-	req->analysers &= AN_REQ_FLT_END;
+		if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
+			_HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
+	}
 
 	if (!(s->flags & SF_ERR_MASK))
 		s->flags |= SF_ERR_LOCAL;
 	if (!(s->flags & SF_FINST_MASK))
-		s->flags |= SF_FINST_R;
+		s->flags |= ((rule->flags & REDIRECT_FLAG_FROM_REQ) ? SF_FINST_R : SF_FINST_H);
 
 	free_trash_chunk(chunk);
 	return 1;
diff --git a/src/proto_sockpair.c b/src/proto_sockpair.c
index e7dd670..23c8f95 100644
--- a/src/proto_sockpair.c
+++ b/src/proto_sockpair.c
@@ -158,7 +158,7 @@
 	listener->state = LI_LISTEN;
 
 	fd_insert(fd, listener, listener->proto->accept,
-	          thread_mask(listener->bind_conf->bind_thread));
+	          thread_mask(listener->bind_conf->bind_thread) & all_threads_mask);
 
 	return err;
 
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index cfd58e6..2f2e0bf 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -832,8 +832,8 @@
 			if (getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &default_tcp_maxseg,
 			    &ready_len) == -1)
 				ha_warning("Failed to get the default value of TCP_MAXSEG\n");
+			close(fd);
 		}
-		close(fd);
 	}
 	if (default_tcp6_maxseg == -1) {
 		default_tcp6_maxseg = -2;
@@ -989,9 +989,9 @@
 			defaultmss = default_tcp6_maxseg;
 
 		getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &tmpmaxseg, &len);
-		if (tmpmaxseg != defaultmss && setsockopt(fd, IPPROTO_TCP,
-						TCP_MAXSEG, &defaultmss,
-						sizeof(defaultmss)) == -1) {
+		if (defaultmss > 0 &&
+		    tmpmaxseg != defaultmss &&
+		    setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &defaultmss, sizeof(defaultmss)) == -1) {
 			msg = "cannot set MSS";
 			err |= ERR_WARN;
 		}
@@ -1082,7 +1082,7 @@
 	listener->state = LI_LISTEN;
 
 	fd_insert(fd, listener, listener->proto->accept,
-	          thread_mask(listener->bind_conf->bind_thread));
+	          thread_mask(listener->bind_conf->bind_thread) & all_threads_mask);
 
  tcp_return:
 	if (msg && errlen) {
@@ -1350,9 +1350,10 @@
 	if (strm) {
 		channel_abort(&strm->req);
 		channel_abort(&strm->res);
-		strm->req.analysers = 0;
-		strm->res.analysers = 0;
-		_HA_ATOMIC_ADD(&strm->be->be_counters.denied_req, 1);
+		strm->req.analysers &= AN_REQ_FLT_END;
+		strm->res.analysers &= AN_RES_FLT_END;
+		if (strm->flags & SF_BE_ASSIGNED)
+			_HA_ATOMIC_ADD(&strm->be->be_counters.denied_req, 1);
 		if (!(strm->flags & SF_ERR_MASK))
 			strm->flags |= SF_ERR_PRXCOND;
 		if (!(strm->flags & SF_FINST_MASK))
diff --git a/src/proto_udp.c b/src/proto_udp.c
index c4434d7..810c314 100644
--- a/src/proto_udp.c
+++ b/src/proto_udp.c
@@ -26,7 +26,7 @@
 
 	if (fd_recv_ready(fd))
 		dgram->data->recv(dgram);
-	else if (fd_send_ready(fd))
+	if (fd_send_ready(fd))
 		dgram->data->send(dgram);
 
 	return;
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 7263240..df813a1 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -187,6 +187,7 @@
 	struct sockaddr_un addr;
 	const char *msg = NULL;
 	const char *path;
+	int maxpathlen;
 	int ext, ready;
 	socklen_t ready_len;
 	int err;
@@ -205,6 +206,8 @@
 		listener->fd = uxst_find_compatible_fd(listener);
 	path = ((struct sockaddr_un *)&listener->addr)->sun_path;
 
+	maxpathlen = MIN(MAXPATHLEN, sizeof(addr.sun_path));
+
 	/* if the listener already has an fd assigned, then we were offered the
 	 * fd by an external process (most likely the parent), and we don't want
 	 * to create a new socket. However we still want to set a few flags on
@@ -216,17 +219,17 @@
 		goto fd_ready;
 
 	if (path[0]) {
-		ret = snprintf(tempname, MAXPATHLEN, "%s.%d.tmp", path, pid);
-		if (ret < 0 || ret >= MAXPATHLEN) {
+		ret = snprintf(tempname, maxpathlen, "%s.%d.tmp", path, pid);
+		if (ret < 0 || ret >= maxpathlen) {
 			err |= ERR_FATAL | ERR_ALERT;
-			msg = "name too long for UNIX socket";
+			msg = "name too long for UNIX socket (limit usually 97)";
 			goto err_return;
 		}
 
-		ret = snprintf(backname, MAXPATHLEN, "%s.%d.bak", path, pid);
-		if (ret < 0 || ret >= MAXPATHLEN) {
+		ret = snprintf(backname, maxpathlen, "%s.%d.bak", path, pid);
+		if (ret < 0 || ret >= maxpathlen) {
 			err |= ERR_FATAL | ERR_ALERT;
-			msg = "name too long for UNIX socket";
+			msg = "name too long for UNIX socket (limit usually 97)";
 			goto err_return;
 		}
 
@@ -250,7 +253,7 @@
 			goto err_return;
 		}
 
-		strncpy(addr.sun_path, tempname, sizeof(addr.sun_path));
+		strncpy(addr.sun_path, tempname, sizeof(addr.sun_path) - 1);
 		addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
 	}
 	else {
@@ -340,7 +343,7 @@
 	listener->state = LI_LISTEN;
 
 	fd_insert(fd, listener, listener->proto->accept,
-	          thread_mask(listener->bind_conf->bind_thread));
+	          thread_mask(listener->bind_conf->bind_thread) & all_threads_mask);
 
 	return err;
 
diff --git a/src/proxy.c b/src/proxy.c
index 301d4d6..d1c926b 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1131,6 +1131,8 @@
 	struct task *task;
 
 	stopping = 1;
+	/* disable busy polling to avoid cpu eating for the new process */
+	global.tune.options &= ~GTUNE_BUSY_POLLING;
 	if (tick_isset(global.hard_stop_after)) {
 		task = task_new(MAX_THREADS_MASK);
 		if (task) {
@@ -1148,10 +1150,10 @@
 		if (p->state == PR_STSTOPPED &&
 		    !LIST_ISEMPTY(&p->conf.listeners) &&
 		    LIST_ELEM(p->conf.listeners.n,
-		    struct listener *, by_fe)->state >= LI_ZOMBIE) {
+		    struct listener *, by_fe)->state > LI_ASSIGNED) {
 			struct listener *l;
 			list_for_each_entry(l, &p->conf.listeners, by_fe) {
-				if (l->state >= LI_ZOMBIE)
+				if (l->state > LI_ASSIGNED)
 					close(l->fd);
 				l->state = LI_INIT;
 			}
@@ -1507,6 +1509,14 @@
 				s->flags |= SF_HTX;
 			}
 		}
+		else if (IS_HTX_STRM(s) && be->mode != PR_MODE_HTTP) {
+			/* If a TCP backend is assgiend to an HTX stream, return
+			 * an error. It may happens for a new stream on a
+			 * previously upgraded connnections. */
+			if (!(s->flags & SF_ERR_MASK))
+				s->flags |= SF_ERR_INTERNAL;
+			return 0;
+		}
 
 		/* If an LB algorithm needs to access some pre-parsed body contents,
 		 * we must not start to forward anything until the connection is
@@ -1575,7 +1585,7 @@
 	es->buf_len = buf_len;
 	es->ev_id   = ev_id;
 
-	len1 = b_size(buf) - buf_len;
+	len1 = b_size(buf) - b_peek_ofs(buf, buf_out);
 	if (len1 > buf_len)
 		len1 = buf_len;
 
@@ -1792,14 +1802,14 @@
 	return 0;
 }
 
-/* dumps server state information into <buf> for all the servers found in backend cli.p0.
+/* dumps server state information for all the servers found in backend cli.p0.
  * These information are all the parameters which may change during HAProxy runtime.
  * By default, we only export to the last known server state file format.
  * These information can be used at next startup to recover same level of server state.
  * It uses the proxy pointer from cli.p0, the proxy's id from cli.i0 and the server's
  * pointer from cli.p1.
  */
-static int dump_servers_state(struct stream_interface *si, struct buffer *buf)
+static int dump_servers_state(struct stream_interface *si)
 {
 	struct appctx *appctx = __objt_appctx(si->end);
 	struct proxy *px = appctx->ctx.cli.p0;
@@ -1841,7 +1851,7 @@
 		if (srv->srvrq && srv->srvrq->name)
 			srvrecord = srv->srvrq->name;
 
-		chunk_appendf(buf,
+		chunk_printf(&trash,
 				"%d %s "
 				"%d %s %s "
 				"%d %d %d %d %ld "
@@ -1893,7 +1903,7 @@
 		curproxy = appctx->ctx.cli.p0;
 		/* servers are only in backends */
 		if (curproxy->cap & PR_CAP_BE) {
-			if (!dump_servers_state(si, &trash))
+			if (!dump_servers_state(si))
 				return 0;
 		}
 		/* only the selected proxy is dumped */
diff --git a/src/raw_sock.c b/src/raw_sock.c
index cc99669..d8a27cd 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -69,9 +69,7 @@
 	if (!conn_ctrl_ready(conn))
 		return 0;
 
-	if (!fd_recv_ready(conn->handle.fd))
-		return 0;
-
+	conn->flags &= ~CO_FL_WAIT_ROOM;
 	conn_refresh_polling_flags(conn);
 	errno = 0;
 
@@ -237,6 +235,7 @@
 	if (!fd_recv_ready(conn->handle.fd))
 		return 0;
 
+	conn->flags &= ~CO_FL_WAIT_ROOM;
 	conn_refresh_polling_flags(conn);
 	errno = 0;
 
diff --git a/src/sample.c b/src/sample.c
index 8094b04..535230f 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -620,6 +620,7 @@
 			break;
 		}
 	}
+	smp->data.type = SMP_T_STR;
 	return 1;
 }
 
@@ -921,6 +922,8 @@
 
 	while (1) {
 		struct sample_conv_expr *conv_expr;
+		int err_arg;
+		int argcnt;
 
 		if (*endt == ')') /* skip last closing parenthesis */
 			endt++;
@@ -963,6 +966,7 @@
 		endt = endw;
 		if (*endt == '(') {
 			/* look for the end of this term */
+			endt = ++endw;
 			while (*endt && *endt != ')')
 				endt++;
 			if (*endt != ')') {
@@ -990,31 +994,24 @@
 		LIST_ADDQ(&(expr->conv_exprs), &(conv_expr->list));
 		conv_expr->conv = conv;
 
-		if (endt != endw) {
-			int err_arg;
-
-			if (!conv->arg_mask) {
-				memprintf(err_msg, "converter '%s' does not support any args", ckw);
-				goto out_error;
-			}
+		al->kw = expr->fetch->kw;
+		al->conv = conv_expr->conv->kw;
+		argcnt = make_arg_list(endw, endt - endw, conv->arg_mask, &conv_expr->arg_p, err_msg, NULL, &err_arg, al);
+		if (argcnt < 0) {
+			memprintf(err_msg, "invalid arg %d in converter '%s' : %s", err_arg+1, ckw, *err_msg);
+			goto out_error;
+		}
 
-			al->kw = expr->fetch->kw;
-			al->conv = conv_expr->conv->kw;
-			if (make_arg_list(endw + 1, endt - endw - 1, conv->arg_mask, &conv_expr->arg_p, err_msg, NULL, &err_arg, al) < 0) {
-				memprintf(err_msg, "invalid arg %d in converter '%s' : %s", err_arg+1, ckw, *err_msg);
-				goto out_error;
-			}
+		if (argcnt && !conv->arg_mask) {
+			memprintf(err_msg, "converter '%s' does not support any args", ckw);
+			goto out_error;
+		}
 
-			if (!conv_expr->arg_p)
-				conv_expr->arg_p = empty_arg_list;
+		if (!conv_expr->arg_p)
+			conv_expr->arg_p = empty_arg_list;
 
-			if (conv->val_args && !conv->val_args(conv_expr->arg_p, conv, file, line, err_msg)) {
-				memprintf(err_msg, "invalid args in converter '%s' : %s", ckw, *err_msg);
-				goto out_error;
-			}
-		}
-		else if (ARGM(conv->arg_mask)) {
-			memprintf(err_msg, "missing args for converter '%s'", ckw);
+		if (conv->val_args && !conv->val_args(conv_expr->arg_p, conv, file, line, err_msg)) {
+			memprintf(err_msg, "invalid args in converter '%s' : %s", ckw, *err_msg);
 			goto out_error;
 		}
 	}
@@ -1463,7 +1460,7 @@
 
 			else {
 				/* Display the displayable chars*. */
-				fprintf(stderr, "<");
+				fputc('<', stderr);
 				for (i = 0; i < tmp.data.u.str.data; i++) {
 					if (isprint(tmp.data.u.str.area[i]))
 						fputc(tmp.data.u.str.area[i],
@@ -1471,9 +1468,10 @@
 					else
 						fputc('.', stderr);
 				}
+				fputc('>', stderr);
 			}
-			fprintf(stderr, ">\n");
 		}
+		fputc('\n', stderr);
 	}
 	return 1;
 }
@@ -1939,7 +1937,8 @@
 		}
 		else {
 			len = 1;
-			str = (char *)&c;
+			_str[0] = c;
+			str = _str;
 		}
 
 		/* Check length */
@@ -2948,7 +2947,7 @@
 static int
 smp_fetch_rand(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	smp->data.u.sint = random();
+	smp->data.u.sint = ha_random();
 
 	/* reduce if needed. Don't do a modulo, use all bits! */
 	if (args && args[0].type == ARGT_SINT)
@@ -2972,6 +2971,9 @@
 static int
 smp_fetch_cpu_calls(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	if (!smp->strm)
+		return 0;
+
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = smp->strm->task->calls;
 	return 1;
@@ -2981,6 +2983,9 @@
 static int
 smp_fetch_cpu_ns_avg(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	if (!smp->strm)
+		return 0;
+
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = smp->strm->task->calls ? smp->strm->task->cpu_time / smp->strm->task->calls : 0;
 	return 1;
@@ -2990,6 +2995,9 @@
 static int
 smp_fetch_cpu_ns_tot(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	if (!smp->strm)
+		return 0;
+
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = smp->strm->task->cpu_time;
 	return 1;
@@ -2999,6 +3007,9 @@
 static int
 smp_fetch_lat_ns_avg(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	if (!smp->strm)
+		return 0;
+
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = smp->strm->task->calls ? smp->strm->task->lat_time / smp->strm->task->calls : 0;
 	return 1;
@@ -3008,6 +3019,9 @@
 static int
 smp_fetch_lat_ns_tot(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	if (!smp->strm)
+		return 0;
+
 	smp->data.type = SMP_T_SINT;
 	smp->data.u.sint = smp->strm->task->lat_time;
 	return 1;
@@ -3026,12 +3040,14 @@
 {
 	if (strcasecmp(args[0].data.str.area, "true") == 0 ||
 	    strcasecmp(args[0].data.str.area, "1") == 0) {
+		free(args[0].data.str.area);
 		args[0].type = ARGT_SINT;
 		args[0].data.sint = 1;
 		return 1;
 	}
 	if (strcasecmp(args[0].data.str.area, "false") == 0 ||
 	    strcasecmp(args[0].data.str.area, "0") == 0) {
+		free(args[0].data.str.area);
 		args[0].type = ARGT_SINT;
 		args[0].data.sint = 0;
 		return 1;
@@ -3097,6 +3113,8 @@
 
 	meth = find_http_meth(args[0].data.str.area, args[0].data.str.data);
 	if (meth != HTTP_METH_OTHER) {
+		free(args[0].data.str.area);
+
 		args[0].type = ARGT_SINT;
 		args[0].data.sint = meth;
 	} else {
@@ -3160,7 +3178,7 @@
 
 		while (byte < 4) {
 			while (bits < 32) {
-				last |= (uint64_t)random() << bits;
+				last |= (uint64_t)ha_random() << bits;
 				bits += rand_max_bits;
 			}
 			rnd[byte++] = last;
diff --git a/src/server.c b/src/server.c
index 776ba10..206ba5d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1634,6 +1634,15 @@
 		srv->ssl_ctx.verify_host = strdup(src->ssl_ctx.verify_host);
 	if (src->ssl_ctx.ciphers != NULL)
 		srv->ssl_ctx.ciphers = strdup(src->ssl_ctx.ciphers);
+	if (src->ssl_ctx.options)
+		srv->ssl_ctx.options = src->ssl_ctx.options;
+	if (src->ssl_ctx.methods.flags)
+		srv->ssl_ctx.methods.flags = src->ssl_ctx.methods.flags;
+	if (src->ssl_ctx.methods.min)
+		srv->ssl_ctx.methods.min = src->ssl_ctx.methods.min;
+	if (src->ssl_ctx.methods.max)
+		srv->ssl_ctx.methods.max = src->ssl_ctx.methods.max;
+
 #if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL)
 	if (src->ssl_ctx.ciphersuites != NULL)
 		srv->ssl_ctx.ciphersuites = strdup(src->ssl_ctx.ciphersuites);
@@ -1732,7 +1741,8 @@
 		srv->cklen  = src->cklen;
 	}
 	srv->use_ssl                  = src->use_ssl;
-	srv->check.addr = srv->agent.addr = src->check.addr;
+	srv->check.addr               = src->check.addr;
+	srv->agent.addr               = src->agent.addr;
 	srv->check.use_ssl            = src->check.use_ssl;
 	srv->check.port               = src->check.port;
 	srv->check.sni                = src->check.sni;
@@ -1848,154 +1858,6 @@
 	return srv;
 }
 
-/*
- * Validate <srv> server health-check settings.
- * Returns 0 if everything is OK, -1 if not.
- */
-static int server_healthcheck_validate(const char *file, int linenum, struct server *srv)
-{
-	struct tcpcheck_rule *r = NULL;
-	struct list *l;
-
-	/*
-	 * We need at least a service port, a check port or the first tcp-check rule must
-	 * be a 'connect' one when checking an IPv4/IPv6 server.
-	 */
-	if ((srv_check_healthcheck_port(&srv->check) != 0) ||
-	    (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
-		return 0;
-
-	r = (struct tcpcheck_rule *)srv->proxy->tcpcheck_rules.n;
-	if (!r) {
-		ha_alert("parsing [%s:%d] : server %s has neither service port nor check port. "
-			 "Check has been disabled.\n",
-			 file, linenum, srv->id);
-		return -1;
-	}
-
-	/* search the first action (connect / send / expect) in the list */
-	l = &srv->proxy->tcpcheck_rules;
-	list_for_each_entry(r, l, list) {
-		if (r->action != TCPCHK_ACT_COMMENT)
-			break;
-	}
-
-	if ((r->action != TCPCHK_ACT_CONNECT) || !r->port) {
-		ha_alert("parsing [%s:%d] : server %s has neither service port nor check port "
-			 "nor tcp_check rule 'connect' with port information. Check has been disabled.\n",
-			 file, linenum, srv->id);
-		return -1;
-	}
-
-	/* scan the tcp-check ruleset to ensure a port has been configured */
-	l = &srv->proxy->tcpcheck_rules;
-	list_for_each_entry(r, l, list) {
-		if ((r->action == TCPCHK_ACT_CONNECT) && (!r->port)) {
-			ha_alert("parsing [%s:%d] : server %s has neither service port nor check port, "
-				 "and a tcp_check rule 'connect' with no port information. Check has been disabled.\n",
-				 file, linenum, srv->id);
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-/*
- * Initialize <srv> health-check structure.
- * Returns the error string in case of memory allocation failure, NULL if not.
- */
-static const char *do_health_check_init(struct server *srv, int check_type, int state)
-{
-	const char *ret;
-
-	if (!srv->do_check)
-		return NULL;
-
-	ret = init_check(&srv->check, check_type);
-	if (ret)
-		return ret;
-
-	srv->check.state |= state;
-	global.maxsock++;
-
-	return NULL;
-}
-
-static int server_health_check_init(const char *file, int linenum,
-                                    struct server *srv, struct proxy *curproxy)
-{
-	const char *ret;
-
-	if (!srv->do_check)
-		return 0;
-
-	if (srv->trackit) {
-		ha_alert("parsing [%s:%d]: unable to enable checks and tracking at the same time!\n",
-			 file, linenum);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
-	if (server_healthcheck_validate(file, linenum, srv) < 0)
-		return ERR_ALERT | ERR_ABORT;
-
-	/* note: check type will be set during the config review phase */
-	ret = do_health_check_init(srv, 0, CHK_ST_CONFIGURED | CHK_ST_ENABLED);
-	if (ret) {
-		ha_alert("parsing [%s:%d] : %s.\n", file, linenum, ret);
-		return ERR_ALERT | ERR_ABORT;
-	}
-
-	return 0;
-}
-
-/*
- * Initialize <srv> agent check structure.
- * Returns the error string in case of memory allocation failure, NULL if not.
- */
-static const char *do_server_agent_check_init(struct server *srv, int state)
-{
-	const char *ret;
-
-	if (!srv->do_agent)
-		return NULL;
-
-	ret = init_check(&srv->agent, PR_O2_LB_AGENT_CHK);
-	if (ret)
-		return ret;
-
-	if (!srv->agent.inter)
-		srv->agent.inter = srv->check.inter;
-
-	srv->agent.state |= state;
-	global.maxsock++;
-
-	return NULL;
-}
-
-static int server_agent_check_init(const char *file, int linenum,
-                                   struct server *srv, struct proxy *curproxy)
-{
-	const char *ret;
-
-	if (!srv->do_agent)
-		return 0;
-
-	if (!srv->agent.port) {
-		ha_alert("parsing [%s:%d] : server %s does not have agent port. Agent check has been disabled.\n",
-			  file, linenum, srv->id);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
-	ret = do_server_agent_check_init(srv, CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT);
-	if (ret) {
-		ha_alert("parsing [%s:%d] : %s.\n", file, linenum, ret);
-		return ERR_ALERT | ERR_ABORT;
-	}
-
-	return 0;
-}
-
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 static int server_sni_expr_init(const char *file, int linenum, char **args, int cur_arg,
                                 struct server *srv, struct proxy *proxy)
@@ -2025,13 +1887,22 @@
 static int server_finalize_init(const char *file, int linenum, char **args, int cur_arg,
                                 struct server *srv, struct proxy *px)
 {
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 	int ret;
+#endif
 
-	if ((ret = server_health_check_init(file, linenum, srv, px)) != 0 ||
-	    (ret = server_agent_check_init(file, linenum, srv, px)) != 0) {
-		return ret;
+	if (srv->do_check && srv->trackit) {
+		ha_alert("parsing [%s:%d]: unable to enable checks and tracking at the same time!\n",
+			 file, linenum);
+		return ERR_ALERT | ERR_FATAL;
 	}
 
+	if (srv->do_agent && !srv->agent.port) {
+		ha_alert("parsing [%s:%d] : server %s does not have agent port. Agent check has been disabled.\n",
+			  file, linenum, srv->id);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 	if ((ret = server_sni_expr_init(file, linenum, args, cur_arg, srv, px)) != 0)
 		return ret;
@@ -2099,9 +1970,6 @@
 	struct server *newsrv;
 
 	for (i = srv->tmpl_info.nb_low + 1; i <= srv->tmpl_info.nb_high; i++) {
-		int check_init_state;
-		int agent_init_state;
-
 		newsrv = new_server(px);
 		if (!newsrv)
 			goto err;
@@ -2118,14 +1986,6 @@
 		/* Set this new server ID. */
 		srv_set_id_from_prefix(newsrv, srv->tmpl_info.prefix, i);
 
-		/* Initial checks states. */
-		check_init_state = CHK_ST_CONFIGURED | CHK_ST_ENABLED;
-		agent_init_state = CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
-
-		if (do_health_check_init(newsrv, px->options2 & PR_O2_CHK_ANY, check_init_state) ||
-		    do_server_agent_check_init(newsrv, agent_init_state))
-			goto err;
-
 		/* Linked backwards first. This will be restablished after parsing. */
 		newsrv->next = px->srv;
 		px->srv = newsrv;
@@ -2147,7 +2007,8 @@
 	return i - srv->tmpl_info.nb_low;
 }
 
-int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy, int parse_addr)
+int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy,
+                 struct proxy *defproxy, int parse_addr, int in_peers_section, int initial_resolve)
 {
 	struct server *newsrv = NULL;
 	const char *err = NULL;
@@ -2177,11 +2038,16 @@
 		/* There is no mandatory first arguments for default server. */
 		if (srv && parse_addr) {
 			if (!*args[2]) {
-				/* 'server' line number of argument check. */
-				ha_alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
-					  file, linenum, args[0]);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
+				if (in_peers_section) {
+					return 0;
+				}
+				else {
+					/* 'server' line number of argument check. */
+					ha_alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
+						  file, linenum, args[0]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
 			}
 
 			err = invalid_char(args[1]);
@@ -2258,7 +2124,7 @@
 			if (!parse_addr)
 				goto skip_addr;
 
-			sk = str2sa_range(args[cur_arg], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0);
+			sk = str2sa_range(args[cur_arg], &port, &port1, &port2, &errmsg, NULL, &fqdn, initial_resolve);
 			if (!sk) {
 				ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 				err_code |= ERR_ALERT | ERR_FATAL;
diff --git a/src/session.c b/src/session.c
index b525bbe..e3d22f8 100644
--- a/src/session.c
+++ b/src/session.c
@@ -286,6 +286,12 @@
 	if (conn_complete_session(cli_conn) >= 0)
 		return 1;
 
+	/* if we reach here we have deliberately decided not to keep this
+	 * session (e.g. tcp-request rule), so that's not an error we should
+	 * try to protect against.
+	 */
+	ret = 0;
+
 	/* error unrolling */
  out_free_sess:
 	 /* prevent call to listener_release during session_free. It will be
diff --git a/src/shctx.c b/src/shctx.c
index fe1b74a..ae9cc1f 100644
--- a/src/shctx.c
+++ b/src/shctx.c
@@ -308,6 +308,10 @@
 	if (maxblocks <= 0)
 		return 0;
 
+	/* make sure to align the records on a pointer size */
+	blocksize = (blocksize + sizeof(void *) - 1) & -sizeof(void *);
+	extra     = (extra     + sizeof(void *) - 1) & -sizeof(void *);
+
 #ifndef USE_PRIVATE_CACHE
 	if (shared)
 		maptype = MAP_SHARED;
diff --git a/src/signal.c b/src/signal.c
index 20236fa..288ef00 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -114,11 +114,19 @@
 	/* man sigprocmask: If SIGBUS, SIGFPE, SIGILL, or SIGSEGV are
 	   generated while they are blocked, the result is undefined, unless
 	   the signal was generated by kill(2),
-	   sigqueue(3), or raise(3) */
+	   sigqueue(3), or raise(3).
+	   Do not ignore WDTSIG or DEBUGSIG either, or it may deadlock the
+	   watchdog */
 	sigdelset(&blocked_sig, SIGBUS);
 	sigdelset(&blocked_sig, SIGFPE);
 	sigdelset(&blocked_sig, SIGILL);
 	sigdelset(&blocked_sig, SIGSEGV);
+#ifdef DEBUGSIG
+	sigdelset(&blocked_sig, DEBUGSIG);
+#endif
+#ifdef WDTSIG
+	sigdelset(&blocked_sig, WDTSIG);
+#endif
 	for (sig = 0; sig < MAX_SIGNAL; sig++)
 		LIST_INIT(&signal_state[sig].handlers);
 }
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index bab4326..4b74c78 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -217,7 +217,7 @@
 	struct wait_event *recv_wait;
 	struct wait_event *send_wait;
 	int xprt_st;                  /* transport layer state, initialized to zero */
-	int tmp_early_data;           /* 1st byte of early data, if any */
+	struct buffer early_buf;      /* buffer to store the early data received */
 	int sent_early_data;          /* Amount of early data we sent so far */
 
 };
@@ -1595,7 +1595,7 @@
 			ctx->xprt_st |= SSL_SOCK_CAEDEPTH_TO_ST(depth);
 		}
 
-		if (__objt_listener(conn->target)->bind_conf->ca_ignerr & (1ULL << err)) {
+		if (err < 64 && __objt_listener(conn->target)->bind_conf->ca_ignerr & (1ULL << err)) {
 			ssl_sock_dump_errors(conn);
 			ERR_clear_error();
 			return 1;
@@ -1609,7 +1609,7 @@
 		ctx->xprt_st |= SSL_SOCK_CRTERROR_TO_ST(err);
 
 	/* check if certificate error needs to be ignored */
-	if (__objt_listener(conn->target)->bind_conf->crt_ignerr & (1ULL << err)) {
+	if (err < 64 && __objt_listener(conn->target)->bind_conf->crt_ignerr & (1ULL << err)) {
 		ssl_sock_dump_errors(conn);
 		ERR_clear_error();
 		return 1;
@@ -2358,32 +2358,16 @@
 	}
 	trash.area[i] = 0;
 
-	/* lookup in full qualified names */
-	node = ebst_lookup(&s->sni_ctx, trash.area);
 
-	/* lookup a not neg filter */
-	for (n = node; n; n = ebmb_next_dup(n)) {
-		if (!container_of(n, struct sni_ctx, name)->neg) {
-			switch(container_of(n, struct sni_ctx, name)->kinfo.sig) {
-			case TLSEXT_signature_ecdsa:
-				if (!node_ecdsa)
-					node_ecdsa = n;
-				break;
-			case TLSEXT_signature_rsa:
-				if (!node_rsa)
-					node_rsa = n;
-				break;
-			default: /* TLSEXT_signature_anonymous|dsa */
-				if (!node_anonymous)
-					node_anonymous = n;
-				break;
-			}
-		}
-	}
-	if (wildp) {
-		/* lookup in wildcards names */
-		node = ebst_lookup(&s->sni_w_ctx, wildp);
+	for (i = 0; i < 2; i++) {
+		if (i == 0) 	/* lookup in full qualified names */
+			node = ebst_lookup(&s->sni_ctx, trash.area);
+		else if (i == 1 && wildp) /* lookup in wildcards names */
+			node = ebst_lookup(&s->sni_w_ctx, wildp);
+		else
+			break;
 		for (n = node; n; n = ebmb_next_dup(n)) {
+			/* lookup a not neg filter */
 			if (!container_of(n, struct sni_ctx, name)->neg) {
 				switch(container_of(n, struct sni_ctx, name)->kinfo.sig) {
 				case TLSEXT_signature_ecdsa:
@@ -2401,18 +2385,17 @@
 				}
 			}
 		}
-	}
-	/* select by key_signature priority order */
-	node = (has_ecdsa_sig && node_ecdsa) ? node_ecdsa
-		: ((has_rsa_sig && node_rsa) ? node_rsa
-		   : (node_anonymous ? node_anonymous
-		      : (node_ecdsa ? node_ecdsa      /* no ecdsa signature case (< TLSv1.2) */
-			 : node_rsa                   /* no rsa signature case (far far away) */
-			 )));
-	if (node) {
-		/* switch ctx */
-		struct ssl_bind_conf *conf = container_of(node, struct sni_ctx, name)->conf;
-		ssl_sock_switchctx_set(ssl, container_of(node, struct sni_ctx, name)->ctx);
+		/* select by key_signature priority order */
+		node = (has_ecdsa_sig && node_ecdsa) ? node_ecdsa
+			: ((has_rsa_sig && node_rsa) ? node_rsa
+			   : (node_anonymous ? node_anonymous
+			      : (node_ecdsa ? node_ecdsa      /* no ecdsa signature case (< TLSv1.2) */
+				 : node_rsa                   /* no rsa signature case (far far away) */
+				 )));
+		if (node) {
+			/* switch ctx */
+			struct ssl_bind_conf *conf = container_of(node, struct sni_ctx, name)->conf;
+			ssl_sock_switchctx_set(ssl, container_of(node, struct sni_ctx, name)->ctx);
 			if (conf) {
 				methodVersions[conf->ssl_methods.min].ssl_set_version(ssl, SET_MIN);
 				methodVersions[conf->ssl_methods.max].ssl_set_version(ssl, SET_MAX);
@@ -2420,6 +2403,7 @@
 					allow_early = 1;
 			}
 			goto allow_early;
+		}
 	}
 #if (!defined SSL_NO_GENERATE_CERTIFICATES)
 	if (s->generate_certs && ssl_sock_generate_certificate(trash.area, s, ssl)) {
@@ -3694,25 +3678,23 @@
 					}
 
 					if (is_bundle) {
-						char dp[MAXPATHLEN+1] = {0}; /* this will be the filename w/o the keytype */
 						int dp_len;
 
 						dp_len = end - de->d_name;
-						snprintf(dp, dp_len + 1, "%s", de->d_name);
 
 						/* increment i and free de until we get to a non-bundle cert
 						 * Note here that we look at de_list[i + 1] before freeing de
-						 * this is important since ignore_entry will free de
+						 * this is important since ignore_entry will free de. This also
+						 * guarantees that de->d_name continues to hold the same prefix.
 						 */
-						while (i + 1 < n && !strncmp(de_list[i + 1]->d_name, dp, dp_len)) {
+						while (i + 1 < n && !strncmp(de_list[i + 1]->d_name, de->d_name, dp_len)) {
 							free(de);
 							i++;
 							de = de_list[i];
 						}
 
-						snprintf(fp, sizeof(fp), "%s/%s", path, dp);
+						snprintf(fp, sizeof(fp), "%s/%.*s", path, dp_len, de->d_name);
 						cfgerr |= ssl_sock_load_multi_cert(fp, bind_conf, NULL, NULL, 0, err);
-
 						/* Successfully processed the bundle */
 						goto ignore_entry;
 					}
@@ -3821,7 +3803,7 @@
 				/* end of string, end of loop */
 				*line = 0;
 				break;
-			} else if (isspace(*line)) {
+			} else if (isspace((unsigned char)*line)) {
 				newarg = 1;
 				*line = 0;
 			} else if (*line == '[') {
@@ -4015,9 +3997,15 @@
 	if (min == max)
 		methodVersions[min].ctx_set_version(ctx, SET_SERVER);
 	else
-		for (i = CONF_TLSV_MIN; i <= CONF_TLSV_MAX; i++)
+		for (i = CONF_TLSV_MIN; i <= CONF_TLSV_MAX; i++) {
+			/* clear every version flags in case SSL_CTX_new()
+			 * returns an SSL_CTX with disabled versions */
+			SSL_CTX_clear_options(ctx, methodVersions[i].option);
+
 			if (flags & methodVersions[i].flag)
 				options |= methodVersions[i].option;
+
+		}
 #else   /* openssl >= 1.1.0 */
 	/* set the max_version is required to cap TLS version or activate new TLS (v1.3) */
         methodVersions[min].ctx_set_version(ctx, SET_MIN);
@@ -4048,10 +4036,8 @@
 	SSL_CTX_set_select_certificate_cb(ctx, ssl_sock_switchctx_cbk);
 	SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_err_cbk);
 #elif (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
-	if (bind_conf->ssl_conf.early_data) {
+	if (bind_conf->ssl_conf.early_data)
 		SSL_CTX_set_options(ctx, SSL_OP_NO_ANTI_REPLAY);
-		SSL_CTX_set_max_early_data(ctx, global.tune.bufsize - global.tune.maxrewrite);
-	}
 	SSL_CTX_set_client_hello_cb(ctx, ssl_sock_switchctx_cbk, NULL);
 	SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_err_cbk);
 #else
@@ -4535,9 +4521,7 @@
 				 curproxy->id, conf_curves, bind_conf->arg, bind_conf->file, bind_conf->line);
 			cfgerr++;
 		}
-#if defined(SSL_CTX_set_ecdh_auto)
 		(void)SSL_CTX_set_ecdh_auto(ctx, 1);
-#endif
 	}
 #endif
 #if defined(SSL_CTX_set_tmp_ecdh) && !defined(OPENSSL_NO_ECDH)
@@ -4757,10 +4741,8 @@
 			return cfgerr;
 		}
 	}
-	if (srv->use_ssl)
+	if (srv->use_ssl == 1)
 		srv->xprt = &ssl_sock;
-	if (srv->check.use_ssl)
-		srv->check.xprt = &ssl_sock;
 
 	ctx = SSL_CTX_new(SSLv23_client_method());
 	if (!ctx) {
@@ -5233,7 +5215,7 @@
 	ctx->wait_event.tasklet->context = ctx;
 	ctx->wait_event.events = 0;
 	ctx->sent_early_data = 0;
-	ctx->tmp_early_data = -1;
+	ctx->early_buf = BUF_NULL;
 	ctx->conn = conn;
 	ctx->send_wait = NULL;
 	ctx->recv_wait = NULL;
@@ -5272,6 +5254,8 @@
 		}
 		ctx->bio = BIO_new(ha_meth);
 		if (!ctx->bio) {
+			SSL_free(ctx->ssl);
+			ctx->ssl = NULL;
 			if (may_retry--) {
 				pool_gc(NULL);
 				goto retry_connect;
@@ -5334,9 +5318,10 @@
 			conn->err_code = CO_ER_SSL_NO_MEM;
 			goto err;
 		}
-
 		ctx->bio = BIO_new(ha_meth);
 		if (!ctx->bio) {
+			SSL_free(ctx->ssl);
+			ctx->ssl = NULL;
 			if (may_retry--) {
 				pool_gc(NULL);
 				goto retry_accept;
@@ -5358,6 +5343,18 @@
 			conn->err_code = CO_ER_SSL_NO_MEM;
 			goto err;
 		}
+
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+		if (__objt_listener(conn->target)->bind_conf->ssl_conf.early_data) {
+			b_alloc(&ctx->early_buf);
+			SSL_set_max_early_data(ctx->ssl,
+			    /* Only allow early data if we managed to allocate
+			     * a buffer.
+			     */
+			    (!b_is_null(&ctx->early_buf)) ?
+			    global.tune.bufsize - global.tune.maxrewrite : 0);
+		}
+#endif
 
 		SSL_set_accept_state(ctx->ssl);
 
@@ -5410,17 +5407,25 @@
 	 * detect early data, except to try to read them
 	 */
 	if (conn->flags & CO_FL_EARLY_SSL_HS) {
-		size_t read_data;
+		size_t read_data = 0;
 
-		ret = SSL_read_early_data(ctx->ssl, &ctx->tmp_early_data,
-		    1, &read_data);
-		if (ret == SSL_READ_EARLY_DATA_ERROR)
-			goto check_error;
-		if (ret == SSL_READ_EARLY_DATA_SUCCESS) {
-			conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
-			return 1;
-		} else
-			conn->flags &= ~CO_FL_EARLY_SSL_HS;
+		while (1) {
+			ret = SSL_read_early_data(ctx->ssl,
+			    b_tail(&ctx->early_buf), b_room(&ctx->early_buf),
+			    &read_data);
+			if (ret == SSL_READ_EARLY_DATA_ERROR)
+				goto check_error;
+			if (read_data > 0) {
+				conn->flags |= CO_FL_EARLY_DATA;
+				b_add(&ctx->early_buf, read_data);
+			}
+			if (ret == SSL_READ_EARLY_DATA_FINISH) {
+				conn->flags &= ~CO_FL_EARLY_SSL_HS;
+				if (!b_data(&ctx->early_buf))
+					b_free(&ctx->early_buf);
+				break;
+			}
+		}
 	}
 #endif
 	/* If we use SSL_do_handshake to process a reneg initiated by
@@ -5810,6 +5815,15 @@
 			return NULL;
 		}
 	}
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+	/* If we have early data and somebody wants to receive, let them */
+	else if (b_data(&ctx->early_buf) && ctx->recv_wait) {
+		ctx->recv_wait->events &= ~SUB_RETRY_RECV;
+			tasklet_wakeup(ctx->recv_wait->tasklet);
+			ctx->recv_wait = NULL;
+
+	}
+#endif
 	return NULL;
 }
 
@@ -5832,6 +5846,20 @@
 	if (!ctx)
 		goto out_error;
 
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+	if (b_data(&ctx->early_buf)) {
+		try = b_contig_space(buf);
+		if (try > b_data(&ctx->early_buf))
+			try = b_data(&ctx->early_buf);
+		memcpy(b_tail(buf), b_head(&ctx->early_buf), try);
+		b_add(buf, try);
+		b_del(&ctx->early_buf, try);
+		if (b_data(&ctx->early_buf) == 0)
+			b_free(&ctx->early_buf);
+		return try;
+	}
+#endif
+
 	if (conn->flags & CO_FL_HANDSHAKE)
 		/* a handshake was requested */
 		return 0;
@@ -5842,7 +5870,6 @@
 	 * EINTR too.
 	 */
 	while (count > 0) {
-		int need_out = 0;
 
 		try = b_contig_space(buf);
 		if (!try)
@@ -5851,45 +5878,6 @@
 		if (try > count)
 			try = count;
 
-		if (((conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_EARLY_DATA)) == CO_FL_EARLY_SSL_HS) &&
-		    ctx->tmp_early_data != -1) {
-			*b_tail(buf) = ctx->tmp_early_data;
-			done++;
-			try--;
-			count--;
-			b_add(buf, 1);
-			ctx->tmp_early_data = -1;
-			continue;
-		}
-
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
-		if (conn->flags & CO_FL_EARLY_SSL_HS) {
-			size_t read_length;
-
-			ret = SSL_read_early_data(ctx->ssl,
-			    b_tail(buf), try, &read_length);
-			if (ret == SSL_READ_EARLY_DATA_SUCCESS &&
-			    read_length > 0)
-				conn->flags |= CO_FL_EARLY_DATA;
-			if (ret == SSL_READ_EARLY_DATA_SUCCESS ||
-			    ret == SSL_READ_EARLY_DATA_FINISH) {
-				if (ret == SSL_READ_EARLY_DATA_FINISH) {
-					/*
-					 * We're done reading the early data,
-					 * let's make the handshake
-					 */
-					conn->flags &= ~CO_FL_EARLY_SSL_HS;
-					conn->flags |= CO_FL_SSL_WAIT_HS;
-					need_out = 1;
-					/* Now initiate the handshake */
-					tasklet_wakeup(ctx->wait_event.tasklet);
-					if (read_length == 0)
-						break;
-				}
-				ret = read_length;
-			}
-		} else
-#endif
 		ret = SSL_read(ctx->ssl, b_tail(buf), try);
 
 		if (conn->flags & CO_FL_ERROR) {
@@ -5939,8 +5927,6 @@
 			/* otherwise it's a real error */
 			goto out_error;
 		}
-		if (need_out)
-			break;
 	}
  leave:
 	return done;
@@ -6177,6 +6163,7 @@
 		}
 #endif
 		SSL_free(ctx->ssl);
+		b_free(&ctx->early_buf);
 		tasklet_free(ctx->wait_event.tasklet);
 		pool_free(ssl_sock_ctx_pool, ctx);
 		_HA_ATOMIC_SUB(&sslconns, 1);
@@ -6688,7 +6675,7 @@
 	}
 #else
 	smp->data.u.sint = ((conn->flags & CO_FL_EARLY_DATA)  &&
-	    (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_HANDSHAKE))) ? 1 : 0;
+	    (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_SSL_WAIT_HS))) ? 1 : 0;
 #endif
 	return 1;
 }
@@ -6822,6 +6809,7 @@
 	X509 *crt = NULL;
 	const EVP_MD *digest;
 	int ret = 0;
+	unsigned int len = 0;
 	struct buffer *smp_trash;
 	struct connection *conn;
 	struct ssl_sock_ctx *ctx;
@@ -6845,9 +6833,8 @@
 
 	smp_trash = get_trash_chunk();
 	digest = EVP_sha1();
-	X509_digest(crt, digest, (unsigned char *) smp_trash->area,
-		    (unsigned int *)&smp_trash->data);
-
+	X509_digest(crt, digest, (unsigned char *) smp_trash->area, &len);
+	smp_trash->data = len;
 	smp->data.u.str = *smp_trash;
 	smp->data.type = SMP_T_BIN;
 	ret = 1;
@@ -7373,6 +7360,7 @@
 {
 	struct connection *conn;
 	struct ssl_sock_ctx *ctx;
+	unsigned int len = 0;
 
 	smp->flags = SMP_F_CONST;
 	smp->data.type = SMP_T_STR;
@@ -7385,12 +7373,13 @@
 
 	smp->data.u.str.area = NULL;
 	SSL_get0_next_proto_negotiated(ctx->ssl,
-	                                (const unsigned char **)&smp->data.u.str.area,
-	                                (unsigned *)&smp->data.u.str.data);
+	                               (const unsigned char **)&smp->data.u.str.area,
+	                               &len);
 
 	if (!smp->data.u.str.area)
 		return 0;
 
+	smp->data.u.str.data = len;
 	return 1;
 }
 #endif
@@ -7401,6 +7390,7 @@
 {
 	struct connection *conn;
 	struct ssl_sock_ctx *ctx;
+	unsigned int len = 0;
 
 	smp->flags = SMP_F_CONST;
 	smp->data.type = SMP_T_STR;
@@ -7414,12 +7404,13 @@
 
 	smp->data.u.str.area = NULL;
 	SSL_get0_alpn_selected(ctx->ssl,
-	                         (const unsigned char **)&smp->data.u.str.area,
-	                         (unsigned *)&smp->data.u.str.data);
+	                       (const unsigned char **)&smp->data.u.str.area,
+	                       &len);
 
 	if (!smp->data.u.str.area)
 		return 0;
 
+	smp->data.u.str.data = len;
 	return 1;
 }
 #endif
@@ -7463,6 +7454,7 @@
 	                                    smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
 	SSL_SESSION *ssl_sess;
 	struct ssl_sock_ctx *ctx;
+	unsigned int len = 0;
 
 	smp->flags = SMP_F_CONST;
 	smp->data.type = SMP_T_BIN;
@@ -7475,11 +7467,11 @@
 	if (!ssl_sess)
 		return 0;
 
-	smp->data.u.str.area = (char *)SSL_SESSION_get_id(ssl_sess,
-							  (unsigned int *)&smp->data.u.str.data);
-	if (!smp->data.u.str.area || !smp->data.u.str.data)
+	smp->data.u.str.area = (char *)SSL_SESSION_get_id(ssl_sess, &len);
+	if (!smp->data.u.str.area || !len)
 		return 0;
 
+	smp->data.u.str.data = len;
 	return 1;
 }
 #endif
@@ -8724,7 +8716,7 @@
 /* parse the "no-check-ssl" server keyword */
 static int srv_parse_no_check_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
 {
-	newsrv->check.use_ssl = 0;
+	newsrv->check.use_ssl = -1;
 	free(newsrv->ssl_ctx.ciphers);
 	newsrv->ssl_ctx.ciphers = NULL;
 	newsrv->ssl_ctx.options &= ~global_ssl.connect_default_ssloptions;
@@ -8751,7 +8743,7 @@
 /* parse the "no-ssl" server keyword */
 static int srv_parse_no_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
 {
-	newsrv->use_ssl = 0;
+	newsrv->use_ssl = -1;
 	free(newsrv->ssl_ctx.ciphers);
 	newsrv->ssl_ctx.ciphers = NULL;
 	return 0;
@@ -8826,6 +8818,16 @@
 	if (global_ssl.connect_default_ciphersuites && !newsrv->ssl_ctx.ciphersuites)
 		newsrv->ssl_ctx.ciphersuites = strdup(global_ssl.connect_default_ciphersuites);
 #endif
+	newsrv->ssl_ctx.options |= global_ssl.connect_default_ssloptions;
+	newsrv->ssl_ctx.methods.flags |= global_ssl.connect_default_sslmethods.flags;
+
+	if (!newsrv->ssl_ctx.methods.min)
+		newsrv->ssl_ctx.methods.min = global_ssl.connect_default_sslmethods.min;
+
+	if (!newsrv->ssl_ctx.methods.max)
+		newsrv->ssl_ctx.methods.max = global_ssl.connect_default_sslmethods.max;
+
+
 	return 0;
 }
 
diff --git a/src/standard.c b/src/standard.c
index 717c14a..b593345 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -938,7 +938,7 @@
 		 */
 		prefix_path_len = (pfx && !abstract) ? strlen(pfx) : 0;
 		max_path_len = (sizeof(un->sun_path) - 1) -
-			(prefix_path_len ? prefix_path_len + 1 + 5 + 1 + 3 : 0);
+			(abstract ? 0 : prefix_path_len + 1 + 5 + 1 + 3);
 
 		adr_len = strlen(str2);
 		if (adr_len > max_path_len) {
@@ -1731,8 +1731,12 @@
  * be shorter. If some forbidden characters are found, the conversion is
  * aborted, the string is truncated before the issue and a negative value is
  * returned, otherwise the operation returns the length of the decoded string.
+ * If the 'in_form' argument is non-nul the string is assumed to be part of
+ * an "application/x-www-form-urlencoded" encoded string, and the '+' will be
+ * turned to a space. If it's zero, this will only be done after a question
+ * mark ('?').
  */
-int url_decode(char *string)
+int url_decode(char *string, int in_form)
 {
 	char *in, *out;
 	int ret = -1;
@@ -1742,7 +1746,7 @@
 	while (*in) {
 		switch (*in) {
 		case '+' :
-			*out++ = ' ';
+			*out++ = in_form ? ' ' : *in;
 			break;
 		case '%' :
 			if (!ishex(in[1]) || !ishex(in[2]))
@@ -1750,6 +1754,9 @@
 			*out++ = (hex2i(in[1]) << 4) + hex2i(in[2]);
 			in += 2;
 			break;
+		case '?':
+			in_form = 1;
+			/* fall through */
 		default:
 			*out++ = *in;
 			break;
@@ -3018,11 +3025,12 @@
 		} else {
 			*gmt_offset = '+';
 		}
+		diff %= 86400U;
 		diff /= 60; /* Convert to minutes */
 		snprintf(gmt_offset+1, 4+1, "%02d%02d", diff/60, diff%60);
 	}
 
-    return gmt_offset;
+	return gmt_offset;
 }
 
 /* gmt2str_log: write a date in the format :
@@ -4348,6 +4356,118 @@
 {
 }
 
+
+/* Random number generator state, see below */
+static uint64_t ha_random_state[2] ALIGNED(2*sizeof(uint64_t));
+
+/* This is a thread-safe implementation of xoroshiro128** described below:
+ *     http://prng.di.unimi.it/
+ * It features a 2^128 long sequence, returns 64 high-quality bits on each call,
+ * supports fast jumps and passes all common quality tests. It is thread-safe,
+ * uses a double-cas on 64-bit architectures supporting it, and falls back to a
+ * local lock on other ones.
+ */
+uint64_t ha_random64()
+{
+	uint64_t result;
+	uint64_t old[2] ALIGNED(2*sizeof(uint64_t));
+	uint64_t new[2] ALIGNED(2*sizeof(uint64_t));
+
+#if defined(USE_THREAD) && (!defined(HA_CAS_IS_8B) || !defined(HA_HAVE_CAS_DW))
+	static HA_SPINLOCK_T rand_lock;
+
+	HA_SPIN_LOCK(OTHER_LOCK, &rand_lock);
+#endif
+
+	old[0] = ha_random_state[0];
+	old[1] = ha_random_state[1];
+
+#if defined(USE_THREAD) && defined(HA_CAS_IS_8B) && defined(HA_HAVE_CAS_DW)
+	do {
+#endif
+		result = rotl64(old[0] * 5, 7) * 9;
+		new[1] = old[0] ^ old[1];
+		new[0] = rotl64(old[0], 24) ^ new[1] ^ (new[1] << 16); // a, b
+		new[1] = rotl64(new[1], 37); // c
+
+#if defined(USE_THREAD) && defined(HA_CAS_IS_8B) && defined(HA_HAVE_CAS_DW)
+	} while (unlikely(!_HA_ATOMIC_DWCAS(ha_random_state, old, new)));
+#else
+	ha_random_state[0] = new[0];
+	ha_random_state[1] = new[1];
+#if defined(USE_THREAD)
+	HA_SPIN_UNLOCK(OTHER_LOCK, &rand_lock);
+#endif
+#endif
+	return result;
+}
+
+/* seeds the random state using up to <len> bytes from <seed>, starting with
+ * the first non-zero byte.
+ */
+void ha_random_seed(const unsigned char *seed, size_t len)
+{
+	size_t pos;
+
+	/* the seed must not be all zeroes, so we pre-fill it with alternating
+	 * bits and overwrite part of them with the block starting at the first
+	 * non-zero byte from the seed.
+	 */
+	memset(ha_random_state, 0x55, sizeof(ha_random_state));
+
+	for (pos = 0; pos < len; pos++)
+		if (seed[pos] != 0)
+			break;
+
+	if (pos == len)
+		return;
+
+	seed += pos;
+	len -= pos;
+
+	if (len > sizeof(ha_random_state))
+		len = sizeof(ha_random_state);
+
+	memcpy(ha_random_state, seed, len);
+}
+
+/* This causes a jump to (dist * 2^96) places in the pseudo-random sequence,
+ * and is equivalent to calling ha_random64() as many times. It is used to
+ * provide non-overlapping sequences of 2^96 numbers (~7*10^28) to up to 2^32
+ * different generators (i.e. different processes after a fork). The <dist>
+ * argument is the distance to jump to and is used in a loop so it rather not
+ * be too large if the processing time is a concern.
+ *
+ * BEWARE: this function is NOT thread-safe and must not be called during
+ * concurrent accesses to ha_random64().
+ */
+void ha_random_jump96(uint32_t dist)
+{
+	while (dist--) {
+		uint64_t s0 = 0;
+		uint64_t s1 = 0;
+		int b;
+
+		for (b = 0; b < 64; b++) {
+			if ((0xd2a98b26625eee7bULL >> b) & 1) {
+				s0 ^= ha_random_state[0];
+				s1 ^= ha_random_state[1];
+			}
+			ha_random64();
+		}
+
+		for (b = 0; b < 64; b++) {
+			if ((0xdddf9b1090aa7ac1ULL >> b) & 1) {
+				s0 ^= ha_random_state[0];
+				s1 ^= ha_random_state[1];
+			}
+			ha_random64();
+		}
+		ha_random_state[0] = s0;
+		ha_random_state[1] = s1;
+	}
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8
diff --git a/src/stats.c b/src/stats.c
index 992005a..f2091bb 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -915,6 +915,9 @@
 		else if (strcmp(field_str(stats, ST_F_STATUS), "DOWN ") == 0) {
 			style = "going_up";
 		}
+		else if (strcmp(field_str(stats, ST_F_STATUS), "DRAIN") == 0) {
+			style = "draining";
+		}
 		else if (strcmp(field_str(stats, ST_F_STATUS), "NOLB ") == 0) {
 			style = "going_down";
 		}
@@ -2915,7 +2918,7 @@
 				/* Ok, a value is found, we can mark the end of the key */
 				*value++ = '\0';
 			}
-			if (url_decode(key) < 0 || url_decode(value) < 0)
+			if (url_decode(key, 1) < 0 || url_decode(value, 1) < 0)
 				break;
 
 			/* Now we can check the key to see what to do */
diff --git a/src/stick_table.c b/src/stick_table.c
index eef7580..e6e5425 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -1916,9 +1916,9 @@
 			return ACT_RET_PRS_ERR;
 		}
 
-		if (rule->arg.gpc.sc >= ACT_ACTION_TRK_SCMAX) {
+		if (rule->arg.gpc.sc >= MAX_SESS_STKCTR) {
 			memprintf(err, "invalid stick table track ID. The max allowed ID is %d",
-			          ACT_ACTION_TRK_SCMAX-1);
+			          MAX_SESS_STKCTR-1);
 			return ACT_RET_PRS_ERR;
 		}
 	}
@@ -1998,9 +1998,9 @@
 			return ACT_RET_PRS_ERR;
 		}
 
-		if (rule->arg.gpc.sc >= ACT_ACTION_TRK_SCMAX) {
+		if (rule->arg.gpc.sc >= MAX_SESS_STKCTR) {
 			memprintf(err, "invalid stick table track ID. The max allowed ID is %d",
-			          ACT_ACTION_TRK_SCMAX-1);
+			          MAX_SESS_STKCTR-1);
 			return ACT_RET_PRS_ERR;
 		}
 	}
@@ -2076,9 +2076,9 @@
 			return ACT_RET_PRS_ERR;
 		}
 
-		if (rule->arg.gpt.sc >= ACT_ACTION_TRK_SCMAX) {
+		if (rule->arg.gpt.sc >= MAX_SESS_STKCTR) {
 			memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d",
-			          args[*arg-1], ACT_ACTION_TRK_SCMAX-1);
+			          args[*arg-1], MAX_SESS_STKCTR-1);
 			return ACT_RET_PRS_ERR;
 		}
 	}
diff --git a/src/stream.c b/src/stream.c
index 4a7ced2..2eb7cfa 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -435,9 +435,13 @@
 	}
 
 	if (s->dns_ctx.dns_requester) {
+		__decl_hathreads(struct dns_resolvers *resolvers = s->dns_ctx.parent->arg.dns.resolvers);
+
+		HA_SPIN_LOCK(DNS_LOCK, &resolvers->lock);
 		free(s->dns_ctx.hostname_dn); s->dns_ctx.hostname_dn = NULL;
 		s->dns_ctx.hostname_dn_len = 0;
 		dns_unlink_resolution(s->dns_ctx.dns_requester);
+		HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
 
 		pool_free(dns_requester_pool, s->dns_ctx.dns_requester);
 		s->dns_ctx.dns_requester = NULL;
@@ -673,8 +677,6 @@
 	struct stream_interface *si = &s->si[1];
 	struct channel *req = &s->req;
 	struct channel *rep = &s->res;
-	struct conn_stream *srv_cs = objt_cs(si->end);
-	struct connection *conn = srv_cs ? srv_cs->conn : objt_conn(si->end);
 
 	/* the client might want to abort */
 	if ((rep->flags & CF_SHUTW) ||
@@ -691,10 +693,7 @@
 
 	/* retryable error ? */
 	if (si->flags & (SI_FL_EXP|SI_FL_ERR)) {
-		if (!(s->flags & SF_SRV_REUSED) && conn) {
-			conn_stop_tracking(conn);
-			conn_full_close(conn);
-		}
+		si_release_endpoint(si);
 
 		if (!si->err_type) {
 			if (si->flags & SI_FL_ERR)
@@ -756,6 +755,19 @@
 			si->conn_retries = 0;
 		}
 	}
+	else if (s->be->options & PR_O_HTTP_PROXY) {
+		/*
+		 * Disable connection retries on plain HTTP proxy mode, because
+		 * on connection failure, the connection is detached from the SI
+		 * and released. So the address is definitely lost. It is a 2.0
+		 * limitation only. Disabling connection retries is the easiest
+		 * way to work around this limitation in this very particular
+		 * case (and no so common). Try the 2.2 if it is a problem (or
+		 * report a bug).
+		 */
+		if (!si->end) /* endpoint already detached */
+			si->conn_retries = 0;
+	}
 
 	/* ensure that we have enough retries left */
 	si->conn_retries--;
@@ -824,8 +836,6 @@
 	struct stream_interface *si = &s->si[1];
 	struct channel *req = &s->req;
 	struct channel *rep = &s->res;
-	struct conn_stream *srv_cs = objt_cs(si->end);
-	struct connection *conn = srv_cs ? srv_cs->conn : objt_conn(si->end);
 
 	/* We know the connection at least succeeded, though it could have
 	 * since met an error for any other reason. At least it didn't time out
@@ -861,10 +871,7 @@
 
 		/* retryable error ? */
 		if (si->flags & SI_FL_ERR) {
-			if (!(s->flags & SF_SRV_REUSED) && conn) {
-				conn_stop_tracking(conn);
-				conn_full_close(conn);
-			}
+			si_release_endpoint(si);
 
 			if (!si->err_type)
 				si->err_type = SI_ET_CONN_ERR;
@@ -1580,13 +1587,15 @@
 		 * An example could be a store of the IP address from an HTTP
 		 * header first, then from the source if not found.
 		 */
-		for (i = 0; i < s->store_count; i++) {
-			if (rule->table.t == s->store[i].table)
-				break;
-		}
+		if (rule->flags & STK_IS_STORE) {
+			for (i = 0; i < s->store_count; i++) {
+				if (rule->table.t == s->store[i].table)
+					break;
+			}
 
-		if (i !=  s->store_count)
-			continue;
+			if (i !=  s->store_count)
+				continue;
+		}
 
 		if (rule->cond) {
 	                ret = acl_exec_cond(rule->cond, px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
@@ -2380,6 +2389,7 @@
 				si_b->state = SI_ST_REQ; /* new connection requested */
 				si_b->conn_retries = s->be->conn_retries;
 				if ((s->be->retry_type &~ PR_RE_CONN_FAILED) &&
+				    (s->be->mode == PR_MODE_HTTP) &&
 				    !(si_b->flags & SI_FL_D_L7_RETRY))
 					si_b->flags |= SI_FL_L7_RETRY;
 			}
@@ -2399,10 +2409,9 @@
 	if (si_state_in(si_b->state, SI_SB_REQ|SI_SB_QUE|SI_SB_TAR|SI_SB_ASS)) {
 		/* prune the request variables and swap to the response variables. */
 		if (s->vars_reqres.scope != SCOPE_RES) {
-			if (!LIST_ISEMPTY(&s->vars_reqres.head)) {
+			if (!LIST_ISEMPTY(&s->vars_reqres.head))
 				vars_prune(&s->vars_reqres, s->sess, s);
-				vars_init(&s->vars_reqres, SCOPE_RES);
-			}
+			vars_init(&s->vars_reqres, SCOPE_RES);
 		}
 
 		do {
@@ -2994,6 +3003,19 @@
 
 	chunk_reset(&trash);
 	stream_dump(&trash, s, "", ' ');
+
+	chunk_appendf(&trash, "filters={");
+	if (HAS_FILTERS(s)) {
+		struct filter *filter;
+
+		list_for_each_entry(filter, &s->strm_flt.filters, list) {
+			if (filter->list.p != &s->strm_flt.filters)
+				chunk_appendf(&trash, ", ");
+			chunk_appendf(&trash, "%p=\"%s\"", filter, FLT_ID(filter));
+		}
+	}
+	chunk_appendf(&trash, "}");
+
 	memprintf(&msg,
 	          "A bogus %s [%p] is spinning at %d calls per second and refuses to die, "
 	          "aborting now! Please report this error to developers "
@@ -3441,6 +3463,15 @@
 	appctx->ctx.sess.section = 0; /* start with stream status */
 	appctx->ctx.sess.pos = 0;
 
+	/* we need to put an end marker into the streams list. We're just moving
+	 * ourselves there, so that once we found ourselves we know we've reached
+	 * the end. Without this we can run forever if new streams arrive faster
+	 * than we can dump them.
+	 */
+	HA_SPIN_LOCK(STRMS_LOCK, &streams_lock);
+	LIST_DEL(&si_strm(appctx->owner)->list);
+	LIST_ADDQ(&streams, &si_strm(appctx->owner)->list);
+	HA_SPIN_UNLOCK(STRMS_LOCK, &streams_lock);
 	return 0;
 }
 
@@ -3492,8 +3523,8 @@
 			LIST_INIT(&appctx->ctx.sess.bref.users);
 		}
 
-		/* and start from where we stopped */
-		while (appctx->ctx.sess.bref.ref != &streams) {
+		/* and start from where we stopped, never going further than ourselves */
+		while (appctx->ctx.sess.bref.ref != si_strm(appctx->owner)->list.n) {
 			char pn[INET6_ADDRSTRLEN];
 			struct stream *curr_strm;
 
diff --git a/src/stream_interface.c b/src/stream_interface.c
index e37d0b9..49d497b 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -682,6 +682,10 @@
 	if (conn->flags & CO_FL_SOCK_WR_SH || oc->flags & CF_SHUTW)
 		return 1;
 
+	/* we must wait because the mux is not installed yet */
+	if (!conn->mux)
+		return 0;
+
 	if (oc->pipe && conn->xprt->snd_pipe && conn->mux->snd_pipe) {
 		ret = conn->mux->snd_pipe(cs, oc->pipe);
 		if (ret > 0)
@@ -1238,6 +1242,10 @@
 	if (ic->flags & CF_SHUTR)
 		return 1;
 
+	/* we must wait because the mux is not installed yet */
+	if (!conn->mux)
+		return 0;
+
 	/* stop here if we reached the end of data */
 	if (cs->flags & CS_FL_EOS)
 		goto end_recv;
@@ -1273,7 +1281,7 @@
 	/* First, let's see if we may splice data across the channel without
 	 * using a buffer.
 	 */
-	if (conn->xprt->rcv_pipe && conn->mux->rcv_pipe &&
+	if (cs->flags & CS_FL_MAY_SPLICE &&
 	    (ic->pipe || ic->to_forward >= MIN_SPLICE_FORWARD) &&
 	    ic->flags & CF_KERN_SPLICING) {
 		if (c_data(ic)) {
@@ -1332,6 +1340,13 @@
 		ic->pipe = NULL;
 	}
 
+	if (ic->pipe && ic->to_forward && !(flags & CO_RFL_BUF_FLUSH) && cs->flags & CS_FL_MAY_SPLICE) {
+		/* don't break splicing by reading, but still call rcv_buf()
+		 * to pass the flag.
+		 */
+		goto done_recv;
+	}
+
 	/* now we'll need a input buffer for the stream */
 	if (!si_alloc_ibuf(si, &(si_strm(si)->buffer_wait)))
 		goto end_recv;
@@ -1360,10 +1375,18 @@
 		}
 
 		if (ret <= 0) {
+			/* if we refrained from reading because we asked for a
+			 * flush to satisfy rcv_pipe(), we must not subscribe
+			 * and instead report that there's not enough room
+			 * here to proceed.
+			 */
+			if (flags & CO_RFL_BUF_FLUSH)
+				si_rx_room_blk(si);
 			break;
 		}
 
-		if (si->flags & SI_FL_L7_RETRY) {
+		/* L7 retries enabled and maximum connection retries not reached */
+		if ((si->flags & SI_FL_L7_RETRY) && si->conn_retries) {
 			struct htx *htx;
 			struct htx_sl *sl;
 
diff --git a/src/task.c b/src/task.c
index 9a55a60..c688c16 100644
--- a/src/task.c
+++ b/src/task.c
@@ -387,7 +387,8 @@
 		struct task *(*process)(struct task *t, void *ctx, unsigned short state);
 
 		t = (struct task *)LIST_ELEM(task_per_thread[tid].task_list.n, struct tasklet *, list);
-		state = _HA_ATOMIC_XCHG(&t->state, TASK_RUNNING);
+		state = (t->state & TASK_SHARED_WQ) | TASK_RUNNING;
+		state = _HA_ATOMIC_XCHG(&t->state, state);
 		__ha_barrier_atomic_store();
 		__tasklet_remove_from_tasklet_list((struct tasklet *)t);
 		if (!TASK_IS_TASKLET(t))
@@ -434,7 +435,7 @@
 			}
 
 			state = _HA_ATOMIC_AND(&t->state, ~TASK_RUNNING);
-			if (state)
+			if (state & TASK_WOKEN_ANY)
 				task_wakeup(t, 0);
 			else
 				task_queue(t);
diff --git a/src/tcp_rules.c b/src/tcp_rules.c
index d81ffe3..57f30ae 100644
--- a/src/tcp_rules.c
+++ b/src/tcp_rules.c
@@ -300,8 +300,8 @@
 	 * - if one rule returns OK, then return OK
 	 * - if one rule returns KO, then return KO
 	 */
-
-	if (rep->flags & CF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
+	if ((rep->flags & CF_SHUTR) || channel_full(rep, global.tune.maxrewrite) ||
+	    !s->be->tcp_rep.inspect_delay || tick_is_expired(rep->analyse_exp, now_ms))
 		partial = SMP_OPT_FINAL;
 	else
 		partial = 0;
@@ -385,6 +385,8 @@
 				case ACT_RET_YIELD:
 					channel_dont_close(rep);
 					s->current_rule = rule;
+					if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
+						rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
 					return 0;
 				}
 				break; /* ACT_RET_STOP/DONE */
@@ -699,7 +701,7 @@
 			memprintf(err,
 			          "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
 			          args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
-			free(expr);
+			release_sample_expr(expr);
 			return -1;
 		}
 
@@ -709,7 +711,7 @@
 				memprintf(err,
 					  "'%s %s %s' : missing length value",
 					  args[0], args[1], args[kw]);
-				free(expr);
+				release_sample_expr(expr);
 				return -1;
 			}
 			/* we copy the table name for now, it will be resolved later */
@@ -718,7 +720,7 @@
 				memprintf(err,
 					  "'%s %s %s' : length must be > 0",
 					  args[0], args[1], args[kw]);
-				free(expr);
+				release_sample_expr(expr);
 				return -1;
 			}
 			arg++;
@@ -777,7 +779,7 @@
 			memprintf(err,
 			          "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
 			          args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
-			free(expr);
+			release_sample_expr(expr);
 			return -1;
 		}
 
@@ -790,7 +792,7 @@
 				memprintf(err,
 					  "'%s %s %s' : missing table name",
 					  args[0], args[1], args[kw]);
-				free(expr);
+				release_sample_expr(expr);
 				return -1;
 			}
 			/* we copy the table name for now, it will be resolved later */
diff --git a/src/wdt.c b/src/wdt.c
index aa89fd4..1cfb2b6 100644
--- a/src/wdt.c
+++ b/src/wdt.c
@@ -18,6 +18,7 @@
 #include <common/initcall.h>
 #include <common/standard.h>
 #include <types/global.h>
+#include <types/signal.h>
 #include <proto/log.h>
 
 
@@ -27,11 +28,6 @@
  */
 #if defined(USE_THREAD) && defined(USE_RT) && (_POSIX_TIMERS > 0) && defined(_POSIX_THREAD_CPUTIME)
 
-/* We'll deliver SIGALRM when we've run out of CPU as it's not intercepted by
- * gdb by default.
- */
-#define WDTSIG SIGALRM
-
 /* Setup (or ping) the watchdog timer for thread <thr>. Returns non-zero on
  * success, zero on failure. It interrupts once per second of CPU time. It
  * happens that timers based on the CPU time are not automatically re-armed
@@ -101,12 +97,12 @@
 
 		/* No doubt now, there's no hop to recover, die loudly! */
 		break;
-
+#ifdef USE_THREAD
 	case SI_TKILL:
 		/* we got a pthread_kill, stop on it */
 		thr = tid;
 		break;
-
+#endif
 	default:
 		/* unhandled other conditions */
 		return;
@@ -156,7 +152,7 @@
  fail1:
 	ti->wd_timer = TIMER_INVALID;
 	ha_warning("Failed to setup watchdog timer for thread %u, disabling lockup detection.\n", tid);
-	return 0;
+	return 1;
 }
 
 void deinit_wdt_per_thread()