Merge ~athos/ubuntu/+source/haproxy:MRE-jammy into ubuntu/+source/haproxy:ubuntu/jammy-devel

Proposed by Athos Ribeiro
Status: Merged
Approved by: git-ubuntu bot
Approved revision: not available
Merged at revision: 24ffc1bf80a0965a8d9c202af143f28eb6dc7821
Proposed branch: ~athos/ubuntu/+source/haproxy:MRE-jammy
Merge into: ubuntu/+source/haproxy:ubuntu/jammy-devel
Diff against target: 5461 lines (+1673/-653)
92 files modified
.cirrus.yml (+1/-1)
.github/matrix.py (+3/-1)
.github/workflows/cross-zoo.yml (+1/-1)
.github/workflows/vtest.yml (+1/-0)
.gitignore (+1/-0)
CHANGELOG (+149/-0)
Makefile (+1/-1)
SUBVERS (+1/-1)
VERDATE (+2/-2)
VERSION (+1/-1)
debian/changelog (+19/-0)
debian/patches/series (+0/-2)
dev/hpack/decode.c (+1/-1)
dev/null (+0/-143)
doc/configuration.txt (+172/-95)
doc/design-thoughts/config-language.txt (+2/-2)
doc/internals/http-parsing.txt (+2/-2)
doc/management.txt (+1/-1)
include/haproxy/connection.h (+11/-1)
include/haproxy/h2.h (+1/-1)
include/haproxy/hlua.h (+1/-0)
include/haproxy/http.h (+19/-0)
include/haproxy/http_ana-t.h (+1/-1)
include/haproxy/http_rules.h (+1/-0)
include/haproxy/listener-t.h (+5/-0)
include/haproxy/listener.h (+27/-11)
include/haproxy/proxy-t.h (+1/-0)
include/haproxy/server.h (+1/-0)
include/haproxy/sink.h (+5/-3)
include/haproxy/spoe-t.h (+1/-0)
include/haproxy/stick_table.h (+1/-1)
include/haproxy/time.h (+2/-1)
include/import/ist.h (+47/-0)
reg-tests/cache/caching_rules.vtc (+96/-0)
reg-tests/http-messaging/h1_to_h1.vtc (+26/-0)
reg-tests/http-messaging/h2_to_h1.vtc (+60/-0)
reg-tests/http-rules/h1or2_to_h1c.vtc (+12/-4)
reg-tests/http-rules/normalize_uri.vtc (+13/-0)
reg-tests/log/log_uri.vtc (+1/-1)
scripts/build-ssl.sh (+1/-1)
scripts/publish-release (+3/-0)
src/backend.c (+0/-2)
src/cache.c (+10/-6)
src/cfgparse-tcp.c (+1/-0)
src/cfgparse.c (+7/-3)
src/channel.c (+1/-1)
src/check.c (+10/-1)
src/chunk.c (+7/-3)
src/debug.c (+24/-3)
src/dns.c (+24/-12)
src/filters.c (+2/-3)
src/flt_spoe.c (+21/-9)
src/h1.c (+36/-7)
src/h1_htx.c (+1/-1)
src/h2.c (+41/-8)
src/haproxy.c (+30/-1)
src/hlua.c (+166/-50)
src/http.c (+1/-1)
src/http_ana.c (+30/-5)
src/http_rules.c (+47/-11)
src/listener.c (+139/-53)
src/log.c (+20/-9)
src/mjson.c (+2/-2)
src/mux_fcgi.c (+1/-1)
src/mux_h1.c (+8/-2)
src/mux_h2.c (+10/-4)
src/mworker.c (+39/-11)
src/namespace.c (+1/-0)
src/proto_uxdg.c (+15/-7)
src/proto_uxst.c (+15/-7)
src/protocol.c (+9/-7)
src/proxy.c (+42/-19)
src/resolvers.c (+5/-2)
src/ring.c (+0/-1)
src/sample.c (+3/-3)
src/server.c (+30/-57)
src/server_state.c (+1/-1)
src/sink.c (+30/-20)
src/sock_inet.c (+18/-0)
src/sock_unix.c (+39/-2)
src/ssl_crtlist.c (+9/-0)
src/ssl_sock.c (+2/-2)
src/stick_table.c (+10/-7)
src/stream_interface.c (+16/-0)
src/task.c (+21/-5)
src/tcp_rules.c (+2/-2)
src/tcp_sample.c (+2/-2)
src/tcpcheck.c (+10/-4)
src/thread.c (+4/-2)
src/time.c (+3/-1)
src/tools.c (+13/-13)
src/trace.c (+1/-1)
Reviewer Review Type Date Requested Status
git-ubuntu bot Approve
Sergio Durigan Junior (community) Approve
Canonical Server Reporter Pending
Review via email: mp+454921@code.launchpad.net

Description of the change

HAProxy MRE introducing the newest point release version for jammy.

All relevant information is available at the MRE bug in LP: #2028418.

PPA: https://launchpad.net/~athos-ribeiro/+archive/ubuntu/haproxy-mre/+packages

DEP8 test results:

  - haproxy/2.4.24-0ubuntu0.22.04.1
    + ✅ haproxy on jammy for amd64 @ 31.10.23 23:06:26 Log️ 🗒️
    + ✅ haproxy on jammy for arm64 @ 31.10.23 23:28:44 Log️ 🗒️
    + ✅ haproxy on jammy for armhf @ 31.10.23 22:56:34 Log️ 🗒️
    + ✅ haproxy on jammy for ppc64el @ 31.10.23 23:00:08 Log️ 🗒️
    + ✅ haproxy on jammy for s390x @ 31.10.23 22:55:01 Log️ 🗒️

To post a comment you must log in.
Revision history for this message
Sergio Durigan Junior (sergiodj) wrote :

Thanks, Athos.

This LGTM. Verified that the package builds, dep8 tests are passing, and I'm satisfied with the results from Microsoft Github's actions, including the "Spec Compliance" test:

https://github.com/athos-ribeiro/haproxy-2.4/actions/runs/6962364281

+1

review: Approve
Revision history for this message
git-ubuntu bot (git-ubuntu-bot) wrote :

Approvers: athos-ribeiro, sergiodj
Uploaders: athos-ribeiro, sergiodj
MP auto-approved

review: Approve
Revision history for this message
Athos Ribeiro (athos) wrote :

Thanks, Sergio!

