Merge ~athos/ubuntu/+source/haproxy:MRE-jammy into ubuntu/+source/haproxy:ubuntu/jammy-devel
- Git
- lp:~athos/ubuntu/+source/haproxy
- MRE-jammy
- Merge into 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) |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| git-ubuntu bot | Approve | ||
| Sergio Durigan Junior (community) | Approve | ||
| Canonical Server Reporter | Pending | ||
|
Review via email:
|
|||
Commit message
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:/
DEP8 test results:
- haproxy/
+ ✅ 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
| 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
| 1 | diff --git a/.cirrus.yml b/.cirrus.yml |
| 2 | index 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 |
| 14 | diff --git a/.github/matrix.py b/.github/matrix.py |
| 15 | index 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: |
| 29 | diff --git a/.github/workflows/cross-zoo.yml b/.github/workflows/cross-zoo.yml |
| 30 | index 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 |
| 42 | diff --git a/.github/workflows/vtest.yml b/.github/workflows/vtest.yml |
| 43 | index 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 |
| 51 | diff --git a/.gitignore b/.gitignore |
| 52 | index 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 |
| 63 | diff --git a/CHANGELOG b/CHANGELOG |
| 64 | index 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 |
| 223 | diff --git a/Makefile b/Makefile |
| 224 | index 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),) |
| 236 | diff --git a/SUBVERS b/SUBVERS |
| 237 | index f042fd0..4c00e4a 100644 |
| 238 | --- a/SUBVERS |
| 239 | +++ b/SUBVERS |
| 240 | @@ -1,2 +1,2 @@ |
| 241 | --f8e3218 |
| 242 | +-d175670 |
| 243 | |
| 244 | diff --git a/VERDATE b/VERDATE |
| 245 | index 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 |
| 253 | diff --git a/VERSION b/VERSION |
| 254 | index 76cb51e..0cb980f 100644 |
| 255 | --- a/VERSION |
| 256 | +++ b/VERSION |
| 257 | @@ -1 +1 @@ |
| 258 | -2.4.22 |
| 259 | +2.4.24 |
| 260 | diff --git a/debian/changelog b/debian/changelog |
| 261 | index 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 |
| 287 | diff --git a/debian/patches/CVE-2023-40225-1.patch b/debian/patches/CVE-2023-40225-1.patch |
| 288 | deleted file mode 100644 |
| 289 | index 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 |
| 558 | diff --git a/debian/patches/CVE-2023-40225-2.patch b/debian/patches/CVE-2023-40225-2.patch |
| 559 | deleted file mode 100644 |
| 560 | index 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; |
| 707 | diff --git a/debian/patches/series b/debian/patches/series |
| 708 | index 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 |
| 717 | diff --git a/dev/hpack/decode.c b/dev/hpack/decode.c |
| 718 | index 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> |
| 730 | diff --git a/doc/configuration.txt b/doc/configuration.txt |
| 731 | index 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 |
| 1225 | diff --git a/doc/design-thoughts/config-language.txt b/doc/design-thoughts/config-language.txt |
| 1226 | index 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 |
| 1241 | diff --git a/doc/internals/http-parsing.txt b/doc/internals/http-parsing.txt |
| 1242 | index 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 | |
| 1259 | diff --git a/doc/management.txt b/doc/management.txt |
| 1260 | index 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 |
| 1272 | diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h |
| 1273 | index 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) { |
| 1302 | diff --git a/include/haproxy/h2.h b/include/haproxy/h2.h |
| 1303 | index 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 | |
| 1315 | diff --git a/include/haproxy/hlua.h b/include/haproxy/hlua.h |
| 1316 | index 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 | |
| 1327 | diff --git a/include/haproxy/http.h b/include/haproxy/http.h |
| 1328 | index 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 | |
| 1357 | diff --git a/include/haproxy/http_ana-t.h b/include/haproxy/http_ana-t.h |
| 1358 | index 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) */ |
| 1370 | diff --git a/include/haproxy/http_rules.h b/include/haproxy/http_rules.h |
| 1371 | index 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 | |
| 1382 | diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h |
| 1383 | index 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 */ |
| 1405 | diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h |
| 1406 | index 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 |
| 1466 | diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h |
| 1467 | index 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 */ |
| 1478 | diff --git a/include/haproxy/server.h b/include/haproxy/server.h |
| 1479 | index 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 | |
| 1490 | diff --git a/include/haproxy/sink.h b/include/haproxy/sink.h |
| 1491 | index 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: |
| 1523 | diff --git a/include/haproxy/spoe-t.h b/include/haproxy/spoe-t.h |
| 1524 | index 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 */ |
| 1535 | diff --git a/include/haproxy/stick_table.h b/include/haproxy/stick_table.h |
| 1536 | index 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); |
| 1548 | diff --git a/include/haproxy/time.h b/include/haproxy/time.h |
| 1549 | index 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; |
| 1562 | diff --git a/include/import/ist.h b/include/import/ist.h |
| 1563 | index 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. |
| 1620 | diff --git a/reg-tests/cache/caching_rules.vtc b/reg-tests/cache/caching_rules.vtc |
| 1621 | index 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 |
| 1732 | diff --git a/reg-tests/http-messaging/h1_to_h1.vtc b/reg-tests/http-messaging/h1_to_h1.vtc |
| 1733 | index 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 |
| 1766 | diff --git a/reg-tests/http-messaging/h2_to_h1.vtc b/reg-tests/http-messaging/h2_to_h1.vtc |
| 1767 | index 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 |
| 1856 | diff --git a/reg-tests/http-rules/h1or2_to_h1c.vtc b/reg-tests/http-rules/h1or2_to_h1c.vtc |
| 1857 | index 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" \ |
| 1930 | diff --git a/reg-tests/http-rules/normalize_uri.vtc b/reg-tests/http-rules/normalize_uri.vtc |
| 1931 | index 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 |
| 1972 | diff --git a/reg-tests/log/log_uri.vtc b/reg-tests/log/log_uri.vtc |
| 1973 | index 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 { |
| 1985 | diff --git a/scripts/build-ssl.sh b/scripts/build-ssl.sh |
| 1986 | index 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 | |
| 1998 | diff --git a/scripts/publish-release b/scripts/publish-release |
| 1999 | index 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 |
| 2012 | diff --git a/src/backend.c b/src/backend.c |
| 2013 | index 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; |
| 2025 | diff --git a/src/cache.c b/src/cache.c |
| 2026 | index 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)); |
| 2066 | diff --git a/src/cfgparse-tcp.c b/src/cfgparse-tcp.c |
| 2067 | index 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 | } |
| 2078 | diff --git a/src/cfgparse.c b/src/cfgparse.c |
| 2079 | index 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 |
| 2113 | diff --git a/src/channel.c b/src/channel.c |
| 2114 | index 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; |
| 2126 | diff --git a/src/check.c b/src/check.c |
| 2127 | index 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 | |
| 2159 | diff --git a/src/chunk.c b/src/chunk.c |
| 2160 | index 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 |
| 2186 | diff --git a/src/debug.c b/src/debug.c |
| 2187 | index 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, |
| 2232 | diff --git a/src/dns.c b/src/dns.c |
| 2233 | index 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); |
| 2305 | diff --git a/src/filters.c b/src/filters.c |
| 2306 | index 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", |
| 2322 | diff --git a/src/flt_spoe.c b/src/flt_spoe.c |
| 2323 | index 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); |
| 2426 | diff --git a/src/h1.c b/src/h1.c |
| 2427 | index 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; |
| 2517 | diff --git a/src/h1_htx.c b/src/h1_htx.c |
| 2518 | index 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) { |
| 2530 | diff --git a/src/h2.c b/src/h2.c |
| 2531 | index 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) */ |
| 2624 | diff --git a/src/haproxy.c b/src/haproxy.c |
| 2625 | index 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; |
| 2695 | diff --git a/src/hlua.c b/src/hlua.c |
| 2696 | index 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; |
| 3155 | diff --git a/src/http.c b/src/http.c |
| 3156 | index 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"; |
| 3168 | diff --git a/src/http_ana.c b/src/http_ana.c |
| 3169 | index 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 | /* |
| 3260 | diff --git a/src/http_rules.c b/src/http_rules.c |
| 3261 | index 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 | |
| 3402 | diff --git a/src/listener.c b/src/listener.c |
| 3403 | index 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 | } |
| 3777 | diff --git a/src/log.c b/src/log.c |
| 3778 | index 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 | |
| 3899 | diff --git a/src/mjson.c b/src/mjson.c |
| 3900 | index 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 */ |
| 3921 | diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c |
| 3922 | index 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 | |
| 3934 | diff --git a/src/mux_h1.c b/src/mux_h1.c |
| 3935 | index 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 | |
| 3970 | diff --git a/src/mux_h2.c b/src/mux_h2.c |
| 3971 | index 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 */ |
| 4054 | diff --git a/src/mworker.c b/src/mworker.c |
| 4055 | index 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; |
| 4189 | diff --git a/src/namespace.c b/src/namespace.c |
| 4190 | index 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 | } |
| 4201 | diff --git a/src/proto_uxdg.c b/src/proto_uxdg.c |
| 4202 | index 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. */ |
| 4261 | diff --git a/src/proto_uxst.c b/src/proto_uxst.c |
| 4262 | index 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. */ |
| 4321 | diff --git a/src/protocol.c b/src/protocol.c |
| 4322 | index 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); |
| 4375 | diff --git a/src/proxy.c b/src/proxy.c |
| 4376 | index 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) |
| 4505 | diff --git a/src/resolvers.c b/src/resolvers.c |
| 4506 | index 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", |
| 4531 | diff --git a/src/ring.c b/src/ring.c |
| 4532 | index 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); |
| 4543 | diff --git a/src/sample.c b/src/sample.c |
| 4544 | index 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; |
| 4571 | diff --git a/src/server.c b/src/server.c |
| 4572 | index 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) |
| 4795 | diff --git a/src/server_state.c b/src/server_state.c |
| 4796 | index 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 | |
| 4808 | diff --git a/src/sink.c b/src/sink.c |
| 4809 | index 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 | } |
| 4949 | diff --git a/src/sock_inet.c b/src/sock_inet.c |
| 4950 | index 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)"); |
| 4978 | diff --git a/src/sock_unix.c b/src/sock_unix.c |
| 4979 | index 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.

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