Uploaded!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.cirrus.yml b/.cirrus.yml
2index deace96..f762e0a 100644
3--- a/.cirrus.yml
4+++ b/.cirrus.yml
5@@ -1,7 +1,7 @@
6 FreeBSD_task:
7 freebsd_instance:
8 matrix:
9- image_family: freebsd-13-1
10+ image_family: freebsd-13-2
11 only_if: $CIRRUS_BRANCH =~ 'master|next'
12 install_script:
13 - pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua53 socat pcre
14diff --git a/.github/matrix.py b/.github/matrix.py
15index abd6caf..e03358b 100644
16--- a/.github/matrix.py
17+++ b/.github/matrix.py
18@@ -44,7 +44,9 @@ def determine_latest_openssl(ssl):
19 return "OPENSSL_VERSION={}".format(latest_tag[8:])
20
21 def determine_latest_libressl(ssl):
22- libressl_download_list = urllib.request.urlopen("http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/")
23+ libressl_download_list = urllib.request.urlopen(
24+ "https://cdn.openbsd.org/pub/OpenBSD/LibreSSL/"
25+ )
26 for line in libressl_download_list.readlines():
27 decoded_line = line.decode("utf-8")
28 if "libressl-" in decoded_line and ".tar.gz.asc" in decoded_line:
29diff --git a/.github/workflows/cross-zoo.yml b/.github/workflows/cross-zoo.yml
30index e2a5816..f2c8d7a 100644
31--- a/.github/workflows/cross-zoo.yml
32+++ b/.github/workflows/cross-zoo.yml
33@@ -97,7 +97,7 @@ jobs:
34 sudo apt-get -yq --force-yes install \
35 gcc-${{ matrix.platform.arch }} \
36 ${{ matrix.platform.libs }}
37- - uses: actions/checkout@v2
38+ - uses: actions/checkout@v3
39
40
41 - name: install quictls
42diff --git a/.github/workflows/vtest.yml b/.github/workflows/vtest.yml
43index 781a2b3..0fdaca8 100644
44--- a/.github/workflows/vtest.yml
45+++ b/.github/workflows/vtest.yml
46@@ -166,3 +166,4 @@ jobs:
47 sudo cat $asan
48 echo "::endgroup::"
49 done
50+ exit 1
51diff --git a/.gitignore b/.gitignore
52index ec9dd62..62a92d1 100644
53--- a/.gitignore
54+++ b/.gitignore
55@@ -38,6 +38,7 @@
56 *.rej
57 *.orig
58 *.bak
59+*.sw[op]
60 # And reject some specific files
61 /admin/halog/halog
62 /admin/dyncookie/dyncookie
63diff --git a/CHANGELOG b/CHANGELOG
64index d59309f..29e701e 100644
65--- a/CHANGELOG
66+++ b/CHANGELOG
67@@ -1,6 +1,155 @@
68 ChangeLog :
69 ===========
70
71+2023/08/19 : 2.4.24
72+ - MINOR: proto_uxst: add resume method
73+ - CLEANUP: listener: function comment typo in stop_listener()
74+ - BUG/MINOR: listener: null pointer dereference suspected by coverity
75+ - MINOR: listener/api: add lli hint to listener functions
76+ - MINOR: listener: add relax_listener() function
77+ - MINOR: listener: workaround for closing a tiny race between resume_listener() and stopping
78+ - MINOR: listener: make sure we don't pause/resume bypassed listeners
79+ - BUG/MEDIUM: listener: fix pause_listener() suspend return value handling
80+ - BUG/MINOR: listener: fix resume_listener() resume return value handling
81+ - BUG/MEDIUM: resume from LI_ASSIGNED in default_resume_listener()
82+ - MINOR: listener: pause_listener() becomes suspend_listener()
83+ - BUG/MEDIUM: listener/proxy: fix listeners notify for proxy resume
84+ - MEDIUM: proto_ux: properly suspend named UNIX listeners
85+ - MINOR: proto_ux: ability to dump ABNS names in error messages
86+ - MINOR: lua: Add a function to get a reference on a table in the stack
87+ - CLEANUP: Remove unused function hlua_get_top_error_string
88+ - MINOR: hlua: add simple hlua reference handling API
89+ - BUG/MINOR: hlua: fix reference leak in core.register_task()
90+ - BUG/MINOR: hlua: fix reference leak in hlua_post_init_state()
91+ - MINOR: hlua: simplify lua locking
92+ - BUG/MEDIUM: hlua: prevent deadlocks with main lua lock
93+ - BUG/MINOR: server: inherit from netns in srv_settings_cpy()
94+ - BUG/MINOR: namespace: missing free in netns_sig_stop()
95+ - BUG/MEDIUM: mworker: increase maxsock with each new worker
96+ - DOC: Add tune.h2.max-frame-size option to table of contents
97+ - BUILD: debug: avoid a build warning related to epoll_wait() in debug code
98+ - BUG/MINOR: tcp_sample: bc_{dst,src} return IP not INT
99+ - BUG/MINOR: cache: A 'max-age=0' cache-control directive can be overriden by a s-maxage
100+ - BUG/MEDIUM: sink: invalid server list in sink_new_from_logsrv()
101+ - BUG/MINOR: sink: missing sft free in sink_deinit()
102+ - BUG/MINOR: ring: size warning incorrectly reported as fatal error
103+ - BUG/MINOR: ring: maxlen warning reported as alert
104+ - BUG/MINOR: log: LF upsets maxlen for UDP targets
105+ - MINOR: sink/api: pass explicit maxlen parameter to sink_write()
106+ - BUG/MEDIUM: log: improper use of logsrv->maxlen for buffer targets
107+ - BUG/MINOR: log: fix missing name error message in cfg_parse_log_forward()
108+ - BUG/MINOR: log: fix multiple error paths in cfg_parse_log_forward()
109+ - BUG/MINOR: log: free errmsg on error in cfg_parse_log_forward()
110+ - BUG/MINOR: sink: invalid sft free in sink_deinit()
111+ - BUG/MINOR: sink: fix errors handling in cfg_post_parse_ring()
112+ - BUG/MINOR: sink/log: properly deinit srv in sink_new_from_logsrv()
113+ - BUG/MINOR: config: Remove final '\n' in error messages
114+ - BUG/MINOR: hlua: hlua_yieldk ctx argument should support pointers
115+ - BUG/MINOR: sample: Fix wrong overflow detection in add/sub conveters
116+ - BUG/MINOR: http: Return the right reason for 302
117+ - CI: explicitely highlight VTest result section if there's something
118+ - BUG/MINOR: hlua: add check for lua_newstate
119+ - BUG/MINOR: h1-htx: Return the right reason for 302 FCGI responses
120+ - BUG/MEDIUM: listener: Acquire proxy's lock in relax_listener() if necessary
121+ - DOC: configuration: describe Td in Timing events
122+ - BUG/MINOR: chunk: fix chunk_appendf() to not write a zero if buffer is full
123+ - BUG/MAJOR: http-ana: Get a fresh trash buffer for each header value replacement
124+ - BUG/MAJOR: http: reject any empty content-length header value
125+ - MINOR: ist: add new function ist_find_range() to find a character range
126+ - MINOR: http: add new function http_path_has_forbidden_char()
127+ - MINOR: h2: pass accept-invalid-http-request down the request parser
128+ - REGTESTS: http-rules: add accept-invalid-http-request for normalize-uri tests
129+ - BUG/MINOR: h1: do not accept '#' as part of the URI component
130+ - BUG/MINOR: h2: reject more chars from the :path pseudo header
131+ - REGTESTS: http-rules: verify that we block '#' by default for normalize-uri
132+ - DOC: clarify the handling of URL fragments in requests
133+ - BUG/MINOR: http: skip leading zeroes in content-length values
134+
135+2023/06/09 : 2.4.23
136+ - DEV: hpack: fix `trash` build regression
137+ - BUG/MINOR: ssl: ssl-(min|max)-ver parameter not duplicated for bundles in crt-list
138+ - BUG/MINOR: mworker: stop doing strtok directly from the env
139+ - BUG/MEDIUM: mworker: don't register mworker_accept_wrapper() when master FD is wrong
140+ - MINOR: startup: HAPROXY_STARTUP_VERSION contains the version used to start
141+ - BUG/MINOR: sched: properly report long_rq when tasks remain in the queue
142+ - BUG/MEDIUM: sched: allow a bit more TASK_HEAVY to be processed when needed
143+ - BUG/MINOR: mworker: prevent incorrect values in uptime
144+ - BUG/MINOR: cache: Cache response even if request has "no-cache" directive
145+ - BUG/MINOR: cache: Check cache entry is complete in case of Vary
146+ - BUG/MINOR: ring: do not realign ring contents on resize
147+ - DOC: config: Fix description of options about HTTP connection modes
148+ - DOC: config: Add the missing tune.fail-alloc option from global listing
149+ - DOC: config: Clarify the meaning of 'hold' in the 'resolvers' section
150+ - BUG/MINOR: http-check: Don't set HTX_SL_F_BODYLESS flag with a log-format body
151+ - BUG/MINOR: http-check: Skip C-L header for empty body when it's not mandatory
152+ - BUG/MINOR: http-ana: Do a L7 retry on read error if there is no response
153+ - BUG/MINOR: ssl: Use 'date' instead of 'now' in ocsp stapling callback
154+ - BUG/MINOR: init: properly detect NUMA bindings on large systems
155+ - BUG/MINOR: init: make sure to always limit the total number of threads
156+ - DOC/CLEANUP: fix typos
157+ - BUG/MINOR: mux-h2: make sure the h2c task exists before refreshing it
158+ - BUG/MEDIUM: listener: duplicate inherited FDs if needed
159+ - BUG/MEDIUM: spoe: Don't set the default traget for the SPOE agent frontend
160+ - BUG/MINOR: proto_ux: report correct error when bind_listener fails
161+ - BUG/MINOR: protocol: fix minor memory leak in protocol_bind_all()
162+ - BUG/MINOR: sock_unix: match finalname with tempname in sock_unix_addrcmp()
163+ - BUG/MEDIUM: connection: Clear flags when a conn is removed from an idle list
164+ - BUG/MEDIUM: connection: Preserve flags when a conn is removed from an idle list
165+ - BUG/MEDIUM: mux-h2: erase h2c->wait_event.tasklet on error path
166+ - BUG/MEDIUM: mux-h1: Wakeup H1C on shutw if there is no I/O subscription
167+ - BUILD: da: extends CFLAGS to support API v3 from 3.1.7 and onwards.
168+ - MINOR: proxy/pool: prevent unnecessary calls to pool_gc()
169+ - DOC: config: strict-sni allows to start without certificate
170+ - BUG/MEDIUM: channel: Improve reports for shut in co_getblk()
171+ - BUG/MEDIUM: dns: Properly handle error when a response consumed
172+ - MINOR: proxy: check if p is NULL in free_proxy()
173+ - BUG/MINOR: sink: free forward_px on deinit()
174+ - BUG/MINOR: log: free log forward proxies on deinit()
175+ - BUG/MINOR: hlua: enforce proper running context for register_x functions
176+ - CLEANUP: hlua: fix conflicting comment in hlua_ctx_destroy()
177+ - BUG/MEDIUM: resolvers: Force the connect timeout for DNS resolutions
178+ - BUG/MINOR: stick_table: alert when type len has incorrect characters
179+ - CI: bump "actions/checkout" to v3 for cross zoo matrix
180+ - REGTESTS: fix the race conditions in log_uri.vtc
181+ - BUG/MEDIUM: log: Properly handle client aborts in syslog applet
182+ - CLEANUP: backend: Remove useless debug message in assign_server()
183+ - BUG/MINOR: cfgparse: make sure to include openssl-compat
184+ - BUG/MEDIUM: proxy/sktable: prevent watchdog trigger on soft-stop
185+ - BUG/MEDIUM: Update read expiration date on synchronous send
186+ - BUG/MINOR: mux-h2: make sure to produce a log on invalid requests
187+ - MINOR: checks: make sure spread-checks is used also at boot time
188+ - MINOR: clock: measure the total boot time
189+ - BUG/MINOR: checks: postpone the startup of health checks by the boot time
190+ - BUG/MINOR: clock: fix the boot time measurement method for 2.6 and older
191+ - BUG/MINOR: tcp-rules: Don't shortened the inspect-delay when EOI is set
192+ - DOC: config: Clarify conditions to shorten the inspect-delay for TCP rules
193+ - DOC: add size format section to manual
194+ - DOC/MINOR: config: Fix typo in description for `ssl_bc` in configuration.txt
195+ - BUG/MINOR: hlua: unsafe hlua_lua2smp() usage
196+ - SCRIPTS: publish-release: update the umask to keep group write access
197+ - BUG/MINOR: log: fix memory error handling in parse_logsrv()
198+ - BUG/MINOR: proxy: missing free in free_proxy for redirect rules
199+ - MINOR: spoe: Don't stop disabled proxies
200+ - BUILD: mjson: Fix warning about unused variables
201+ - BUG/MINOR: debug: do not emit empty lines in thread dumps
202+ - BUG/MEDIUM: spoe: Don't start new applet if there are enough idle ones
203+ - CI: switch to Fastly CDN to download LibreSSL
204+ - BUILD: ssl: switch LibreSSL to Fastly CDN
205+ - BUG/MINOR: server: incorrect report for tracking servers leaving drain
206+ - MINOR: server: explicitly commit state change in srv_update_status()
207+ - BUG/MINOR: server: don't miss proxy stats update on server state transitions
208+ - BUG/MINOR: server: don't miss server stats update on server state transitions
209+ - BUG/MINOR: server: don't use date when restoring last_change from state file
210+ - CI: cirrus-ci: bump FreeBSD image to 13-1
211+ - BUG/MEDIUM: filters: Don't deinit filters for disabled proxies during startup
212+ - MINOR: proxy: add http_free_redirect_rule() function
213+ - BUG/MINOR: http_rules: fix errors paths in http_parse_redirect_rule()
214+ - DOC: config: Fix bind/server/peer documentation in the peers section
215+ - CONTRIB: Add vi file extensions to .gitignore
216+ - BUG/MINOR: spoe: Only skip sending new frame after a receive attempt
217+ - BUG/MINOR: cfgparse-tcp: leak when re-declaring interface from bind line
218+ - BUG/MINOR: proxy: add missing interface bind free in free_proxy
219+
220 2023/02/14 : 2.4.22
221 - BUG/MINOR: fcgi-app: prevent 'use-fcgi-app' in default section
222 - BUG/MEDIUM: ssl: wrong eviction from the session cache tree
223diff --git a/Makefile b/Makefile
224index 40c2b10..05067bc 100644
225--- a/Makefile
226+++ b/Makefile
227@@ -675,7 +675,7 @@ OPTIONS_OBJS += $(DEVICEATLAS_LIB)/json.o
228 OPTIONS_OBJS += $(DEVICEATLAS_LIB)/dac.o
229 endif
230 OPTIONS_OBJS += addons/deviceatlas/da.o
231-OPTIONS_CFLAGS += $(if $(DEVICEATLAS_INC),-I$(DEVICEATLAS_INC))
232+OPTIONS_CFLAGS += $(if $(DEVICEATLAS_INC),-I$(DEVICEATLAS_INC)) $(if $(DEVICEATLAS_SRC),-DDATLAS_DA_NOCACHE)
233 endif
234
235 ifneq ($(USE_51DEGREES),)
236diff --git a/SUBVERS b/SUBVERS
237index f042fd0..4c00e4a 100644
238--- a/SUBVERS
239+++ b/SUBVERS
240@@ -1,2 +1,2 @@
241--f8e3218
242+-d175670
243
244diff --git a/VERDATE b/VERDATE
245index d8a3372..ea4dc97 100644
246--- a/VERDATE
247+++ b/VERDATE
248@@ -1,2 +1,2 @@
249-2023-02-14 16:57:13 +0100
250-2023/02/14
251+2023-08-19 11:25:53 +0200
252+2023/08/19
253diff --git a/VERSION b/VERSION
254index 76cb51e..0cb980f 100644
255--- a/VERSION
256+++ b/VERSION
257@@ -1 +1 @@
258-2.4.22
259+2.4.24
260diff --git a/debian/changelog b/debian/changelog
261index 907c44c..68e9c7d 100644
262--- a/debian/changelog
263+++ b/debian/changelog
264@@ -1,3 +1,22 @@
265+haproxy (2.4.24-0ubuntu0.22.04.1) jammy; urgency=medium
266+
267+ * New upstream release (LP: #2028418)
268+ - Major and critical bug fixes according to the upstream changelog:
269+ + BUG/MAJOR: http-ana: Get a fresh trash buffer for each header value
270+ replacement
271+ + BUG/MAJOR: http: reject any empty content-length header value
272+ - For further information, refer to the upstream changelog at
273+ https://www.haproxy.org/download/2.4/src/CHANGELOG and to the upstream
274+ release announcements at
275+ https://www.mail-archive.com/haproxy@formilux.org/msg43664.html
276+ (2.4.23), and
277+ https://www.mail-archive.com/haproxy@formilux.org/msg43901.html (2.4.24)
278+ - Remove patches applied by upstream in debian/patches:
279+ + CVE-2023-40225-1.patch
280+ + CVE-2023-40225-2.patch
281+
282+ -- Athos Ribeiro <athos.ribeiro@canonical.com> Tue, 31 Oct 2023 11:16:29 -0300
283+
284 haproxy (2.4.22-0ubuntu0.22.04.2) jammy-security; urgency=medium
285
286 * SECURITY UPDATE: incorrect handling of empty content-length header
287diff --git a/debian/patches/CVE-2023-40225-1.patch b/debian/patches/CVE-2023-40225-1.patch
288deleted file mode 100644
289index a4b918d..0000000
290--- a/debian/patches/CVE-2023-40225-1.patch
291+++ /dev/null
292@@ -1,265 +0,0 @@
293-From ba9afd2774c03e434165475b537d0462801f49bb Mon Sep 17 00:00:00 2001
294-From: Willy Tarreau <w@1wt.eu>
295-Date: Wed, 9 Aug 2023 08:32:48 +0200
296-Subject: [PATCH] BUG/MAJOR: http: reject any empty content-length header
297- value
298-
299-The content-length header parser has its dedicated function, in order
300-to take extreme care about invalid, unparsable, or conflicting values.
301-But there's a corner case in it, by which it stops comparing values
302-when reaching the end of the header. This has for a side effect that
303-an empty value or a value that ends with a comma does not deserve
304-further analysis, and it acts as if the header was absent.
305-
306-While this is not necessarily a problem for the value ending with a
307-comma as it will be cause a header folding and will disappear, it is a
308-problem for the first isolated empty header because this one will not
309-be recontructed when next ones are seen, and will be passed as-is to the
310-backend server. A vulnerable HTTP/1 server hosted behind haproxy that
311-would just use this first value as "0" and ignore the valid one would
312-then not be protected by haproxy and could be attacked this way, taking
313-the payload for an extra request.
314-
315-In field the risk depends on the server. Most commonly used servers
316-already have safe content-length parsers, but users relying on haproxy
317-to protect a known-vulnerable server might be at risk (and the risk of
318-a bug even in a reputable server should never be dismissed).
319-
320-A configuration-based work-around consists in adding the following rule
321-in the frontend, to explicitly reject requests featuring an empty
322-content-length header that would have not be folded into an existing
323-one:
324-
325- http-request deny if { hdr_len(content-length) 0 }
326-
327-The real fix consists in adjusting the parser so that it always expects a
328-value at the beginning of the header or after a comma. It will now reject
329-requests and responses having empty values anywhere in the C-L header.
330-
331-This needs to be backported to all supported versions. Note that the
332-modification was made to functions h1_parse_cont_len_header() and
333-http_parse_cont_len_header(). Prior to 2.8 the latter was in
334-h2_parse_cont_len_header(). One day the two should be refused but the
335-former is also used by Lua.
336-
337-The HTTP messaging reg-tests were completed to test these cases.
338-
339-Thanks to Ben Kallus of Dartmouth College and Narf Industries for
340-reporting this! (this is in GH #2237).
341-
342-(cherry picked from commit 6492f1f29d738457ea9f382aca54537f35f9d856)
343-Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
344-(cherry picked from commit a32f99f6f991d123ea3e307bf8aa63220836d365)
345-Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
346-(cherry picked from commit 65921ee12d88e9fb1fa9f6cd8198fd64b3a3f37f)
347-Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
348-(cherry picked from commit d17c50010d591d1c070e1cb0567a06032d8869e9)
349-[wt: applied to h2_parse_cont_len_header() in src/h2.c instead]
350-Signed-off-by: Willy Tarreau <w@1wt.eu>
351----
352- reg-tests/http-messaging/h1_to_h1.vtc | 26 ++++++++++++++
353- reg-tests/http-messaging/h2_to_h1.vtc | 60 +++++++++++++++++++++++++++++++++
354- src/h1.c | 20 +++++++++--
355- src/h2.c | 20 +++++++++--
356- 4 files changed, 120 insertions(+), 6 deletions(-)
357-
358---- haproxy-2.4.22.orig/reg-tests/http-messaging/h1_to_h1.vtc
359-+++ haproxy-2.4.22/reg-tests/http-messaging/h1_to_h1.vtc
360-@@ -275,3 +275,29 @@ client c3h1 -connect ${h1_feh1_sock} {
361- # arrive here.
362- expect_close
363- } -run
364-+
365-+client c4h1 -connect ${h1_feh1_sock} {
366-+ # this request is invalid and advertises an invalid C-L ending with an
367-+ # empty value, which results in a stream error.
368-+ txreq \
369-+ -req "GET" \
370-+ -url "/test31.html" \
371-+ -hdr "content-length: 0," \
372-+ -hdr "connection: close"
373-+ rxresp
374-+ expect resp.status == 400
375-+ expect_close
376-+} -run
377-+
378-+client c5h1 -connect ${h1_feh1_sock} {
379-+ # this request is invalid and advertises an empty C-L, which results
380-+ # in a stream error.
381-+ txreq \
382-+ -req "GET" \
383-+ -url "/test41.html" \
384-+ -hdr "content-length:" \
385-+ -hdr "connection: close"
386-+ rxresp
387-+ expect resp.status == 400
388-+ expect_close
389-+} -run
390---- haproxy-2.4.22.orig/reg-tests/http-messaging/h2_to_h1.vtc
391-+++ haproxy-2.4.22/reg-tests/http-messaging/h2_to_h1.vtc
392-@@ -10,6 +10,8 @@ barrier b1 cond 2 -cyclic
393- barrier b2 cond 2 -cyclic
394- barrier b3 cond 2 -cyclic
395- barrier b4 cond 2 -cyclic
396-+barrier b5 cond 2 -cyclic
397-+barrier b6 cond 2 -cyclic
398-
399- server s1 {
400- rxreq
401-@@ -31,6 +33,12 @@ server s1 {
402-
403- barrier b4 sync
404- # the next request is never received
405-+
406-+ barrier b5 sync
407-+ # the next request is never received
408-+
409-+ barrier b6 sync
410-+ # the next request is never received
411- } -repeat 2 -start
412-
413- haproxy h1 -conf {
414-@@ -121,6 +129,32 @@ client c1h2 -connect ${h1_feh2_sock} {
415- txdata -data "this is sent and ignored"
416- rxrst
417- } -run
418-+
419-+ # fifth request is invalid and advertises an invalid C-L ending with an
420-+ # empty value, which results in a stream error.
421-+ stream 9 {
422-+ barrier b5 sync
423-+ txreq \
424-+ -req "GET" \
425-+ -scheme "https" \
426-+ -url "/test5.html" \
427-+ -hdr "content-length" "0," \
428-+ -nostrend
429-+ rxrst
430-+ } -run
431-+
432-+ # sixth request is invalid and advertises an empty C-L, which results
433-+ # in a stream error.
434-+ stream 11 {
435-+ barrier b6 sync
436-+ txreq \
437-+ -req "GET" \
438-+ -scheme "https" \
439-+ -url "/test6.html" \
440-+ -hdr "content-length" "" \
441-+ -nostrend
442-+ rxrst
443-+ } -run
444- } -run
445-
446- # HEAD requests : don't work well yet
447-@@ -263,4 +297,30 @@ client c3h2 -connect ${h1_feh2_sock} {
448- txdata -data "this is sent and ignored"
449- rxrst
450- } -run
451-+
452-+ # fifth request is invalid and advertises invalid C-L ending with an
453-+ # empty value, which results in a stream error.
454-+ stream 9 {
455-+ barrier b5 sync
456-+ txreq \
457-+ -req "POST" \
458-+ -scheme "https" \
459-+ -url "/test25.html" \
460-+ -hdr "content-length" "0," \
461-+ -nostrend
462-+ rxrst
463-+ } -run
464-+
465-+ # sixth request is invalid and advertises an empty C-L, which results
466-+ # in a stream error.
467-+ stream 11 {
468-+ barrier b6 sync
469-+ txreq \
470-+ -req "POST" \
471-+ -scheme "https" \
472-+ -url "/test26.html" \
473-+ -hdr "content-length" "" \
474-+ -nostrend
475-+ rxrst
476-+ } -run
477- } -run
478---- haproxy-2.4.22.orig/src/h1.c
479-+++ haproxy-2.4.22/src/h1.c
480-@@ -34,13 +34,20 @@ int h1_parse_cont_len_header(struct h1m
481- int not_first = !!(h1m->flags & H1_MF_CLEN);
482- struct ist word;
483-
484-- word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
485-+ word.ptr = value->ptr;
486- e = value->ptr + value->len;
487-
488-- while (++word.ptr < e) {
489-+ while (1) {
490-+ if (word.ptr >= e) {
491-+ /* empty header or empty value */
492-+ goto fail;
493-+ }
494-+
495- /* skip leading delimiter and blanks */
496-- if (unlikely(HTTP_IS_LWS(*word.ptr)))
497-+ if (unlikely(HTTP_IS_LWS(*word.ptr))) {
498-+ word.ptr++;
499- continue;
500-+ }
501-
502- /* digits only now */
503- for (cl = 0, n = word.ptr; n < e; n++) {
504-@@ -79,6 +86,13 @@ int h1_parse_cont_len_header(struct h1m
505- h1m->flags |= H1_MF_CLEN;
506- h1m->curr_len = h1m->body_len = cl;
507- *value = word;
508-+
509-+ /* Now either n==e and we're done, or n points to the comma,
510-+ * and we skip it and continue.
511-+ */
512-+ if (n++ == e)
513-+ break;
514-+
515- word.ptr = n;
516- }
517- /* here we've reached the end with a single value or a series of
518---- haproxy-2.4.22.orig/src/h2.c
519-+++ haproxy-2.4.22/src/h2.c
520-@@ -80,13 +80,20 @@ int h2_parse_cont_len_header(unsigned in
521- int not_first = !!(*msgf & H2_MSGF_BODY_CL);
522- struct ist word;
523-
524-- word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
525-+ word.ptr = value->ptr;
526- e = value->ptr + value->len;
527-
528-- while (++word.ptr < e) {
529-+ while (1) {
530-+ if (word.ptr >= e) {
531-+ /* empty header or empty value */
532-+ goto fail;
533-+ }
534-+
535- /* skip leading delimiter and blanks */
536-- if (unlikely(HTTP_IS_LWS(*word.ptr)))
537-+ if (unlikely(HTTP_IS_LWS(*word.ptr))) {
538-+ word.ptr++;
539- continue;
540-+ }
541-
542- /* digits only now */
543- for (cl = 0, n = word.ptr; n < e; n++) {
544-@@ -125,6 +132,13 @@ int h2_parse_cont_len_header(unsigned in
545- *msgf |= H2_MSGF_BODY_CL;
546- *body_len = cl;
547- *value = word;
548-+
549-+ /* Now either n==e and we're done, or n points to the comma,
550-+ * and we skip it and continue.
551-+ */
552-+ if (n++ == e)
553-+ break;
554-+
555- word.ptr = n;
556- }
557- /* here we've reached the end with a single value or a series of
558diff --git a/debian/patches/CVE-2023-40225-2.patch b/debian/patches/CVE-2023-40225-2.patch
559deleted file mode 100644
560index 3e92c0d..0000000
561--- a/debian/patches/CVE-2023-40225-2.patch
562+++ /dev/null
563@@ -1,143 +0,0 @@
564-From c48acd15011dc6c66977bc088065693f430821df Mon Sep 17 00:00:00 2001
565-From: Willy Tarreau <w@1wt.eu>
566-Date: Wed, 9 Aug 2023 11:02:34 +0200
567-Subject: [PATCH] BUG/MINOR: http: skip leading zeroes in content-length
568- values
569-
570-Ben Kallus also noticed that we preserve leading zeroes on content-length
571-values. While this is totally valid, it would be safer to at least trim
572-them before passing the value, because a bogus server written to parse
573-using "strtol(value, NULL, 0)" could inadvertently take a leading zero
574-as a prefix for an octal value. While there is not much that can be done
575-to protect such servers in general (e.g. lack of check for overflows etc),
576-at least it's quite cheap to make sure the transmitted value is normalized
577-and not taken for an octal one.
578-
579-This is not really a bug, rather a missed opportunity to sanitize the
580-input, but is marked as a bug so that we don't forget to backport it to
581-stable branches.
582-
583-A combined regtest was added to h1or2_to_h1c which already validates
584-end-to-end syntax consistency on aggregate headers.
585-
586-(cherry picked from commit 22731762d9fe2c98d9e6c3942b1568266b23c69f)
587-Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
588-(cherry picked from commit c33738c7d4ed50e1758dbedd39dfe5bd929a5076)
589-Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
590-(cherry picked from commit 84462c9390c3efe95b748bb9fc3bd2edc3decd2f)
591-Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
592-(cherry picked from commit e2e464018fda41758bd42d3bcd6ac623c88a110e)
593-[wt: applied the http.c to h2.c]
594-Signed-off-by: Willy Tarreau <w@1wt.eu>
595----
596- reg-tests/http-rules/h1or2_to_h1c.vtc | 16 ++++++++++++----
597- src/h1.c | 8 ++++++++
598- src/h2.c | 8 ++++++++
599- 3 files changed, 28 insertions(+), 4 deletions(-)
600-
601---- haproxy-2.4.22.orig/reg-tests/http-rules/h1or2_to_h1c.vtc
602-+++ haproxy-2.4.22/reg-tests/http-rules/h1or2_to_h1c.vtc
603-@@ -27,11 +27,11 @@ server s1 {
604- -body "This is a body"
605-
606- expect req.method == "GET"
607-- expect req.http.fe-sl1-crc == 992395575
608-- expect req.http.fe-sl2-crc == 1270056220
609-+ expect req.http.fe-sl1-crc == 1874847043
610-+ expect req.http.fe-sl2-crc == 1142278307
611- expect req.http.fe-hdr-crc == 1719311923
612-- expect req.http.be-sl1-crc == 2604236007
613-- expect req.http.be-sl2-crc == 4181358964
614-+ expect req.http.be-sl1-crc == 3455320059
615-+ expect req.http.be-sl2-crc == 2509326257
616- expect req.http.be-hdr-crc == 3634102538
617- } -repeat 2 -start
618-
619-@@ -53,6 +53,7 @@ haproxy h1 -conf {
620- http-request set-var(req.path) path
621- http-request set-var(req.query) query
622- http-request set-var(req.param) url_param(qs_arg)
623-+ http-request set-var(req.cl) req.fhdr(content-length)
624-
625- http-request set-header sl1 "sl1: "
626-
627-@@ -65,8 +66,10 @@ haproxy h1 -conf {
628-
629- http-request set-header sl1 "%[req.fhdr(sl1)] method=<%[var(req.method)]>; uri=<%[var(req.uri)]>; path=<%[var(req.path)]>;"
630- http-request set-header sl1 "%[req.fhdr(sl1)] query=<%[var(req.query)]>; param=<%[var(req.param)]>"
631-+ http-request set-header sl1 "%[req.fhdr(sl1)] cl=<%[var(req.cl)]>"
632- http-request set-header sl2 "%[req.fhdr(sl2)] method=<%[method]>; uri=<%[url]>; path=<%[path]>; "
633- http-request set-header sl2 "%[req.fhdr(sl2)] query=<%[query]>; param=<%[url_param(qs_arg)]>"
634-+ http-request set-header sl2 "%[req.fhdr(sl2)] cl=<%[req.fhdr(content-length)]>"
635- http-request set-header hdr "%[req.fhdr(hdr)] hdr1=<%[req.hdr(hdr1)]>; fhdr1=<%[req.fhdr(hdr1)]>;"
636- http-request set-header hdr "%[req.fhdr(hdr)] hdr2=<%[req.hdr(hdr2)]>; fhdr2=<%[req.fhdr(hdr2)]>;"
637- http-request set-header hdr "%[req.fhdr(hdr)] hdr3=<%[req.hdr(hdr3)]>; fhdr3=<%[req.fhdr(hdr3)]>;"
638-@@ -120,6 +123,7 @@ haproxy h1 -conf {
639- http-request set-var(req.path) path
640- http-request set-var(req.query) query
641- http-request set-var(req.param) url_param(qs_arg)
642-+ http-request set-var(req.cl) req.fhdr(content-length)
643-
644- http-request set-header sl1 "sl1: "
645-
646-@@ -132,8 +136,10 @@ haproxy h1 -conf {
647-
648- http-request set-header sl1 "%[req.fhdr(sl1)] method=<%[var(req.method)]>; uri=<%[var(req.uri)]>; path=<%[var(req.path)]>;"
649- http-request set-header sl1 "%[req.fhdr(sl1)] query=<%[var(req.query)]>; param=<%[var(req.param)]>"
650-+ http-request set-header sl1 "%[req.fhdr(sl1)] cl=<%[var(req.cl)]>"
651- http-request set-header sl2 "%[req.fhdr(sl2)] method=<%[method]>; uri=<%[url]>; path=<%[path]>; "
652- http-request set-header sl2 "%[req.fhdr(sl2)] query=<%[query]>; param=<%[url_param(qs_arg)]>"
653-+ http-request set-header sl2 "%[req.fhdr(sl2)] cl=<%[req.fhdr(content-length)]>"
654- http-request set-header hdr "%[req.fhdr(hdr)] hdr1=<%[req.hdr(hdr1)]>; fhdr1=<%[req.fhdr(hdr1)]>;"
655- http-request set-header hdr "%[req.fhdr(hdr)] hdr2=<%[req.hdr(hdr2)]>; fhdr2=<%[req.fhdr(hdr2)]>;"
656- http-request set-header hdr "%[req.fhdr(hdr)] hdr3=<%[req.hdr(hdr3)]>; fhdr3=<%[req.fhdr(hdr3)]>;"
657-@@ -171,6 +177,7 @@ client c1h1 -connect ${h1_feh1_sock} {
658- txreq \
659- -req GET \
660- -url /path/to/file.extension?qs_arg=qs_value \
661-+ -hdr "content-length: 000, 00" \
662- -hdr "hdr1: val1" \
663- -hdr "hdr2: val2a" \
664- -hdr "hdr2: val2b" \
665-@@ -205,6 +212,7 @@ client c1h2 -connect ${h1_feh2_sock} {
666- -req GET \
667- -scheme "https" \
668- -url /path/to/file.extension?qs_arg=qs_value \
669-+ -hdr "content-length" "000, 00" \
670- -hdr "hdr1" "val1" \
671- -hdr "hdr2" " val2a" \
672- -hdr "hdr2" " val2b" \
673---- haproxy-2.4.22.orig/src/h1.c
674-+++ haproxy-2.4.22/src/h1.c
675-@@ -58,6 +58,14 @@ int h1_parse_cont_len_header(struct h1m
676- goto fail;
677- break;
678- }
679-+
680-+ if (unlikely(!cl && n > word.ptr)) {
681-+ /* There was a leading zero before this digit,
682-+ * let's trim it.
683-+ */
684-+ word.ptr = n;
685-+ }
686-+
687- if (unlikely(cl > ULLONG_MAX / 10ULL))
688- goto fail; /* multiply overflow */
689- cl = cl * 10ULL;
690---- haproxy-2.4.22.orig/src/h2.c
691-+++ haproxy-2.4.22/src/h2.c
692-@@ -104,6 +104,14 @@ int h2_parse_cont_len_header(unsigned in
693- goto fail;
694- break;
695- }
696-+
697-+ if (unlikely(!cl && n > word.ptr)) {
698-+ /* There was a leading zero before this digit,
699-+ * let's trim it.
700-+ */
701-+ word.ptr = n;
702-+ }
703-+
704- if (unlikely(cl > ULLONG_MAX / 10ULL))
705- goto fail; /* multiply overflow */
706- cl = cl * 10ULL;
707diff --git a/debian/patches/series b/debian/patches/series
708index c488f2c..276b0d5 100644
709--- a/debian/patches/series
710+++ b/debian/patches/series
711@@ -4,5 +4,3 @@ haproxy.service-add-documentation.patch
712 # applied during the build process:
713 # debianize-dconv.patch
714 reproducible.patch
715-CVE-2023-40225-1.patch
716-CVE-2023-40225-2.patch
717diff --git a/dev/hpack/decode.c b/dev/hpack/decode.c
718index ae82512..13c95c7 100644
719--- a/dev/hpack/decode.c
720+++ b/dev/hpack/decode.c
721@@ -30,7 +30,7 @@ uint8_t buf[MAX_RQ_SIZE];
722 char trash_buf[MAX_RQ_SIZE];
723 char tmp_buf[MAX_RQ_SIZE];
724
725-struct buffer trash = { .area = trash_buf, .data = 0, .size = sizeof(trash_buf) };
726+THREAD_LOCAL struct buffer trash = { .area = trash_buf, .data = 0, .size = sizeof(trash_buf) };
727 struct buffer tmp = { .area = tmp_buf, .data = 0, .size = sizeof(tmp_buf) };
728
729 /* displays a <len> long memory block at <buf>, assuming first byte of <buf>
730diff --git a/doc/configuration.txt b/doc/configuration.txt
731index 0c23dd1..7b5ee65 100644
732--- a/doc/configuration.txt
733+++ b/doc/configuration.txt
734@@ -3,7 +3,7 @@
735 Configuration Manual
736 ----------------------
737 version 2.4
738- 2023/02/14
739+ 2023/08/19
740
741
742 This document covers the configuration language as implemented in the version
743@@ -42,7 +42,8 @@ Summary
744 2.3. Environment variables
745 2.4. Conditional blocks
746 2.5. Time format
747-2.6. Examples
748+2.6. Size format
749+2.7. Examples
750
751 3. Global parameters
752 3.1. Process management and security
753@@ -753,6 +754,10 @@ file, or could be inherited by a program (See 3.7. Programs):
754 * HAPROXY_MASTER_CLI: In master-worker mode, listeners addresses of the master
755 CLI, separated by semicolons.
756
757+* HAPROXY_STARTUP_VERSION: contains the version used to start, in master-worker
758+ mode this is the version which was used to start the master, even after
759+ updating the binary and reloading.
760+
761 In addition, some pseudo-variables are internally resolved and may be used as
762 regular variables. Pseudo-variables always start with a dot ('.'), and are the
763 only ones where the dot is permitted. The current list of pseudo-variables is:
764@@ -909,7 +914,23 @@ for every keyword. Supported units are :
765 - d : days. 1d = 24h = 1440m = 86400s = 86400000ms
766
767
768-2.6. Examples
769+2.6. Size format
770+----------------
771+
772+Some parameters involve values representing size, such as bandwidth limits.
773+These values are generally expressed in bytes (unless explicitly stated
774+otherwise) but may be expressed in any other unit by suffixing the unit to the
775+numeric value. It is important to consider this because it will not be repeated
776+for every keyword. Supported units are case insensitive :
777+
778+ - k : kilobytes. 1 kilobyte = 1024 bytes
779+ - m : megabytes. 1 megabyte = 1048576 bytes
780+ - g : gigabytes. 1 gigabyte = 1073741824 bytes
781+
782+Both time and size formats require integers, decimal notation is not allowed.
783+
784+
785+2.7. Examples
786 -------------
787
788 # Simple configuration for an HTTP proxy listening on port 80 on all
789@@ -1063,10 +1084,12 @@ The following keywords are supported in the "global" section :
790 - tune.bufsize
791 - tune.chksize
792 - tune.comp.maxlevel
793+ - tune.fail-alloc
794 - tune.fd.edge-triggered
795 - tune.h2.header-table-size
796 - tune.h2.initial-window-size
797 - tune.h2.max-concurrent-streams
798+ - tune.h2.max-frame-size
799 - tune.http.cookielen
800 - tune.http.logurilen
801 - tune.http.maxhdr
802@@ -2975,7 +2998,8 @@ peers <peersect>
803 Creates a new peer list with name <peersect>. It is an independent section,
804 which is referenced by one or more stick-tables.
805
806-bind [<address>]:<port_range> [, ...] [param*]
807+bind [<address>]:port [param*]
808+bind /<path> [param*]
809 Defines the binding parameters of the local peer of this "peers" section.
810 Such lines are not supported with "peer" line in the same "peers" section.
811
812@@ -3010,16 +3034,17 @@ log <address> [len <length>] [format <format>] [sample <ranges>:<sample_size>]
813 log information about the "peers" listener. See "log" option for proxies for
814 more details.
815
816-peer <peername> <ip>:<port> [param*]
817+peer <peername> [<address>]:port [param*]
818+peer <peername> /<path> [param*]
819 Defines a peer inside a peers section.
820 If <peername> is set to the local peer name (by default hostname, or forced
821 using "-L" command line option or "localpeer" global configuration setting),
822- HAProxy will listen for incoming remote peer connection on <ip>:<port>.
823- Otherwise, <ip>:<port> defines where to connect to in order to join the
824- remote peer, and <peername> is used at the protocol level to identify and
825+ HAProxy will listen for incoming remote peer connection on the provided
826+ address. Otherwise, the address defines where to connect to in order to join
827+ the remote peer, and <peername> is used at the protocol level to identify and
828 validate the remote peer on the server side.
829
830- During a soft restart, local peer <ip>:<port> is used by the old instance to
831+ During a soft restart, local peer address is used by the old instance to
832 connect the new one and initiate a complete replication (teaching process).
833
834 It is strongly recommended to have the exact same peers declaration on all
835@@ -3033,12 +3058,13 @@ peer <peername> <ip>:<port> [param*]
836 Note: "peer" keyword may transparently be replaced by "server" keyword (see
837 "server" keyword explanation below).
838
839-server <peername> [<ip>:<port>] [param*]
840+server <peername> [<address>:<port>] [param*]
841+server <peername> [/<path>] [param*]
842 As previously mentioned, "peer" keyword may be replaced by "server" keyword
843 with a support for all "server" parameters found in 5.2 paragraph that are
844- related to transport settings. If the underlying peer is local, <ip>:<port>
845- parameters must not be present; these parameters must be provided on a "bind"
846- line (see "bind" keyword of this "peers" section).
847+ related to transport settings. If the underlying peer is local, the address
848+ parameter must not be present; it must be provided on a "bind" line (see
849+ "bind" keyword of this "peers" section).
850
851 A number of "server" parameters are irrelevant for "peers" sections. Peers by
852 nature do not support dynamic host name resolution nor health checks, hence
853@@ -8189,7 +8215,8 @@ no option accept-invalid-http-request
854 remaining ones are blocked by default unless this option is enabled. This
855 option also relaxes the test on the HTTP version, it allows HTTP/0.9 requests
856 to pass through (no version specified) and multiple digits for both the major
857- and the minor version.
858+ and the minor version. Finally, this option also allows incoming URLs to
859+ contain fragment references ('#' after the path).
860
861 This option should never be enabled by default as it hides application bugs
862 and open security breaches. It should only be deployed after a problem has
863@@ -8632,18 +8659,18 @@ no option http-ignore-probes
864
865 option http-keep-alive
866 no option http-keep-alive
867- Enable or disable HTTP keep-alive from client to server
868+ Enable or disable HTTP keep-alive from client to server for HTTP/1.x
869+ connections
870 May be used in sections : defaults | frontend | listen | backend
871 yes | yes | yes | yes
872 Arguments : none
873
874 By default HAProxy operates in keep-alive mode with regards to persistent
875- connections: for each connection it processes each request and response, and
876- leaves the connection idle on both sides between the end of a response and
877- the start of a new request. This mode may be changed by several options such
878- as "option http-server-close" or "option httpclose". This option allows to
879- set back the keep-alive mode, which can be useful when another mode was used
880- in a defaults section.
881+ HTTP/1.x connections: for each connection it processes each request and
882+ response, and leaves the connection idle on both sides. This mode may be
883+ changed by several options such as "option http-server-close" or "option
884+ httpclose". This option allows to set back the keep-alive mode, which can be
885+ useful when another mode was used in a defaults section.
886
887 Setting "option http-keep-alive" enables HTTP keep-alive mode on the client-
888 and server- sides. This provides the lowest latency on the client side (slow
889@@ -8660,15 +8687,6 @@ no option http-keep-alive
890 compared to the cost of retrieving the associated object from the server.
891
892 This last case can happen when the server is a fast static server of cache.
893- In this case, the server will need to be properly tuned to support high enough
894- connection counts because connections will last until the client sends another
895- request.
896-
897- If the client request has to go to another backend or another server due to
898- content switching or the load balancing algorithm, the idle connection will
899- immediately be closed and a new one re-opened. Option "prefer-last-server" is
900- available to try optimize server selection so that if the server currently
901- attached to an idle connection is usable, it will be used.
902
903 At the moment, logs will not indicate whether requests came from the same
904 session or not. The accept date reported in the logs corresponds to the end
905@@ -8678,12 +8696,10 @@ no option http-keep-alive
906 not set.
907
908 This option disables and replaces any previous "option httpclose" or "option
909- http-server-close". When backend and frontend options differ, all of these 4
910- options have precedence over "option http-keep-alive".
911+ http-server-close".
912
913 See also : "option httpclose",, "option http-server-close",
914- "option prefer-last-server", "option http-pretend-keepalive",
915- and "1.1. The HTTP transaction model".
916+ "option prefer-last-server" and "option http-pretend-keepalive".
917
918
919 option http-no-delay
920@@ -8722,19 +8738,19 @@ no option http-no-delay
921
922 option http-pretend-keepalive
923 no option http-pretend-keepalive
924- Define whether HAProxy will announce keepalive to the server or not
925+ Define whether HAProxy will announce keepalive for HTTP/1.x connection to the
926+ server or not
927 May be used in sections : defaults | frontend | listen | backend
928 yes | no | yes | yes
929 Arguments : none
930
931 When running with "option http-server-close" or "option httpclose", HAProxy
932- adds a "Connection: close" header to the request forwarded to the server.
933- Unfortunately, when some servers see this header, they automatically refrain
934- from using the chunked encoding for responses of unknown length, while this
935- is totally unrelated. The immediate effect is that this prevents HAProxy from
936- maintaining the client connection alive. A second effect is that a client or
937- a cache could receive an incomplete response without being aware of it, and
938- consider the response complete.
939+ adds a "Connection: close" header to the HTTP/1.x request forwarded to the
940+ server. Unfortunately, when some servers see this header, they automatically
941+ refrain from using the chunked encoding for responses of unknown length,
942+ while this is totally unrelated. The effect is that a client or a cache could
943+ receive an incomplete response without being aware of it, and consider the
944+ response complete.
945
946 By setting "option http-pretend-keepalive", HAProxy will make the server
947 believe it will keep the connection alive. The server will then not fall back
948@@ -8754,9 +8770,7 @@ no option http-pretend-keepalive
949 This option may be set in backend and listen sections. Using it in a frontend
950 section will be ignored and a warning will be reported during startup. It is
951 a backend related option, so there is no real reason to set it on a
952- frontend. This option may be combined with "option httpclose", which will
953- cause keepalive to be announced to the server and close to be announced to
954- the client. This practice is discouraged though.
955+ frontend.
956
957 If this option has been enabled in a "defaults" section, it can be disabled
958 in a specific instance by prepending the "no" keyword before it.
959@@ -8794,26 +8808,25 @@ option http-restrict-req-hdr-names { preserve | delete | reject }
960
961 option http-server-close
962 no option http-server-close
963- Enable or disable HTTP connection closing on the server side
964+ Enable or disable HTTP/1.x connection closing on the server side
965 May be used in sections : defaults | frontend | listen | backend
966 yes | yes | yes | yes
967 Arguments : none
968
969 By default HAProxy operates in keep-alive mode with regards to persistent
970- connections: for each connection it processes each request and response, and
971- leaves the connection idle on both sides between the end of a response and
972- the start of a new request. This mode may be changed by several options such
973- as "option http-server-close" or "option httpclose". Setting "option
974- http-server-close" enables HTTP connection-close mode on the server side
975- while keeping the ability to support HTTP keep-alive and pipelining on the
976- client side. This provides the lowest latency on the client side (slow
977- network) and the fastest session reuse on the server side to save server
978- resources, similarly to "option httpclose". It also permits non-keepalive
979- capable servers to be served in keep-alive mode to the clients if they
980- conform to the requirements of RFC7230. Please note that some servers do not
981- always conform to those requirements when they see "Connection: close" in the
982- request. The effect will be that keep-alive will never be used. A workaround
983- consists in enabling "option http-pretend-keepalive".
984+ HTTP/1.x connections: for each connection it processes each request and
985+ response, and leaves the connection idle on both sides. This mode may be
986+ changed by several options such as "option http-server-close" or "option
987+ httpclose". Setting "option http-server-close" enables HTTP connection-close
988+ mode on the server side while keeping the ability to support HTTP keep-alive
989+ and pipelining on the client side. This provides the lowest latency on the
990+ client side (slow network) and the fastest session reuse on the server side
991+ to save server resources, similarly to "option httpclose". It also permits
992+ non-keepalive capable servers to be served in keep-alive mode to the clients
993+ if they conform to the requirements of RFC7230. Please note that some servers
994+ do not always conform to those requirements when they see "Connection: close"
995+ in the request. The effect will be that keep-alive will never be used. A
996+ workaround consists in enabling "option http-pretend-keepalive".
997
998 At the moment, logs will not indicate whether requests came from the same
999 session or not. The accept date reported in the logs corresponds to the end
1000@@ -8831,8 +8844,8 @@ no option http-server-close
1001 If this option has been enabled in a "defaults" section, it can be disabled
1002 in a specific instance by prepending the "no" keyword before it.
1003
1004- See also : "option httpclose", "option http-pretend-keepalive",
1005- "option http-keep-alive", and "1.1. The HTTP transaction model".
1006+ See also : "option httpclose", "option http-pretend-keepalive" and
1007+ "option http-keep-alive".
1008
1009 option http-use-proxy-header
1010 no option http-use-proxy-header
1011@@ -8929,37 +8942,37 @@ option httpchk <method> <uri> <version>
1012
1013 option httpclose
1014 no option httpclose
1015- Enable or disable HTTP connection closing
1016+ Enable or disable HTTP/1.x connection closing
1017 May be used in sections : defaults | frontend | listen | backend
1018 yes | yes | yes | yes
1019 Arguments : none
1020
1021 By default HAProxy operates in keep-alive mode with regards to persistent
1022- connections: for each connection it processes each request and response, and
1023- leaves the connection idle on both sides between the end of a response and
1024- the start of a new request. This mode may be changed by several options such
1025- as "option http-server-close" or "option httpclose".
1026-
1027- If "option httpclose" is set, HAProxy will close connections with the server
1028- and the client as soon as the request and the response are received. It will
1029- also check if a "Connection: close" header is already set in each direction,
1030- and will add one if missing. Any "Connection" header different from "close"
1031- will also be removed.
1032+ HTTP/1.x connections: for each connection it processes each request and
1033+ response, and leaves the connection idle on both sides. This mode may be
1034+ changed by several options such as "option http-server-close" or "option
1035+ httpclose".
1036+
1037+ If "option httpclose" is set, HAProxy will close the client or the server
1038+ connection, depending where the option is set. Only the frontend is
1039+ considered for client connections while the frontend and the backend are
1040+ considered for server ones. In this case the option is enabled if at least
1041+ one of the frontend or backend holding the connection has it enabled. If the
1042+ option is set on a listener, it is applied both on client and server
1043+ connections. It will check if a "Connection: close" header is already set in
1044+ each direction, and will add one if missing.
1045
1046 This option may also be combined with "option http-pretend-keepalive", which
1047- will disable sending of the "Connection: close" header, but will still cause
1048- the connection to be closed once the whole response is received.
1049+ will disable sending of the "Connection: close" request header, but will
1050+ still cause the connection to be closed once the whole response is received.
1051
1052- This option may be set both in a frontend and in a backend. It is enabled if
1053- at least one of the frontend or backend holding a connection has it enabled.
1054 It disables and replaces any previous "option http-server-close" or "option
1055- http-keep-alive". Please check section 4 ("Proxies") to see how this option
1056- combines with others when frontend and backend options differ.
1057+ http-keep-alive".
1058
1059 If this option has been enabled in a "defaults" section, it can be disabled
1060 in a specific instance by prepending the "no" keyword before it.
1061
1062- See also : "option http-server-close" and "1.1. The HTTP transaction model".
1063+ See also : "option http-server-close".
1064
1065
1066 option httplog [ clf ]
1067@@ -12558,6 +12571,9 @@ tcp-request inspect-delay <timeout>
1068 Obviously this is unlikely to be very useful and might even be racy, so such
1069 setups are not recommended.
1070
1071+ Note the inspection delay is shortened if an connection error or shutdown is
1072+ experienced or if the request buffer appears as full.
1073+
1074 As soon as a rule matches, the request is released and continues as usual. If
1075 the timeout is reached and no rule matches, the default policy will be to let
1076 it pass through unaffected.
1077@@ -13551,7 +13567,8 @@ crt <cert>
1078 who provide a valid TLS Server Name Indication field matching one of their
1079 CN or alt subjects. Wildcards are supported, where a wildcard character '*'
1080 is used instead of the first hostname component (e.g. *.example.org matches
1081- www.example.org but not www.sub.example.org).
1082+ www.example.org but not www.sub.example.org). If an empty directory is used,
1083+ HAProxy will not start unless the "strict-sni" keyword is used.
1084
1085 If no SNI is provided by the client or if the SSL library does not support
1086 TLS extensions, or if the client provides an SNI hostname which does not
1087@@ -13972,8 +13989,11 @@ ssl-min-ver [ SSLv3 | TLSv1.0 | TLSv1.1 | TLSv1.2 | TLSv1.3 ]
1088 strict-sni
1089 This setting is only available when support for OpenSSL was built in. The
1090 SSL/TLS negotiation is allow only if the client provided an SNI which match
1091- a certificate. The default certificate is not used.
1092- See the "crt" option for more information.
1093+ a certificate. The default certificate is not used. This option also allows
1094+ to start without any certificate on a bind line, so an empty directory could
1095+ be used and filled later from the stats socket.
1096+ See the "crt" option for more information. See "add ssl crt-list" command in
1097+ the management guide.
1098
1099 tcp-ut <delay>
1100 Sets the TCP User Timeout for all incoming connections instantiated from this
1101@@ -15315,15 +15335,53 @@ parse-resolv-conf
1102 placed in the resolvers section in place of this directive.
1103
1104 hold <status> <period>
1105- Defines <period> during which the last name resolution should be kept based
1106- on last resolution <status>
1107- <status> : last name resolution status. Acceptable values are "nx",
1108- "other", "refused", "timeout", "valid", "obsolete".
1109- <period> : interval between two successive name resolution when the last
1110- answer was in <status>. It follows the HAProxy time format.
1111- <period> is in milliseconds by default.
1112+ Upon receiving the DNS response <status>, determines whether a server's state
1113+ should change from UP to DOWN. To make that determination, it checks whether
1114+ any valid status has been received during the past <period> in order to
1115+ counteract the just received invalid status.
1116+
1117+ <status> : last name resolution status.
1118+ nx After receiving an NXDOMAIN status, check for any valid
1119+ status during the concluding period.
1120+
1121+ refused After receiving a REFUSED status, check for any valid
1122+ status during the concluding period.
1123+
1124+ timeout After the "timeout retry" has struck, check for any
1125+ valid status during the concluding period.
1126+
1127+ other After receiving any other invalid status, check for any
1128+ valid status during the concluding period.
1129+
1130+ valid Applies only to "http-request do-resolve" and
1131+ "tcp-request content do-resolve" actions. It defines the
1132+ period for which the server will maintain a valid response
1133+ before triggering another resolution. It does not affect
1134+ dynamic resolution of servers.
1135
1136- Default value is 10s for "valid", 0s for "obsolete" and 30s for others.
1137+ obsolete Defines how long to wait before removing obsolete DNS
1138+ records after an updated answer record is received. It
1139+ applies to SRV records.
1140+
1141+ <period> : Amount of time into the past during which a valid response must
1142+ have been received. It follows the HAProxy time format and is in
1143+ milliseconds by default.
1144+
1145+ For a server that relies on dynamic DNS resolution to determine its IP
1146+ address, receiving an invalid DNS response, such as NXDOMAIN, will lead to
1147+ changing the server's state from UP to DOWN. The hold directives define how
1148+ far into the past to look for a valid response. If a valid response has been
1149+ received within <period>, the just received invalid status will be ignored.
1150+
1151+ Unless a valid response has been receiving during the concluding period, the
1152+ server will be marked as DOWN. For example, if "hold nx 30s" is set and the
1153+ last received DNS response was NXDOMAIN, the server will be marked DOWN
1154+ unless a valid response has been received during the last 30 seconds.
1155+
1156+ A server in the DOWN state will be marked UP immediately upon receiving a
1157+ valid status from the DNS server.
1158+
1159+ A separate behavior exists for "hold valid" and "hold obsolete".
1160
1161 resolve_retries <nb>
1162 Defines the number <nb> of queries to send to resolve a server name before
1163@@ -18450,7 +18508,7 @@ future information. Those generally include the results of SSL negotiations.
1164 ssl_bc : boolean
1165 Returns true when the back connection was made via an SSL/TLS transport
1166 layer and is locally deciphered. This means the outgoing connection was made
1167- other a server with the "ssl" option. It can be used in a tcp-check or an
1168+ to a server with the "ssl" option. It can be used in a tcp-check or an
1169 http-check ruleset.
1170
1171 ssl_bc_alg_keysize : integer
1172@@ -19623,7 +19681,11 @@ path : string
1173 information from databases and keep them in caches. Note that with outgoing
1174 caches, it would be wiser to use "url" instead. With ACLs, it's typically
1175 used to match exact file names (e.g. "/login.php"), or directory parts using
1176- the derivative forms. See also the "url" and "base" fetch methods.
1177+ the derivative forms. See also the "url" and "base" fetch methods. Please
1178+ note that any fragment reference in the URI ('#' after the path) is strictly
1179+ forbidden by the HTTP standard and will be rejected. However, if the frontend
1180+ receiving the request has "option accept-invalid-http-request", then this
1181+ fragment part will be accepted and will also appear in the path.
1182
1183 ACL derivatives :
1184 path : exact string match
1185@@ -19641,7 +19703,11 @@ pathq : string
1186 relative URI, excluding the scheme and the authority part, if any. Indeed,
1187 while it is the common representation for an HTTP/1.1 request target, in
1188 HTTP/2, an absolute URI is often used. This sample fetch will return the same
1189- result in both cases.
1190+ result in both cases. Please note that any fragment reference in the URI ('#'
1191+ after the path) is strictly forbidden by the HTTP standard and will be
1192+ rejected. However, if the frontend receiving the request has "option
1193+ accept-invalid-http-request", then this fragment part will be accepted and
1194+ will also appear in the path.
1195
1196 query : string
1197 This extracts the request's query string, which starts after the first
1198@@ -19874,7 +19940,11 @@ url : string
1199 "path" is preferred over using "url", because clients may send a full URL as
1200 is normally done with proxies. The only real use is to match "*" which does
1201 not match in "path", and for which there is already a predefined ACL. See
1202- also "path" and "base".
1203+ also "path" and "base". Please note that any fragment reference in the URI
1204+ ('#' after the path) is strictly forbidden by the HTTP standard and will be
1205+ rejected. However, if the frontend receiving the request has "option
1206+ accept-invalid-http-request", then this fragment part will be accepted and
1207+ will also appear in the url.
1208
1209 ACL derivatives :
1210 url : exact string match
1211@@ -20971,6 +21041,13 @@ Timings events in TCP mode:
1212 header (empty line) was never seen, most likely because the server timeout
1213 stroke before the server managed to process the request.
1214
1215+ - Td: this is the total transfer time of the response payload till the last
1216+ byte sent to the client. In HTTP it starts after the last response header
1217+ (after Tr).
1218+
1219+ The data sent are not guaranteed to be received by the client, they can be
1220+ stuck in either the kernel or the network.
1221+
1222 - Ta: total active time for the HTTP request, between the moment the proxy
1223 received the first byte of the request header and the emission of the last
1224 byte of the response body. The exception is when the "logasap" option is
1225diff --git a/doc/design-thoughts/config-language.txt b/doc/design-thoughts/config-language.txt
1226index 510ada6..20c4fbd 100644
1227--- a/doc/design-thoughts/config-language.txt
1228+++ b/doc/design-thoughts/config-language.txt
1229@@ -24,9 +24,9 @@ Pour les filtres :
1230 <operator> = [ == | =~ | =* | =^ | =/ | != | !~ | !* | !^ | !/ ]
1231 <pattern> = "<string>"
1232 <action> = [ allow | permit | deny | delete | replace | switch | add | set | redir ]
1233- <args> = optionnal action args
1234+ <args> = optional action args
1235
1236- exemples:
1237+ examples:
1238
1239 req in URI =^ "/images" switch images
1240 req in h(host) =* ".mydomain.com" switch mydomain
1241diff --git a/doc/internals/http-parsing.txt b/doc/internals/http-parsing.txt
1242index 494558b..8b3f239 100644
1243--- a/doc/internals/http-parsing.txt
1244+++ b/doc/internals/http-parsing.txt
1245@@ -325,11 +325,11 @@ Unfortunately, some products such as Apache allow such characters :-/
1246
1247 - each http_txn has 1 request message (http_req), and 0 or 1 response message
1248 (http_rtr). Each of them has 1 and only one http_txn. An http_txn holds
1249- informations such as the HTTP method, the URI, the HTTP version, the
1250+ information such as the HTTP method, the URI, the HTTP version, the
1251 transfer-encoding, the HTTP status, the authorization, the req and rtr
1252 content-length, the timers, logs, etc... The backend and server which process
1253 the request are also known from the http_txn.
1254
1255-- both request and response messages hold header and parsing informations, such
1256+- both request and response messages hold header and parsing information, such
1257 as the parsing state, start of headers, start of message, captures, etc...
1258
1259diff --git a/doc/management.txt b/doc/management.txt
1260index 56f119e..1177aa2 100644
1261--- a/doc/management.txt
1262+++ b/doc/management.txt
1263@@ -2563,7 +2563,7 @@ show resolvers [<resolvers section id>]
1264 other: any other DNS errors
1265 invalid: invalid DNS response (from a protocol point of view)
1266 too_big: too big response
1267- outdated: number of response arrived too late (after an other name server)
1268+ outdated: number of response arrived too late (after another name server)
1269
1270 show servers conn [<backend>]
1271 Dump the current and idle connections state of the servers belonging to the
1272diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
1273index 4b8b098..157387b 100644
1274--- a/include/haproxy/connection.h
1275+++ b/include/haproxy/connection.h
1276@@ -218,6 +218,16 @@ static inline void conn_xprt_shutw_hard(struct connection *c)
1277 c->xprt->shutw(c, c->xprt_ctx, 0);
1278 }
1279
1280+/* Used to know if a connection is in an idle list. It returns connection flag
1281+ * corresponding to the idle list if the connection is idle (CO_FL_SAFE_LIST or
1282+ * CO_FL_IDLE_LIST) or 0 otherwise. Note that if the connection is scheduled to
1283+ * be removed, 0 is returned, regardless the connection flags.
1284+ */
1285+static inline unsigned int conn_get_idle_flag(const struct connection *conn)
1286+{
1287+ return (!MT_LIST_INLIST(&conn->toremove_list) ? conn->flags & CO_FL_LIST_MASK : 0);
1288+}
1289+
1290 /* This is used at the end of the socket IOCB to possibly create the mux if it
1291 * was not done yet, or wake it up if flags changed compared to old_flags or if
1292 * need_wake insists on this. It returns <0 if the connection was destroyed and
1293@@ -266,7 +276,7 @@ static inline int conn_notify_mux(struct connection *conn, int old_flags, int fo
1294 ((conn->flags ^ old_flags) & CO_FL_NOTIFY_DONE) ||
1295 ((old_flags & CO_FL_WAIT_XPRT) && !(conn->flags & CO_FL_WAIT_XPRT))) &&
1296 conn->mux && conn->mux->wake) {
1297- uint conn_in_list = conn->flags & CO_FL_LIST_MASK;
1298+ uint conn_in_list = conn_get_idle_flag(conn);
1299 struct server *srv = objt_server(conn->target);
1300
1301 if (conn_in_list) {
1302diff --git a/include/haproxy/h2.h b/include/haproxy/h2.h
1303index 8d2aa95..4f872b9 100644
1304--- a/include/haproxy/h2.h
1305+++ b/include/haproxy/h2.h
1306@@ -207,7 +207,7 @@ extern struct h2_frame_definition h2_frame_definition[H2_FT_ENTRIES];
1307 /* various protocol processing functions */
1308
1309 int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned long long *body_len);
1310-int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len);
1311+int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len, int relaxed);
1312 int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len, char *upgrade_protocol);
1313 int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx);
1314
1315diff --git a/include/haproxy/hlua.h b/include/haproxy/hlua.h
1316index 21e4534..d77f92e 100644
1317--- a/include/haproxy/hlua.h
1318+++ b/include/haproxy/hlua.h
1319@@ -54,6 +54,7 @@ int hlua_post_init();
1320 void hlua_applet_tcp_fct(struct appctx *ctx);
1321 void hlua_applet_http_fct(struct appctx *ctx);
1322 struct task *hlua_process_task(struct task *task, void *context, unsigned int state);
1323+void hlua_yieldk(lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k, int timeout, unsigned int flags);
1324
1325 #else /* USE_LUA */
1326
1327diff --git a/include/haproxy/http.h b/include/haproxy/http.h
1328index 8a86cb6..e8c5b85 100644
1329--- a/include/haproxy/http.h
1330+++ b/include/haproxy/http.h
1331@@ -134,6 +134,25 @@ static inline enum http_etag_type http_get_etag_type(const struct ist etag)
1332 return ETAG_INVALID;
1333 }
1334
1335+/* Looks into <ist> for forbidden characters for :path values (0x00..0x1F,
1336+ * 0x20, 0x23), starting at pointer <start> which must be within <ist>.
1337+ * Returns non-zero if such a character is found, 0 otherwise. When run on
1338+ * unlikely header match, it's recommended to first check for the presence
1339+ * of control chars using ist_find_ctl().
1340+ */
1341+static inline int http_path_has_forbidden_char(const struct ist ist, const char *start)
1342+{
1343+ do {
1344+ if ((uint8_t)*start <= 0x23) {
1345+ if ((uint8_t)*start < 0x20)
1346+ return 1;
1347+ if ((1U << ((uint8_t)*start & 0x1F)) & ((1<<3) | (1<<0)))
1348+ return 1;
1349+ }
1350+ start++;
1351+ } while (start < istend(ist));
1352+ return 0;
1353+}
1354
1355 #endif /* _HAPROXY_HTTP_H */
1356
1357diff --git a/include/haproxy/http_ana-t.h b/include/haproxy/http_ana-t.h
1358index 89d41dd..508721d 100644
1359--- a/include/haproxy/http_ana-t.h
1360+++ b/include/haproxy/http_ana-t.h
1361@@ -59,7 +59,7 @@
1362 /* cacheability management, bits values 0x1000 to 0x3000 (0-3 shift 12) */
1363 #define TX_CACHEABLE 0x00001000 /* at least part of the response is cacheable */
1364 #define TX_CACHE_COOK 0x00002000 /* a cookie in the response is cacheable */
1365-#define TX_CACHE_IGNORE 0x00004000 /* do not retrieve object from cache, or avoid caching response */
1366+#define TX_CACHE_IGNORE 0x00004000 /* do not retrieve object from cache */
1367 #define TX_CACHE_SHIFT 12 /* bit shift */
1368
1369 #define TX_CON_WANT_TUN 0x00008000 /* Will be a tunnel (CONNECT or 101-Switching-Protocol) */
1370diff --git a/include/haproxy/http_rules.h b/include/haproxy/http_rules.h
1371index 060e3e8..e048064 100644
1372--- a/include/haproxy/http_rules.h
1373+++ b/include/haproxy/http_rules.h
1374@@ -34,6 +34,7 @@ extern struct action_kw_list http_after_res_keywords;
1375 struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy);
1376 struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy);
1377 struct act_rule *parse_http_after_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy);
1378+void http_free_redirect_rule(struct redirect_rule *rdr);
1379 struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
1380 const char **args, char **errmsg, int use_fmt, int dir);
1381
1382diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h
1383index 476e65a..dac71ec 100644
1384--- a/include/haproxy/listener-t.h
1385+++ b/include/haproxy/listener-t.h
1386@@ -205,6 +205,9 @@ struct bind_conf {
1387 struct rx_settings settings; /* all the settings needed for the listening socket */
1388 };
1389
1390+#define LI_F_FINALIZED 0x00000002 /* listener made it to the READY||LIMITED||FULL state at least once, may be suspended/resumed safely */
1391+#define LI_F_SUSPENDED 0x00000004 /* listener has been suspended using suspend_listener(), it is either is LI_PAUSED or LI_ASSIGNED state */
1392+
1393 /* The listener will be directly referenced by the fdtab[] which holds its
1394 * socket. The listener provides the protocol-specific accept() function to
1395 * the fdtab.
1396@@ -215,6 +218,8 @@ struct listener {
1397 short int nice; /* nice value to assign to the instantiated tasks */
1398 int luid; /* listener universally unique ID, used for SNMP */
1399 int options; /* socket options : LI_O_* */
1400+ uint16_t flags; /* listener flags: LI_F_* */
1401+ /* 2-bytes hole here */
1402 __decl_thread(HA_RWLOCK_T lock);
1403
1404 struct fe_counters *counters; /* statistics counters */
1405diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h
1406index bb502c6..44af4bc 100644
1407--- a/include/haproxy/listener.h
1408+++ b/include/haproxy/listener.h
1409@@ -39,29 +39,45 @@ void listener_set_state(struct listener *l, enum li_state st);
1410 * closes upon SHUT_WR and refuses to rebind. So a common validation path
1411 * involves SHUT_WR && listen && SHUT_RD. In case of success, the FD's polling
1412 * is disabled. It normally returns non-zero, unless an error is reported.
1413- * It will need to operate under the proxy's lock. The caller is
1414- * responsible for indicating in lpx whether the proxy locks is
1415- * already held (non-zero) or not (zero) so that the function picks it.
1416+ * It will need to operate under the proxy's lock and the listener's lock.
1417+ * suspend() may totally stop a listener if it doesn't support the PAUSED
1418+ * state, in which case state will be set to ASSIGNED.
1419+ * The caller is responsible for indicating in lpx, lli whether the respective
1420+ * locks are already held (non-zero) or not (zero) so that the function pick
1421+ * the missing ones, in this order.
1422 */
1423-int pause_listener(struct listener *l, int lpx);
1424+int suspend_listener(struct listener *l, int lpx, int lli);
1425
1426 /* This function tries to resume a temporarily disabled listener.
1427 * The resulting state will either be LI_READY or LI_FULL. 0 is returned
1428 * in case of failure to resume (eg: dead socket).
1429- * It will need to operate under the proxy's lock. The caller is
1430- * responsible for indicating in lpx whether the proxy locks is
1431- * already held (non-zero) or not (zero) so that the function picks it.
1432+ * It will need to operate under the proxy's lock and the listener's lock.
1433+ * The caller is responsible for indicating in lpx, lli whether the respective
1434+ * locks are already held (non-zero) or not (zero) so that the function pick
1435+ * the missing ones, in this order.
1436 */
1437-int resume_listener(struct listener *l, int lpx);
1438+int resume_listener(struct listener *l, int lpx, int lli);
1439+
1440+/* Same as resume_listener(), but will only work to resume from
1441+ * LI_FULL or LI_LIMITED states because we try to relax listeners that
1442+ * were temporarily restricted and not to resume inactive listeners that
1443+ * may have been paused or completely stopped in the meantime.
1444+ * Returns positive value for success and 0 for failure.
1445+ * It will need to operate under the proxy's lock and the listener's lock.
1446+ * The caller is responsible for indicating in lpx, lli whether the respective
1447+ * locks are already held (non-zero) or not (zero) so that the function pick
1448+ * the missing ones, in this order.
1449+ */
1450+int relax_listener(struct listener *l, int lpx, int lli);
1451
1452 /*
1453 * This function completely stops a listener. It will need to operate under the
1454- * proxy's lock and the protocol's lock. The caller is
1455- * responsible for indicating in lpx, lpr whether the respective locks are
1456+ * proxy's lock, the protocol's and the listener's lock. The caller is
1457+ * responsible for indicating in lpx, lpr, lli whether the respective locks are
1458 * already held (non-zero) or not (zero) so that the function picks the missing
1459 * ones, in this order.
1460 */
1461-void stop_listener(struct listener *l, int lpx, int lpr);
1462+void stop_listener(struct listener *l, int lpx, int lpr, int lli);
1463
1464 /* This function adds the specified listener's file descriptor to the polling
1465 * lists if it is in the LI_LISTEN state. The listener enters LI_READY or
1466diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h
1467index a5a129a..5aa1ad3 100644
1468--- a/include/haproxy/proxy-t.h
1469+++ b/include/haproxy/proxy-t.h
1470@@ -402,6 +402,7 @@ struct proxy {
1471 unsigned int li_paused; /* total number of listeners paused (LI_PAUSED) */
1472 unsigned int li_bound; /* total number of listeners ready (LI_LISTEN) */
1473 unsigned int li_ready; /* total number of listeners ready (>=LI_READY) */
1474+ unsigned int li_suspended; /* total number of listeners suspended (could be paused or unbound) */
1475
1476 /* warning: these structs are huge, keep them at the bottom */
1477 struct sockaddr_storage dispatch_addr; /* the default address to connect to */
1478diff --git a/include/haproxy/server.h b/include/haproxy/server.h
1479index fdb285c..66dcaf9 100644
1480--- a/include/haproxy/server.h
1481+++ b/include/haproxy/server.h
1482@@ -299,6 +299,7 @@ static inline void srv_release_conn(struct server *srv, struct connection *conn)
1483 /* Remove the connection from any tree (safe, idle or available) */
1484 HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
1485 conn_delete_from_tree(&conn->hash_node->node);
1486+ conn->flags &= ~CO_FL_LIST_MASK;
1487 HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
1488 }
1489
1490diff --git a/include/haproxy/sink.h b/include/haproxy/sink.h
1491index a9f8099..71cad0f 100644
1492--- a/include/haproxy/sink.h
1493+++ b/include/haproxy/sink.h
1494@@ -32,7 +32,8 @@ extern struct proxy *sink_proxies_list;
1495
1496 struct sink *sink_find(const char *name);
1497 struct sink *sink_new_fd(const char *name, const char *desc, enum log_fmt, int fd);
1498-ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
1499+ssize_t __sink_write(struct sink *sink, size_t maxlen,
1500+ const struct ist msg[], size_t nmsg,
1501 int level, int facility, struct ist * metadata);
1502 int sink_announce_dropped(struct sink *sink, int facility);
1503
1504@@ -44,7 +45,8 @@ int sink_announce_dropped(struct sink *sink, int facility);
1505 * The function returns the number of Bytes effectively sent or announced.
1506 * or <= 0 in other cases.
1507 */
1508-static inline ssize_t sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
1509+static inline ssize_t sink_write(struct sink *sink, size_t maxlen,
1510+ const struct ist msg[], size_t nmsg,
1511 int level, int facility, struct ist *metadata)
1512 {
1513 ssize_t sent;
1514@@ -69,7 +71,7 @@ static inline ssize_t sink_write(struct sink *sink, const struct ist msg[], size
1515 }
1516
1517 HA_RWLOCK_RDLOCK(LOGSRV_LOCK, &sink->ctx.lock);
1518- sent = __sink_write(sink, msg, nmsg, level, facility, metadata);
1519+ sent = __sink_write(sink, maxlen, msg, nmsg, level, facility, metadata);
1520 HA_RWLOCK_RDUNLOCK(LOGSRV_LOCK, &sink->ctx.lock);
1521
1522 fail:
1523diff --git a/include/haproxy/spoe-t.h b/include/haproxy/spoe-t.h
1524index 197f47b..38e3272 100644
1525--- a/include/haproxy/spoe-t.h
1526+++ b/include/haproxy/spoe-t.h
1527@@ -308,6 +308,7 @@ struct spoe_agent {
1528 struct freq_ctr conn_per_sec; /* connections per second */
1529 struct freq_ctr err_per_sec; /* connection errors per second */
1530
1531+ unsigned int idles; /* # of idle applets */
1532 struct eb_root idle_applets; /* idle SPOE applets available to process data */
1533 struct list applets; /* all SPOE applets for this agent */
1534 struct list sending_queue; /* Queue of streams waiting to send data */
1535diff --git a/include/haproxy/stick_table.h b/include/haproxy/stick_table.h
1536index c9fb85e..5fd8d1e 100644
1537--- a/include/haproxy/stick_table.h
1538+++ b/include/haproxy/stick_table.h
1539@@ -46,7 +46,7 @@ void stksess_free(struct stktable *t, struct stksess *ts);
1540 int stksess_kill(struct stktable *t, struct stksess *ts, int decrefcount);
1541
1542 int stktable_init(struct stktable *t);
1543-int stktable_parse_type(char **args, int *idx, unsigned long *type, size_t *key_size);
1544+int stktable_parse_type(char **args, int *idx, unsigned long *type, size_t *key_size, const char *file, int linenum);
1545 int parse_stick_table(const char *file, int linenum, char **args,
1546 struct stktable *t, char *id, char *nid, struct peers *peers);
1547 struct stksess *stktable_get_entry(struct stktable *table, struct stktable_key *key);
1548diff --git a/include/haproxy/time.h b/include/haproxy/time.h
1549index c8358e0..e3bb74e 100644
1550--- a/include/haproxy/time.h
1551+++ b/include/haproxy/time.h
1552@@ -54,7 +54,8 @@ extern THREAD_LOCAL unsigned int samp_time; /* total elapsed time over
1553 extern THREAD_LOCAL unsigned int idle_time; /* total idle time over current sample */
1554 extern THREAD_LOCAL struct timeval now; /* internal date is a monotonic function of real clock */
1555 extern THREAD_LOCAL struct timeval date; /* the real current date */
1556-extern struct timeval start_date; /* the process's start date */
1557+extern struct timeval start_date; /* the process's start date */
1558+extern struct timeval ready_date; /* date when the process was considered ready */
1559 extern THREAD_LOCAL struct timeval before_poll; /* system date before calling poll() */
1560 extern THREAD_LOCAL struct timeval after_poll; /* system date after leaving poll() */
1561 extern volatile unsigned long long global_now;
1562diff --git a/include/import/ist.h b/include/import/ist.h
1563index 539a27d..31566b1 100644
1564--- a/include/import/ist.h
1565+++ b/include/import/ist.h
1566@@ -746,6 +746,53 @@ static inline const char *ist_find_ctl(const struct ist ist)
1567 return NULL;
1568 }
1569
1570+/* Returns a pointer to the first character found <ist> that belongs to the
1571+ * range [min:max] inclusive, or NULL if none is present. The function is
1572+ * optimized for strings having no such chars by processing up to sizeof(long)
1573+ * bytes at once on architectures supporting efficient unaligned accesses.
1574+ * Despite this it is not very fast (~0.43 byte/cycle) and should mostly be
1575+ * used on low match probability when it can save a call to a much slower
1576+ * function. Will not work for characters 0x80 and above. It's optimized for
1577+ * min and max to be known at build time.
1578+ */
1579+static inline const char *ist_find_range(const struct ist ist, unsigned char min, unsigned char max)
1580+{
1581+ const union { unsigned long v; } __attribute__((packed)) *u;
1582+ const char *curr = (void *)ist.ptr - sizeof(long);
1583+ const char *last = curr + ist.len;
1584+ unsigned long l1, l2;
1585+
1586+ /* easier with an exclusive boundary */
1587+ max++;
1588+
1589+ do {
1590+ curr += sizeof(long);
1591+ if (curr > last)
1592+ break;
1593+ u = (void *)curr;
1594+ /* add 0x<min><min><min><min>..<min> then subtract
1595+ * 0x<max><max><max><max>..<max> to the value to generate a
1596+ * carry in the lower byte if the byte contains a lower value.
1597+ * If we generate a bit 7 that was not there, it means the byte
1598+ * was min..max.
1599+ */
1600+ l2 = u->v;
1601+ l1 = ~l2 & ((~0UL / 255) * 0x80); /* 0x808080...80 */
1602+ l2 += (~0UL / 255) * min; /* 0x<min><min>..<min> */
1603+ l2 -= (~0UL / 255) * max; /* 0x<max><max>..<max> */
1604+ } while ((l1 & l2) == 0);
1605+
1606+ last += sizeof(long);
1607+ if (__builtin_expect(curr < last, 0)) {
1608+ do {
1609+ if ((unsigned char)(*curr - min) < (unsigned char)(max - min))
1610+ return curr;
1611+ curr++;
1612+ } while (curr < last);
1613+ }
1614+ return NULL;
1615+}
1616+
1617 /* looks for first occurrence of character <chr> in string <ist> and returns
1618 * the tail of the string starting with this character, or (ist.end,0) if not
1619 * found.
1620diff --git a/reg-tests/cache/caching_rules.vtc b/reg-tests/cache/caching_rules.vtc
1621index 114b2fd..10840e1 100644
1622--- a/reg-tests/cache/caching_rules.vtc
1623+++ b/reg-tests/cache/caching_rules.vtc
1624@@ -67,6 +67,42 @@ server s1 {
1625 txresp -hdr "Cache-Control: max-age=500" \
1626 -hdr "Age: 100" -bodylen 140
1627
1628+
1629+ # "Control-Cache: no-cache" on client request but still stored in cache
1630+ rxreq
1631+ expect req.url == "/nocache"
1632+ txresp -hdr "Cache-Control: max-age=500" \
1633+ -hdr "Age: 100" -bodylen 140
1634+
1635+ rxreq
1636+ expect req.url == "/nocache"
1637+ txresp -hdr "Cache-Control: max-age=500" \
1638+ -hdr "Age: 100" -bodylen 140
1639+
1640+
1641+ # max-age=0
1642+ rxreq
1643+ expect req.url == "/maxage_zero"
1644+ txresp -hdr "Cache-Control: max-age=0" \
1645+ -bodylen 150
1646+
1647+ rxreq
1648+ expect req.url == "/maxage_zero"
1649+ txresp -hdr "Cache-Control: max-age=0" \
1650+ -bodylen 150
1651+
1652+ # Overridden null max-age
1653+ rxreq
1654+ expect req.url == "/overridden"
1655+ txresp -hdr "Cache-Control: max-age=1, s-maxage=5" \
1656+ -bodylen 160
1657+
1658+ rxreq
1659+ expect req.url == "/overridden_null_maxage"
1660+ txresp -hdr "Cache-Control: max-age=0, s-maxage=5" \
1661+ -bodylen 190
1662+
1663+
1664 } -start
1665
1666 server s2 {
1667@@ -222,4 +258,64 @@ client c1 -connect ${h1_fe_sock} {
1668 expect resp.bodylen == 140
1669 expect resp.http.X-Cache-Hit == 1
1670
1671+ # Cache-Control: no-cache
1672+ txreq -url "/nocache" -hdr "Cache-Control: no-cache"
1673+ rxresp
1674+ expect resp.status == 200
1675+ expect resp.bodylen == 140
1676+ expect resp.http.X-Cache-Hit == 0
1677+
1678+ txreq -url "/nocache" -hdr "Cache-Control: no-cache"
1679+ rxresp
1680+ expect resp.status == 200
1681+ expect resp.bodylen == 140
1682+ expect resp.http.X-Cache-Hit == 0
1683+
1684+ txreq -url "/nocache"
1685+ rxresp
1686+ expect resp.status == 200
1687+ expect resp.bodylen == 140
1688+ expect resp.http.X-Cache-Hit == 1
1689+
1690+ # max-age=0 (control test for the overridden null max-age test below)
1691+ txreq -url "/maxage_zero"
1692+ rxresp
1693+ expect resp.status == 200
1694+ expect resp.bodylen == 150
1695+ expect resp.http.X-Cache-Hit == 0
1696+
1697+ txreq -url "/maxage_zero"
1698+ rxresp
1699+ expect resp.status == 200
1700+ expect resp.bodylen == 150
1701+ expect resp.http.X-Cache-Hit == 0
1702+
1703+ # Overridden max-age directive
1704+ txreq -url "/overridden"
1705+ rxresp
1706+ expect resp.status == 200
1707+ expect resp.bodylen == 160
1708+ expect resp.http.X-Cache-Hit == 0
1709+
1710+ txreq -url "/overridden"
1711+ rxresp
1712+ expect resp.status == 200
1713+ expect resp.bodylen == 160
1714+ expect resp.http.X-Cache-Hit == 1
1715+
1716+ txreq -url "/overridden_null_maxage"
1717+ rxresp
1718+ expect resp.status == 200
1719+ expect resp.bodylen == 190
1720+ expect resp.http.X-Cache-Hit == 0
1721+
1722+ # The previous response should have been cached even if it had
1723+ # a max-age=0 since it also had a positive s-maxage
1724+ txreq -url "/overridden_null_maxage"
1725+ rxresp
1726+ expect resp.status == 200
1727+ expect resp.bodylen == 190
1728+ expect resp.http.X-Cache-Hit == 1
1729+
1730+
1731 } -run
1732diff --git a/reg-tests/http-messaging/h1_to_h1.vtc b/reg-tests/http-messaging/h1_to_h1.vtc
1733index c7d0085..603c032 100644
1734--- a/reg-tests/http-messaging/h1_to_h1.vtc
1735+++ b/reg-tests/http-messaging/h1_to_h1.vtc
1736@@ -275,3 +275,29 @@ client c3h1 -connect ${h1_feh1_sock} {
1737 # arrive here.
1738 expect_close
1739 } -run
1740+
1741+client c4h1 -connect ${h1_feh1_sock} {
1742+ # this request is invalid and advertises an invalid C-L ending with an
1743+ # empty value, which results in a stream error.
1744+ txreq \
1745+ -req "GET" \
1746+ -url "/test31.html" \
1747+ -hdr "content-length: 0," \
1748+ -hdr "connection: close"
1749+ rxresp
1750+ expect resp.status == 400
1751+ expect_close
1752+} -run
1753+
1754+client c5h1 -connect ${h1_feh1_sock} {
1755+ # this request is invalid and advertises an empty C-L, which results
1756+ # in a stream error.
1757+ txreq \
1758+ -req "GET" \
1759+ -url "/test41.html" \
1760+ -hdr "content-length:" \
1761+ -hdr "connection: close"
1762+ rxresp
1763+ expect resp.status == 400
1764+ expect_close
1765+} -run
1766diff --git a/reg-tests/http-messaging/h2_to_h1.vtc b/reg-tests/http-messaging/h2_to_h1.vtc
1767index 0d2b1e5..ec7a7c1 100644
1768--- a/reg-tests/http-messaging/h2_to_h1.vtc
1769+++ b/reg-tests/http-messaging/h2_to_h1.vtc
1770@@ -10,6 +10,8 @@ barrier b1 cond 2 -cyclic
1771 barrier b2 cond 2 -cyclic
1772 barrier b3 cond 2 -cyclic
1773 barrier b4 cond 2 -cyclic
1774+barrier b5 cond 2 -cyclic
1775+barrier b6 cond 2 -cyclic
1776
1777 server s1 {
1778 rxreq
1779@@ -31,6 +33,12 @@ server s1 {
1780
1781 barrier b4 sync
1782 # the next request is never received
1783+
1784+ barrier b5 sync
1785+ # the next request is never received
1786+
1787+ barrier b6 sync
1788+ # the next request is never received
1789 } -repeat 2 -start
1790
1791 haproxy h1 -conf {
1792@@ -121,6 +129,32 @@ client c1h2 -connect ${h1_feh2_sock} {
1793 txdata -data "this is sent and ignored"
1794 rxrst
1795 } -run
1796+
1797+ # fifth request is invalid and advertises an invalid C-L ending with an
1798+ # empty value, which results in a stream error.
1799+ stream 9 {
1800+ barrier b5 sync
1801+ txreq \
1802+ -req "GET" \
1803+ -scheme "https" \
1804+ -url "/test5.html" \
1805+ -hdr "content-length" "0," \
1806+ -nostrend
1807+ rxrst
1808+ } -run
1809+
1810+ # sixth request is invalid and advertises an empty C-L, which results
1811+ # in a stream error.
1812+ stream 11 {
1813+ barrier b6 sync
1814+ txreq \
1815+ -req "GET" \
1816+ -scheme "https" \
1817+ -url "/test6.html" \
1818+ -hdr "content-length" "" \
1819+ -nostrend
1820+ rxrst
1821+ } -run
1822 } -run
1823
1824 # HEAD requests : don't work well yet
1825@@ -263,4 +297,30 @@ client c3h2 -connect ${h1_feh2_sock} {
1826 txdata -data "this is sent and ignored"
1827 rxrst
1828 } -run
1829+
1830+ # fifth request is invalid and advertises invalid C-L ending with an
1831+ # empty value, which results in a stream error.
1832+ stream 9 {
1833+ barrier b5 sync
1834+ txreq \
1835+ -req "POST" \
1836+ -scheme "https" \
1837+ -url "/test25.html" \
1838+ -hdr "content-length" "0," \
1839+ -nostrend
1840+ rxrst
1841+ } -run
1842+
1843+ # sixth request is invalid and advertises an empty C-L, which results
1844+ # in a stream error.
1845+ stream 11 {
1846+ barrier b6 sync
1847+ txreq \
1848+ -req "POST" \
1849+ -scheme "https" \
1850+ -url "/test26.html" \
1851+ -hdr "content-length" "" \
1852+ -nostrend
1853+ rxrst
1854+ } -run
1855 } -run
1856diff --git a/reg-tests/http-rules/h1or2_to_h1c.vtc b/reg-tests/http-rules/h1or2_to_h1c.vtc
1857index 81b53e7..7490172 100644
1858--- a/reg-tests/http-rules/h1or2_to_h1c.vtc
1859+++ b/reg-tests/http-rules/h1or2_to_h1c.vtc
1860@@ -27,11 +27,11 @@ server s1 {
1861 -body "This is a body"
1862
1863 expect req.method == "GET"
1864- expect req.http.fe-sl1-crc == 992395575
1865- expect req.http.fe-sl2-crc == 1270056220
1866+ expect req.http.fe-sl1-crc == 1874847043
1867+ expect req.http.fe-sl2-crc == 1142278307
1868 expect req.http.fe-hdr-crc == 1719311923
1869- expect req.http.be-sl1-crc == 2604236007
1870- expect req.http.be-sl2-crc == 4181358964
1871+ expect req.http.be-sl1-crc == 3455320059
1872+ expect req.http.be-sl2-crc == 2509326257
1873 expect req.http.be-hdr-crc == 3634102538
1874 } -repeat 2 -start
1875
1876@@ -53,6 +53,7 @@ haproxy h1 -conf {
1877 http-request set-var(req.path) path
1878 http-request set-var(req.query) query
1879 http-request set-var(req.param) url_param(qs_arg)
1880+ http-request set-var(req.cl) req.fhdr(content-length)
1881
1882 http-request set-header sl1 "sl1: "
1883
1884@@ -65,8 +66,10 @@ haproxy h1 -conf {
1885
1886 http-request set-header sl1 "%[req.fhdr(sl1)] method=<%[var(req.method)]>; uri=<%[var(req.uri)]>; path=<%[var(req.path)]>;"
1887 http-request set-header sl1 "%[req.fhdr(sl1)] query=<%[var(req.query)]>; param=<%[var(req.param)]>"
1888+ http-request set-header sl1 "%[req.fhdr(sl1)] cl=<%[var(req.cl)]>"
1889 http-request set-header sl2 "%[req.fhdr(sl2)] method=<%[method]>; uri=<%[url]>; path=<%[path]>; "
1890 http-request set-header sl2 "%[req.fhdr(sl2)] query=<%[query]>; param=<%[url_param(qs_arg)]>"
1891+ http-request set-header sl2 "%[req.fhdr(sl2)] cl=<%[req.fhdr(content-length)]>"
1892 http-request set-header hdr "%[req.fhdr(hdr)] hdr1=<%[req.hdr(hdr1)]>; fhdr1=<%[req.fhdr(hdr1)]>;"
1893 http-request set-header hdr "%[req.fhdr(hdr)] hdr2=<%[req.hdr(hdr2)]>; fhdr2=<%[req.fhdr(hdr2)]>;"
1894 http-request set-header hdr "%[req.fhdr(hdr)] hdr3=<%[req.hdr(hdr3)]>; fhdr3=<%[req.fhdr(hdr3)]>;"
1895@@ -120,6 +123,7 @@ haproxy h1 -conf {
1896 http-request set-var(req.path) path
1897 http-request set-var(req.query) query
1898 http-request set-var(req.param) url_param(qs_arg)
1899+ http-request set-var(req.cl) req.fhdr(content-length)
1900
1901 http-request set-header sl1 "sl1: "
1902
1903@@ -132,8 +136,10 @@ haproxy h1 -conf {
1904
1905 http-request set-header sl1 "%[req.fhdr(sl1)] method=<%[var(req.method)]>; uri=<%[var(req.uri)]>; path=<%[var(req.path)]>;"
1906 http-request set-header sl1 "%[req.fhdr(sl1)] query=<%[var(req.query)]>; param=<%[var(req.param)]>"
1907+ http-request set-header sl1 "%[req.fhdr(sl1)] cl=<%[var(req.cl)]>"
1908 http-request set-header sl2 "%[req.fhdr(sl2)] method=<%[method]>; uri=<%[url]>; path=<%[path]>; "
1909 http-request set-header sl2 "%[req.fhdr(sl2)] query=<%[query]>; param=<%[url_param(qs_arg)]>"
1910+ http-request set-header sl2 "%[req.fhdr(sl2)] cl=<%[req.fhdr(content-length)]>"
1911 http-request set-header hdr "%[req.fhdr(hdr)] hdr1=<%[req.hdr(hdr1)]>; fhdr1=<%[req.fhdr(hdr1)]>;"
1912 http-request set-header hdr "%[req.fhdr(hdr)] hdr2=<%[req.hdr(hdr2)]>; fhdr2=<%[req.fhdr(hdr2)]>;"
1913 http-request set-header hdr "%[req.fhdr(hdr)] hdr3=<%[req.hdr(hdr3)]>; fhdr3=<%[req.fhdr(hdr3)]>;"
1914@@ -171,6 +177,7 @@ client c1h1 -connect ${h1_feh1_sock} {
1915 txreq \
1916 -req GET \
1917 -url /path/to/file.extension?qs_arg=qs_value \
1918+ -hdr "content-length: 000, 00" \
1919 -hdr "hdr1: val1" \
1920 -hdr "hdr2: val2a" \
1921 -hdr "hdr2: val2b" \
1922@@ -205,6 +212,7 @@ client c1h2 -connect ${h1_feh2_sock} {
1923 -req GET \
1924 -scheme "https" \
1925 -url /path/to/file.extension?qs_arg=qs_value \
1926+ -hdr "content-length" "000, 00" \
1927 -hdr "hdr1" "val1" \
1928 -hdr "hdr2" " val2a" \
1929 -hdr "hdr2" " val2b" \
1930diff --git a/reg-tests/http-rules/normalize_uri.vtc b/reg-tests/http-rules/normalize_uri.vtc
1931index 6a1dc31..a144075 100644
1932--- a/reg-tests/http-rules/normalize_uri.vtc
1933+++ b/reg-tests/http-rules/normalize_uri.vtc
1934@@ -127,6 +127,7 @@ haproxy h1 -conf {
1935
1936 frontend fe_fragment_strip
1937 bind "fd@${fe_fragment_strip}"
1938+ option accept-invalid-http-request
1939
1940 http-request set-var(txn.before) url
1941 http-request normalize-uri fragment-strip
1942@@ -139,6 +140,7 @@ haproxy h1 -conf {
1943
1944 frontend fe_fragment_encode
1945 bind "fd@${fe_fragment_encode}"
1946+ option accept-invalid-http-request
1947
1948 http-request set-var(txn.before) url
1949 http-request normalize-uri fragment-encode
1950@@ -149,6 +151,11 @@ haproxy h1 -conf {
1951
1952 default_backend be
1953
1954+ frontend fe_fragment_block
1955+ bind "fd@${fe_fragment_block}"
1956+ http-request normalize-uri fragment-strip
1957+ default_backend be
1958+
1959 backend be
1960 server s1 ${s1_addr}:${s1_port}
1961
1962@@ -534,3 +541,9 @@ client c10 -connect ${h1_fe_fragment_encode_sock} {
1963 expect resp.http.before == "*"
1964 expect resp.http.after == "*"
1965 } -run
1966+
1967+client c11 -connect ${h1_fe_fragment_block_sock} {
1968+ txreq -url "/#foo"
1969+ rxresp
1970+ expect resp.status == 400
1971+} -run
1972diff --git a/reg-tests/log/log_uri.vtc b/reg-tests/log/log_uri.vtc
1973index b5a5753..60b0bdb 100644
1974--- a/reg-tests/log/log_uri.vtc
1975+++ b/reg-tests/log/log_uri.vtc
1976@@ -5,7 +5,7 @@ feature ignore_unknown_macro
1977
1978 server s1 {
1979 rxreq
1980- txresp
1981+ txresp -hdr "Connection: close"
1982 } -repeat 4 -start
1983
1984 syslog Slg_1 -level info {
1985diff --git a/scripts/build-ssl.sh b/scripts/build-ssl.sh
1986index e1d89a0..4934a4e 100755
1987--- a/scripts/build-ssl.sh
1988+++ b/scripts/build-ssl.sh
1989@@ -59,7 +59,7 @@ build_openssl () {
1990 download_libressl () {
1991 if [ ! -f "download-cache/libressl-${LIBRESSL_VERSION}.tar.gz" ]; then
1992 wget -P download-cache/ \
1993- "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz"
1994+ "https://cdn.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz"
1995 fi
1996 }
1997
1998diff --git a/scripts/publish-release b/scripts/publish-release
1999index 3cf32d8..9066d4a 100755
2000--- a/scripts/publish-release
2001+++ b/scripts/publish-release
2002@@ -22,6 +22,9 @@ NEW=
2003 DIR=
2004 DOC=( )
2005
2006+# need to have group write on emitted files for others to update
2007+umask 002
2008+
2009 die() {
2010 [ "$#" -eq 0 ] || echo "$*" >&2
2011 exit 1
2012diff --git a/src/backend.c b/src/backend.c
2013index 64780e4..c6b5f23 100644
2014--- a/src/backend.c
2015+++ b/src/backend.c
2016@@ -590,8 +590,6 @@ int assign_server(struct stream *s)
2017 struct server *srv = NULL, *prev_srv;
2018 int err;
2019
2020- DPRINTF(stderr,"assign_server : s=%p\n",s);
2021-
2022 err = SRV_STATUS_INTERNAL;
2023 if (unlikely(s->pend_pos || s->flags & SF_ASSIGNED))
2024 goto out_err;
2025diff --git a/src/cache.c b/src/cache.c
2026index ecf62fb..30e0b7d 100644
2027--- a/src/cache.c
2028+++ b/src/cache.c
2029@@ -1083,7 +1083,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
2030
2031 http_check_response_for_cacheability(s, &s->res);
2032
2033- if (!(txn->flags & TX_CACHEABLE) || !(txn->flags & TX_CACHE_COOK) || (txn->flags & TX_CACHE_IGNORE))
2034+ if (!(txn->flags & TX_CACHEABLE) || !(txn->flags & TX_CACHE_COOK))
2035 goto out;
2036
2037 shctx_lock(shctx);
2038@@ -1776,8 +1776,10 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
2039
2040 shctx_lock(shctx_ptr(cache));
2041 res = entry_exist(cache, s->txn->cache_hash);
2042- /* We must not use an entry that is not complete. */
2043- if (res && res->complete) {
2044+ /* We must not use an entry that is not complete but the check will be
2045+ * performed after we look for a potential secondary entry (in case of
2046+ * Vary). */
2047+ if (res) {
2048 struct appctx *appctx;
2049 entry_block = block_ptr(res);
2050 shctx_row_inc_hot(shctx_ptr(cache), entry_block);
2051@@ -1804,9 +1806,11 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
2052 res = NULL;
2053 }
2054
2055- /* We looked for a valid secondary entry and could not find one,
2056- * the request must be forwarded to the server. */
2057- if (!res) {
2058+ /* We either looked for a valid secondary entry and could not
2059+ * find one, or the entry we want to use is not complete. We
2060+ * can't use the cache's entry and must forward the request to
2061+ * the server. */
2062+ if (!res || !res->complete) {
2063 shctx_lock(shctx_ptr(cache));
2064 shctx_row_dec_hot(shctx_ptr(cache), entry_block);
2065 shctx_unlock(shctx_ptr(cache));
2066diff --git a/src/cfgparse-tcp.c b/src/cfgparse-tcp.c
2067index a15a110..e91e7a3 100644
2068--- a/src/cfgparse-tcp.c
2069+++ b/src/cfgparse-tcp.c
2070@@ -165,6 +165,7 @@ static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, stru
2071 return ERR_ALERT | ERR_FATAL;
2072 }
2073
2074+ ha_free(&conf->settings.interface);
2075 conf->settings.interface = strdup(args[cur_arg + 1]);
2076 return 0;
2077 }
2078diff --git a/src/cfgparse.c b/src/cfgparse.c
2079index 27bab1d..01014c0 100644
2080--- a/src/cfgparse.c
2081+++ b/src/cfgparse.c
2082@@ -69,6 +69,7 @@
2083 #include <haproxy/mailers.h>
2084 #include <haproxy/namespace.h>
2085 #include <haproxy/obj_type-t.h>
2086+#include <haproxy/openssl-compat.h>
2087 #include <haproxy/peers-t.h>
2088 #include <haproxy/peers.h>
2089 #include <haproxy/pool.h>
2090@@ -2650,9 +2651,6 @@ int check_config_validity()
2091 * Now, check for the integrity of all that we have collected.
2092 */
2093
2094- /* will be needed further to delay some tasks */
2095- tv_update_date(0,1);
2096-
2097 if (!global.tune.max_http_hdr)
2098 global.tune.max_http_hdr = MAX_HTTP_HDR;
2099
2100@@ -2679,6 +2677,12 @@ int check_config_validity()
2101 #endif
2102 global.nbthread = numa_cores ? numa_cores :
2103 thread_cpus_enabled_at_boot;
2104+
2105+ if (global.nbthread > MAX_THREADS) {
2106+ ha_diag_warning("nbthread not set, found %d CPUs, limiting to %d threads. Please set nbthreads in the global section to silence this warning.\n",
2107+ global.nbthread, MAX_THREADS);
2108+ global.nbthread = MAX_THREADS;
2109+ }
2110 }
2111 all_threads_mask = nbits(global.nbthread);
2112 #endif
2113diff --git a/src/channel.c b/src/channel.c
2114index 524d104..94dfbcf 100644
2115--- a/src/channel.c
2116+++ b/src/channel.c
2117@@ -398,7 +398,7 @@ int co_getblk(const struct channel *chn, char *blk, int len, int offset)
2118 if (chn->flags & CF_SHUTW)
2119 return -1;
2120
2121- if (len + offset > co_data(chn)) {
2122+ if (len + offset > co_data(chn) || co_data(chn) == 0) {
2123 if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
2124 return -1;
2125 return 0;
2126diff --git a/src/check.c b/src/check.c
2127index 2205063..54704bd 100644
2128--- a/src/check.c
2129+++ b/src/check.c
2130@@ -1356,6 +1356,7 @@ static int start_check_task(struct check *check, int mininter,
2131 {
2132 struct task *t;
2133 unsigned long thread_mask = MAX_THREADS_MASK;
2134+ ulong boottime = tv_ms_remain(&start_date, &ready_date);
2135
2136 if (check->type == PR_O2_EXT_CHK)
2137 thread_mask = 1;
2138@@ -1374,11 +1375,19 @@ static int start_check_task(struct check *check, int mininter,
2139 if (mininter < srv_getinter(check))
2140 mininter = srv_getinter(check);
2141
2142+ if (global.spread_checks > 0) {
2143+ int rnd;
2144+
2145+ rnd = srv_getinter(check) * global.spread_checks / 100;
2146+ rnd -= (int) (2 * rnd * (ha_random32() / 4294967295.0));
2147+ mininter += rnd;
2148+ }
2149+
2150 if (global.max_spread_checks && mininter > global.max_spread_checks)
2151 mininter = global.max_spread_checks;
2152
2153 /* check this every ms */
2154- t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
2155+ t->expire = tick_add(now_ms, MS_TO_TICKS(boottime + mininter * srvpos / nbcheck));
2156 check->start = now;
2157 task_queue(t);
2158
2159diff --git a/src/chunk.c b/src/chunk.c
2160index 5c720c1..abc039d 100644
2161--- a/src/chunk.c
2162+++ b/src/chunk.c
2163@@ -152,15 +152,19 @@ int chunk_printf(struct buffer *chk, const char *fmt, ...)
2164 int chunk_appendf(struct buffer *chk, const char *fmt, ...)
2165 {
2166 va_list argp;
2167+ size_t room;
2168 int ret;
2169
2170 if (!chk->area || !chk->size)
2171 return 0;
2172
2173+ room = chk->size - chk->data;
2174+ if (!room)
2175+ return chk->data;
2176+
2177 va_start(argp, fmt);
2178- ret = vsnprintf(chk->area + chk->data, chk->size - chk->data, fmt,
2179- argp);
2180- if (ret >= chk->size - chk->data)
2181+ ret = vsnprintf(chk->area + chk->data, room, fmt, argp);
2182+ if (ret >= room)
2183 /* do not copy anything in case of truncation */
2184 chk->area[chk->data] = 0;
2185 else
2186diff --git a/src/debug.c b/src/debug.c
2187index f9eccad..6582381 100644
2188--- a/src/debug.c
2189+++ b/src/debug.c
2190@@ -272,9 +272,10 @@ void ha_task_dump(struct buffer *buf, const struct task *task, const char *pfx)
2191 if (hlua && hlua->T) {
2192 chunk_appendf(buf, "stack traceback:\n ");
2193 append_prefixed_str(buf, hlua_traceback(hlua->T, "\n "), pfx, '\n', 0);
2194- b_putchr(buf, '\n');
2195 }
2196- else
2197+
2198+ /* we may need to terminate the current line */
2199+ if (*b_peek(buf, b_data(buf)-1) != '\n')
2200 b_putchr(buf, '\n');
2201 #endif
2202 }
2203@@ -1037,7 +1038,27 @@ static int debug_iohandler_fd(struct appctx *appctx)
2204 S_ISLNK(statbuf.st_mode) ? "link":
2205 S_ISSOCK(statbuf.st_mode) ? "sock":
2206 #ifdef USE_EPOLL
2207- epoll_wait(fd, NULL, 0, 0) != -1 || errno != EBADF ? "epol":
2208+ /* trick: epoll_ctl() will return -ENOENT when trying
2209+ * to remove from a valid epoll FD an FD that was not
2210+ * registered against it. But we don't want to risk
2211+ * disabling a random FD. Instead we'll create a new
2212+ * one by duplicating 0 (it should be valid since
2213+ * pointing to a terminal or /dev/null), and try to
2214+ * remove it.
2215+ */
2216+ ({
2217+ int fd2 = dup(0);
2218+ int ret = fd2;
2219+ if (ret >= 0) {
2220+ ret = epoll_ctl(fd, EPOLL_CTL_DEL, fd2, NULL);
2221+ if (ret == -1 && errno == ENOENT)
2222+ ret = 0; // that's a real epoll
2223+ else
2224+ ret = -1; // it's something else
2225+ close(fd2);
2226+ }
2227+ ret;
2228+ }) == 0 ? "epol" :
2229 #endif
2230 "????",
2231 (uint)statbuf.st_mode & 07777,
2232diff --git a/src/dns.c b/src/dns.c
2233index c4b2af0..3277f40 100644
2234--- a/src/dns.c
2235+++ b/src/dns.c
2236@@ -656,30 +656,35 @@ read:
2237 struct dns_query *query;
2238
2239 if (!ds->rx_msg.len) {
2240- /* next message len is not fully available into the channel */
2241- if (co_data(si_oc(si)) < 2)
2242- break;
2243-
2244 /* retrieve message len */
2245- co_getblk(si_oc(si), (char *)&msg_len, 2, 0);
2246+ ret = co_getblk(si_oc(si), (char *)&msg_len, 2, 0);
2247+ if (ret <= 0) {
2248+ if (ret == -1)
2249+ goto close;
2250+ si_cant_get(si);
2251+ break;
2252+ }
2253
2254 /* mark as consumed */
2255 co_skip(si_oc(si), 2);
2256
2257 /* store message len */
2258 ds->rx_msg.len = ntohs(msg_len);
2259- }
2260-
2261- if (!co_data(si_oc(si))) {
2262- /* we need more data but nothing is available */
2263- break;
2264+ if (!ds->rx_msg.len)
2265+ continue;
2266 }
2267
2268 if (co_data(si_oc(si)) + ds->rx_msg.offset < ds->rx_msg.len) {
2269 /* message only partially available */
2270
2271 /* read available data */
2272- co_getblk(si_oc(si), ds->rx_msg.area + ds->rx_msg.offset, co_data(si_oc(si)), 0);
2273+ ret = co_getblk(si_oc(si), ds->rx_msg.area + ds->rx_msg.offset, co_data(si_oc(si)), 0);
2274+ if (ret <= 0) {
2275+ if (ret == -1)
2276+ goto close;
2277+ si_cant_get(si);
2278+ break;
2279+ }
2280
2281 /* update message offset */
2282 ds->rx_msg.offset += co_data(si_oc(si));
2283@@ -688,13 +693,20 @@ read:
2284 co_skip(si_oc(si), co_data(si_oc(si)));
2285
2286 /* we need to wait for more data */
2287+ si_cant_get(si);
2288 break;
2289 }
2290
2291 /* enough data is available into the channel to read the message until the end */
2292
2293 /* read from the channel until the end of the message */
2294- co_getblk(si_oc(si), ds->rx_msg.area + ds->rx_msg.offset, ds->rx_msg.len - ds->rx_msg.offset, 0);
2295+ ret = co_getblk(si_oc(si), ds->rx_msg.area + ds->rx_msg.offset, ds->rx_msg.len - ds->rx_msg.offset, 0);
2296+ if (ret <= 0) {
2297+ if (ret == -1)
2298+ goto close;
2299+ si_cant_get(si);
2300+ break;
2301+ }
2302
2303 /* consume all data until the end of the message from the channel */
2304 co_skip(si_oc(si), ds->rx_msg.len - ds->rx_msg.offset);
2305diff --git a/src/filters.c b/src/filters.c
2306index f64c192..be2b380 100644
2307--- a/src/filters.c
2308+++ b/src/filters.c
2309@@ -292,10 +292,9 @@ flt_init_all()
2310 int err_code = ERR_NONE;
2311
2312 for (px = proxies_list; px; px = px->next) {
2313- if (px->disabled) {
2314- flt_deinit(px);
2315+ if (px->disabled)
2316 continue;
2317- }
2318+
2319 err_code |= flt_init(px);
2320 if (err_code & (ERR_ABORT|ERR_FATAL)) {
2321 ha_alert("Failed to initialize filters for proxy '%s'.\n",
2322diff --git a/src/flt_spoe.c b/src/flt_spoe.c
2323index 4826aa0..ff90043 100644
2324--- a/src/flt_spoe.c
2325+++ b/src/flt_spoe.c
2326@@ -1245,6 +1245,7 @@ spoe_release_appctx(struct appctx *appctx)
2327 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
2328 eb32_delete(&spoe_appctx->node);
2329 _HA_ATOMIC_DEC(&agent->counters.idles);
2330+ agent->rt[tid].idles--;
2331 }
2332
2333 appctx->st0 = SPOE_APPCTX_ST_END;
2334@@ -1458,6 +1459,7 @@ spoe_handle_connecting_appctx(struct appctx *appctx)
2335
2336 default:
2337 _HA_ATOMIC_INC(&agent->counters.idles);
2338+ agent->rt[tid].idles++;
2339 appctx->st0 = SPOE_APPCTX_ST_IDLE;
2340 SPOE_APPCTX(appctx)->node.key = 0;
2341 eb32_insert(&agent->rt[tid].idle_applets, &SPOE_APPCTX(appctx)->node);
2342@@ -1700,12 +1702,6 @@ spoe_handle_processing_appctx(struct appctx *appctx)
2343 (agent->b.be->nbpend ||
2344 (srv && (srv->nbpend || (srv->maxconn && srv->served >= srv_dynamic_maxconn(srv))))));
2345
2346- /* Don"t try to send new frame we are waiting for at lease a ack, in
2347- * sync mode or if applet must be closed ASAP
2348- */
2349- if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK || (close_asap && SPOE_APPCTX(appctx)->cur_fpa))
2350- skip_sending = 1;
2351-
2352 /* receiving_frame loop */
2353 while (!skip_receiving) {
2354 ret = spoe_handle_receiving_frame_appctx(appctx, &skip_receiving);
2355@@ -1726,6 +1722,12 @@ spoe_handle_processing_appctx(struct appctx *appctx)
2356 }
2357 }
2358
2359+ /* Don"t try to send new frame we are waiting for at lease a ack, in
2360+ * sync mode or if applet must be closed ASAP
2361+ */
2362+ if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK || (close_asap && SPOE_APPCTX(appctx)->cur_fpa))
2363+ skip_sending = 1;
2364+
2365 /* send_frame loop */
2366 while (!skip_sending && SPOE_APPCTX(appctx)->cur_fpa < agent->max_fpa) {
2367 ret = spoe_handle_sending_frame_appctx(appctx, &skip_sending);
2368@@ -1772,6 +1774,7 @@ spoe_handle_processing_appctx(struct appctx *appctx)
2369 goto next;
2370 }
2371 _HA_ATOMIC_INC(&agent->counters.idles);
2372+ agent->rt[tid].idles++;
2373 appctx->st0 = SPOE_APPCTX_ST_IDLE;
2374 eb32_insert(&agent->rt[tid].idle_applets, &SPOE_APPCTX(appctx)->node);
2375 }
2376@@ -1937,6 +1940,7 @@ spoe_handle_appctx(struct appctx *appctx)
2377
2378 case SPOE_APPCTX_ST_IDLE:
2379 _HA_ATOMIC_DEC(&agent->counters.idles);
2380+ agent->rt[tid].idles--;
2381 eb32_delete(&SPOE_APPCTX(appctx)->node);
2382 if (stopping &&
2383 LIST_ISEMPTY(&agent->rt[tid].sending_queue) &&
2384@@ -2078,8 +2082,8 @@ spoe_queue_context(struct spoe_context *ctx)
2385 struct spoe_appctx *spoe_appctx;
2386
2387 /* Check if we need to create a new SPOE applet or not. */
2388- if (!eb_is_empty(&agent->rt[tid].idle_applets) &&
2389- (agent->rt[tid].processing == 1 || agent->rt[tid].processing < read_freq_ctr(&agent->rt[tid].processing_per_sec)))
2390+ if (agent->rt[tid].processing < agent->rt[tid].idles ||
2391+ agent->rt[tid].processing < read_freq_ctr(&agent->rt[tid].processing_per_sec))
2392 goto end;
2393
2394 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2395@@ -2995,6 +2999,14 @@ spoe_sig_stop(struct sig_handler *sh)
2396 while (p) {
2397 struct flt_conf *fconf;
2398
2399+ /* SPOE filter are not initialized for disabled proxoes. Move to
2400+ * the next one
2401+ */
2402+ if (p->disabled) {
2403+ p = p->next;
2404+ continue;
2405+ }
2406+
2407 list_for_each_entry(fconf, &p->filter_configs, list) {
2408 struct spoe_config *conf;
2409 struct spoe_agent *agent;
2410@@ -3036,7 +3048,6 @@ spoe_init(struct proxy *px, struct flt_conf *fconf)
2411 conf->agent_fe.accept = frontend_accept;
2412 conf->agent_fe.srv = NULL;
2413 conf->agent_fe.timeout.client = TICK_ETERNITY;
2414- conf->agent_fe.default_target = &spoe_applet.obj_type;
2415 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
2416
2417 if (!sighandler_registered) {
2418@@ -3128,6 +3139,7 @@ spoe_check(struct proxy *px, struct flt_conf *fconf)
2419 conf->agent->rt[i].engine_id = NULL;
2420 conf->agent->rt[i].frame_size = conf->agent->max_frame_size;
2421 conf->agent->rt[i].processing = 0;
2422+ conf->agent->rt[i].idles = 0;
2423 LIST_INIT(&conf->agent->rt[i].applets);
2424 LIST_INIT(&conf->agent->rt[i].sending_queue);
2425 LIST_INIT(&conf->agent->rt[i].waiting_queue);
2426diff --git a/src/h1.c b/src/h1.c
2427index 73de48b..42fe670 100644
2428--- a/src/h1.c
2429+++ b/src/h1.c
2430@@ -34,13 +34,20 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value)
2431 int not_first = !!(h1m->flags & H1_MF_CLEN);
2432 struct ist word;
2433
2434- word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
2435+ word.ptr = value->ptr;
2436 e = value->ptr + value->len;
2437
2438- while (++word.ptr < e) {
2439+ while (1) {
2440+ if (word.ptr >= e) {
2441+ /* empty header or empty value */
2442+ goto fail;
2443+ }
2444+
2445 /* skip leading delimiter and blanks */
2446- if (unlikely(HTTP_IS_LWS(*word.ptr)))
2447+ if (unlikely(HTTP_IS_LWS(*word.ptr))) {
2448+ word.ptr++;
2449 continue;
2450+ }
2451
2452 /* digits only now */
2453 for (cl = 0, n = word.ptr; n < e; n++) {
2454@@ -51,6 +58,14 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value)
2455 goto fail;
2456 break;
2457 }
2458+
2459+ if (unlikely(!cl && n > word.ptr)) {
2460+ /* There was a leading zero before this digit,
2461+ * let's trim it.
2462+ */
2463+ word.ptr = n;
2464+ }
2465+
2466 if (unlikely(cl > ULLONG_MAX / 10ULL))
2467 goto fail; /* multiply overflow */
2468 cl = cl * 10ULL;
2469@@ -79,6 +94,13 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value)
2470 h1m->flags |= H1_MF_CLEN;
2471 h1m->curr_len = h1m->body_len = cl;
2472 *value = word;
2473+
2474+ /* Now either n==e and we're done, or n points to the comma,
2475+ * and we skip it and continue.
2476+ */
2477+ if (n++ == e)
2478+ break;
2479+
2480 word.ptr = n;
2481 }
2482 /* here we've reached the end with a single value or a series of
2483@@ -466,13 +488,13 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
2484 case H1_MSG_RQURI:
2485 http_msg_rquri:
2486 #ifdef HA_UNALIGNED_LE
2487- /* speedup: skip bytes not between 0x21 and 0x7e inclusive */
2488+ /* speedup: skip bytes not between 0x24 and 0x7e inclusive */
2489 while (ptr <= end - sizeof(int)) {
2490- int x = *(int *)ptr - 0x21212121;
2491+ int x = *(int *)ptr - 0x24242424;
2492 if (x & 0x80808080)
2493 break;
2494
2495- x -= 0x5e5e5e5e;
2496+ x -= 0x5b5b5b5b;
2497 if (!(x & 0x80808080))
2498 break;
2499
2500@@ -484,8 +506,15 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
2501 goto http_msg_ood;
2502 }
2503 http_msg_rquri2:
2504- if (likely((unsigned char)(*ptr - 33) <= 93)) /* 33 to 126 included */
2505+ if (likely((unsigned char)(*ptr - 33) <= 93)) { /* 33 to 126 included */
2506+ if (*ptr == '#') {
2507+ if (h1m->err_pos < -1) /* PR_O2_REQBUG_OK not set */
2508+ goto invalid_char;
2509+ if (h1m->err_pos == -1) /* PR_O2_REQBUG_OK set: just log */
2510+ h1m->err_pos = ptr - start + skip;
2511+ }
2512 EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri2, http_msg_ood, state, H1_MSG_RQURI);
2513+ }
2514
2515 if (likely(HTTP_IS_SPHT(*ptr))) {
2516 sl.rq.u.len = ptr - sl.rq.u.ptr;
2517diff --git a/src/h1_htx.c b/src/h1_htx.c
2518index 650acba..24769f0 100644
2519--- a/src/h1_htx.c
2520+++ b/src/h1_htx.c
2521@@ -259,7 +259,7 @@ static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
2522 else if (isteqi(hdrs[hdr].n, ist("location"))) {
2523 code = 302;
2524 status = ist("302");
2525- reason = ist("Moved Temporarily");
2526+ reason = ist("Found");
2527 }
2528 }
2529 if (!code) {
2530diff --git a/src/h2.c b/src/h2.c
2531index dd1f7d9..4da78c8 100644
2532--- a/src/h2.c
2533+++ b/src/h2.c
2534@@ -80,13 +80,20 @@ int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned lon
2535 int not_first = !!(*msgf & H2_MSGF_BODY_CL);
2536 struct ist word;
2537
2538- word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
2539+ word.ptr = value->ptr;
2540 e = value->ptr + value->len;
2541
2542- while (++word.ptr < e) {
2543+ while (1) {
2544+ if (word.ptr >= e) {
2545+ /* empty header or empty value */
2546+ goto fail;
2547+ }
2548+
2549 /* skip leading delimiter and blanks */
2550- if (unlikely(HTTP_IS_LWS(*word.ptr)))
2551+ if (unlikely(HTTP_IS_LWS(*word.ptr))) {
2552+ word.ptr++;
2553 continue;
2554+ }
2555
2556 /* digits only now */
2557 for (cl = 0, n = word.ptr; n < e; n++) {
2558@@ -97,6 +104,14 @@ int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned lon
2559 goto fail;
2560 break;
2561 }
2562+
2563+ if (unlikely(!cl && n > word.ptr)) {
2564+ /* There was a leading zero before this digit,
2565+ * let's trim it.
2566+ */
2567+ word.ptr = n;
2568+ }
2569+
2570 if (unlikely(cl > ULLONG_MAX / 10ULL))
2571 goto fail; /* multiply overflow */
2572 cl = cl * 10ULL;
2573@@ -125,6 +140,13 @@ int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned lon
2574 *msgf |= H2_MSGF_BODY_CL;
2575 *body_len = cl;
2576 *value = word;
2577+
2578+ /* Now either n==e and we're done, or n points to the comma,
2579+ * and we skip it and continue.
2580+ */
2581+ if (n++ == e)
2582+ break;
2583+
2584 word.ptr = n;
2585 }
2586 /* here we've reached the end with a single value or a series of
2587@@ -385,8 +407,12 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
2588 *
2589 * The Cookie header will be reassembled at the end, and for this, the <list>
2590 * will be used to create a linked list, so its contents may be destroyed.
2591+ *
2592+ * When <relaxed> is non-nul, some non-dangerous checks will be ignored. This
2593+ * is in order to satisfy "option accept-invalid-http-request" for
2594+ * interoperability purposes.
2595 */
2596-int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len)
2597+int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len, int relaxed)
2598 {
2599 struct ist phdr_val[H2_PHDR_NUM_ENTRIES];
2600 uint32_t fields; /* bit mask of H2_PHDR_FND_* */
2601@@ -422,11 +448,18 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms
2602 }
2603
2604 /* RFC7540#10.3: intermediaries forwarding to HTTP/1 must take care of
2605- * rejecting NUL, CR and LF characters.
2606+ * rejecting NUL, CR and LF characters. For :path we reject all CTL
2607+ * chars, spaces, and '#'.
2608 */
2609- ctl = ist_find_ctl(list[idx].v);
2610- if (unlikely(ctl) && has_forbidden_char(list[idx].v, ctl))
2611- goto fail;
2612+ if (phdr == H2_PHDR_IDX_PATH && !relaxed) {
2613+ ctl = ist_find_range(list[idx].v, 0, '#');
2614+ if (unlikely(ctl) && http_path_has_forbidden_char(list[idx].v, ctl))
2615+ goto fail;
2616+ } else {
2617+ ctl = ist_find_ctl(list[idx].v);
2618+ if (unlikely(ctl) && has_forbidden_char(list[idx].v, ctl))
2619+ goto fail;
2620+ }
2621
2622 if (phdr > 0 && phdr < H2_PHDR_NUM_ENTRIES) {
2623 /* insert a pseudo header by its index (in phdr) and value (in value) */
2624diff --git a/src/haproxy.c b/src/haproxy.c
2625index 2f85293..dd1130d 100644
2626--- a/src/haproxy.c
2627+++ b/src/haproxy.c
2628@@ -1479,6 +1479,7 @@ static void init(int argc, char **argv)
2629 struct post_check_fct *pcf;
2630 int ideal_maxconn;
2631
2632+ setenv("HAPROXY_STARTUP_VERSION", HAPROXY_VERSION, 0);
2633 global.mode = MODE_STARTING;
2634 old_argv = copy_argv(argc, argv);
2635 if (!old_argv) {
2636@@ -1958,7 +1959,19 @@ static void init(int argc, char **argv)
2637 /* defaults sections are not needed anymore */
2638 proxy_destroy_all_defaults();
2639
2640+ /* update the ready date that will be used to count the startup time
2641+ * during config checks (e.g. to schedule certain tasks if needed)
2642+ */
2643+ gettimeofday(&date, NULL);
2644+ ready_date = date;
2645+
2646+ /* Note: global.nbthread will be initialized as part of this call */
2647 err_code |= check_config_validity();
2648+
2649+ /* update the ready date to also account for the check time */
2650+ gettimeofday(&date, NULL);
2651+ ready_date = date;
2652+
2653 for (px = proxies_list; px; px = px->next) {
2654 struct server *srv;
2655 struct post_proxy_check_fct *ppcf;
2656@@ -2481,6 +2494,18 @@ void deinit(void)
2657 free_proxy(p0);
2658 }/* end while(p) */
2659
2660+
2661+ /* we don't need to free sink_proxies_list proxies since it is
2662+ * already handled in sink_deinit()
2663+ */
2664+ p = cfg_log_forward;
2665+ /* we need to manually clean cfg_log_forward proxy list */
2666+ while (p) {
2667+ p0 = p;
2668+ p = p->next;
2669+ free_proxy(p0);
2670+ }
2671+
2672 while (ua) {
2673 struct stat_scope *scope, *scopep;
2674
2675@@ -3153,6 +3178,10 @@ int main(int argc, char **argv)
2676 global.maxsock);
2677 }
2678
2679+ /* update the ready date a last time to also account for final setup time */
2680+ gettimeofday(&date, NULL);
2681+ ready_date = date;
2682+
2683 if (global.mode & (MODE_DAEMON | MODE_MWORKER | MODE_MWORKER_WAIT)) {
2684 struct proxy *px;
2685 struct peers *curpeers;
2686@@ -3219,7 +3248,7 @@ int main(int argc, char **argv)
2687 if (child->relative_pid == relative_pid &&
2688 child->reloads == 0 && child->options & PROC_O_TYPE_WORKER &&
2689 child->pid == -1) {
2690- child->timestamp = now.tv_sec;
2691+ child->timestamp = date.tv_sec;
2692 child->pid = ret;
2693 child->version = strdup(haproxy_version);
2694 break;
2695diff --git a/src/hlua.c b/src/hlua.c
2696index aea338f..2716f84 100644
2697--- a/src/hlua.c
2698+++ b/src/hlua.c
2699@@ -146,7 +146,7 @@ lua_State *hlua_init_state(int thread_id);
2700 /* This function takes the Lua global lock. Keep this function's visibility
2701 * global so that it can appear in stack dumps and performance profiles!
2702 */
2703-void lua_take_global_lock()
2704+static inline void lua_take_global_lock()
2705 {
2706 HA_SPIN_LOCK(LUA_LOCK, &hlua_global_lock);
2707 }
2708@@ -156,16 +156,44 @@ static inline void lua_drop_global_lock()
2709 HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock);
2710 }
2711
2712+/* lua lock helpers: only lock when required
2713+ *
2714+ * state_id == 0: we're operating on the main lua stack (shared between
2715+ * os threads), so we need to acquire the main lock
2716+ *
2717+ * If the thread already owns the lock (_hlua_locked != 0), skip the lock
2718+ * attempt. This could happen if we run under protected lua environment.
2719+ * Not doing this could result in deadlocks because of nested locking
2720+ * attempts from the same thread
2721+ */
2722+static THREAD_LOCAL int _hlua_locked = 0;
2723+static inline void hlua_lock(struct hlua *hlua)
2724+{
2725+ if (hlua->state_id != 0)
2726+ return;
2727+ if (!_hlua_locked)
2728+ lua_take_global_lock();
2729+ _hlua_locked += 1;
2730+}
2731+static inline void hlua_unlock(struct hlua *hlua)
2732+{
2733+ if (hlua->state_id != 0)
2734+ return;
2735+ BUG_ON(_hlua_locked <= 0);
2736+ _hlua_locked--;
2737+ /* drop the lock once the lock count reaches 0 */
2738+ if (!_hlua_locked)
2739+ lua_drop_global_lock();
2740+}
2741+
2742 #define SET_SAFE_LJMP_L(__L, __HLUA) \
2743 ({ \
2744 int ret; \
2745- if ((__HLUA)->state_id == 0) \
2746- lua_take_global_lock(); \
2747+ hlua_lock(__HLUA); \
2748 if (setjmp(safe_ljmp_env) != 0) { \
2749 lua_atpanic(__L, hlua_panic_safe); \
2750 ret = 0; \
2751- if ((__HLUA)->state_id == 0) \
2752- lua_drop_global_lock(); \
2753+ hlua_unlock(__HLUA); \
2754 } else { \
2755 lua_atpanic(__L, hlua_panic_ljmp); \
2756 ret = 1; \
2757@@ -179,8 +207,7 @@ static inline void lua_drop_global_lock()
2758 #define RESET_SAFE_LJMP_L(__L, __HLUA) \
2759 do { \
2760 lua_atpanic(__L, hlua_panic_safe); \
2761- if ((__HLUA)->state_id == 0) \
2762- lua_drop_global_lock(); \
2763+ hlua_unlock(__HLUA); \
2764 } while(0)
2765
2766 #define SET_SAFE_LJMP(__HLUA) \
2767@@ -351,7 +378,8 @@ static inline int fcn_ref_to_stack_id(struct hlua_function *fcn)
2768
2769 /* Used to check an Lua function type in the stack. It creates and
2770 * returns a reference of the function. This function throws an
2771- * error if the rgument is not a "function".
2772+ * error if the argument is not a "function".
2773+ * When no longer used, the ref must be released with hlua_unref()
2774 */
2775 __LJMP unsigned int hlua_checkfunction(lua_State *L, int argno)
2776 {
2777@@ -363,14 +391,59 @@ __LJMP unsigned int hlua_checkfunction(lua_State *L, int argno)
2778 return luaL_ref(L, LUA_REGISTRYINDEX);
2779 }
2780
2781-/* Return the string that is of the top of the stack. */
2782-const char *hlua_get_top_error_string(lua_State *L)
2783+/* Used to check an Lua table type in the stack. It creates and
2784+ * returns a reference of the table. This function throws an
2785+ * error if the argument is not a "table".
2786+ * When no longer used, the ref must be released with hlua_unref()
2787+ */
2788+__LJMP unsigned int hlua_checktable(lua_State *L, int argno)
2789+{
2790+ if (!lua_istable(L, argno)) {
2791+ const char *msg = lua_pushfstring(L, "table expected, got %s", luaL_typename(L, argno));
2792+ WILL_LJMP(luaL_argerror(L, argno, msg));
2793+ }
2794+ lua_pushvalue(L, argno);
2795+ return luaL_ref(L, LUA_REGISTRYINDEX);
2796+}
2797+
2798+/* Get a reference to the object that is at the top of the stack
2799+ * The referenced object will be popped from the stack
2800+ *
2801+ * The function returns the reference to the object which must
2802+ * be cleared using hlua_unref() when no longer used
2803+ */
2804+__LJMP int hlua_ref(lua_State *L)
2805+{
2806+ return MAY_LJMP(luaL_ref(L, LUA_REGISTRYINDEX));
2807+}
2808+
2809+/* Pushes a reference previously created using luaL_ref(L, LUA_REGISTRYINDEX)
2810+ * on <L> stack
2811+ * (ie: hlua_checkfunction(), hlua_checktable() or hlua_ref())
2812+ *
2813+ * When the reference is no longer used, it should be released by calling
2814+ * hlua_unref()
2815+ *
2816+ * <L> can be from any co-routine as long as it belongs to the same lua
2817+ * parent state that the one used to get the reference.
2818+ */
2819+void hlua_pushref(lua_State *L, int ref)
2820+{
2821+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
2822+}
2823+
2824+/* Releases a reference previously created using luaL_ref(L, LUA_REGISTRYINDEX)
2825+ * (ie: hlua_checkfunction(), hlua_checktable() or hlua_ref())
2826+ *
2827+ * This will allow the reference to be reused and the referred object
2828+ * to be garbage collected.
2829+ *
2830+ * <L> can be from any co-routine as long as it belongs to the same lua
2831+ * parent state that the one used to get the reference.
2832+ */
2833+void hlua_unref(lua_State *L, int ref)
2834 {
2835- if (lua_gettop(L) < 1)
2836- return "unknown error";
2837- if (lua_type(L, -1) != LUA_TSTRING)
2838- return "unknown error";
2839- return lua_tostring(L, -1);
2840+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
2841 }
2842
2843 __LJMP const char *hlua_traceback(lua_State *L, const char* sep)
2844@@ -1054,7 +1127,7 @@ static inline void hlua_sendlog(struct proxy *px, int level, const char *msg)
2845 /* This function just ensure that the yield will be always
2846 * returned with a timeout and permit to set some flags
2847 */
2848-__LJMP void hlua_yieldk(lua_State *L, int nresults, int ctx,
2849+__LJMP void hlua_yieldk(lua_State *L, int nresults, lua_KContext ctx,
2850 lua_KFunction k, int timeout, unsigned int flags)
2851 {
2852 struct hlua *hlua;
2853@@ -1084,21 +1157,12 @@ __LJMP void hlua_yieldk(lua_State *L, int nresults, int ctx,
2854 * initialisation fails (example: out of memory error), the lua function
2855 * throws an error (longjmp).
2856 *
2857- * In some case (at least one), this function can be called from safe
2858- * environment, so we must not initialise it. While the support of
2859- * threads appear, the safe environment set a lock to ensure only one
2860- * Lua execution at a time. If we initialize safe environment in another
2861- * safe environment, we have a dead lock.
2862- *
2863- * set "already_safe" true if the context is initialized form safe
2864- * Lua function.
2865- *
2866 * This function manipulates two Lua stacks: the main and the thread. Only
2867 * the main stack can fail. The thread is not manipulated. This function
2868 * MUST NOT manipulate the created thread stack state, because it is not
2869 * protected against errors thrown by the thread stack.
2870 */
2871-int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task, int already_safe)
2872+int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task)
2873 {
2874 lua->Mref = LUA_REFNIL;
2875 lua->flags = 0;
2876@@ -1106,30 +1170,26 @@ int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task, int already
2877 lua->wake_time = TICK_ETERNITY;
2878 lua->state_id = state_id;
2879 LIST_INIT(&lua->com);
2880- if (!already_safe) {
2881- if (!SET_SAFE_LJMP_PARENT(lua)) {
2882- lua->Tref = LUA_REFNIL;
2883- return 0;
2884- }
2885+ if (!SET_SAFE_LJMP_PARENT(lua)) {
2886+ lua->Tref = LUA_REFNIL;
2887+ return 0;
2888 }
2889 lua->T = lua_newthread(hlua_states[state_id]);
2890 if (!lua->T) {
2891 lua->Tref = LUA_REFNIL;
2892- if (!already_safe)
2893- RESET_SAFE_LJMP_PARENT(lua);
2894+ RESET_SAFE_LJMP_PARENT(lua);
2895 return 0;
2896 }
2897 hlua_sethlua(lua);
2898 lua->Tref = luaL_ref(hlua_states[state_id], LUA_REGISTRYINDEX);
2899 lua->task = task;
2900- if (!already_safe)
2901- RESET_SAFE_LJMP_PARENT(lua);
2902+ RESET_SAFE_LJMP_PARENT(lua);
2903 return 1;
2904 }
2905
2906 /* Used to destroy the Lua coroutine when the attached stream or task
2907 * is destroyed. The destroy also the memory context. The struct "lua"
2908- * is not freed.
2909+ * will be freed.
2910 */
2911 void hlua_ctx_destroy(struct hlua *lua)
2912 {
2913@@ -1291,8 +1351,7 @@ static enum hlua_exec hlua_ctx_resume(struct hlua *lua, int yield_allowed)
2914 /* Lock the whole Lua execution. This lock must be before the
2915 * label "resume_execution".
2916 */
2917- if (lua->state_id == 0)
2918- lua_take_global_lock();
2919+ hlua_lock(lua);
2920
2921 resume_execution:
2922
2923@@ -1439,8 +1498,7 @@ resume_execution:
2924 }
2925
2926 /* This is the main exit point, remove the Lua lock. */
2927- if (lua->state_id == 0)
2928- lua_drop_global_lock();
2929+ hlua_unlock(lua);
2930
2931 return ret;
2932 }
2933@@ -3770,7 +3828,9 @@ __LJMP static int hlua_applet_tcp_set_var(lua_State *L)
2934 memset(&smp, 0, sizeof(smp));
2935 hlua_lua2smp(L, 3, &smp);
2936
2937- /* Store the sample in a variable. */
2938+ /* Store the sample in a variable. We don't need to dup the smp, vars API
2939+ * already takes care of duplicating dynamic var data.
2940+ */
2941 smp_set_owner(&smp, s->be, s->sess, s, 0);
2942
2943 if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
2944@@ -4255,7 +4315,9 @@ __LJMP static int hlua_applet_http_set_var(lua_State *L)
2945 memset(&smp, 0, sizeof(smp));
2946 hlua_lua2smp(L, 3, &smp);
2947
2948- /* Store the sample in a variable. */
2949+ /* Store the sample in a variable. We don't need to dup the smp, vars API
2950+ * already takes care of duplicating dynamic var data.
2951+ */
2952 smp_set_owner(&smp, s->be, s->sess, s, 0);
2953
2954 if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
2955@@ -5350,7 +5412,9 @@ __LJMP static int hlua_set_var(lua_State *L)
2956 memset(&smp, 0, sizeof(smp));
2957 hlua_lua2smp(L, 3, &smp);
2958
2959- /* Store the sample in a variable. */
2960+ /* Store the sample in a variable. We don't need to dup the smp, vars API
2961+ * already takes care of duplicating dynamic var data.
2962+ */
2963 smp_set_owner(&smp, htxn->p, htxn->s->sess, htxn->s, htxn->dir & SMP_OPT_DIR);
2964
2965 if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
2966@@ -6373,6 +6437,11 @@ __LJMP static int hlua_register_init(lua_State *L)
2967
2968 MAY_LJMP(check_args(L, 1, "register_init"));
2969
2970+ if (hlua_gethlua(L)) {
2971+ /* runtime processing */
2972+ WILL_LJMP(luaL_error(L, "register_init: not available outside of body context"));
2973+ }
2974+
2975 ref = MAY_LJMP(hlua_checkfunction(L, 1));
2976
2977 init = calloc(1, sizeof(*init));
2978@@ -6433,11 +6502,14 @@ static int hlua_register_task(lua_State *L)
2979 task->context = hlua;
2980 task->process = hlua_process_task;
2981
2982- if (!hlua_ctx_init(hlua, state_id, task, 1))
2983+ if (!hlua_ctx_init(hlua, state_id, task))
2984 goto alloc_error;
2985
2986 /* Restore the function in the stack. */
2987 lua_rawgeti(hlua->T, LUA_REGISTRYINDEX, ref);
2988+ /* function ref not needed anymore since it was pushed to the substack */
2989+ hlua_unref(L, ref);
2990+
2991 hlua->nargs = 0;
2992
2993 /* Schedule task. */
2994@@ -6480,7 +6552,7 @@ static int hlua_sample_conv_wrapper(const struct arg *arg_p, struct sample *smp,
2995 }
2996 HLUA_INIT(hlua);
2997 stream->hlua = hlua;
2998- if (!hlua_ctx_init(stream->hlua, fcn_ref_to_stack_id(fcn), stream->task, 0)) {
2999+ if (!hlua_ctx_init(stream->hlua, fcn_ref_to_stack_id(fcn), stream->task)) {
3000 SEND_ERR(stream->be, "Lua converter '%s': can't initialize Lua context.\n", fcn->name);
3001 return 0;
3002 }
3003@@ -6548,6 +6620,10 @@ static int hlua_sample_conv_wrapper(const struct arg *arg_p, struct sample *smp,
3004
3005 /* Convert the returned value in sample. */
3006 hlua_lua2smp(stream->hlua->T, -1, smp);
3007+ /* dup the smp before popping the related lua value and
3008+ * returning it to haproxy
3009+ */
3010+ smp_dup(smp);
3011 lua_pop(stream->hlua->T, 1);
3012 return 1;
3013
3014@@ -6617,7 +6693,7 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp
3015 }
3016 hlua->T = NULL;
3017 stream->hlua = hlua;
3018- if (!hlua_ctx_init(stream->hlua, fcn_ref_to_stack_id(fcn), stream->task, 0)) {
3019+ if (!hlua_ctx_init(stream->hlua, fcn_ref_to_stack_id(fcn), stream->task)) {
3020 SEND_ERR(stream->be, "Lua sample-fetch '%s': can't initialize Lua context.\n", fcn->name);
3021 return 0;
3022 }
3023@@ -6683,6 +6759,10 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp
3024
3025 /* Convert the returned value in sample. */
3026 hlua_lua2smp(stream->hlua->T, -1, smp);
3027+ /* dup the smp before popping the related lua value and
3028+ * returning it to haproxy
3029+ */
3030+ smp_dup(smp);
3031 lua_pop(stream->hlua->T, 1);
3032
3033 /* Set the end of execution flag. */
3034@@ -6740,6 +6820,11 @@ __LJMP static int hlua_register_converters(lua_State *L)
3035
3036 MAY_LJMP(check_args(L, 2, "register_converters"));
3037
3038+ if (hlua_gethlua(L)) {
3039+ /* runtime processing */
3040+ WILL_LJMP(luaL_error(L, "register_converters: not available outside of body context"));
3041+ }
3042+
3043 /* First argument : converter name. */
3044 name = MAY_LJMP(luaL_checkstring(L, 1));
3045
3046@@ -6819,6 +6904,11 @@ __LJMP static int hlua_register_fetches(lua_State *L)
3047
3048 MAY_LJMP(check_args(L, 2, "register_fetches"));
3049
3050+ if (hlua_gethlua(L)) {
3051+ /* runtime processing */
3052+ WILL_LJMP(luaL_error(L, "register_fetches: not available outside of body context"));
3053+ }
3054+
3055 /* First argument : sample-fetch name. */
3056 name = MAY_LJMP(luaL_checkstring(L, 1));
3057
3058@@ -6945,7 +7035,7 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
3059 }
3060 HLUA_INIT(hlua);
3061 s->hlua = hlua;
3062- if (!hlua_ctx_init(s->hlua, fcn_ref_to_stack_id(rule->arg.hlua_rule->fcn), s->task, 0)) {
3063+ if (!hlua_ctx_init(s->hlua, fcn_ref_to_stack_id(rule->arg.hlua_rule->fcn), s->task)) {
3064 SEND_ERR(px, "Lua action '%s': can't initialize Lua context.\n",
3065 rule->arg.hlua_rule->fcn->name);
3066 goto end;
3067@@ -7130,7 +7220,7 @@ static int hlua_applet_tcp_init(struct appctx *ctx, struct proxy *px, struct str
3068 * permits to save performances because a systematic
3069 * Lua initialization cause 5% performances loss.
3070 */
3071- if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(ctx->rule->arg.hlua_rule->fcn), task, 0)) {
3072+ if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(ctx->rule->arg.hlua_rule->fcn), task)) {
3073 SEND_ERR(px, "Lua applet tcp '%s': can't initialize Lua context.\n",
3074 ctx->rule->arg.hlua_rule->fcn->name);
3075 return 0;
3076@@ -7323,7 +7413,7 @@ static int hlua_applet_http_init(struct appctx *ctx, struct proxy *px, struct st
3077 * permits to save performances because a systematic
3078 * Lua initialization cause 5% performances loss.
3079 */
3080- if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(ctx->rule->arg.hlua_rule->fcn), task, 0)) {
3081+ if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(ctx->rule->arg.hlua_rule->fcn), task)) {
3082 SEND_ERR(px, "Lua applet http '%s': can't initialize Lua context.\n",
3083 ctx->rule->arg.hlua_rule->fcn->name);
3084 return 0;
3085@@ -7666,6 +7756,11 @@ __LJMP static int hlua_register_action(lua_State *L)
3086 if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
3087 WILL_LJMP(luaL_error(L, "'register_action' needs between 3 and 4 arguments"));
3088
3089+ if (hlua_gethlua(L)) {
3090+ /* runtime processing */
3091+ WILL_LJMP(luaL_error(L, "register_action: not available outside of body context"));
3092+ }
3093+
3094 /* First argument : converter name. */
3095 name = MAY_LJMP(luaL_checkstring(L, 1));
3096
3097@@ -7832,6 +7927,11 @@ __LJMP static int hlua_register_service(lua_State *L)
3098
3099 MAY_LJMP(check_args(L, 3, "register_service"));
3100
3101+ if (hlua_gethlua(L)) {
3102+ /* runtime processing */
3103+ WILL_LJMP(luaL_error(L, "register_service: not available outside of body context"));
3104+ }
3105+
3106 /* First argument : converter name. */
3107 name = MAY_LJMP(luaL_checkstring(L, 1));
3108
3109@@ -7949,7 +8049,7 @@ static int hlua_cli_parse_fct(char **args, char *payload, struct appctx *appctx,
3110 appctx->ctx.hlua_cli.task->process = hlua_applet_wakeup;
3111
3112 /* Initialises the Lua context */
3113- if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(fcn), appctx->ctx.hlua_cli.task, 0)) {
3114+ if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(fcn), appctx->ctx.hlua_cli.task)) {
3115 SEND_ERR(NULL, "Lua cli '%s': can't initialize Lua context.\n", fcn->name);
3116 goto error;
3117 }
3118@@ -8105,6 +8205,11 @@ __LJMP static int hlua_register_cli(lua_State *L)
3119
3120 MAY_LJMP(check_args(L, 3, "register_cli"));
3121
3122+ if (hlua_gethlua(L)) {
3123+ /* runtime processing */
3124+ WILL_LJMP(luaL_error(L, "register_cli: not available outside of body context"));
3125+ }
3126+
3127 /* First argument : an array of maximum 5 keywords. */
3128 if (!lua_istable(L, 1))
3129 WILL_LJMP(luaL_argerror(L, 1, "1st argument must be a table"));
3130@@ -8546,6 +8651,10 @@ int hlua_post_init_state(lua_State *L)
3131
3132 list_for_each_entry(init, &hlua_init_functions[hlua_state_id], l) {
3133 lua_rawgeti(L, LUA_REGISTRYINDEX, init->function_ref);
3134+ /* function ref should be released right away since it was pushed
3135+ * on the stack and will not be used anymore
3136+ */
3137+ hlua_unref(L, init->function_ref);
3138
3139 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
3140 ret = lua_resume(L, L, 0, &nres);
3141@@ -8784,6 +8893,13 @@ lua_State *hlua_init_state(int thread_num)
3142 /* Init main lua stack. */
3143 L = lua_newstate(hlua_alloc, &hlua_global_allocator);
3144
3145+ if (!L) {
3146+ fprintf(stderr,
3147+ "Lua init: critical error: lua_newstate() returned NULL."
3148+ " This may possibly be caused by a memory allocation error.\n");
3149+ exit(1);
3150+ }
3151+
3152 /* Initialise Lua context to NULL */
3153 context = lua_getextraspace(L);
3154 *context = NULL;
3155diff --git a/src/http.c b/src/http.c
3156index c10b433..647f433 100644
3157--- a/src/http.c
3158+++ b/src/http.c
3159@@ -403,7 +403,7 @@ const char *http_get_reason(unsigned int status)
3160 case 226: return "IM Used";
3161 case 300: return "Multiple Choices";
3162 case 301: return "Moved Permanently";
3163- case 302: return "Moved Temporarily";
3164+ case 302: return "Found";
3165 case 303: return "See Other";
3166 case 304: return "Not Modified";
3167 case 305: return "Use Proxy";
3168diff --git a/src/http_ana.c b/src/http_ana.c
3169index 2e2095b..b557da8 100644
3170--- a/src/http_ana.c
3171+++ b/src/http_ana.c
3172@@ -1390,6 +1390,13 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
3173 if (objt_cs(s->si[1].end))
3174 conn = __objt_cs(s->si[1].end)->conn;
3175
3176+ if ((si_b->flags & SI_FL_L7_RETRY) &&
3177+ (s->be->retry_type & PR_RE_DISCONNECTED) &&
3178+ (!conn || conn->err_code != CO_ER_SSL_EARLY_FAILED)) {
3179+ if (co_data(rep) || do_l7_retry(s, si_b) == 0)
3180+ return 0;
3181+ }
3182+
3183 /* Perform a L7 retry because server refuses the early data. */
3184 if ((si_b->flags & SI_FL_L7_RETRY) &&
3185 (s->be->retry_type & PR_RE_EARLY_ERROR) &&
3186@@ -2693,10 +2700,11 @@ int http_replace_hdrs(struct stream* s, struct htx *htx, struct ist name,
3187 const char *str, struct my_regex *re, int full)
3188 {
3189 struct http_hdr_ctx ctx;
3190- struct buffer *output = get_trash_chunk();
3191
3192 ctx.blk = NULL;
3193 while (http_find_header(htx, name, &ctx, full)) {
3194+ struct buffer *output = get_trash_chunk();
3195+
3196 if (!regex_exec_match2(re, ctx.value.ptr, ctx.value.len, MAX_MATCH, pmatch, 0))
3197 continue;
3198
3199@@ -3862,6 +3870,7 @@ void http_check_response_for_cacheability(struct stream *s, struct channel *res)
3200 struct htx *htx;
3201 int has_freshness_info = 0;
3202 int has_validator = 0;
3203+ int has_null_maxage = 0;
3204
3205 if (txn->status < 200) {
3206 /* do not try to cache interim responses! */
3207@@ -3886,10 +3895,16 @@ void http_check_response_for_cacheability(struct stream *s, struct channel *res)
3208 txn->flags |= TX_CACHEABLE | TX_CACHE_COOK;
3209 continue;
3210 }
3211+ /* This max-age might be overridden by a s-maxage directive, do
3212+ * not unset the TX_CACHEABLE yet. */
3213+ if (isteqi(ctx.value, ist("max-age=0"))) {
3214+ has_null_maxage = 1;
3215+ continue;
3216+ }
3217+
3218 if (isteqi(ctx.value, ist("private")) ||
3219 isteqi(ctx.value, ist("no-cache")) ||
3220 isteqi(ctx.value, ist("no-store")) ||
3221- isteqi(ctx.value, ist("max-age=0")) ||
3222 isteqi(ctx.value, ist("s-maxage=0"))) {
3223 txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
3224 continue;
3225@@ -3900,13 +3915,23 @@ void http_check_response_for_cacheability(struct stream *s, struct channel *res)
3226 continue;
3227 }
3228
3229- if (istmatchi(ctx.value, ist("s-maxage")) ||
3230- istmatchi(ctx.value, ist("max-age"))) {
3231+ if (istmatchi(ctx.value, ist("s-maxage"))) {
3232+ has_freshness_info = 1;
3233+ has_null_maxage = 0; /* The null max-age is overridden, ignore it */
3234+ continue;
3235+ }
3236+ if (istmatchi(ctx.value, ist("max-age"))) {
3237 has_freshness_info = 1;
3238 continue;
3239 }
3240 }
3241
3242+ /* We had a 'max-age=0' directive but no extra s-maxage, do not cache
3243+ * the response. */
3244+ if (has_null_maxage) {
3245+ txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
3246+ }
3247+
3248 /* If no freshness information could be found in Cache-Control values,
3249 * look for an Expires header. */
3250 if (!has_freshness_info) {
3251@@ -3929,7 +3954,7 @@ void http_check_response_for_cacheability(struct stream *s, struct channel *res)
3252 /* We won't store an entry that has neither a cache validator nor an
3253 * explicit expiration time, as suggested in RFC 7234#3. */
3254 if (!has_freshness_info && !has_validator)
3255- txn->flags |= TX_CACHE_IGNORE;
3256+ txn->flags &= ~TX_CACHEABLE;
3257 }
3258
3259 /*
3260diff --git a/src/http_rules.c b/src/http_rules.c
3261index 1b21133..52b77c6 100644
3262--- a/src/http_rules.c
3263+++ b/src/http_rules.c
3264@@ -301,6 +301,26 @@ struct act_rule *parse_http_after_res_cond(const char **args, const char *file,
3265 return NULL;
3266 }
3267
3268+/* completely free redirect rule */
3269+void http_free_redirect_rule(struct redirect_rule *rdr)
3270+{
3271+ struct logformat_node *lf, *lfb;
3272+
3273+ if (rdr->cond) {
3274+ prune_acl_cond(rdr->cond);
3275+ free(rdr->cond);
3276+ }
3277+ free(rdr->rdr_str);
3278+ free(rdr->cookie_str);
3279+ list_for_each_entry_safe(lf, lfb, &rdr->rdr_fmt, list) {
3280+ LIST_DELETE(&lf->list);
3281+ release_sample_expr(lf->expr);
3282+ free(lf->arg);
3283+ free(lf);
3284+ }
3285+ free(rdr);
3286+}
3287+
3288 /* Parses a redirect rule. Returns the redirect rule on success or NULL on error,
3289 * with <err> filled with the error message. If <use_fmt> is not null, builds a
3290 * dynamic log-format rule instead of a static string. Parameter <dir> indicates
3291@@ -309,7 +329,7 @@ struct act_rule *parse_http_after_res_cond(const char **args, const char *file,
3292 struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
3293 const char **args, char **errmsg, int use_fmt, int dir)
3294 {
3295- struct redirect_rule *rule;
3296+ struct redirect_rule *rule = NULL;
3297 int cur_arg;
3298 int type = REDIRECT_TYPE_NONE;
3299 int code = 302;
3300@@ -370,7 +390,7 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
3301 memprintf(errmsg,
3302 "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)",
3303 args[cur_arg - 1], args[cur_arg]);
3304- return NULL;
3305+ goto err;
3306 }
3307 }
3308 else if (strcmp(args[cur_arg], "drop-query") == 0) {
3309@@ -384,7 +404,7 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
3310 cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg);
3311 if (!cond) {
3312 memprintf(errmsg, "error in condition: %s", *errmsg);
3313- return NULL;
3314+ goto err;
3315 }
3316 break;
3317 }
3318@@ -392,32 +412,32 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
3319 memprintf(errmsg,
3320 "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')",
3321 args[cur_arg]);
3322- return NULL;
3323+ goto err;
3324 }
3325 cur_arg++;
3326 }
3327
3328 if (type == REDIRECT_TYPE_NONE) {
3329 memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')");
3330- return NULL;
3331+ goto err;
3332 }
3333
3334 if (dir && type != REDIRECT_TYPE_LOCATION) {
3335 memprintf(errmsg, "response only supports redirect type 'location'");
3336- return NULL;
3337+ goto err;
3338 }
3339
3340 rule = calloc(1, sizeof(*rule));
3341- if (!rule) {
3342- memprintf(errmsg, "parsing [%s:%d]: out of memory.", file, linenum);
3343- return NULL;
3344- }
3345+ if (!rule)
3346+ goto out_of_memory;
3347 rule->cond = cond;
3348 LIST_INIT(&rule->rdr_fmt);
3349
3350 if (!use_fmt) {
3351 /* old-style static redirect rule */
3352 rule->rdr_str = strdup(destination);
3353+ if (!rule->rdr_str)
3354+ goto out_of_memory;
3355 rule->rdr_len = strlen(destination);
3356 }
3357 else {
3358@@ -435,7 +455,7 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
3359 cap |= (dir ? SMP_VAL_BE_HRS_HDR : SMP_VAL_BE_HRQ_HDR);
3360 if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
3361 if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP, cap, errmsg)) {
3362- return NULL;
3363+ goto err;
3364 }
3365 free(curproxy->conf.lfs_file);
3366 curproxy->conf.lfs_file = strdup(curproxy->conf.args.file);
3367@@ -450,11 +470,15 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
3368 rule->cookie_len = strlen(cookie);
3369 if (cookie_set) {
3370 rule->cookie_str = malloc(rule->cookie_len + 10);
3371+ if (!rule->cookie_str)
3372+ goto out_of_memory;
3373 memcpy(rule->cookie_str, cookie, rule->cookie_len);
3374 memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
3375 rule->cookie_len += 9;
3376 } else {
3377 rule->cookie_str = malloc(rule->cookie_len + 21);
3378+ if (!rule->cookie_str)
3379+ goto out_of_memory;
3380 memcpy(rule->cookie_str, cookie, rule->cookie_len);
3381 memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
3382 rule->cookie_len += 20;
3383@@ -468,6 +492,18 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
3384
3385 missing_arg:
3386 memprintf(errmsg, "missing argument for '%s'", args[cur_arg]);
3387+ goto err;
3388+ out_of_memory:
3389+ memprintf(errmsg, "parsing [%s:%d]: out of memory.", file, linenum);
3390+ err:
3391+ if (rule)
3392+ http_free_redirect_rule(rule);
3393+ else if (cond) {
3394+ /* rule not yet allocated, but cond already is */
3395+ prune_acl_cond(cond);
3396+ free(cond);
3397+ }
3398+
3399 return NULL;
3400 }
3401
3402diff --git a/src/listener.c b/src/listener.c
3403index 67616f5..cd295e9 100644
3404--- a/src/listener.c
3405+++ b/src/listener.c
3406@@ -251,6 +251,7 @@ void listener_set_state(struct listener *l, enum li_state st)
3407 case LI_LIMITED:
3408 BUG_ON(l->rx.fd == -1);
3409 _HA_ATOMIC_INC(&px->li_ready);
3410+ l->flags |= LI_F_FINALIZED;
3411 break;
3412 }
3413 }
3414@@ -297,15 +298,15 @@ void enable_listener(struct listener *listener)
3415 }
3416
3417 /*
3418- * This function completely stops a listener. It will need to operate under the
3419- * It will need to operate under the proxy's lock and the protocol's lock.
3420- * The caller is responsible for indicating in lpx, lpr whether the
3421- * respective locks are already held (non-zero) or not (zero) so that the
3422- * function picks the missing ones, in this order.
3423+ * This function completely stops a listener.
3424 * The proxy's listeners count is updated and the proxy is
3425 * disabled and woken up after the last one is gone.
3426+ * It will need to operate under the proxy's lock, the protocol's lock and
3427+ * the listener's lock. The caller is responsible for indicating in lpx,
3428+ * lpr, lli whether the respective locks are already held (non-zero) or
3429+ * not (zero) so that the function picks the missing ones, in this order.
3430 */
3431-void stop_listener(struct listener *l, int lpx, int lpr)
3432+void stop_listener(struct listener *l, int lpx, int lpr, int lli)
3433 {
3434 struct proxy *px = l->bind_conf->frontend;
3435
3436@@ -316,13 +317,14 @@ void stop_listener(struct listener *l, int lpx, int lpr)
3437 return;
3438 }
3439
3440- if (!lpx)
3441+ if (!lpx && px)
3442 HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock);
3443
3444 if (!lpr)
3445 HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
3446
3447- HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
3448+ if (!lli)
3449+ HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
3450
3451 if (l->state > LI_INIT) {
3452 do_unbind_listener(l);
3453@@ -330,15 +332,17 @@ void stop_listener(struct listener *l, int lpx, int lpr)
3454 if (l->state >= LI_ASSIGNED)
3455 __delete_listener(l);
3456
3457- proxy_cond_disable(px);
3458+ if (px)
3459+ proxy_cond_disable(px);
3460 }
3461
3462- HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
3463+ if (!lli)
3464+ HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
3465
3466 if (!lpr)
3467 HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
3468
3469- if (!lpx)
3470+ if (!lpx && px)
3471 HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock);
3472 }
3473
3474@@ -359,19 +363,17 @@ void default_add_listener(struct protocol *proto, struct listener *listener)
3475
3476 /* default function called to suspend a listener: it simply passes the call to
3477 * the underlying receiver. This is find for most socket-based protocols. This
3478- * must be called under the listener's lock. It will return non-zero on success,
3479- * 0 on failure. If no receiver-level suspend is provided, the operation is
3480- * assumed to succeed.
3481+ * must be called under the listener's lock. It will return < 0 in case of
3482+ * failure, 0 if the listener was totally stopped, or > 0 if correctly paused..
3483+ * If no receiver-level suspend is provided, the operation is assumed
3484+ * to succeed.
3485 */
3486 int default_suspend_listener(struct listener *l)
3487 {
3488- int ret = 1;
3489-
3490 if (!l->rx.proto->rx_suspend)
3491 return 1;
3492
3493- ret = l->rx.proto->rx_suspend(&l->rx);
3494- return ret > 0 ? ret : 0;
3495+ return l->rx.proto->rx_suspend(&l->rx);
3496 }
3497
3498
3499@@ -389,8 +391,28 @@ int default_resume_listener(struct listener *l)
3500
3501 if (l->state == LI_ASSIGNED) {
3502 char msg[100];
3503+ char *errmsg;
3504 int err;
3505
3506+ /* first, try to bind the receiver */
3507+ err = l->rx.proto->fam->bind(&l->rx, &errmsg);
3508+ if (err != ERR_NONE) {
3509+ if (err & ERR_WARN)
3510+ ha_warning("Resuming listener: %s\n", errmsg);
3511+ else if (err & ERR_ALERT)
3512+ ha_alert("Resuming listener: %s\n", errmsg);
3513+ ha_free(&errmsg);
3514+ if (err & (ERR_FATAL | ERR_ABORT)) {
3515+ ret = 0;
3516+ goto end;
3517+ }
3518+ }
3519+
3520+ /* then, try to listen:
3521+ * for now there's still always a listening function
3522+ * (same check performed in protocol_bind_all()
3523+ */
3524+ BUG_ON(!l->rx.proto->listen);
3525 err = l->rx.proto->listen(l, msg, sizeof(msg));
3526 if (err & ERR_ALERT)
3527 ha_alert("Resuming listener: %s\n", msg);
3528@@ -422,42 +444,66 @@ int default_resume_listener(struct listener *l)
3529 * closes upon SHUT_WR and refuses to rebind. So a common validation path
3530 * involves SHUT_WR && listen && SHUT_RD. In case of success, the FD's polling
3531 * is disabled. It normally returns non-zero, unless an error is reported.
3532- * It will need to operate under the proxy's lock. The caller is
3533- * responsible for indicating in lpx whether the proxy locks is
3534- * already held (non-zero) or not (zero) so that the function picks it.
3535+ * suspend() may totally stop a listener if it doesn't support the PAUSED
3536+ * state, in which case state will be set to ASSIGNED.
3537+ * It will need to operate under the proxy's lock and the listener's lock.
3538+ * The caller is responsible for indicating in lpx, lli whether the respective
3539+ * locks are already held (non-zero) or not (zero) so that the function pick
3540+ * the missing ones, in this order.
3541 */
3542-int pause_listener(struct listener *l, int lpx)
3543+int suspend_listener(struct listener *l, int lpx, int lli)
3544 {
3545 struct proxy *px = l->bind_conf->frontend;
3546 int ret = 1;
3547
3548- if (!lpx)
3549+ if (!lpx && px)
3550 HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock);
3551
3552- HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
3553+ if (!lli)
3554+ HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
3555
3556 if ((global.mode & (MODE_DAEMON | MODE_MWORKER)) &&
3557 !(proc_mask(l->rx.settings->bind_proc) & pid_bit))
3558 goto end;
3559
3560- if (l->state <= LI_PAUSED)
3561+ if (!(l->flags & LI_F_FINALIZED) || l->state <= LI_PAUSED)
3562 goto end;
3563
3564- if (l->rx.proto->suspend)
3565+ if (l->rx.proto->suspend) {
3566 ret = l->rx.proto->suspend(l);
3567+ /* if the suspend() fails, we don't want to change the
3568+ * current listener state
3569+ */
3570+ if (ret < 0)
3571+ goto end;
3572+ }
3573
3574 MT_LIST_DELETE(&l->wait_queue);
3575
3576- listener_set_state(l, LI_PAUSED);
3577+ /* ret == 0 means that the suspend() has been turned into
3578+ * an unbind(), meaning the listener is now stopped (ie: ABNS), we need
3579+ * to report this state change properly
3580+ */
3581+ listener_set_state(l, ((ret) ? LI_PAUSED : LI_ASSIGNED));
3582+
3583+ if (px && !(l->flags & LI_F_SUSPENDED))
3584+ px->li_suspended++;
3585+ l->flags |= LI_F_SUSPENDED;
3586+
3587+ /* at this point, everything is under control, no error should be
3588+ * returned to calling function
3589+ */
3590+ ret = 1;
3591
3592 if (px && !px->li_ready) {
3593 ha_warning("Paused %s %s.\n", proxy_cap_str(px->cap), px->id);
3594 send_log(px, LOG_WARNING, "Paused %s %s.\n", proxy_cap_str(px->cap), px->id);
3595 }
3596 end:
3597- HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
3598+ if (!lli)
3599+ HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
3600
3601- if (!lpx)
3602+ if (!lpx && px)
3603 HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock);
3604
3605 return ret;
3606@@ -469,23 +515,24 @@ int pause_listener(struct listener *l, int lpx)
3607 * or LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
3608 * Listeners bound to a different process are not woken up unless we're in
3609 * foreground mode, and are ignored. If the listener was only in the assigned
3610- * state, it's totally rebound. This can happen if a pause() has completely
3611+ * state, it's totally rebound. This can happen if a suspend() has completely
3612 * stopped it. If the resume fails, 0 is returned and an error might be
3613 * displayed.
3614- * It will need to operate under the proxy's lock. The caller is
3615- * responsible for indicating in lpx whether the proxy locks is
3616- * already held (non-zero) or not (zero) so that the function picks it.
3617+ * It will need to operate under the proxy's lock and the listener's lock.
3618+ * The caller is responsible for indicating in lpx, lli whether the respective
3619+ * locks are already held (non-zero) or not (zero) so that the function pick
3620+ * the missing ones, in this order.
3621 */
3622-int resume_listener(struct listener *l, int lpx)
3623+int resume_listener(struct listener *l, int lpx, int lli)
3624 {
3625 struct proxy *px = l->bind_conf->frontend;
3626- int was_paused = px && px->li_paused;
3627 int ret = 1;
3628
3629- if (!lpx)
3630+ if (!lpx && px)
3631 HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock);
3632
3633- HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
3634+ if (!lli)
3635+ HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
3636
3637 /* check that another thread didn't to the job in parallel (e.g. at the
3638 * end of listen_accept() while we'd come from dequeue_all_listeners().
3639@@ -497,15 +544,14 @@ int resume_listener(struct listener *l, int lpx)
3640 !(proc_mask(l->rx.settings->bind_proc) & pid_bit))
3641 goto end;
3642
3643- if (l->state == LI_READY)
3644- goto end;
3645-
3646- /* the listener might have been stopped in parallel */
3647- if (l->state < LI_PAUSED)
3648+ if (!(l->flags & LI_F_FINALIZED) || l->state == LI_READY)
3649 goto end;
3650
3651- if (l->rx.proto->resume)
3652+ if (l->rx.proto->resume) {
3653 ret = l->rx.proto->resume(l);
3654+ if (!ret)
3655+ goto end; /* failure to resume */
3656+ }
3657
3658 if (l->maxconn && l->nbconn >= l->maxconn) {
3659 l->rx.proto->disable(l);
3660@@ -517,21 +563,61 @@ int resume_listener(struct listener *l, int lpx)
3661 listener_set_state(l, LI_READY);
3662
3663 done:
3664- if (was_paused && !px->li_paused) {
3665+ if (px && (l->flags & LI_F_SUSPENDED))
3666+ px->li_suspended--;
3667+ l->flags &= ~LI_F_SUSPENDED;
3668+
3669+ if (px && !px->li_suspended) {
3670 ha_warning("Resumed %s %s.\n", proxy_cap_str(px->cap), px->id);
3671 send_log(px, LOG_WARNING, "Resumed %s %s.\n", proxy_cap_str(px->cap), px->id);
3672 }
3673 end:
3674- HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
3675+ if (!lli)
3676+ HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
3677+
3678+ if (!lpx && px)
3679+ HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock);
3680+
3681+ return ret;
3682+}
3683+
3684+/* Same as resume_listener(), but will only work to resume from
3685+ * LI_FULL or LI_LIMITED states because we try to relax listeners that
3686+ * were temporarily restricted and not to resume inactive listeners that
3687+ * may have been paused or completely stopped in the meantime.
3688+ * Returns positive value for success and 0 for failure.
3689+ * It will need to operate under the proxy's lock and the listener's lock.
3690+ * The caller is responsible for indicating in lpx, lli whether the respective
3691+ * locks are already held (non-zero) or not (zero) so that the function pick
3692+ * the missing ones, in this order.
3693+ */
3694+int relax_listener(struct listener *l, int lpx, int lli)
3695+{
3696+ struct proxy *px = l->bind_conf->frontend;
3697+ int ret = 1;
3698+
3699+ if (!lpx && px)
3700+ HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock);
3701+
3702+ if (!lli)
3703+ HA_RWLOCK_WRLOCK(LISTENER_LOCK, &l->lock);
3704+
3705+ if (l->state != LI_FULL && l->state != LI_LIMITED)
3706+ goto end; /* listener may be suspended or even stopped */
3707+ ret = resume_listener(l, 1, 1);
3708+
3709+ end:
3710+ if (!lli)
3711+ HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &l->lock);
3712
3713- if (!lpx)
3714+ if (!lpx && px)
3715 HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock);
3716
3717 return ret;
3718 }
3719
3720 /* Marks a ready listener as full so that the stream code tries to re-enable
3721- * it upon next close() using resume_listener().
3722+ * it upon next close() using relax_listener().
3723 */
3724 static void listener_full(struct listener *l)
3725 {
3726@@ -569,7 +655,7 @@ void dequeue_all_listeners()
3727 /* This cannot fail because the listeners are by definition in
3728 * the LI_LIMITED state.
3729 */
3730- resume_listener(listener, 0);
3731+ relax_listener(listener, 0, 0);
3732 }
3733 }
3734
3735@@ -582,7 +668,7 @@ void dequeue_proxy_listeners(struct proxy *px)
3736 /* This cannot fail because the listeners are by definition in
3737 * the LI_LIMITED state.
3738 */
3739- resume_listener(listener, 0);
3740+ relax_listener(listener, 0, 0);
3741 }
3742 }
3743
3744@@ -1108,7 +1194,7 @@ void listener_accept(struct listener *l)
3745 (!tick_isset(global_listener_queue_task->expire) ||
3746 tick_is_expired(global_listener_queue_task->expire, now_ms))))) {
3747 /* at least one thread has to this when quitting */
3748- resume_listener(l, 0);
3749+ relax_listener(l, 0, 0);
3750
3751 /* Dequeues all of the listeners waiting for a resource */
3752 dequeue_all_listeners();
3753@@ -1127,7 +1213,7 @@ void listener_accept(struct listener *l)
3754 * Let's put it to pause in this case.
3755 */
3756 if (l->rx.proto && l->rx.proto->rx_listening(&l->rx) == 0) {
3757- pause_listener(l, 0);
3758+ suspend_listener(l, 0, 0);
3759 goto end;
3760 }
3761
3762@@ -1167,12 +1253,12 @@ void listener_release(struct listener *l)
3763 _HA_ATOMIC_DEC(&l->thr_conn[tid]);
3764
3765 if (l->state == LI_FULL || l->state == LI_LIMITED)
3766- resume_listener(l, 0);
3767+ relax_listener(l, 0, 0);
3768
3769 /* Dequeues all of the listeners waiting for a resource */
3770 dequeue_all_listeners();
3771
3772- if (!MT_LIST_ISEMPTY(&fe->listener_queue) &&
3773+ if (fe && !MT_LIST_ISEMPTY(&fe->listener_queue) &&
3774 (!fe->fe_sps_lim || freq_ctr_remain(&fe->fe_sess_per_sec, fe->fe_sps_lim, 0) > 0))
3775 dequeue_proxy_listeners(fe);
3776 }
3777diff --git a/src/log.c b/src/log.c
3778index fbc8a2f..c112a49 100644
3779--- a/src/log.c
3780+++ b/src/log.c
3781@@ -856,6 +856,10 @@ int parse_logsrv(char **args, struct list *logsrvs, int do_del, const char *file
3782 }
3783
3784 node = malloc(sizeof(*node));
3785+ if (!node) {
3786+ memprintf(err, "out of memory error");
3787+ goto error;
3788+ }
3789 memcpy(node, logsrv, sizeof(struct logsrv));
3790 node->ref = logsrv;
3791 LIST_INIT(&node->list);
3792@@ -1877,12 +1881,18 @@ static inline void __do_send_log(struct logsrv *logsrv, int nblogger, int level,
3793 send:
3794 if (logsrv->type == LOG_TARGET_BUFFER) {
3795 struct ist msg;
3796+ size_t maxlen = logsrv->maxlen;
3797
3798 msg = ist2(message, size);
3799 if (msg.len > logsrv->maxlen)
3800 msg.len = logsrv->maxlen;
3801
3802- sent = sink_write(logsrv->sink, &msg, 1, level, facility, metadata);
3803+ /* make room for the final '\n' which may be forcefully inserted
3804+ * by tcp forwarder applet (sink_forward_io_handler)
3805+ */
3806+ maxlen -= 1;
3807+
3808+ sent = sink_write(logsrv->sink, maxlen, &msg, 1, level, facility, metadata);
3809 }
3810 else if (logsrv->addr.ss_family == AF_CUST_EXISTING_FD) {
3811 struct ist msg;
3812@@ -1895,7 +1905,7 @@ static inline void __do_send_log(struct logsrv *logsrv, int nblogger, int level,
3813 }
3814 else {
3815 int i = 0;
3816- int totlen = logsrv->maxlen;
3817+ int totlen = logsrv->maxlen - 1; /* save space for the final '\n' */
3818
3819 for (i = 0 ; i < nbelem ; i++ ) {
3820 iovec[i].iov_base = msg_header[i].ptr;
3821@@ -3767,7 +3777,7 @@ static void syslog_io_handler(struct appctx *appctx)
3822 size_t size;
3823
3824 max_accept = l->maxaccept ? l->maxaccept : 1;
3825- while (co_data(si_oc(si))) {
3826+ while (1) {
3827 char c;
3828
3829 if (max_accept <= 0)
3830@@ -3898,14 +3908,14 @@ static struct applet syslog_applet = {
3831 */
3832 int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3833 {
3834- int err_code = 0;
3835+ int err_code = ERR_NONE;
3836 struct proxy *px;
3837 char *errmsg = NULL;
3838 const char *err = NULL;
3839
3840 if (strcmp(args[0], "log-forward") == 0) {
3841 if (!*args[1]) {
3842- ha_alert("parsing [%s:%d] : missing name for ip-forward section.\n", file, linenum);
3843+ ha_alert("parsing [%s:%d] : missing name for log-forward section.\n", file, linenum);
3844 err_code |= ERR_ALERT | ERR_ABORT;
3845 goto out;
3846 }
3847@@ -3926,6 +3936,7 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3848 ha_alert("Parsing [%s:%d]: log-forward section '%s' has the same name as another log-forward section declared at %s:%d.\n",
3849 file, linenum, args[1], px->conf.file, px->conf.line);
3850 err_code |= ERR_ALERT | ERR_FATAL;
3851+ goto out;
3852 }
3853
3854 px = proxy_find_by_name(args[1], 0, 0);
3855@@ -3934,6 +3945,7 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3856 file, linenum, args[1], proxy_type_str(px),
3857 px->id, px->conf.file, px->conf.line);
3858 err_code |= ERR_ALERT | ERR_FATAL;
3859+ goto out;
3860 }
3861
3862 px = calloc(1, sizeof *px);
3863@@ -3955,7 +3967,6 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3864 px->accept = frontend_accept;
3865 px->default_target = &syslog_applet.obj_type;
3866 px->id = strdup(args[1]);
3867-
3868 }
3869 else if (strcmp(args[0], "maxconn") == 0) { /* maxconn */
3870 if (warnifnotcap(cfg_log_forward, PR_CAP_FE, file, linenum, args[0], " Maybe you want 'fullconn' instead ?"))
3871@@ -4007,9 +4018,9 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3872 else {
3873 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
3874 file, linenum, args[0], args[1], args[2]);
3875- err_code |= ERR_ALERT | ERR_FATAL;
3876- goto out;
3877 }
3878+ err_code |= ERR_ALERT | ERR_FATAL;
3879+ goto out;
3880 }
3881 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
3882 l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : MAX_ACCEPT;
3883@@ -4147,7 +4158,6 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3884 }
3885 else if (res) {
3886 memprintf(&errmsg, "unexpected character '%c' in 'timeout client'", *res);
3887- return -1;
3888 }
3889
3890 if (res) {
3891@@ -4163,6 +4173,7 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3892 goto out;
3893 }
3894 out:
3895+ ha_free(&errmsg);
3896 return err_code;
3897 }
3898
3899diff --git a/src/mjson.c b/src/mjson.c
3900index 549b0d5..73b7a57 100644
3901--- a/src/mjson.c
3902+++ b/src/mjson.c
3903@@ -192,7 +192,7 @@ static int plen1(const char *s) {
3904 }
3905
3906 static int plen2(const char *s) {
3907- int i = 0, n = 0;
3908+ int i = 0, __attribute__((unused)) n = 0;
3909 while (s[i] != '\0' && s[i] != '.' && s[i] != '[')
3910 n++, i += s[i] == '\\' ? 2 : 1;
3911 // printf("PLEN: s: [%s], [%.*s] => %d\n", s, i, s, n);
3912@@ -724,7 +724,7 @@ static int is_digit(int c) {
3913 /* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
3914 static double mystrtod(const char *str, char **end) {
3915 double d = 0.0;
3916- int sign = 1, n = 0;
3917+ int sign = 1, __attribute__((unused)) n = 0;
3918 const char *p = str, *a = str;
3919
3920 /* decimal part */
3921diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c
3922index 7334b56..5025163 100644
3923--- a/src/mux_fcgi.c
3924+++ b/src/mux_fcgi.c
3925@@ -3031,7 +3031,7 @@ struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned int state)
3926 conn = fconn->conn;
3927 TRACE_POINT(FCGI_EV_FCONN_WAKE, conn);
3928
3929- conn_in_list = conn->flags & CO_FL_LIST_MASK;
3930+ conn_in_list = conn_get_idle_flag(conn);
3931 if (conn_in_list)
3932 conn_delete_from_tree(&conn->hash_node->node);
3933
3934diff --git a/src/mux_h1.c b/src/mux_h1.c
3935index c29c4ed..1324c20 100644
3936--- a/src/mux_h1.c
3937+++ b/src/mux_h1.c
3938@@ -935,8 +935,10 @@ static void h1_release(struct h1c *h1c)
3939 h1c->task = NULL;
3940 }
3941
3942- if (h1c->wait_event.tasklet)
3943+ if (h1c->wait_event.tasklet) {
3944 tasklet_free(h1c->wait_event.tasklet);
3945+ h1c->wait_event.tasklet = NULL;
3946+ }
3947
3948 h1s_destroy(h1c->h1s);
3949 if (conn) {
3950@@ -2912,7 +2914,7 @@ struct task *h1_io_cb(struct task *t, void *ctx, unsigned int state)
3951 /* Remove the connection from the list, to be sure nobody attempts
3952 * to use it while we handle the I/O events
3953 */
3954- conn_in_list = conn->flags & CO_FL_LIST_MASK;
3955+ conn_in_list = conn_get_idle_flag(conn);
3956 if (conn_in_list)
3957 conn_delete_from_tree(&conn->hash_node->node);
3958
3959@@ -3340,6 +3342,10 @@ static void h1_shutw_conn(struct connection *conn)
3960 TRACE_ENTER(H1_EV_H1C_END, conn);
3961 conn_xprt_shutw(conn);
3962 conn_sock_shutw(conn, (h1c && !(h1c->flags & H1C_F_ST_SILENT_SHUT)));
3963+
3964+ if (h1c->wait_event.tasklet && !h1c->wait_event.events)
3965+ tasklet_wakeup(h1c->wait_event.tasklet);
3966+
3967 TRACE_LEAVE(H1_EV_H1C_END, conn);
3968 }
3969
3970diff --git a/src/mux_h2.c b/src/mux_h2.c
3971index c880c37..61fd1a4 100644
3972--- a/src/mux_h2.c
3973+++ b/src/mux_h2.c
3974@@ -987,6 +987,7 @@ static int h2_init(struct connection *conn, struct proxy *prx, struct session *s
3975
3976 h2c->proxy = prx;
3977 h2c->task = NULL;
3978+ h2c->wait_event.tasklet = NULL;
3979 h2c->idle_start = now_ms;
3980 if (tick_isset(h2c->timeout)) {
3981 t = task_new(tid_bit);
3982@@ -2759,6 +2760,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
3983 /* unrecoverable error ? */
3984 if (h2c->st0 >= H2_CS_ERROR) {
3985 TRACE_USER("Unrecoverable error decoding H2 trailers", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_STRM_NEW|H2_EV_STRM_END, h2c->conn, 0, &rxbuf);
3986+ sess_log(h2c->conn->owner);
3987 goto out;
3988 }
3989
3990@@ -2773,6 +2775,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
3991 /* Failed to decode this frame (e.g. too large request)
3992 * but the HPACK decompressor is still synchronized.
3993 */
3994+ sess_log(h2c->conn->owner);
3995 h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
3996 TRACE_USER("Stream error decoding H2 trailers", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_STRM_NEW|H2_EV_STRM_END, h2c->conn, 0, &rxbuf);
3997 h2c->st0 = H2_CS_FRAME_E;
3998@@ -2784,6 +2787,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
3999 * the data and send another RST.
4000 */
4001 error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len, NULL);
4002+ sess_log(h2c->conn->owner);
4003 h2s = (struct h2s*)h2_error_stream;
4004 goto send_rst;
4005 }
4006@@ -2803,6 +2807,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
4007 /* unrecoverable error ? */
4008 if (h2c->st0 >= H2_CS_ERROR) {
4009 TRACE_USER("Unrecoverable error decoding H2 request", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_STRM_NEW|H2_EV_STRM_END, h2c->conn, 0, &rxbuf);
4010+ sess_log(h2c->conn->owner);
4011 goto out;
4012 }
4013
4014@@ -2817,6 +2822,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
4015 /* Failed to decode this stream (e.g. too large request)
4016 * but the HPACK decompressor is still synchronized.
4017 */
4018+ sess_log(h2c->conn->owner);
4019 h2s = (struct h2s*)h2_error_stream;
4020 goto send_rst;
4021 }
4022@@ -3977,11 +3983,10 @@ struct task *h2_io_cb(struct task *t, void *ctx, unsigned int state)
4023 conn = h2c->conn;
4024 TRACE_ENTER(H2_EV_H2C_WAKE, conn);
4025
4026- conn_in_list = conn->flags & CO_FL_LIST_MASK;
4027-
4028 /* Remove the connection from the list, to be sure nobody attempts
4029 * to use it while we handle the I/O events
4030 */
4031+ conn_in_list = conn_get_idle_flag(conn);
4032 if (conn_in_list)
4033 conn_delete_from_tree(&conn->hash_node->node);
4034
4035@@ -4386,7 +4391,7 @@ static void h2_detach(struct conn_stream *cs)
4036 /* refresh the timeout if none was active, so that the last
4037 * leaving stream may arm it.
4038 */
4039- if (!tick_isset(h2c->task->expire))
4040+ if (h2c->task && !tick_isset(h2c->task->expire))
4041 h2c_update_timeout(h2c);
4042 return;
4043 }
4044@@ -4912,7 +4917,8 @@ next_frame:
4045 if (h2c->flags & H2_CF_IS_BACK)
4046 outlen = h2_make_htx_response(list, htx, &msgf, body_len, upgrade_protocol);
4047 else
4048- outlen = h2_make_htx_request(list, htx, &msgf, body_len);
4049+ outlen = h2_make_htx_request(list, htx, &msgf, body_len,
4050+ !!(((const struct session *)h2c->conn->owner)->fe->options2 & PR_O2_REQBUG_OK));
4051
4052 if (outlen < 0 || htx_free_space(htx) < global.tune.maxrewrite) {
4053 /* too large headers? this is a stream error only */
4054diff --git a/src/mworker.c b/src/mworker.c
4055index d78f4ce..6415fd4 100644
4056--- a/src/mworker.c
4057+++ b/src/mworker.c
4058@@ -130,15 +130,24 @@ void mworker_proc_list_to_env()
4059
4060 /*
4061 * unserialize the proc list from the environment
4062+ * Return < 0 upon error.
4063 */
4064 int mworker_env_to_proc_list()
4065 {
4066- char *msg, *token = NULL, *s1;
4067+ char *env, *msg, *omsg = NULL, *token = NULL, *s1;
4068+ int err = 0;
4069
4070- msg = getenv("HAPROXY_PROCESSES");
4071- if (!msg)
4072+ env = getenv("HAPROXY_PROCESSES");
4073+ if (!env)
4074 return 0;
4075
4076+ omsg = msg = strdup(env);
4077+ if (!msg) {
4078+ ha_alert("Out of memory while trying to allocate a worker process structure.");
4079+ err = -1;
4080+ goto out;
4081+ }
4082+
4083 while ((token = strtok_r(msg, "|", &s1))) {
4084 struct mworker_proc *child;
4085 char *subtoken = NULL;
4086@@ -148,8 +157,9 @@ int mworker_env_to_proc_list()
4087
4088 child = calloc(1, sizeof(*child));
4089 if (!child) {
4090- ha_alert("Out of memory while trying to allocate a worker process structure.");
4091- return -1;
4092+ ha_alert("out of memory while trying to allocate a worker process structure.");
4093+ err = -1;
4094+ goto out;
4095 }
4096
4097 while ((subtoken = strtok_r(token, ";", &s2))) {
4098@@ -171,6 +181,8 @@ int mworker_env_to_proc_list()
4099
4100 } else if (strncmp(subtoken, "fd=", 3) == 0) {
4101 child->ipc_fd[0] = atoi(subtoken+3);
4102+ if (child->ipc_fd[0] > -1)
4103+ global.maxsock++;
4104 } else if (strncmp(subtoken, "pid=", 4) == 0) {
4105 child->pid = atoi(subtoken+4);
4106 } else if (strncmp(subtoken, "rpid=", 5) == 0) {
4107@@ -198,7 +210,9 @@ int mworker_env_to_proc_list()
4108
4109 unsetenv("HAPROXY_PROCESSES");
4110
4111- return 0;
4112+out:
4113+ free(omsg);
4114+ return err;
4115 }
4116
4117 /* Signal blocking and unblocking */
4118@@ -394,6 +408,9 @@ static int mworker_pipe_register_per_thread()
4119 if (tid != 0)
4120 return 1;
4121
4122+ if (proc_self->ipc_fd[1] < 0) /* proc_self was incomplete and we can't find the socketpair */
4123+ return 1;
4124+
4125 fcntl(proc_self->ipc_fd[1], F_SETFL, O_NONBLOCK);
4126 /* In multi-tread, we need only one thread to process
4127 * events on the pipe with master
4128@@ -487,12 +504,15 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
4129 struct stream_interface *si = appctx->owner;
4130 struct mworker_proc *child;
4131 int old = 0;
4132- int up = now.tv_sec - proc_self->timestamp;
4133+ int up = date.tv_sec - proc_self->timestamp;
4134 char *uptime = NULL;
4135
4136 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
4137 return 1;
4138
4139+ if (up < 0) /* must never be negative because of clock drift */
4140+ up = 0;
4141+
4142 chunk_reset(&trash);
4143
4144 chunk_printf(&trash, "#%-14s %-15s %-15s %-15s %-15s %-15s\n", "<PID>", "<type>", "<relative PID>", "<reloads>", "<uptime>", "<version>");
4145@@ -504,7 +524,9 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
4146
4147 chunk_appendf(&trash, "# workers\n");
4148 list_for_each_entry(child, &proc_list, list) {
4149- up = now.tv_sec - child->timestamp;
4150+ up = date.tv_sec - child->timestamp;
4151+ if (up < 0) /* must never be negative because of clock drift */
4152+ up = 0;
4153
4154 if (!(child->options & PROC_O_TYPE_WORKER))
4155 continue;
4156@@ -525,7 +547,9 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
4157
4158 chunk_appendf(&trash, "# old workers\n");
4159 list_for_each_entry(child, &proc_list, list) {
4160- up = now.tv_sec - child->timestamp;
4161+ up = date.tv_sec - child->timestamp;
4162+ if (up <= 0) /* must never be negative because of clock drift */
4163+ up = 0;
4164
4165 if (!(child->options & PROC_O_TYPE_WORKER))
4166 continue;
4167@@ -544,7 +568,9 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
4168 chunk_appendf(&trash, "# programs\n");
4169 old = 0;
4170 list_for_each_entry(child, &proc_list, list) {
4171- up = now.tv_sec - child->timestamp;
4172+ up = date.tv_sec - child->timestamp;
4173+ if (up < 0) /* must never be negative because of clock drift */
4174+ up = 0;
4175
4176 if (!(child->options & PROC_O_TYPE_PROG))
4177 continue;
4178@@ -561,7 +587,9 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
4179 if (old) {
4180 chunk_appendf(&trash, "# old programs\n");
4181 list_for_each_entry(child, &proc_list, list) {
4182- up = now.tv_sec - child->timestamp;
4183+ up = date.tv_sec - child->timestamp;
4184+ if (up < 0) /* must never be negative because of clock drift */
4185+ up = 0;
4186
4187 if (!(child->options & PROC_O_TYPE_PROG))
4188 continue;
4189diff --git a/src/namespace.c b/src/namespace.c
4190index 1fc8439..9cc85a3 100644
4191--- a/src/namespace.c
4192+++ b/src/namespace.c
4193@@ -54,6 +54,7 @@ static void netns_sig_stop(struct sig_handler *sh)
4194 entry = container_of(node, struct netns_entry, node);
4195 free(entry->node.key);
4196 close(entry->fd);
4197+ free(entry);
4198 node = next;
4199 }
4200 }
4201diff --git a/src/proto_uxdg.c b/src/proto_uxdg.c
4202index 7eb5a62..b4583c5 100644
4203--- a/src/proto_uxdg.c
4204+++ b/src/proto_uxdg.c
4205@@ -30,6 +30,7 @@
4206 #include <haproxy/protocol.h>
4207 #include <haproxy/sock.h>
4208 #include <haproxy/sock_unix.h>
4209+#include <haproxy/tools.h>
4210
4211 static int uxdg_bind_listener(struct listener *listener, char *errmsg, int errlen);
4212 static void uxdg_enable_listener(struct listener *listener);
4213@@ -95,6 +96,7 @@ int uxdg_bind_listener(struct listener *listener, char *errmsg, int errlen)
4214
4215 if (!(listener->rx.flags & RX_F_BOUND)) {
4216 msg = "receiving socket not bound";
4217+ err |= ERR_FATAL | ERR_ALERT;
4218 goto uxdg_return;
4219 }
4220
4221@@ -102,8 +104,11 @@ int uxdg_bind_listener(struct listener *listener, char *errmsg, int errlen)
4222
4223 uxdg_return:
4224 if (msg && errlen) {
4225- const char *path = ((struct sockaddr_un *)&listener->rx.addr)->sun_path;
4226- snprintf(errmsg, errlen, "%s [%s]", msg, path);
4227+ char *path_str;
4228+
4229+ path_str = sa2str((struct sockaddr_storage *)&listener->rx.addr, 0, 0);
4230+ snprintf(errmsg, errlen, "%s for [%s]", msg, ((path_str) ? path_str : ""));
4231+ ha_free(&path_str);
4232 }
4233 return err;
4234 }
4235@@ -125,17 +130,20 @@ static void uxdg_disable_listener(struct listener *l)
4236 }
4237
4238 /* Suspend a receiver. Returns < 0 in case of failure, 0 if the receiver
4239- * was totally stopped, or > 0 if correctly suspended. Nothing is done for
4240- * plain unix sockets since currently it's the new process which handles
4241- * the renaming. Abstract sockets are completely unbound and closed so
4242- * there's no need to stop the poller.
4243+ * was totally stopped, or > 0 if correctly suspended. For plain unix sockets
4244+ * we only disable the listener to prevent data from being handled but nothing
4245+ * more is done since currently it's the new process which handles the renaming.
4246+ * Abstract sockets are completely unbound and closed so there's no need to stop
4247+ * the poller.
4248 */
4249 static int uxdg_suspend_receiver(struct receiver *rx)
4250 {
4251 struct listener *l = LIST_ELEM(rx, struct listener *, rx);
4252
4253- if (((struct sockaddr_un *)&rx->addr)->sun_path[0])
4254+ if (((struct sockaddr_un *)&rx->addr)->sun_path[0]) {
4255+ uxdg_disable_listener(l);
4256 return 1;
4257+ }
4258
4259 /* Listener's lock already held. Call lockless version of
4260 * unbind_listener. */
4261diff --git a/src/proto_uxst.c b/src/proto_uxst.c
4262index 621eb39..121450b 100644
4263--- a/src/proto_uxst.c
4264+++ b/src/proto_uxst.c
4265@@ -59,6 +59,7 @@ struct protocol proto_uxst = {
4266 .add = default_add_listener,
4267 .unbind = default_unbind_listener,
4268 .suspend = default_suspend_listener,
4269+ .resume = default_resume_listener,
4270 .accept_conn = sock_accept_conn,
4271 .ctrl_init = sock_conn_ctrl_init,
4272 .ctrl_close = sock_conn_ctrl_close,
4273@@ -120,6 +121,7 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
4274
4275 if (!(listener->rx.flags & RX_F_BOUND)) {
4276 msg = "receiving socket not bound";
4277+ err |= ERR_FATAL | ERR_ALERT;
4278 goto uxst_return;
4279 }
4280
4281@@ -141,8 +143,11 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
4282 close(fd);
4283 uxst_return:
4284 if (msg && errlen) {
4285- const char *path = ((struct sockaddr_un *)&listener->rx.addr)->sun_path;
4286- snprintf(errmsg, errlen, "%s [%s]", msg, path);
4287+ char *path_str;
4288+
4289+ path_str = sa2str((struct sockaddr_storage *)&listener->rx.addr, 0, 0);
4290+ snprintf(errmsg, errlen, "%s for [%s]", msg, ((path_str) ? path_str : ""));
4291+ ha_free(&path_str);
4292 }
4293 return err;
4294 }
4295@@ -164,17 +169,20 @@ static void uxst_disable_listener(struct listener *l)
4296 }
4297
4298 /* Suspend a receiver. Returns < 0 in case of failure, 0 if the receiver
4299- * was totally stopped, or > 0 if correctly suspended. Nothing is done for
4300- * plain unix sockets since currently it's the new process which handles
4301- * the renaming. Abstract sockets are completely unbound and closed so
4302- * there's no need to stop the poller.
4303+ * was totally stopped, or > 0 if correctly suspended. For plain unix sockets
4304+ * we only disable the listener to prevent data from being handled but nothing
4305+ * more is done since currently it's the new process which handles the renaming.
4306+ * Abstract sockets are completely unbound and closed so there's no need to stop
4307+ * the poller.
4308 */
4309 static int uxst_suspend_receiver(struct receiver *rx)
4310 {
4311 struct listener *l = LIST_ELEM(rx, struct listener *, rx);
4312
4313- if (((struct sockaddr_un *)&rx->addr)->sun_path[0])
4314+ if (((struct sockaddr_un *)&rx->addr)->sun_path[0]) {
4315+ uxst_disable_listener(l);
4316 return 1;
4317+ }
4318
4319 /* Listener's lock already held. Call lockless version of
4320 * unbind_listener. */
4321diff --git a/src/protocol.c b/src/protocol.c
4322index 1806bed..7e56b9a 100644
4323--- a/src/protocol.c
4324+++ b/src/protocol.c
4325@@ -86,8 +86,10 @@ int protocol_bind_all(int verbose)
4326 else if (lerr & ERR_WARN)
4327 ha_warning("Starting %s %s: %s\n",
4328 proxy_type_str(px), px->id, errmsg);
4329- ha_free(&errmsg);
4330 }
4331+ if (lerr != ERR_NONE)
4332+ ha_free(&errmsg);
4333+
4334 if (lerr & ERR_ABORT)
4335 break;
4336
4337@@ -155,15 +157,15 @@ void protocol_stop_now(void)
4338 list_for_each_entry(proto, &protocols, list) {
4339 list_for_each_entry_safe(listener, lback, &proto->receivers, rx.proto_list)
4340 if (!listener->bind_conf->frontend->grace)
4341- stop_listener(listener, 0, 1);
4342+ stop_listener(listener, 0, 1, 0);
4343 }
4344 HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
4345 }
4346
4347-/* pauses all listeners of all registered protocols. This is typically
4348+/* suspends all listeners of all registered protocols. This is typically
4349 * used on SIG_TTOU to release all listening sockets for the time needed to
4350- * try to bind a new process. The listeners enter LI_PAUSED. It returns
4351- * ERR_NONE, with ERR_FATAL on failure.
4352+ * try to bind a new process. The listeners enter LI_PAUSED or LI_ASSIGNED.
4353+ * It returns ERR_NONE, with ERR_FATAL on failure.
4354 */
4355 int protocol_pause_all(void)
4356 {
4357@@ -175,7 +177,7 @@ int protocol_pause_all(void)
4358 HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
4359 list_for_each_entry(proto, &protocols, list) {
4360 list_for_each_entry(listener, &proto->receivers, rx.proto_list)
4361- if (!pause_listener(listener, 0))
4362+ if (!suspend_listener(listener, 0, 0))
4363 err |= ERR_FATAL;
4364 }
4365 HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
4366@@ -197,7 +199,7 @@ int protocol_resume_all(void)
4367 HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
4368 list_for_each_entry(proto, &protocols, list) {
4369 list_for_each_entry(listener, &proto->receivers, rx.proto_list)
4370- if (!resume_listener(listener, 0))
4371+ if (!resume_listener(listener, 0, 0))
4372 err |= ERR_FATAL;
4373 }
4374 HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
4375diff --git a/src/proxy.c b/src/proxy.c
4376index 0e204e7..21966c0 100644
4377--- a/src/proxy.c
4378+++ b/src/proxy.c
4379@@ -32,6 +32,7 @@
4380 #include <haproxy/global.h>
4381 #include <haproxy/http_ana.h>
4382 #include <haproxy/http_htx.h>
4383+#include <haproxy/http_rules.h>
4384 #include <haproxy/listener.h>
4385 #include <haproxy/log.h>
4386 #include <haproxy/obj_type-t.h>
4387@@ -146,6 +147,9 @@ void free_proxy(struct proxy *p)
4388 struct proxy_deinit_fct *pxdf;
4389 struct server_deinit_fct *srvdf;
4390
4391+ if (!p)
4392+ return;
4393+
4394 free(p->conf.file);
4395 free(p->id);
4396 free(p->cookie_name);
4397@@ -218,18 +222,7 @@ void free_proxy(struct proxy *p)
4398
4399 list_for_each_entry_safe(rdr, rdrb, &p->redirect_rules, list) {
4400 LIST_DELETE(&rdr->list);
4401- if (rdr->cond) {
4402- prune_acl_cond(rdr->cond);
4403- free(rdr->cond);
4404- }
4405- free(rdr->rdr_str);
4406- list_for_each_entry_safe(lf, lfb, &rdr->rdr_fmt, list) {
4407- LIST_DELETE(&lf->list);
4408- release_sample_expr(lf->expr);
4409- free(lf->arg);
4410- free(lf);
4411- }
4412- free(rdr);
4413+ http_free_redirect_rule(rdr);
4414 }
4415
4416 list_for_each_entry_safe(log, logb, &p->logsrvs, list) {
4417@@ -310,6 +303,7 @@ void free_proxy(struct proxy *p)
4418 bind_conf->xprt->destroy_bind_conf(bind_conf);
4419 free(bind_conf->file);
4420 free(bind_conf->arg);
4421+ free(bind_conf->settings.interface);
4422 LIST_DELETE(&bind_conf->by_fe);
4423 free(bind_conf);
4424 }
4425@@ -1872,11 +1866,40 @@ struct task *manage_proxy(struct task *t, void *context, unsigned int state)
4426 * to push to a new process and
4427 * we are free to flush the table.
4428 */
4429- stktable_trash_oldest(p->table, p->table->current);
4430- pool_gc(NULL);
4431+ int budget;
4432+ int cleaned_up;
4433+
4434+ /* We purposely enforce a budget limitation since we don't want
4435+ * to spend too much time purging old entries
4436+ *
4437+ * This is known to cause the watchdog to occasionnaly trigger if
4438+ * the table is huge and all entries become available for purge
4439+ * at the same time
4440+ *
4441+ * Moreover, we must also anticipate the pool_gc() call which
4442+ * will also be much slower if there is too much work at once
4443+ */
4444+ budget = MIN(p->table->current, (1 << 15)); /* max: 32K */
4445+ cleaned_up = stktable_trash_oldest(p->table, budget);
4446+ if (cleaned_up) {
4447+ /* immediately release freed memory since we are stopping */
4448+ pool_gc(NULL);
4449+ if (cleaned_up > (budget / 2)) {
4450+ /* most of the budget was used to purge entries,
4451+ * it is very likely that there are still trashable
4452+ * entries in the table, reschedule a new cleanup
4453+ * attempt ASAP
4454+ */
4455+ t->expire = TICK_ETERNITY;
4456+ task_wakeup(t, TASK_WOKEN_RES);
4457+ return t;
4458+ }
4459+ }
4460 }
4461 if (p->table->current) {
4462- /* some entries still remain, let's recheck in one second */
4463+ /* some entries still remain but are not yet available
4464+ * for cleanup, let's recheck in one second
4465+ */
4466 next = tick_first(next, tick_add(now_ms, 1000));
4467 }
4468 }
4469@@ -2052,7 +2075,7 @@ int pause_proxy(struct proxy *p)
4470 goto end;
4471
4472 list_for_each_entry(l, &p->conf.listeners, by_fe)
4473- pause_listener(l, 1);
4474+ suspend_listener(l, 1, 0);
4475
4476 if (p->li_ready) {
4477 ha_warning("%s %s failed to enter pause mode.\n", proxy_cap_str(p->cap), p->id);
4478@@ -2081,7 +2104,7 @@ void stop_proxy(struct proxy *p)
4479 HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock);
4480
4481 list_for_each_entry(l, &p->conf.listeners, by_fe)
4482- stop_listener(l, 1, 0);
4483+ stop_listener(l, 1, 0, 0);
4484
4485 if (!p->disabled && !p->li_ready) {
4486 /* might be just a backend */
4487@@ -2110,7 +2133,7 @@ int resume_proxy(struct proxy *p)
4488
4489 fail = 0;
4490 list_for_each_entry(l, &p->conf.listeners, by_fe) {
4491- if (!resume_listener(l, 1)) {
4492+ if (!resume_listener(l, 1, 0)) {
4493 int port;
4494
4495 port = get_host_port(&l->rx.addr);
4496@@ -2813,7 +2836,7 @@ static int cli_parse_set_maxconn_frontend(char **args, char *payload, struct app
4497 px->maxconn = v;
4498 list_for_each_entry(l, &px->conf.listeners, by_fe) {
4499 if (l->state == LI_FULL)
4500- resume_listener(l, 1);
4501+ relax_listener(l, 1, 0);
4502 }
4503
4504 if (px->maxconn > px->feconn)
4505diff --git a/src/resolvers.c b/src/resolvers.c
4506index 7a37b33..b0bc6de 100644
4507--- a/src/resolvers.c
4508+++ b/src/resolvers.c
4509@@ -3206,7 +3206,7 @@ void resolvers_setup_proxy(struct proxy *px)
4510 px->conn_retries = 1;
4511 px->timeout.server = TICK_ETERNITY;
4512 px->timeout.client = TICK_ETERNITY;
4513- px->timeout.connect = TICK_ETERNITY;
4514+ px->timeout.connect = 1000; // by default same than timeout.resolve
4515 px->accept = NULL;
4516 px->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON;
4517 px->bind_proc = 0; /* will be filled by users */
4518@@ -3627,8 +3627,11 @@ resolv_out:
4519 }
4520 if (args[1][2] == 't')
4521 curr_resolvers->timeout.retry = tout;
4522- else
4523+ else {
4524 curr_resolvers->timeout.resolve = tout;
4525+ curr_resolvers->px->timeout.connect = tout;
4526+ }
4527+
4528 }
4529 else {
4530 ha_alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments got '%s'.\n",
4531diff --git a/src/ring.c b/src/ring.c
4532index ba1da1b..977b58e 100644
4533--- a/src/ring.c
4534+++ b/src/ring.c
4535@@ -91,7 +91,6 @@ struct ring *ring_resize(struct ring *ring, size_t size)
4536 b_getblk(&ring->buf, area, ring->buf.data, 0);
4537 area = HA_ATOMIC_XCHG(&ring->buf.area, area);
4538 ring->buf.size = size;
4539- ring->buf.head = 0;
4540 }
4541
4542 HA_RWLOCK_WRUNLOCK(LOGSRV_LOCK, &ring->lock);
4543diff --git a/src/sample.c b/src/sample.c
4544index d9ae4c1..a294f80 100644
4545--- a/src/sample.c
4546+++ b/src/sample.c
4547@@ -1528,7 +1528,7 @@ static int sample_conv_debug(const struct arg *arg_p, struct sample *smp, void *
4548
4549 done:
4550 line = ist2(buf->area, buf->data);
4551- sink_write(sink, &line, 1, 0, 0, NULL);
4552+ sink_write(sink, 0, &line, 1, 0, 0, NULL);
4553 end:
4554 free_trash_chunk(buf);
4555 return 1;
4556@@ -2964,12 +2964,12 @@ static inline long long int arith_add(long long int a, long long int b)
4557 * +------+----------+----------+
4558 */
4559 if ((a ^ b) >= 0) {
4560- /* signs are different. */
4561+ /* signs are same. */
4562 if (a < 0) {
4563 if (LLONG_MIN - a > b)
4564 return LLONG_MIN;
4565 }
4566- if (LLONG_MAX - a < b)
4567+ else if (LLONG_MAX - a < b)
4568 return LLONG_MAX;
4569 }
4570 return a + b;
4571diff --git a/src/server.c b/src/server.c
4572index df67e1d..fe82679 100644
4573--- a/src/server.c
4574+++ b/src/server.c
4575@@ -2279,6 +2279,7 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
4576 if (srv_tmpl)
4577 srv->srvrq = src->srvrq;
4578
4579+ srv->netns = src->netns;
4580 srv->check.via_socks4 = src->check.via_socks4;
4581 srv->socks4_addr = src->socks4_addr;
4582 }
4583@@ -4901,6 +4902,7 @@ static void srv_update_status(struct server *s)
4584 struct proxy *px = s->proxy;
4585 int prev_srv_count = s->proxy->srv_bck + s->proxy->srv_act;
4586 int srv_was_stopping = (s->cur_state == SRV_ST_STOPPING) || (s->cur_admin & SRV_ADMF_DRAIN);
4587+ enum srv_state srv_prev_state = s->cur_state;
4588 int log_level;
4589 struct buffer *tmptrash = NULL;
4590
4591@@ -4915,7 +4917,6 @@ static void srv_update_status(struct server *s)
4592 s->next_admin = s->cur_admin;
4593
4594 if ((s->cur_state != SRV_ST_STOPPED) && (s->next_state == SRV_ST_STOPPED)) {
4595- s->last_change = now.tv_sec;
4596 if (s->proxy->lbprm.set_server_status_down)
4597 s->proxy->lbprm.set_server_status_down(s);
4598
4599@@ -4946,13 +4947,9 @@ static void srv_update_status(struct server *s)
4600 free_trash_chunk(tmptrash);
4601 tmptrash = NULL;
4602 }
4603- if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4604- set_backend_down(s->proxy);
4605-
4606 s->counters.down_trans++;
4607 }
4608 else if ((s->cur_state != SRV_ST_STOPPING) && (s->next_state == SRV_ST_STOPPING)) {
4609- s->last_change = now.tv_sec;
4610 if (s->proxy->lbprm.set_server_status_down)
4611 s->proxy->lbprm.set_server_status_down(s);
4612
4613@@ -4976,22 +4973,10 @@ static void srv_update_status(struct server *s)
4614 free_trash_chunk(tmptrash);
4615 tmptrash = NULL;
4616 }
4617-
4618- if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4619- set_backend_down(s->proxy);
4620 }
4621 else if (((s->cur_state != SRV_ST_RUNNING) && (s->next_state == SRV_ST_RUNNING))
4622 || ((s->cur_state != SRV_ST_STARTING) && (s->next_state == SRV_ST_STARTING))) {
4623- if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) {
4624- if (s->proxy->last_change < now.tv_sec) // ignore negative times
4625- s->proxy->down_time += now.tv_sec - s->proxy->last_change;
4626- s->proxy->last_change = now.tv_sec;
4627- }
4628-
4629- if (s->cur_state == SRV_ST_STOPPED && s->last_change < now.tv_sec) // ignore negative times
4630- s->down_time += now.tv_sec - s->last_change;
4631
4632- s->last_change = now.tv_sec;
4633 if (s->next_state == SRV_ST_STARTING && s->warmup)
4634 task_schedule(s->warmup, tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20))));
4635
4636@@ -5037,9 +5022,6 @@ static void srv_update_status(struct server *s)
4637 free_trash_chunk(tmptrash);
4638 tmptrash = NULL;
4639 }
4640-
4641- if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4642- set_backend_down(s->proxy);
4643 }
4644 else if (s->cur_eweight != s->next_eweight) {
4645 /* now propagate the status change to any LB algorithms */
4646@@ -5053,9 +5035,6 @@ static void srv_update_status(struct server *s)
4647 if (px->lbprm.set_server_status_down)
4648 px->lbprm.set_server_status_down(s);
4649 }
4650-
4651- if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4652- set_backend_down(s->proxy);
4653 }
4654
4655 s->next_admin = next_admin;
4656@@ -5093,13 +5072,9 @@ static void srv_update_status(struct server *s)
4657 free_trash_chunk(tmptrash);
4658 tmptrash = NULL;
4659 }
4660- /* commit new admin status */
4661-
4662- s->cur_admin = s->next_admin;
4663 }
4664 else { /* server was still running */
4665 check->health = 0; /* failure */
4666- s->last_change = now.tv_sec;
4667
4668 s->next_state = SRV_ST_STOPPED;
4669 if (s->proxy->lbprm.set_server_status_down)
4670@@ -5134,9 +5109,6 @@ static void srv_update_status(struct server *s)
4671 free_trash_chunk(tmptrash);
4672 tmptrash = NULL;
4673 }
4674- if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4675- set_backend_down(s->proxy);
4676-
4677 s->counters.down_trans++;
4678 }
4679 }
4680@@ -5156,7 +5128,6 @@ static void srv_update_status(struct server *s)
4681 * that the server might still be in drain mode, which is naturally dealt
4682 * with by the lower level functions.
4683 */
4684-
4685 if (s->check.state & CHK_ST_ENABLED) {
4686 s->check.state &= ~CHK_ST_PAUSED;
4687 check->health = check->rise; /* start OK but check immediately */
4688@@ -5169,7 +5140,6 @@ static void srv_update_status(struct server *s)
4689 s->next_state = SRV_ST_STOPPING;
4690 }
4691 else {
4692- s->last_change = now.tv_sec;
4693 s->next_state = SRV_ST_STARTING;
4694 if (s->slowstart > 0) {
4695 if (s->warmup)
4696@@ -5227,11 +5197,6 @@ static void srv_update_status(struct server *s)
4697 px->lbprm.set_server_status_down(s);
4698 }
4699
4700- if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4701- set_backend_down(s->proxy);
4702- else if (!prev_srv_count && (s->proxy->srv_bck || s->proxy->srv_act))
4703- s->proxy->last_change = now.tv_sec;
4704-
4705 /* If the server is set with "on-marked-up shutdown-backup-sessions",
4706 * and it's not a backup server and its effective weight is > 0,
4707 * then it can accept new connections, so we shut down all streams
4708@@ -5301,15 +5266,12 @@ static void srv_update_status(struct server *s)
4709 }
4710 }
4711 /* don't report anything when leaving drain mode and remaining in maintenance */
4712-
4713- s->cur_admin = s->next_admin;
4714 }
4715
4716 if (!(s->next_admin & SRV_ADMF_MAINT)) {
4717 if (!(s->cur_admin & SRV_ADMF_DRAIN) && (s->next_admin & SRV_ADMF_DRAIN)) {
4718 /* drain state is applied only if not yet in maint */
4719
4720- s->last_change = now.tv_sec;
4721 if (px->lbprm.set_server_status_down)
4722 px->lbprm.set_server_status_down(s);
4723
4724@@ -5337,26 +5299,14 @@ static void srv_update_status(struct server *s)
4725 free_trash_chunk(tmptrash);
4726 tmptrash = NULL;
4727 }
4728-
4729- if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4730- set_backend_down(s->proxy);
4731 }
4732 else if ((s->cur_admin & SRV_ADMF_DRAIN) && !(s->next_admin & SRV_ADMF_DRAIN)) {
4733 /* OK completely leaving drain mode */
4734- if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) {
4735- if (s->proxy->last_change < now.tv_sec) // ignore negative times
4736- s->proxy->down_time += now.tv_sec - s->proxy->last_change;
4737- s->proxy->last_change = now.tv_sec;
4738- }
4739-
4740- if (s->last_change < now.tv_sec) // ignore negative times
4741- s->down_time += now.tv_sec - s->last_change;
4742- s->last_change = now.tv_sec;
4743 server_recalc_eweight(s, 0);
4744
4745 tmptrash = alloc_trash_chunk();
4746 if (tmptrash) {
4747- if (!(s->next_admin & SRV_ADMF_FDRAIN)) {
4748+ if (s->cur_admin & SRV_ADMF_FDRAIN) {
4749 chunk_printf(tmptrash,
4750 "%sServer %s/%s is %s (leaving forced drain)",
4751 s->flags & SRV_F_BACKUP ? "Backup " : "",
4752@@ -5420,15 +5370,38 @@ static void srv_update_status(struct server *s)
4753 free_trash_chunk(tmptrash);
4754 tmptrash = NULL;
4755 }
4756-
4757- /* commit new admin status */
4758-
4759- s->cur_admin = s->next_admin;
4760 }
4761 }
4762
4763 /* Re-set log strings to empty */
4764 *s->adm_st_chg_cause = 0;
4765+
4766+ /* explicitly commit state changes (even if it was already applied implicitly
4767+ * by some lb state change function), so we don't miss anything
4768+ */
4769+ srv_lb_commit_status(s);
4770+
4771+ /* check if server stats must be updated due the the server state change */
4772+ if (srv_prev_state != s->cur_state) {
4773+ if (srv_prev_state == SRV_ST_STOPPED) {
4774+ /* server was down and no longer is */
4775+ if (s->last_change < now.tv_sec) // ignore negative times
4776+ s->down_time += now.tv_sec - s->last_change;
4777+ }
4778+ s->last_change = now.tv_sec;
4779+ }
4780+
4781+ /* check if backend stats must be updated due to the server state change */
4782+ if (prev_srv_count && s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
4783+ set_backend_down(s->proxy); /* backend going down */
4784+ else if (!prev_srv_count && (s->proxy->srv_bck || s->proxy->srv_act)) {
4785+ /* backend was down and is back up again:
4786+ * no helper function, updating last_change and backend downtime stats
4787+ */
4788+ if (s->proxy->last_change < now.tv_sec) // ignore negative times
4789+ s->proxy->down_time += now.tv_sec - s->proxy->last_change;
4790+ s->proxy->last_change = now.tv_sec;
4791+ }
4792 }
4793
4794 struct task *srv_cleanup_toremove_conns(struct task *task, void *context, unsigned int state)
4795diff --git a/src/server_state.c b/src/server_state.c
4796index 18d9cd6..4ef5167 100644
4797--- a/src/server_state.c
4798+++ b/src/server_state.c
4799@@ -322,7 +322,7 @@ static void srv_state_srv_update(struct server *srv, int version, char **params)
4800 srv_adm_set_drain(srv);
4801 }
4802
4803- srv->last_change = date.tv_sec - srv_last_time_change;
4804+ srv->last_change = now.tv_sec - srv_last_time_change;
4805 srv->check.status = srv_check_status;
4806 srv->check.result = srv_check_result;
4807
4808diff --git a/src/sink.c b/src/sink.c
4809index b7f111c..4d811eb 100644
4810--- a/src/sink.c
4811+++ b/src/sink.c
4812@@ -162,10 +162,13 @@ struct sink *sink_new_buf(const char *name, const char *desc, enum log_fmt fmt,
4813 * array <msg> to sink <sink>. Formatting according to the sink's preference is
4814 * done here. Lost messages are NOT accounted for. It is preferable to call
4815 * sink_write() instead which will also try to emit the number of dropped
4816- * messages when there are any. It returns >0 if it could write anything,
4817- * <=0 otherwise.
4818+ * messages when there are any. It will stop writing at <maxlen> instead of
4819+ * sink->maxlen if <maxlen> is positive and inferior to sink->maxlen.
4820+ *
4821+ * It returns >0 if it could write anything, <=0 otherwise.
4822 */
4823- ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
4824+ ssize_t __sink_write(struct sink *sink, size_t maxlen,
4825+ const struct ist msg[], size_t nmsg,
4826 int level, int facility, struct ist *metadata)
4827 {
4828 struct ist *pfx = NULL;
4829@@ -177,11 +180,13 @@ struct sink *sink_new_buf(const char *name, const char *desc, enum log_fmt fmt,
4830 pfx = build_log_header(sink->fmt, level, facility, metadata, &npfx);
4831
4832 send:
4833+ if (!maxlen)
4834+ maxlen = ~0;
4835 if (sink->type == SINK_TYPE_FD) {
4836- return fd_write_frag_line(sink->ctx.fd, sink->maxlen, pfx, npfx, msg, nmsg, 1);
4837+ return fd_write_frag_line(sink->ctx.fd, MIN(maxlen, sink->maxlen), pfx, npfx, msg, nmsg, 1);
4838 }
4839 else if (sink->type == SINK_TYPE_BUFFER) {
4840- return ring_write(sink->ctx.ring, sink->maxlen, pfx, npfx, msg, nmsg);
4841+ return ring_write(sink->ctx.ring, MIN(maxlen, sink->maxlen), pfx, npfx, msg, nmsg);
4842 }
4843 return 0;
4844 }
4845@@ -223,7 +228,7 @@ int sink_announce_dropped(struct sink *sink, int facility)
4846 metadata[LOG_META_PID] = ist2(pidstr, strlen(pidstr));
4847 }
4848
4849- if (__sink_write(sink, msgvec, 1, LOG_NOTICE, facility, metadata) <= 0)
4850+ if (__sink_write(sink, 0, msgvec, 1, LOG_NOTICE, facility, metadata) <= 0)
4851 return 0;
4852 /* success! */
4853 HA_ATOMIC_SUB(&sink->ctx.dropped, dropped);
4854@@ -829,7 +834,7 @@ int cfg_parse_ring(const char *file, int linenum, char **args, int kwm)
4855 if (size < cfg_sink->ctx.ring->buf.size) {
4856 ha_warning("parsing [%s:%d] : ignoring new size '%llu' that is smaller than current size '%llu' for ring '%s'.\n",
4857 file, linenum, (ullong)size, (ullong)cfg_sink->ctx.ring->buf.size, cfg_sink->name);
4858- err_code |= ERR_ALERT | ERR_FATAL;
4859+ err_code |= ERR_WARN;
4860 goto err;
4861 }
4862
4863@@ -1023,8 +1028,8 @@ struct sink *sink_new_from_logsrv(struct logsrv *logsrv)
4864 /* the servers are linked backwards
4865 * first into proxy
4866 */
4867- p->srv = srv;
4868 srv->next = p->srv;
4869+ p->srv = srv;
4870
4871 /* allocate sink_forward_target descriptor */
4872 sft = calloc(1, sizeof(*sft));
4873@@ -1074,6 +1079,9 @@ struct sink *sink_new_from_logsrv(struct logsrv *logsrv)
4874
4875 return sink;
4876 error:
4877+ if (srv)
4878+ free_server(srv);
4879+
4880 if (p) {
4881 if (p->id)
4882 free(p->id);
4883@@ -1083,16 +1091,6 @@ error:
4884 free(p);
4885 }
4886
4887- if (srv) {
4888- if (srv->id)
4889- free(srv->id);
4890- if (srv->conf.file)
4891- free((void *)srv->conf.file);
4892- if (srv->per_thr)
4893- free(srv->per_thr);
4894- free(srv);
4895- }
4896-
4897 if (sft)
4898 free(sft);
4899
4900@@ -1125,7 +1123,7 @@ int cfg_post_parse_ring()
4901 ha_warning("ring '%s' event max length '%u' exceeds size, forced to size '%lu'.\n",
4902 cfg_sink->name, cfg_sink->maxlen, (unsigned long)b_size(&cfg_sink->ctx.ring->buf));
4903 cfg_sink->maxlen = b_size(&cfg_sink->ctx.ring->buf);
4904- err_code |= ERR_ALERT;
4905+ err_code |= ERR_WARN;
4906 }
4907
4908 /* prepare forward server descriptors */
4909@@ -1151,11 +1149,16 @@ int cfg_post_parse_ring()
4910 if (!ring_attach(cfg_sink->ctx.ring)) {
4911 ha_alert("server '%s' sets too many watchers > 255 on ring '%s'.\n", srv->id, cfg_sink->name);
4912 err_code |= ERR_ALERT | ERR_FATAL;
4913+ ha_free(&sft);
4914+ break;
4915 }
4916 cfg_sink->sft = sft;
4917 srv = srv->next;
4918 }
4919- sink_init_forward(cfg_sink);
4920+ if (sink_init_forward(cfg_sink) == 0) {
4921+ ha_alert("error when trying to initialize sink buffer forwarding.\n");
4922+ err_code |= ERR_ALERT | ERR_FATAL;
4923+ }
4924 }
4925 }
4926 cfg_sink = NULL;
4927@@ -1284,14 +1287,21 @@ static void sink_init()
4928 static void sink_deinit()
4929 {
4930 struct sink *sink, *sb;
4931+ struct sink_forward_target *sft_next;
4932
4933 list_for_each_entry_safe(sink, sb, &sink_list, sink_list) {
4934 if (sink->type == SINK_TYPE_BUFFER)
4935 ring_free(sink->ctx.ring);
4936 LIST_DELETE(&sink->sink_list);
4937 task_destroy(sink->forward_task);
4938+ free_proxy(sink->forward_px);
4939 free(sink->name);
4940 free(sink->desc);
4941+ while (sink->sft) {
4942+ sft_next = sink->sft->next;
4943+ free(sink->sft);
4944+ sink->sft = sft_next;
4945+ }
4946 free(sink);
4947 }
4948 }
4949diff --git a/src/sock_inet.c b/src/sock_inet.c
4950index fb69981..7523617 100644
4951--- a/src/sock_inet.c
4952+++ b/src/sock_inet.c
4953@@ -313,6 +313,24 @@ int sock_inet_bind_receiver(struct receiver *rx, char **errmsg)
4954 }
4955 }
4956
4957+ if (ext && fd < global.maxsock && fdtab[fd].owner) {
4958+ /* This FD was already bound so this means that it was already
4959+ * known and registered before parsing, hence it's an inherited
4960+ * FD. The only reason why it's already known here is that it
4961+ * has been registered multiple times (multiple listeners on the
4962+ * same, or a "shards" directive on the line). There cannot be
4963+ * multiple listeners on one FD but at least we can create a
4964+ * new one from the original one. We won't reconfigure it,
4965+ * however, as this was already done for the first one.
4966+ */
4967+ fd = dup(fd);
4968+ if (fd == -1) {
4969+ err |= ERR_RETRYABLE | ERR_ALERT;
4970+ memprintf(errmsg, "cannot dup() receiving socket (%s)", strerror(errno));
4971+ goto bind_return;
4972+ }
4973+ }
4974+
4975 if (fd >= global.maxsock) {
4976 err |= ERR_FATAL | ERR_ABORT | ERR_ALERT;
4977 memprintf(errmsg, "not enough free sockets (raise '-n' parameter)");
4978diff --git a/src/sock_unix.c b/src/sock_unix.c
4979index 9913f4f..d62c164 100644
4980--- a/src/sock_unix.c
4981+++ b/src/sock_unix.c
4982@@ -94,7 +94,21 @@ int sock_unix_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_st
4983
4984 /* Now we have a difference. It's OK if they are within or after a
4985 * sequence of digits following a dot, and are followed by ".tmp".
4986+ *
4987+ * make sure to perform the check against tempname if the compared
4988+ * string is in "final" format (does not end with ".XXXX.tmp").
4989+ *
4990+ * Examples:
4991+ * /tmp/test matches with /tmp/test.1822.tmp
4992+ * /tmp/test.1822.tmp matches with /tmp/test.XXXX.tmp
4993 */
4994+ if (au->sun_path[idx] == 0 || bu->sun_path[idx] == 0) {
4995+ if (au->sun_path[idx] == '.' || bu->sun_path[idx] == '.')
4996+ dot = idx; /* try to match against temp path */
4997+ else
4998+ return -1; /* invalid temp path */
4999+ }
5000+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches