Compare commits

..

No commits in common. "master" and "v3.4-dev13" have entirely different histories.

153 changed files with 1603 additions and 4210 deletions

18
.cirrus.yml Normal file
View file

@ -0,0 +1,18 @@
FreeBSD_task:
freebsd_instance:
matrix:
image_family: freebsd-14-3
only_if: $CIRRUS_BRANCH =~ 'master|next'
install_script:
- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2
script:
- sudo sysctl kern.corefile=/tmp/%N.%P.core
- sudo sysctl kern.sugid_coredump=1
- scripts/build-vtest.sh
- gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
- ./haproxy -vv
- ldd haproxy
test_script:
- env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel
on_failure:
debug_script: (for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done && ls /tmp/haproxy.*.core && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core)

View file

@ -1,38 +0,0 @@
name: FreeBSD
on:
push:
branches:
- master
- next
workflow_dispatch:
permissions:
contents: read
jobs:
clang:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- name: "Checkout repository"
uses: actions/checkout@v6
- name: "Build and test on FreeBSD"
uses: vmactions/freebsd-vm@v1
with:
release: "14.3"
prepare: |
pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2 python3
run: |
sysctl kern.corefile=/tmp/%N.%P.core
sysctl kern.sugid_coredump=1
scripts/build-vtest.sh
gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
./haproxy -vv
ldd haproxy
if ! env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel; then
for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done
ls /tmp/haproxy.*.core 2>/dev/null && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core
exit 1
fi

View file

@ -2,7 +2,7 @@ name: Illumos
on: on:
schedule: schedule:
- cron: "0 3 * * 1" - cron: "0 0 25 * *"
workflow_dispatch: workflow_dispatch:
permissions: permissions:

180
CHANGELOG
View file

@ -1,186 +1,6 @@
ChangeLog : ChangeLog :
=========== ===========
2026/06/03 : 3.5-dev0
- MINOR: version: mention that it's development again
2026/06/03 : 3.4.0
- BUG/MINOR: tcpcheck: Check LDAP response to not read more data than available
- BUG/MINOR: ssl-gencert: validate SNI characters to prevent SAN certificate injection
- BUG/MINOR: mux-h1: H2 preface rejection doesn't update stick-table glitches
- BUG/MEDIUM: cpu-topo: Enforce thread-hard-limit on policy
- BUG/MEDIUM: qmux: do not crash on too large record
- BUG/MEDIUM: qmux: do not crash on receiving an invalid first frame
- BUG/MINOR: qmux: reject too large initial record
- Revert "BUG/MEDIUM: dns: fix long loops in additional records parse on name failure"
- BUG/MINOR: qpack: Fix index calculation in debug functions
- BUG/MINOR: qpack: fix potential null-pointer dereference in qpack_dht_insert()
- CLEANUP: qpack: fix copy-paste typo in value Huffman debug string
- BUG/MINOR: qpack: fix sign bit mask in qpack_decode_fs_pfx()
- CLEANUP: qpack: fix copy-paste typo in value Huffman debug string for WLN
- BUG/MINOR: qpack: fix huff_dec() error handling in qpack_decode_fs()
- CLEANUP: qpack: move encoded macros to qpack-t.h to avoid duplication
- BUG/MEDIUM: quic: handle ECONNREFUSED on RX side
- BUG/MINOR: quic: Fix memory leak in quic_deallocate_dghdlrs()
- BUG/MEDIUM: lua: defer Lua VM initialisation to the first Lua config keyword
- REGTESTS: lua: fix tune.lua.openlibs in Lua reg-tests
- BUG/MINOR: mux-h2: Count padding for connection flow control on error path
- BUILD: addons: convert 51d addon to EXTRA_MAKE
- BUILD: addons: convert deviceatlas addon to EXTRA_MAKE
- BUILD: addons: convert WURFL addon to EXTRA_MAKE
- MINOR: mux_quic/flags: add missing flags
- BUG/MINOR: mux_quic: open an idle QCS on reset on BE side
- BUG/MINOR: mux_quic: fix BE conn removal on app shutdown
- BUG/MINOR: mux_quic: prevent BE reuse with an errored conn
- BUG/MINOR: quic: fix ack range node pool_free call passing wrong pointer type
- MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts
- BUG/MEDIUM: quic: reset cwnd in slow_start on persistent congestion (cubic)
- BUG/MEDIUM: quic: reset consecutive_losses on exit from recovery period (cubic)
- BUG/MINOR: quic: update drs->lost before calling on_ack_recv
- Revert "MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts"
- BUG/MEDIUM: lua: register hlua_init() as a pre-check to fix crash without Lua config
- REGTESTS: quic: disable quic/ocsp_auto_update for now
- BUG/MINOR: threads: set at least grp_max when mtpg is too small
- BUG/MEDIUM: threads: ignore max-threads-per-group when thread-groups is set
- CLEANUP: thread: indicate when max-threads-per-group is ignored
- MINOR: cpu-topo: notify when cpu-policy is ignored due to other settings
- MINOR: thread: report when thread-groups or nbthread results in less threads
- BUILD: makefile: include EXTRA_MAKE in the .build_opts construction
- BUG/MINOR: quic: Fix another buffer overflow with sockaddr_in46
- MINOR: quic: Copy sin6_flowinfo and sin6_scope_id too
- BUILD: Makefile: put EXTRA_MAKE help at the right place
- BUG/MINOR: cache: fix cache tree iteration
- BUG/MEDIUM: resolvers: Wait a bit before calling the xprt prepare_srv
- CLEANUP: addons/51degrees: initialize variables
- MINOR: addons/51degrees: handle memory allocation failures
- CLEANUP: ncbmbuf: improve handling of memory allocation errors in unit tests
- CLEANUP: admin/halog: improve handling of memory allocation errors
- DOC: internals: clarify ambiguous wording in core-principles
- DOC: internals: add a threat model definition
- DOC: add security.txt describing how to report security issues
- DOC: security: also add a note to exclude dev/ and admin/
- BUG/MEDIUM: qmux: Close connection on invalid frame
- CLEANUP: fix comment typo
- BUG/MEDIUM: h3: fix MAX_PUSH_ID handling
- BUG/MINOR: cache: Fix copy of value when parsing maxage
- BUG/MEDIUM: mux-h1: Dup connection/upgrade value to parse it when making headers
- BUG/MEDIUM: htx: Fix headers rollback on partial copy in htx_xfer()
- MINOR: deinit: release the in-memory copy of shared libs
- MINOR: debug: add -dA to dump an archive of all dependencies
- BUG/MEDIUM: ssl: Make sure the alpn length is small enough
- BUG/MINOR: applet: Commit changes into input buffer after sending HTX data
- BUG/MINOR: mux-spop: Fix possible off-by-one OOB read in spop_get_varint()
- BUG/MEDIUM: leastconn: Unlock the write lock on allocation failure
- BUG/MINOR: tasks: Increase the right niced_task counter
- BUILD: makefile: search for Lua 5.5 as well
- DEV: dev/gdb: improve ebtree pointer handling
- DEV: dev/gdb: add simple task dump
- DEV: dev/gdb: add simple thread dump
- DEV: dev/gdb: add fdtab dump
- DOC: config: add a few more explanation in http-reusee regarding sni-auto
- REGTESTS: add basic QMux tests
- BUG/MINOR: http-act: Properly handle final evaluation in pause action
- BUILD: makefile/lua: use the system's default library before all other variants
- BUG/MINOR: startup: unbreak chroot with CAP_SYS_CHROOT
- BUG/MINOR: haterm: do not try to bind QUIC when not supported
- BUG/MINOR: haterm: also apply the tcp-bind-opts to clear TCP "bind" lines
- CLEANUP: haterm: do not try to bind to SSL when not built in
- MINOR: haterm: enable ktls on the SSL bind line when supported
- CI: github: replace cirrus by a vmactions/freebsd-vm job
- BUILD: makefile: fix build error with GNU make 4.2.1 and /bin/dash
- BUG/MEDIUM: channel: Fix condition to know if a channel may send
- BUG/MEDIUM: vars: Properly eval set-var-fmt action for emtpy log-format string
- CI: github: run illumos job weekly on Mondays at 03:00 instead of monthly
- BUG/MEDIUM: stream: Don't use small buffer on queuing with a request data filter
- BUG/MINOR: jwe: don't write randoms past MAX_DECRYPTED_CEK_LEN in RSA_PKCS1_PADDING
- BUG/MEDIUM: chunk: do not rely on small trash by default for expressions
- CLEANUP: map: always test pat->ref in sample_conv_map_key()
- DEV: patchbot: prepare for new version 3.5-dev
- MINOR: version: mention that it's 3.4 LTS now.
2026/05/26 : 3.4-dev14
- MINOR: config: shm-stats-file is no longer experimental
- BUILD: proxy: unstatify the proxies_del_lock to avoid a warning without threads
- BUG/MEDIUM: net_helper: fix a remaining possibly infinite loop in converters
- MINOR: ssl_sock: remove unneeded check on QMux flags
- MINOR: connection: define xprt_add_l6hs()
- MINOR: xprt_qmux: define default value for get_alpn
- MINOR: connection: define mask CO_FL_WAIT_XPRT_L6
- MINOR: session: support QMux in clear on FE side
- MINOR: backend: support QMux in clear for BE side
- BUG/MINOR: ocsp: Manage date too far away in the future
- MINOR: mux_quic: handle STOP_SENDING in QMux
- MINOR: mux_quic: handle MAX_STREAMS for uni stream in QMux
- MINOR: mux_quic: do not crash on unhandled QMux frame reception
- BUG/MEDIUM: applet: Properly handle receives of size 0
- BUG/MEDIUM: resolvers: Fix test on dn label size in resolv_dn_label_to_str()
- BUG/MEDIUM: ssl-gencert: Unlock LRU cache if failing to generate certificate
- BUG/MINOR: quic: fix ODCID lookup from derived value
- BUG/MEDIUM: dict: hold lock while decrementing refcount in dict_entry_unref
- BUG/MINOR: tcpchecks: Limit parsing of agent-check reply to the buffer
- BUG/MEDIUM: hlua: Fix integer underflow when receiving line from lua cosocket
- BUG/MEDIUM: cli: Fix parsing of pattern finishing a command payload
- BUG/MEDIUM: acme: NUL terminate response buffer before PEM parsing
- BUILD: intops: mask the fail value in array_size_or_fail()
- BUG/MEDIUM: log-forward: make sure the month is unsigned
- BUG/MEDIUM: regex: allocate a large enough pcre2 match for all matches
- BUG/MEDIUM: tcpcheck/spoe: bound the SPOP error code to valid values
- BUG/MEDIUM: cache: fix a refcount leak for missed secondary entries
- BUG/MINOR: log: free logformat expr on compile failure in cfg_parse_log_profile
- BUG/MINOR: resolvers: fix room for trailing zero in resolv_dn_label_to_str()
- BUG/MINOR: resolvers: fix risk of appending garbage past the domain name
- BUG/MINOR: mux-h2: validate HEADERS frame length before reading stream dep
- BUG/MINOR: log: look for the end of priority before the end of the buffer
- BUG/MINOR: dict: fix refcount race on insert collision
- BUG/MINOR: init: use more than ha_random64() for the cluster secret
- BUG/MINOR: sample: limit the be2hex converter's chunk size
- CLEANUP: resolvers: use read_n32() instead of open-coded big-endian read
- CLEANUP: resolvers: remove pool_free(NULL) in SRV additional record matching
- CLEANUP: resolvers: fix comment typos and wrong filenames in file headers
- BUG/MINOR: haterm: fix the random suffix multiplication
- MINOR: haterm: enable h3 for TCP bindings
- MINOR: haterm: do not emit a warning when not using SSL
- BUG/MEDIUM: h1: drop headers whose names contain invalid chars
- BUG/MEDIUM: h1: limit status codes to 3 digits by default
- BUG/MEDIUM: cache: always verify the primary hash in get_secondary_entry()
- BUG/MINOR: cache: also recognize directives in the form "token="
- BUG/MINOR: resolvers: relax size checks in authority record parsing
- BUG/MINOR: sample: request an extra output byte for the url_dec converter
- BUG/MINOR: http-fetch: check against the whole token in get_http_auth()
- BUG/MEDIUM: acme: protect against risk of null-deref on connection failure
- BUG/MINOR: http-ext: always check remaining data when reading rfc7239 nodeport
- BUG/MINOR: base64: return empty string for empty input in base64dec()
- BUG/MINOR: payload: fix the handshake length bounds check smp_client_hello_parse()
- BUG/MINOR: ssl-hello: make use of the null-terminated servername
- BUG/MINOR: resolvers: switch to a better PRNG for query IDs
- BUG/MINOR: addons/51d: NUL-terminate headers before passing them to Trie API
- BUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output
- MINOR: tools: provide a function to generate a hashed random pair
- MEDIUM: init: fall back to ha_random64_pair_hashed() for the cluster secret
- MEDIUM: tools: use the hashed random pair for UUID generation
- MEDIUM: h1: use ha_random64_pair_hashed() for the WebSocket key
- MEDIUM: quic: use ha_random64_pair_hashed() to generate the QUIC retry tokens
- MEDIUM: tools: switch the main PRNG to a thread-local xoshiro256**
- BUG/MEDIUM: h3: reject client push stream
- BUG/MINOR: h3: reject server push stream
- BUG/MINOR: h3: reject client CANCEL_PUSH frame
- BUG/MINOR: h3: adjust error on PUSH_PROMISE frame reception
- BUG/MINOR: h3: reject server MAX_PUSH_ID frame
- BUG/MEDIUM: auth: fix unconfigured password NULL deref
- BUG/MINOR: h3: add missing break on rcv_buf()
- BUG/MINOR: hlua: prevent Lua from passing CR/LF/NUL in HTTP headers
- BUG/MINOR: qmux: do not crash on frame parsing issue
- BUG/MINOR: quic: reject packet too short for HP decryption
- BUG/MINOR: jwe: enforce GCM tag length to 128 bits
- BUG/MEDIUM: jwe: substitute random CEK on RSA1_5 decryption failure per RFC 7516 #11.5
- BUG/MEDIUM: mux-fcgi: reject stream ID 0 for application records
- MINOR: http: Add function to remove all occurrences of a value in a header
- MINOR: h1: Add a H1M flag to specify a non-empty 'Upgrade:' header was parsed
- BUG/MEDIUM: h1-htx: Sanitize parsing to properly handle upgrade requests
- BUG/MINOR: mux-fcgi: Use relative offset to compute contig data in demux buf
- BUG/MINOR: mux-spop: Use relative offset to compute contig data in demux buf
- CLEANUP: mux-fcgi/mux-spop: Remove copy/pasted comment about slow realign
2026/05/20 : 3.4-dev13 2026/05/20 : 3.4-dev13
- BUG/MINOR: backend: correct parameter value validation in get_server_ph_post() - BUG/MINOR: backend: correct parameter value validation in get_server_ph_post()
- BUG/MINOR: config/dns: properly fail on duplicate nameserver name detection - BUG/MINOR: config/dns: properly fail on duplicate nameserver name detection

View file

@ -11,7 +11,7 @@ this task seriously and are doing a good job at backporting important fixes.
If for any reason you would prefer a different version than the one packaged If for any reason you would prefer a different version than the one packaged
for your system, you want to be certain to have all the fixes or to get some for your system, you want to be certain to have all the fixes or to get some
commercial support, other choices are available at https://www.haproxy.com/. commercial support, other choices are available at http://www.haproxy.com/.
Areas covered in this document Areas covered in this document
@ -426,9 +426,9 @@ Lua is an embedded programming language supported by HAProxy to provide more
advanced scripting capabilities. Only versions 5.3 and above are supported. advanced scripting capabilities. Only versions 5.3 and above are supported.
In order to enable Lua support, please specify "USE_LUA=1" on the command line. In order to enable Lua support, please specify "USE_LUA=1" on the command line.
Some systems provide this library under various names to avoid conflicts with Some systems provide this library under various names to avoid conflicts with
previous versions. By default, HAProxy looks for "lua5.5", "lua55", "lua5.4", previous versions. By default, HAProxy looks for "lua5.4", "lua54", "lua5.3",
"lua54", "lua5.3", "lua53", "lua". If your system uses a different naming, you "lua53", "lua". If your system uses a different naming, you may need to set the
may need to set the library name in the "LUA_LIB_NAME" variable. library name in the "LUA_LIB_NAME" variable.
If Lua is not provided on your system, it can be very simply built locally. It If Lua is not provided on your system, it can be very simply built locally. It
can be downloaded from https://www.lua.org/, extracted and built, for example : can be downloaded from https://www.lua.org/, extracted and built, for example :

View file

@ -61,6 +61,7 @@
# USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init # USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init
# USE_THREAD_DUMP : use the more advanced thread state dump system. Automatic. # USE_THREAD_DUMP : use the more advanced thread state dump system. Automatic.
# USE_OT : enable the OpenTracing filter # USE_OT : enable the OpenTracing filter
# EXTRA_MAKE : space-separated list of external addons using a Makefile.inc
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only. # USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
# USE_LIBATOMIC : force to link with/without libatomic. Automatic. # USE_LIBATOMIC : force to link with/without libatomic. Automatic.
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours # USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
@ -94,7 +95,6 @@
# SILENT_DEFINE may be used to specify other defines which will not be # SILENT_DEFINE may be used to specify other defines which will not be
# reported by "haproxy -vv". # reported by "haproxy -vv".
# EXTRA is used to force building or not building some extra tools. # EXTRA is used to force building or not building some extra tools.
# EXTRA_MAKE space-separated list of external addons using a Makefile.inc
# DESTDIR is not set by default and is used for installation only. # DESTDIR is not set by default and is used for installation only.
# It might be useful to set DESTDIR if you want to install haproxy # It might be useful to set DESTDIR if you want to install haproxy
# in a sandbox. # in a sandbox.
@ -124,7 +124,7 @@
# LUA_LIB : force the lib path to lua # LUA_LIB : force the lib path to lua
# LUA_INC : force the include path to lua # LUA_INC : force the include path to lua
# LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of # LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of
# priority: lua5.5, lua55, lua5.4, lua54, lua5.3, lua53, lua). # priority : lua5.4, lua54, lua5.3, lua53, lua).
# OT_DEBUG : compile the OpenTracing filter in debug mode # OT_DEBUG : compile the OpenTracing filter in debug mode
# OT_INC : force the include path to libopentracing-c-wrapper # OT_INC : force the include path to libopentracing-c-wrapper
# OT_LIB : force the lib path to libopentracing-c-wrapper # OT_LIB : force the lib path to libopentracing-c-wrapper
@ -684,15 +684,15 @@ OPTIONS_OBJS += src/quic_openssl_compat.o
endif endif
ifneq ($(USE_LUA:0=),) ifneq ($(USE_LUA:0=),)
check_lua_inc = $(shell if [ ! -e /usr/include/lua.h -a -e $(2)$(1)/lua.h ]; then echo $(2)$(1); fi;) check_lua_inc = $(shell if [ -d $(2)$(1) ]; then echo $(2)$(1); fi;)
LUA_INC := $(firstword $(foreach lib,lua5.5 lua55 lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/"))) LUA_INC := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
check_lua_lib = $(shell echo "int main(){}" | $(CC) $(if $(LUA_INC),-I$(LUA_INC)) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1)) check_lua_lib = $(shell echo "int main(){}" | $(CC) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1))
LUA_LD_FLAGS := -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic) $(if $(LUA_LIB),-L$(LUA_LIB)) LUA_LD_FLAGS := -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic) $(if $(LUA_LIB),-L$(LUA_LIB))
# Try to automatically detect the Lua library if not set # Try to automatically detect the Lua library if not set
ifeq ($(LUA_LIB_NAME),) ifeq ($(LUA_LIB_NAME),)
LUA_LIB_NAME := $(firstword $(foreach lib,lua lua5.5 lua55 lua5.4 lua54 lua5.3 lua53,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS)))) LUA_LIB_NAME := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
endif endif
# Lua lib name must be set now (forced/detected above) # Lua lib name must be set now (forced/detected above)
@ -722,15 +722,70 @@ ifneq ($(USE_PROMEX:0=),)
endif endif
ifneq ($(USE_DEVICEATLAS:0=),) ifneq ($(USE_DEVICEATLAS:0=),)
EXTRA_MAKE += addons/deviceatlas # Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
# can be used to disable the cache support if needed (this also removes the necessity of having
# a C++ toolchain installed).
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
include addons/deviceatlas/Makefile.inc
OPTIONS_OBJS += addons/deviceatlas/da.o
endif endif
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
# regardless of the version since they are mutually exclusive. The version
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
51DEGREES_INC = $(51DEGREES_SRC)
51DEGREES_LIB = $(51DEGREES_SRC)
51DEGREES_VER = 3
ifneq ($(USE_51DEGREES:0=),) ifneq ($(USE_51DEGREES:0=),)
EXTRA_MAKE += addons/51degrees ifeq ($(51DEGREES_VER),4) # v4 here
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
endif
USE_LIBATOMIC = implicit
endif # 51DEGREES_VER==4
ifeq ($(51DEGREES_VER),3) # v3 here
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
else
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
endif
else
ifneq ($(51DEGREES_VER),4)
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
endif
endif # 51DEGREES_VER==3
OPTIONS_OBJS += addons/51degrees/51d.o
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
USE_MATH = implicit
endif # USE_51DEGREES endif # USE_51DEGREES
ifneq ($(USE_WURFL:0=),) ifneq ($(USE_WURFL:0=),)
EXTRA_MAKE += addons/wurfl # Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
# to WURFL headers and libraries if needed.
WURFL_INC = $(WURFL_SRC)
WURFL_LIB = $(WURFL_SRC)
OPTIONS_OBJS += addons/wurfl/wurfl.o
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
ifneq ($(WURFL_DEBUG),)
WURFL_CFLAGS += -DWURFL_DEBUG
endif
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
endif
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
endif endif
ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),) ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),)
@ -1003,7 +1058,7 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
ifneq ($(TARGET),) ifneq ($(TARGET),)
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),) ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(EXTRA_MAKE) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi) build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
.build_opts: $(build_opts) .build_opts: $(build_opts)
else else
.build_opts: .build_opts:

View file

@ -4,7 +4,7 @@
[![Illumos](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml) [![Illumos](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml)
[![NetBSD](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml) [![NetBSD](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml)
[![CrossCompile](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml) [![CrossCompile](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml)
[![FreeBSD](https://github.com/haproxy/haproxy/actions/workflows/freebsd.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/freebsd.yml) [![FreeBSD](https://api.cirrus-ci.com/github/haproxy/haproxy.svg?task=FreeBSD)](https://cirrus-ci.com/github/haproxy/haproxy/)
[![VTest](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml) [![VTest](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml)
![HAProxy logo](doc/HAProxyCommunityEdition_60px.png) ![HAProxy logo](doc/HAProxyCommunityEdition_60px.png)

View file

@ -1,2 +1,2 @@
$Format:%ci$ $Format:%ci$
2026/06/03 2026/05/20

View file

@ -1 +1 @@
3.5-dev0 3.4-dev13

View file

@ -127,16 +127,7 @@ static int _51d_property_name_list(char **args, int section_type, struct proxy *
while (*(args[cur_arg])) { while (*(args[cur_arg])) {
name = calloc(1, sizeof(*name)); name = calloc(1, sizeof(*name));
if (!name) {
memprintf(err, "'%s' failed to allocate memory.", args[0]);
return -1;
}
name->name = strdup(args[cur_arg]); name->name = strdup(args[cur_arg]);
if (!name->name) {
free(name);
memprintf(err, "'%s' failed to allocate memory.", args[0]);
return -1;
}
LIST_APPEND(&global_51degrees.property_names, &name->list); LIST_APPEND(&global_51degrees.property_names, &name->list);
++cur_arg; ++cur_arg;
} }
@ -312,7 +303,6 @@ static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) {
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets) static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
{ {
struct buffer *temp = get_trash_chunk();
struct channel *chn; struct channel *chn;
struct htx *htx; struct htx *htx;
struct http_hdr_ctx ctx; struct http_hdr_ctx ctx;
@ -334,15 +324,7 @@ static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOff
if (http_find_header(htx, name, &ctx, 1)) { if (http_find_header(htx, name, &ctx, 1)) {
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i); (offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
/* Copy value into trash and NUL-terminate before passing to the (offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
* 51Degrees Trie API, which expects a C string.
*/
if (ctx.value.len >= temp->size)
continue;
memcpy(temp->area, ctx.value.ptr, ctx.value.len);
temp->area[ctx.value.len] = '\0';
temp->data = ctx.value.len + 1;
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, temp->area);
offsets->size++; offsets->size++;
} }
} }
@ -937,10 +919,6 @@ static int init_51degrees(void)
list_for_each_entry(name, &global_51degrees.property_names, list) list_for_each_entry(name, &global_51degrees.property_names, list)
++i; ++i;
_51d_property_list = calloc(i, sizeof(*_51d_property_list)); _51d_property_list = calloc(i, sizeof(*_51d_property_list));
if (!_51d_property_list) {
ha_alert("51Degrees: Failed to allocate property list.\n");
return (ERR_FATAL | ERR_ALERT);
}
i = 0; i = 0;
list_for_each_entry(name, &global_51degrees.property_names, list) list_for_each_entry(name, &global_51degrees.property_names, list)
@ -1075,7 +1053,7 @@ static int init_51degrees(void)
static void deinit_51degrees(void) static void deinit_51degrees(void)
{ {
struct _51d_property_names *_51d_prop_name = NULL, *_51d_prop_nameb = NULL; struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED)
free(global_51degrees.header_names); free(global_51degrees.header_names);

View file

@ -1,37 +0,0 @@
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
# regardless of the version since they are mutually exclusive. The version
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
51DEGREES_INC = $(51DEGREES_SRC)
51DEGREES_LIB = $(51DEGREES_SRC)
51DEGREES_VER = 3
ifeq ($(51DEGREES_VER),4) # v4 here
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
endif
USE_LIBATOMIC = implicit
endif # 51DEGREES_VER==4
ifeq ($(51DEGREES_VER),3) # v3 here
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
else
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
endif
else
ifneq ($(51DEGREES_VER),4)
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
endif
endif # 51DEGREES_VER==3
OPTIONS_OBJS += addons/51degrees/51d.o
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
USE_MATH = implicit

View file

@ -1,13 +1,6 @@
# DEVICEATLAS_SRC : DeviceAtlas API source root path # DEVICEATLAS_SRC : DeviceAtlas API source root path
# Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
# can be used to disable the cache support if needed (this also removes the necessity of having
# a C++ toolchain installed).
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
CXX := c++ CXX := c++
CXXLIB := -lstdc++ CXXLIB := -lstdc++
@ -35,7 +28,5 @@ OPTIONS_OBJS += $(DEVICEATLAS_SRC)/dadwcurl.o
OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o
endif endif
OPTIONS_OBJS += addons/deviceatlas/da.o
addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp
$(cmd_CXX) $(CXXFLAGS) -c -o $@ $< $(cmd_CXX) $(CXXFLAGS) -c -o $@ $<

View file

@ -1,13 +0,0 @@
# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
# to WURFL headers and libraries if needed.
WURFL_INC = $(WURFL_SRC)
WURFL_LIB = $(WURFL_SRC)
OPTIONS_OBJS += addons/wurfl/wurfl.o
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
ifneq ($(WURFL_DEBUG),)
WURFL_CFLAGS += -DWURFL_DEBUG
endif
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
endif
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl

View file

@ -1801,8 +1801,6 @@ void filter_count_ip(const char *source_field, const char *accept_field, const c
*/ */
if (unlikely(!ustat)) if (unlikely(!ustat))
ustat = calloc(1, sizeof(*ustat)); ustat = calloc(1, sizeof(*ustat));
if (!ustat)
return;
ustat->nb_err = err; ustat->nb_err = err;
ustat->nb_req = 1; ustat->nb_req = 1;

View file

@ -26,7 +26,7 @@ end
# returns $node filled with the first node of ebroot $arg0 # returns $node filled with the first node of ebroot $arg0
define ebtree_first define ebtree_first
# browse ebtree left until encountering leaf # browse ebtree left until encountering leaf
set $node = (struct eb_node *)((struct eb_root*)$arg0)->b[0] set $node = (struct eb_node *)$arg0->b[0]
while 1 while 1
_ebtree_set_tag_node $node _ebtree_set_tag_node $node
if $tag == 0 if $tag == 0
@ -41,7 +41,7 @@ end
# finds next ebtree node after $arg0, and returns it in $node # finds next ebtree node after $arg0, and returns it in $node
define ebtree_next define ebtree_next
# get parent # get parent
set $node = (struct eb_root *)((struct eb_node *)$arg0)->leaf_p set $node = (struct eb_root *)$arg0->leaf_p
# Walking up from right branch, so we cannot be below root # Walking up from right branch, so we cannot be below root
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0 # while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
while 1 while 1

View file

@ -1,39 +0,0 @@
# list info about open FD
define fd_dump
set $f = 0
while $f < $g.maxsock
if fdtab[$f].owner != 0
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx cb=%p ownr=%p st=%#x refc=%#x tkov=%u gen=%u\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].iocb, fdtab[$f].owner, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation
end
set $f = $f + 1
end
end
# only those attached to a listener
define fd_dump_listener
set $f = 0
while $f < $g.maxsock
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_accept_iocb
set $c = (struct listener *)fdtab[$f].owner
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u listener=%p(%s): flg=%#x state=%d fe=%p(%s) acc=%p\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->name, $c->flags, $c->state, $c->bind_conf.frontend, $c->bind_conf.frontend.id, $c->bind_conf.accept
end
set $f = $f + 1
end
end
# only those attached to a connection
define fd_dump_conn
set $f = 0
while $f < $g.maxsock
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_conn_iocb
set $c = (struct connection *)fdtab[$f].owner
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u conn=%p: flg=%#x err=%#x ctrl=%p xprt=%p mux=%p", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->flags, $c->err_code, $c->ctrl, $c->xprt, $c->mux
if *$c->target == OBJ_TYPE_LISTENER
set $s = (struct session *)$c->owner
printf " sess=%p: fe=%p id=%s age=%dms", $s, $s->fe, $s->fe->id, (*global_now_ns - $s->accept_ts) / 1000000
end
printf "\n"
end
set $f = $f + 1
end
end

View file

@ -1,31 +0,0 @@
# lists all tasks in the wait queue whose ebroot pointed to by $arg0
# e.g.
# task_dump_wq &ha_tgroup_ctx[0].timers
# task_dump_wq &ha_thread_ctx[0].timers
#
define task_dump_rq
set $tot=0
ebtree_first ($arg0)
while ($node != 0)
set $tot = $tot+1
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).rq)
printf "task %p ",$p
p -pretty off -- /a *$p
ebtree_next $node
end
printf "Total: %d tasks.\n",$tot
end
define task_dump_wq
set $tot=0
ebtree_first ($arg0)
while ($node != 0)
set $tot = $tot+1
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).wq)
printf "task %p ",$p
p -pretty off -- /a *$p
ebtree_next $node
end
printf "Total: %d tasks.\n",$tot
end

View file

@ -1,10 +0,0 @@
# list info about current threads (ptr, now_ms, queue, current)
define thread_dump
set $t = 0
while $t < $g.nbthread
set $i = $ti[$t].pth_id
set $h = $tc[$t].current
printf "Tid %4d: pth=%p mono=%llu now_ms=%u fl=0x%02x rq=%d cq=%d current=%p\n", $t, $i, $tc[$t].curr_mono_time, (unsigned)(($tc[$t].curr_mono_time + now_offset)/1000000), $tc[$t].flags, $tc[$t].current_queue, $tc[$t].rq_total, $h
set $t = $t + 1
end
end

View file

@ -1,70 +0,0 @@
BEGININPUT
BEGINCONTEXT
HAProxy's development cycle consists in one development branch, and multiple
maintenance branches.
All the development is made into the development branch exclusively. This
includes mostly new features, doc updates, cleanups and or course, fixes.
The maintenance branches, also called stable branches, never see any
development, and only receive ultra-safe fixes for bugs that affect them,
that are picked from the development branch.
Branches are numbered in 0.1 increments. Every 6 months, upon a new major
release, the development branch enters maintenance and a new development branch
is created with a new, higher version. The current development branch is
3.5-dev, and maintenance branches are 3.4 and below.
Fixes created in the development branch for issues that were introduced in an
earlier branch are applied in descending order to each and every version till
that branch that introduced the issue: 3.4 first, then 3.2, then 3.1, then 3.0
and so on. This operation is called "backporting". A fix for an issue is never
backported beyond the branch that introduced the issue. An important point is
that the project maintainers really aim at zero regression in maintenance
branches, so they're never willing to take any risk backporting patches that
are not deemed strictly necessary.
Fixes consist of patches managed using the Git version control tool and are
identified by a Git commit ID and a commit message. For this reason we
indistinctly talk about backporting fixes, commits, or patches; all mean the
same thing. When mentioning commit IDs, developers always use a short form
made of the first 8 characters only, and expect the AI assistant to do the
same.
It seldom happens that some fixes depend on changes that were brought by other
patches that were not in some branches and that will need to be backported as
well for the fix to work. In this case, such information is explicitly provided
in the commit message by the patch's author in natural language.
Developers are serious and always indicate if a patch needs to be backported.
Sometimes they omit the exact target branch, or they will say that the patch is
"needed" in some older branch, but it means the same. If a commit message
doesn't mention any backport instructions, it means that the commit does not
have to be backported. And patches that are not strictly bug fixes nor doc
improvements are normally not backported. For example, fixes for design
limitations, architectural improvements and performance optimizations are
considered too risky for a backport. Finally, all bug fixes are tagged as
"BUG" at the beginning of their subject line. Patches that are not tagged as
such are not bugs, and must never be backported unless their commit message
explicitly requests so.
ENDCONTEXT
A developer is reviewing the development branch, trying to spot which commits
need to be backported to maintenance branches. This person is already expert
on HAProxy and everything related to Git, patch management, and the risks
associated with backports, so he doesn't want to be told how to proceed nor to
review the contents of the patch.
The goal for this developer is to get some help from the AI assistant to save
some precious time on this tedious review work. In order to do a better job, he
needs an accurate summary of the information and instructions found in each
commit message. Specifically he needs to figure if the patch fixes a problem
affecting an older branch or not, if it needs to be backported, if so to which
branches, and if other patches need to be backported along with it.
The indented text block below after an "id" line and starting with a Subject line
is a commit message from the HAProxy development branch that describes a patch
applied to that branch, starting with its subject line, please read it carefully.

View file

@ -1,29 +0,0 @@
ENDINPUT
BEGININSTRUCTION
You are an AI assistant that follows instruction extremely well. Help as much
as you can, responding to a single question using a single response.
The developer wants to know if he needs to backport the patch above to fix
maintenance branches, for which branches, and what possible dependencies might
be mentioned in the commit message. Carefully study the commit message and its
backporting instructions if any (otherwise it should probably not be backported),
then provide a very concise and short summary that will help the developer decide
to backport it, or simply to skip it.
Start by explaining in one or two sentences what you recommend for this one and why.
Finally, based on your analysis, give your general conclusion as "Conclusion: X"
where X is a single word among:
- "yes", if you recommend to backport the patch right now either because
it explicitly states this or because it's a fix for a bug that affects
a maintenance branch (3.4 or lower);
- "wait", if this patch explicitly mentions that it must be backported, but
only after waiting some time.
- "no", if nothing clearly indicates a necessity to backport this patch (e.g.
lack of explicit backport instructions, or it's just an improvement);
- "uncertain" otherwise for cases not covered above
ENDINSTRUCTION
Explanation:

View file

@ -2,8 +2,8 @@
HAProxy HAProxy
Configuration Manual Configuration Manual
---------------------- ----------------------
version 3.5 version 3.4
2026/06/03 2026/05/20
This document covers the configuration language as implemented in the version This document covers the configuration language as implemented in the version
@ -3329,7 +3329,7 @@ setenv <name> <value>
the configuration file sees the new value. See also "presetenv", "resetenv", the configuration file sees the new value. See also "presetenv", "resetenv",
and "unsetenv". and "unsetenv".
shm-stats-file <name> shm-stats-file <name> [ EXPERIMENTAL ]
When this directive is set, it enables the use of shared memory for storing When this directive is set, it enables the use of shared memory for storing
stats counters. <name> is used as argument to shm_open() to open the shared stats counters. <name> is used as argument to shm_open() to open the shared
memory at a unique location. It also means that the directive is only memory at a unique location. It also means that the directive is only
@ -3345,7 +3345,7 @@ shm-stats-file <name>
See also "guid", "guid-prefix" and "shm-stats-file-max-objects" See also "guid", "guid-prefix" and "shm-stats-file-max-objects"
shm-stats-file-max-objects <number> shm-stats-file-max-objects <number> [ EXPERIMENTAL ]
This setting defines the maximum number of objects the shared memory used This setting defines the maximum number of objects the shared memory used
for shared counters will be able to store per thread group. It is directly for shared counters will be able to store per thread group. It is directly
related to the maximum memory size of the shm and is used to "premap" the related to the maximum memory size of the shm and is used to "premap" the
@ -4940,8 +4940,8 @@ tune.lua.openlibs [all | none | <lib>[,<lib>...]]
tune.lua.openlibs string,math,table,utf8 # safe subset, no I/O or OS tune.lua.openlibs string,math,table,utf8 # safe subset, no I/O or OS
tune.lua.openlibs all # default, load everything tune.lua.openlibs all # default, load everything
This setting must be set before any "lua-load", "lua-load-per-thread" or This setting must be set before any "lua-load" or "lua-load-per-thread"
"lua-prepend-path" directive, otherwise a parse error is returned. directive, otherwise a parse error is returned.
tune.lua.service-timeout <timeout> tune.lua.service-timeout <timeout>
This is the execution timeout for the Lua services. This is useful for This is the execution timeout for the Lua services. This is useful for
@ -9105,9 +9105,7 @@ http-reuse { never | safe | aggressive | always }
- proxy protocol - proxy protocol
- TOS and mark socket options - TOS and mark socket options
- connection name, determined either by the result of the evaluation of the - connection name, determined either by the result of the evaluation of the
"pool-conn-name" expression if present, otherwise by the "sni" expression, "pool-conn-name" expression if present, otherwise by the "sni" expression
which defaults to "req.hdr(host),field(1,:)", i.e. uses the incoming
request's "Host" header field without the colon nor the port number.
In some occasions, connection lookup or reuse is not performed due to extra In some occasions, connection lookup or reuse is not performed due to extra
restrictions. This is determined by the reuse strategy specified via the restrictions. This is determined by the reuse strategy specified via the
@ -9181,26 +9179,6 @@ http-reuse { never | safe | aggressive | always }
too few connections are kept open. It may be desirable in this case to adjust too few connections are kept open. It may be desirable in this case to adjust
such thresholds or simply to increase the global "maxconn" value. such thresholds or simply to increase the global "maxconn" value.
In some rare cases, when the host name is used to distinguish outgoing TLS
connections (e.g. forward proxy), where most request target different hosts,
the reuse rate will be very low, and the automatic eviction of rarely used
connections will kick in before connections have a chance to be reused,
because the mechanism continuously measures the average number of connections
needed to deliver the service without exhausting resources. In such
situations, setting "pool-low-conn" to a value close to the average expected
number of idle connections may help preserve more connections by encouraging
threads to setup their own instead of trying to pick other threads' and
shrinking the pool of available connections.
If a locally hosted server uses a single certificate (with multiple host
names or wildcards) and operates multiple sites, it may be more effective to
just use "no-sni-auto" on the "server" line to avoid reserving a connection
to a single Host name. This will significantly increase the reuse rate. Some
servers might perform excessive checks between Host and SNI though, resulting
in rejecting subsequent requests, so this option requires preliminary
validation. The default behavior ("sni-auto") is to be safe even with such
servers.
When thread groups are explicitly enabled, it is important to understand that When thread groups are explicitly enabled, it is important to understand that
idle connections are only usable between threads from a same group. As such idle connections are only usable between threads from a same group. As such
it may happen that unfair load between groups leads to more idle connections it may happen that unfair load between groups leads to more idle connections
@ -9984,7 +9962,7 @@ no option accept-unsafe-violations-in-http-request
When this option is set, the following rules are observed: When this option is set, the following rules are observed:
* In H1 only, invalid characters, including NULL character, in header name * In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped. will be accepted;
* In H1 only, NULL character in header value will be accepted; * In H1 only, NULL character in header value will be accepted;
@ -10049,11 +10027,8 @@ no option accept-unsafe-violations-in-http-response
When this option is set, the following rules are observed: When this option is set, the following rules are observed:
* In H1 only, status codes longer than 3 digits but whose value fits in 16
bits are not rejected.
* In H1 only, invalid characters, including NULL character, in header name * In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped. will be accepted;
* In H1 only, NULL character in header value will be accepted; * In H1 only, NULL character in header value will be accepted;
@ -19921,13 +19896,11 @@ sni-auto
May be used in the following contexts: tcp, http, log, peers, ring May be used in the following contexts: tcp, http, log, peers, ring
The "sni-auto" parameter enables the automatic SNI selection, if no value was The "sni-auto" parameter enables the automatic SNI selection, if no value was
already set. It sets the "sni" expression to "req.hdr(host),field(1,:)", already set. It is enabled by default but this parameter may be used as
which means that an SNI will be presented with the Host name of the request "server" setting to reset any "no-sni-auto" setting which would have been
that is being sent to the server, but dropping the port number. It is enabled inherited from "default-server" directive as default value. It may also be
by default but this parameter may be used as "server" setting to reset any used as "default-server" setting to reset any previous "default-server"
"no-sni-auto" setting which would have been inherited from "default-server" "no-sni-auto" setting.
directive as default value. It may also be used as "default-server" setting
to reset any previous "default-server" "no-sni-auto" setting.
For HTTPS connections, the selected SNI is based on the request host header For HTTPS connections, the selected SNI is based on the request host header
value, if found. Otherwise it remains unset. For other protocols, the option value, if found. Otherwise it remains unset. For other protocols, the option
@ -32637,9 +32610,9 @@ https://github.com/haproxy/wiki/wiki/ACME:--native-haproxy
Current limitations: Current limitations:
- The feature is limited to the http-01, dns-01 or dns-persist-01 challenges - The feature is limited to the http-01, dns-01 or dns-persist-01 challenges
for now. http-01 is completely handled by HAProxy, but dns-01 and for now. http-01 is completely handled by HAProxy, but dns-01 and
dns-persist-01 needs either the dataplaneAPI, a lua script using event_hdl or dns-persist-01 needs either the dataplaneAPI or another 3rd party
another 3rd party tool to talk to a DNS provider API. dns-persist-01 only tool to talk to a DNS provider API. dns-persist-01 only needs the TXT entry
needs the TXT entry to be set once, so it could be set manually without a tool. to be set once, so it could be set manually without a tool.
- It is possible to start without an existing certificate on the disk. To do - It is possible to start without an existing certificate on the disk. To do
so, the certificate must configured in a crt-store. so, the certificate must configured in a crt-store.
When using the "acme" keyword in a crt-store, a temporary key pair will be When using the "acme" keyword in a crt-store, a temporary key pair will be
@ -32710,8 +32683,6 @@ challenge-ready <value>[,<value>]*
"acme challenge_ready <crt> domain <domain>" on the master CLI or "acme challenge_ready <crt> domain <domain>" on the master CLI or
the stats socket. This allows an external DNS provisioning tool to the stats socket. This allows an external DNS provisioning tool to
confirm that the TXT record has been set before HAProxy proceeds. confirm that the TXT record has been set before HAProxy proceeds.
It is also possible to signal the "cli" readiness using the
ACME.challenge_ready() lua function.
dns - perform a DNS pre-check by resolving the TXT record for dns - perform a DNS pre-check by resolving the TXT record for
"_acme-challenge.<domain>" using the configured "default" resolvers "_acme-challenge.<domain>" using the configured "default" resolvers

View file

@ -526,6 +526,12 @@ In addition, some variables are related to the global runqueue:
unsigned int grq_total; /* total number of entries in the global run queue, atomic */ unsigned int grq_total; /* total number of entries in the global run queue, atomic */
static unsigned int global_rqueue_ticks; /* insertion count in the grq, use rq_lock */ static unsigned int global_rqueue_ticks; /* insertion count in the grq, use rq_lock */
And others to the global wait queue:
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
__decl_aligned_rwlock(wq_lock); /* RW lock related to the wait queue */
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
2022-06-14 - progress on task affinity 2022-06-14 - progress on task affinity
========== ==========

View file

@ -52,16 +52,6 @@ make sure to respect this ordering when adding new ones.
cast and freed. The const char* is here to leave more freedom to use consts cast and freed. The const char* is here to leave more freedom to use consts
when making such options lists. when making such options lists.
- void hap_register_hlua_state_init(int (*fct)())
This adds a call to function <fct> to the list of functions to be called each
time a new Lua state is created by hlua_init_state(). This allows source
files other than hlua.c to register objects and functions into the Lua API
without modifying hlua.c directly. The function <fct> must return an ERR_*
code. If the errmsg pointer is set on return, it is printed as an alert,
warning, or notice depending on the error code. If ERR_ABORT or ERR_FATAL is
returned, haproxy will exit with status 1.
- void hap_register_per_thread_alloc(int (*fct)()) - void hap_register_per_thread_alloc(int (*fct)())
This adds a call to function <fct> to the list of functions to be called when This adds a call to function <fct> to the list of functions to be called when
@ -334,18 +324,6 @@ alphanumerically ordered:
is that it allows to register multiple <post> callbacks and to register them is that it allows to register multiple <post> callbacks and to register them
elsewhere in the code. elsewhere in the code.
- REGISTER_HLUA_STATE_INIT(fct)
Registers function <fct> to be called for each new Lua state created by
hlua_init_state(). This allows source files other than hlua.c to register
objects and functions into the Lua API without modifying hlua.c directly.
This is done by registering a call to hap_register_hlua_state_init(fct) at
stage STG_REGISTER. The function <fct> must be of type
(int (*fct)(lua_State *L, char **errmsg)) and must return an ERR_* code.
If the errmsg pointer is set on return, it is printed as an alert, warning,
or notice depending on the error code. If ERR_ABORT or ERR_FATAL is returned,
haproxy will exit with status 1.
- REGISTER_PER_THREAD_ALLOC(fct) - REGISTER_PER_THREAD_ALLOC(fct)
Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER. Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER.

View file

@ -21,11 +21,8 @@ HAPROXY CORE PRINCIPLES
- Aliases: uchar (unsigned char), uint (unsigned int), ulong (unsigned long), - Aliases: uchar (unsigned char), uint (unsigned int), ulong (unsigned long),
ushort (unsigned short), ullong (unsigned long long), llong (long long), ushort (unsigned short), ullong (unsigned long long), llong (long long),
schar (signed char). schar (signed char).
- size_t is always the same size as long, but its underlying type is often - size_t always same size as long but often declared as uint on 32-bit and
uint on 32-bit and ulong on 64-bit. This is a frequent source of build ulong on 64-bit. Do not use in printf() without a cast (ulong with "%lu").
errors on 32-bit platforms (e.g. passing a size_t where a long* is
expected, or printing one with "%lu"); always cast in printf() (ulong
with "%lu").
- Main platforms are x86_64 and aarch64 with high thread counts (>=64). - Main platforms are x86_64 and aarch64 with high thread counts (>=64).
- Unaligned accesses are permitted for archs that support them; portable - Unaligned accesses are permitted for archs that support them; portable
wrappers in net_helper.h (read_u32(), write_u32() etc). wrappers in net_helper.h (read_u32(), write_u32() etc).
@ -39,8 +36,7 @@ HAPROXY CORE PRINCIPLES
3. MEMORY MANAGEMENT AND POOLS 3. MEMORY MANAGEMENT AND POOLS
- Pools are used for runtime allocation; malloc/free are for boot code only. - Pools are used for runtime allocation; malloc/free are for boot code only.
- pool_alloc() semantics match malloc(); the return must always be tested. - pool_alloc() semantics match malloc(); the return must always be tested.
- pool_alloc() and malloc() are not interchangeable: memory obtained from one - pool_alloc() and malloc() are not interchangeable / compatible.
must not be released using the other's free function.
- pool_free() semantics match free(); it is a no-op on NULL. - pool_free() semantics match free(); it is a no-op on NULL.
- pool_free() makes the pointer invalid immediately; it must not be touched - pool_free() makes the pointer invalid immediately; it must not be touched
or passed to pool_free() again. or passed to pool_free() again.
@ -61,8 +57,8 @@ HAPROXY CORE PRINCIPLES
- idempotent functions b_alloc() and b_free() use pools to manage the - idempotent functions b_alloc() and b_free() use pools to manage the
storage area and check <size> to know if alloc/free still needed. storage area and check <size> to know if alloc/free still needed.
- a non-contiguous version exists (ncbuf, ncbmbuf), allowing holes anywhere - a non-contiguous version exists (ncbuf, ncbmbuf), allowing holes anywhere
in data. ncbuf mandates holes of at least 8 bytes, while ncbmbuf relies on in data. The former mandates holes of at least 8 bytes. The second relies
a bitmap of populated places. on a bitmap of populated places.
- another string API exists, "ist", representing a pointer and a length in a - another string API exists, "ist", representing a pointer and a length in a
struct that is returned by inline functions and macros. It is described in struct that is returned by inline functions and macros. It is described in
doc/internals/api/ist.txt doc/internals/api/ist.txt
@ -77,11 +73,8 @@ HAPROXY CORE PRINCIPLES
- Chunks are used for linear operations like chunk_printf(). - Chunks are used for linear operations like chunk_printf().
- Trash is a thread-local temporary buffer; scope stays within the caller. - Trash is a thread-local temporary buffer; scope stays within the caller.
- trash always the same size as a buffer (global.tune.bufsize). - trash always the same size as a buffer (global.tune.bufsize).
- get_trash_chunk() provides rotating thread-local trash chunks. Since almost - get_trash_chunk() provides up to 3 rotating thread-local trash chunks (with
any function may itself call get_trash_chunk(), a returned chunk is only a scope spanning from the call to the next function call).
guaranteed valid until the next call into another function and must not be
held across such a call. The rotation lets a single function safely use up
to 3 distinct chunks at once for its own data manipulation.
- For longer lived trash chunks, alloc_trash_chunk() is available but must be - For longer lived trash chunks, alloc_trash_chunk() is available but must be
released using free_trash_chunk() on leaving. released using free_trash_chunk() on leaving.
- standard doubly-linked lists (struct list) are provided via macros LIST_*. - standard doubly-linked lists (struct list) are provided via macros LIST_*.
@ -110,9 +103,8 @@ HAPROXY CORE PRINCIPLES
"ti" current thread info. "ti" current thread info.
- "tgid" always current tg number, "tg_ctx" current tg context. - "tgid" always current tg number, "tg_ctx" current tg context.
- HA_ATOMIC_* for atomic operations on integers and pointers (includes load - HA_ATOMIC_* for atomic operations on integers and pointers (includes load
and store). DWCAS is available on some platforms but requires an equivalent and store). DWCAS available on some platforms but requires an equivalent
fallback on the others (possibly a more complex operation, e.g. emulation for other ones.
using two or more CAS).
- The _HA_ATOMIC_* version (leading underscore) do not use barriers so these - The _HA_ATOMIC_* version (leading underscore) do not use barriers so these
must be explicit (__ha_barrier_*). must be explicit (__ha_barrier_*).
- Atomic loops must use CPU relaxation or exponential back-off. - Atomic loops must use CPU relaxation or exponential back-off.
@ -186,9 +178,8 @@ HAPROXY CORE PRINCIPLES
- Avoid static storage when persistence is not needed. - Avoid static storage when persistence is not needed.
- Macros in uppercase unless they're used to wrap functions which then get a - Macros in uppercase unless they're used to wrap functions which then get a
leading underscore. leading underscore.
- Explicitly compare against 0 the return of functions that yield an integer - Explicitly compare functions returning non-zero with 0 (e.g. strcmp) unless
which is not a boolean (e.g. strcmp), unless they return a boolean (e.g. they explicitly return a boolean (e.g. isalnum) or a pointer (e.g. strchr).
isalnum) or a pointer (e.g. strchr).
- Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness - Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness
mistakes. mistakes.
- turn non-zero integer to boolean using "!" or "!!". - turn non-zero integer to boolean using "!" or "!!".

View file

@ -1,233 +0,0 @@
HAProxy Threat Model & Trust Boundaries
This document defines the security boundaries of HAProxy, explicitly outlining
what does and does not constitute a security vulnerability. Its purpose is to
give reporters, developers and reviewers a single, predictable basis for
judging an issue's real-world impact.
The project's strong preference is to fix issues quickly and in the open.
Public handling gets fixes to users sooner and spares the ecosystem
(distributions in particular) the heavy cost of embargo coordination, which in
practice has rarely served users. Private, coordinated disclosure is reserved
for the few cases whose real-world impact genuinely warrants it, judged from
the severity ordering (section 6) and the mitigations (section 5). An issue
that is technically in scope but contained in practice does not, by itself,
call for an embargo.
These boundaries apply strictly to officially supported, documented builds
running under a sane, production-ready configuration. Security guarantees are
explicitly voided when using opt-in unsafe knobs, undocumented behavior, or
experimental features. A configuration that merely lacks a recommended
hardening step (for instance, no chroot) does not by itself move a
client-triggered bug out of scope; the missing mitigation only widens the
blast radius (sections 5 and 6).
1. ASSETS TO PROTECT
HAProxy sits on the critical path of the services it fronts, so its
availability and the integrity and confidentiality of the configuration and
secrets it holds are all essential to protect. The assets below are not
ranked here; their relative severity is ranked in section 6.
- Integrity and confidentiality of the host and configuration: a compromise
of the network-facing worker must not extend to the filesystem, nor to the
configuration and its dependencies (private keys, Lua scripts, maps,
crt-lists, ACLs). On a properly configured system the default structural
mitigations prevent this, leaving only a compromise of the master process
as a residual path (see section 5).
- Confidentiality of long-lived secrets: TLS private keys and certificates
above all. Unlike transient client data, their disclosure is permanent and
systemic (impersonation and traffic decryption until every key is rotated
and revoked).
- Availability of the proxied service: being on the critical path, keeping
HAProxy serving is paramount. A small, cheap amount of attacker input
must neither consume a disproportionate amount of CPU or memory
(asymmetric DoS, see section 3) nor crash or stall the process.
- Confidentiality and isolation of client data: data belonging to one
connection, stream or client must never leak to another, and process
memory (including uninitialized memory) must never leak to a client.
- Process integrity (memory safety): no RCE, memory corruption or undefined
behaviour (UB) reachable from untrusted input.
- Correct enforcement of the configured policy: access controls, routing and
header manipulations decided by the configuration must not be bypassable
by crafted input.
2. ATTACKER AND ENTRY POINTS
- The reference attacker is an untrusted client able to send arbitrary
bytes to a frontend: raw TCP payloads, HTTP/1, HTTP/2 and HTTP/3 (QUIC)
traffic, and arbitrary TLS handshake records.
- Entry points in scope are therefore the listeners and everything that
parses or transforms client-supplied data: TLS, the HTTP muxes, HTX,
header/URL processing, sample fetches and converters acting on request
data, stick-tables fed by client data, the cache, and the QUIC/H3 stack.
- A secondary untrusted source is the DNS resolver path: even though
nameservers are configured, their answers arrive over UDP and can be
spoofed by an off-path attacker, so the response parser handles
attacker-influenced input.
3. WHAT QUALIFIES AS A SECURITY BUG (IN SCOPE)
- Memory-safety issues (overflow, out-of-bounds, use-after-free, type
confusion, UB) reachable from untrusted client input.
- Cross-client or cross-stream effects: HTTP request smuggling, response
splitting, cache poisoning, and any mixing of data between concurrent
streams or connections (notably in the H2/H3 multiplexers).
- Disclosure of process memory or of another client's data to a client.
- Bypass of a policy that the configuration is meant to enforce (e.g.
defeating an http-request deny/acl through request crafting).
- Asymmetric / algorithmic denial of service: a single or a few cheap
requests causing disproportionate CPU or memory usage (hash-collision
flooding, catastrophic regex backtracking, quadratic parsing, unbounded
allocation, etc). This is distinct from volumetric DoS (see 4).
- Misuse of a third-party library on untrusted input: feeding malformed
client data into OpenSSL, PCRE, Lua, zlib, etc. in a way that corrupts
memory or crashes the process is in scope. A vulnerability inside the
library itself is handled by that library's project, not here.
- Mishandling of spoofable DNS responses: memory corruption, crashes or
cache/state poisoning in the resolver caused by a crafted DNS answer are
in scope, despite nameservers being nominally trusted (see section 2).
4. WHAT DOES NOT QUALIFY (OUT OF SCOPE)
The following do not fall into the security-bug category.
Trusted peers, servers and protocols:
- attacks that require a non-compliant or malicious server: in a reverse
proxy, servers are trusted, or ejected. This covers server-to-client
attacks in general.
- attacks on protocols only used with trusted peers: peers, PROXY protocol,
CIP (NetScaler Client-IP insertion), SOCKS, a local server reached over
an ABNS or UNIX socket, an FCGI server, etc., as well as TLS servers
contacted by the internal httpclient.
- malfunction of a trusted auxiliary service (log server, ring output,
CLI API consumer, etc.).
Privileged or local access (the actor is already trusted):
- any problem triggered through admin access to the CLI.
- anything requiring access to the master CLI.
- anything requiring access to the command line.
- anything requiring write access to the configuration file or any of its
dependencies (Lua scripts, certificates, crt-list, acl, map, etc.).
- anything requiring a configuration running as root, or chrooted to "/"
(i.e. with no effective chroot).
Opt-in unsafe or experimental knobs (the operator disabled a safety):
- anything requiring "experimental-mode on" on the CLI.
- anything requiring "insecure-fork-wanted".
- anything requiring "accept-unsafe-violations-*".
- anything requiring "expose-experimental-directives".
Misconfiguration:
- anything requiring a configuration that emits warnings at boot.
- anything requiring a nonsensical configuration, e.g. a server looping back
to the frontend, non-standard header processing or URL rewriting, or an
excessively large number of headers or excessively large header/body
sizes.
Volumetric or otherwise detectable activity:
- anything requiring such a high and sustained level of activity that it
would be detected and blocked in production (e.g. billions of requests or
connections). This is volumetric DoS, as opposed to the asymmetric DoS of
section 3.
Inherent protocol limitations:
- anything that is a limitation of a standard protocol rather than an
implementation flaw. For example, HTTP/1 has no way to abort a single
transfer without closing the connection, so a client aborting a transfer
will necessarily cause the corresponding server-side connection to be
closed; this is by design of the protocol, not a vulnerability.
Features that are not security boundaries:
- the stats page, including its admin mode, relies on HTTP basic
authentication and was never meant to be a security boundary. Exposing a
public-facing, admin-enabled stats page is therefore not covered.
- configuring a listener to accept the PROXY protocol or CIP from senders
that are not restricted to trusted ones is a misconfiguration: these
headers are believed on trust, so the listener must be reachable only by
the trusted L4/L7 component that prepends them.
Side channels:
- cryptographic and micro-architectural side channels (timing, cache,
speculative execution, etc.) are out of scope. Constant-time handling of
secrets is pursued on a best-effort basis as ordinary hardening where it
clearly matters, but observable timing or resource variations are not
handled as security bugs.
Log integrity:
- escaping of data emitted to logs is a configuration responsibility.
Injection of control characters or forged fields through logged client
data (e.g. when default escaping is disabled, or when a downstream log
consumer mis-parses) is not covered.
5. DEFENSE IN DEPTH (DEFAULT HARDENING)
A correctly deployed HAProxy combines several built-in mitigations that
bound the impact of a successful compromise. These are deliberately taken
into account when assessing the real-world severity of an issue and the
handling it deserves: when one of them contains the practical impact of a
bug, that bug rarely warrants a coordinated embargo and is usually better
fixed quickly and in the open, where users get the fix sooner. They lower
severity, not the obligation to fix: an exploitable memory-safety bug
reachable from client input is still corrected as a bug.
- No fork()/exec() in the worker: the worker never forks nor runs external
programs, so an attacker who achieves code execution has little ability
to spawn a shell or launch persistent background code. ("insecure-fork-
wanted" deliberately disables this and is itself out of scope, see
section 4.)
- chroot and privilege drop: in the sane configuration this document
assumes, the worker drops to an unprivileged user/group and chroots into
an empty, unwritable directory. Injected code therefore has no filesystem
access and very limited means to act on the host.
- Activity watchdog: a thread that stops making progress, e.g. hijacked
into an attacker-controlled loop or otherwise stuck, no longer services
the event loop; the watchdog detects this lack of activity and kills the
process after a few seconds rather than letting it be silently held.
- Master/worker separation: only the worker is exposed to the network and
runs the parsers reachable by clients, and it is the unprivileged,
chrooted process. The master keeps privileges and filesystem access but
has no network exposure. The master must therefore be protected as the
trusted, more privileged component; an attacker is assumed to face only
the worker. The master must under no circumstances be reachable from the
worker (e.g. a master CLI bound to a TCP socket such as localhost is
trivially reachable from compromised worker code and defeats this critical
separation).
6. SEVERITY ORDERING
The worst-case outcomes below are ranked by their realistic impact on a
standard configuration, from most to least severe, and the effort spent
guarding against each is proportional to that severity. The ranking reflects
the master/worker privilege split and the containment provided by the
section-5 mitigations.
1. Remote code execution in the master process. The master is privileged
and has filesystem access, so compromising it defeats every
containment, leaks every secret, and can subvert or take down the
whole service.
2. Chosen disclosure of long-lived secrets, TLS private keys and
certificates above all. Unlike an outage the damage is permanent and
silent: stolen keys allow impersonation, interception and, absent
forward secrecy, decryption of captured traffic, until every affected
key is rotated and revoked across the ecosystem; a restart does not
undo it. "Chosen" sets this rank, not scope: any disclosure of process
memory or of another client's data to a client is in scope (section 3);
this top rank is reserved for a targeted exfiltration, where the
attacker steers the read to a known secret. A leak that cannot be
steered toward a specific secret is still an in-scope disclosure bug,
but ranks far lower - often no worse than the crash such a read tends
to cause first.
3. Crash of the master process. It brings the entire service down and
prevents workers from being respawned: a full but recoverable outage.
4. Crash of the worker process. A transient outage: in-flight connections
are lost and traffic is interrupted for the fraction of a second it
takes to respawn.
5. Remote code execution in the worker process. Contained by no-fork,
chroot, privilege drop and the watchdog, its availability impact is
usually below a worker crash, except in the unlikely case where it
unlocks the chosen disclosure of level 2, which is hard to reach
through the internals from injected code.
6. Policy bypass. Serious, but with no direct availability impact.
7. SECURITY-RELEVANT INVARIANTS AND DEFAULTS
The values below define the conditions HAProxy is designed to operate
within, and may be relied upon by parsing and processing code. A suspected
vulnerability that can only be triggered by conditions outside them
(typically values pushed beyond the stated limits) does not qualify as
security-relevant:
- trash buffers and struct buffer storage are always at least a few kB.
- default buffer size is 16 kB (15 kB max input, as 1 kB is reserved for
rewrites), tunable up to <256 MB.
- default log line is 1 kB, tunable up to <=64 kB.

View file

@ -1,7 +1,7 @@
----------------------- -----------------------
HAProxy Starter Guide HAProxy Starter Guide
----------------------- -----------------------
version 3.5 version 3.4
This document is an introduction to HAProxy for all those who don't know it, as This document is an introduction to HAProxy for all those who don't know it, as
@ -97,9 +97,6 @@ to the mailing list whose responses are present in these documents.
protocol which is implemented by HAProxy and a number of third party protocol which is implemented by HAProxy and a number of third party
products. products.
- security.txt : how to report a security issue, and what does and does not
qualify as a vulnerability.
- README : how to build HAProxy from sources - README : how to build HAProxy from sources
@ -1689,7 +1686,15 @@ information you might later regret. Since the issue tracker presents itself as
a very long thread, please avoid pasting very long dumps (a few hundreds lines a very long thread, please avoid pasting very long dumps (a few hundreds lines
or more) and attach them instead. or more) and attach them instead.
If you believe you may have found a security issue, please refer to the file If you've found what you're absolutely certain can be considered a critical
doc/security.txt. It explains what does and does not qualify as a vulnerability security issue that would put many users in serious trouble if discussed in a
in HAProxy, and how to report a genuine one privately. Most suspected issues public place, then you can send it with the reproducer to security@haproxy.org.
turn out to be ordinary bugs that are better reported as described above. A small team of trusted developers will receive it and will be able to propose
a fix. We usually don't use embargoes and once a fix is available it gets
merged. In some rare circumstances it can happen that a release is coordinated
with software vendors. Please note that this process usually messes up with
everyone's work, and that rushed up releases can sometimes introduce new bugs,
so it's best avoided unless strictly necessary; as such, there is often little
consideration for reports that needlessly cause such extra burden, and the best
way to see your work credited usually is to provide a working fix, which will
appear in changelogs.

View file

@ -893,7 +893,9 @@ Core class
**context**: init, task, action **context**: init, task, action
This function returns a new object of a *httpclient* class. This function returns a new object of a *httpclient* class. An *httpclient*
object must be used to process one and only one request. It must never be
reused to process several requests.
:returns: A :ref:`httpclient_class` object. :returns: A :ref:`httpclient_class` object.
@ -1029,12 +1031,6 @@ Core class
Be careful when subscribing to this type since many events might be Be careful when subscribing to this type since many events might be
generated. generated.
**ACME** Family:
* **ACME_DEPLOY**: when a dns-01 challenge TXT record must be deployed
externally before HAProxy can proceed with the ACME challenge
* **ACME_NEWCERT**: when a new certificate is successfully installed
.. Note:: .. Note::
Use **SERVER** in **event_types** to subscribe to all server events types Use **SERVER** in **event_types** to subscribe to all server events types
at once. Note that this should only be used for testing purposes since a at once. Note that this should only be used for testing purposes since a
@ -1052,8 +1048,7 @@ Core class
* **event** (*string*): the event type (one of the **event_types** specified * **event** (*string*): the event type (one of the **event_types** specified
when subscribing) when subscribing)
* **event_data**: specific to each event family (For **SERVER** family, * **event_data**: specific to each event family (For **SERVER** family,
a :ref:`server_event_class` object; for **ACME** family, a :ref:`server_event_class` object)
a :ref:`acme_event_class` object)
* **sub**: class to manage the subscription from within the event * **sub**: class to manage the subscription from within the event
(a :ref:`event_sub_class` object) (a :ref:`event_sub_class` object)
* **when**: timestamp corresponding to the date when the event was generated. * **when**: timestamp corresponding to the date when the event was generated.
@ -2585,7 +2580,10 @@ HTTPClient class
.. js:class:: HTTPClient .. js:class:: HTTPClient
The httpclient class allows issue of outbound HTTP requests through a simple The httpclient class allows issue of outbound HTTP requests through a simple
API without the knowledge of HAProxy internals. API without the knowledge of HAProxy internals. Any instance must be used to
process one and only one request. It must never be reused to process several
requests.
.. js:function:: HTTPClient.get(httpclient, request) .. js:function:: HTTPClient.get(httpclient, request)
.. js:function:: HTTPClient.head(httpclient, request) .. js:function:: HTTPClient.head(httpclient, request)
.. js:function:: HTTPClient.put(httpclient, request) .. js:function:: HTTPClient.put(httpclient, request)
@ -2614,8 +2612,7 @@ HTTPClient class
haproxy address format. haproxy address format.
:param integer request.timeout: Optional timeout parameter, set a :param integer request.timeout: Optional timeout parameter, set a
"timeout server" on the connections. "timeout server" on the connections.
:returns: Lua table containing the response. If an internal error occurs (e.g. :returns: Lua table containing the response
connection failure, timeout, etc.), the ``status`` field will be set to 0.
.. code-block:: lua .. code-block:: lua
@ -4742,75 +4739,6 @@ CertCache class
CertCache.set{filename="certs/localhost9994.pem.rsa", crt=crt} CertCache.set{filename="certs/localhost9994.pem.rsa", crt=crt}
ACME class
==========
.. js:class:: ACME
This class provides access to the ACME (Automatic Certificate Management
Environment) subsystem. It allows Lua scripts to interact with ongoing ACME
certificate challenges.
.. js:function:: ACME.challenge_ready(crt, dns)
Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
Returns the number of remaining challenges, or 0 if all challenges are ready
and validation has been triggered. Raises a Lua error if the certificate or
domain is not found.
:param string crt: The filename of the certificate.
:param string dns: The domain name for which the challenge is ready.
:returns: The number of remaining challenges (integer), or 0 when all
challenges are done and validation has been triggered.
.. _acme_event_class:
AcmeEvent class
===============
.. js:class:: AcmeEvent
This class is provided with every **ACME** event.
See :js:func:`core.event_sub()` for more info.
.. js:attribute:: AcmeEvent.crtname
Contains the certificate store name.
.. js:attribute:: AcmeEvent.domain
Contains the domain being challenged.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.thumbprint
Contains the account key JWK thumbprint.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.dns_record
Contains the DNS TXT record value that must be set at
``_acme-challenge.<domain>``.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.provider
Contains the DNS provider name configured in the ACME section.
Only set if a provider was configured.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.vars
Contains the ACME vars string configured in the ACME section.
Only set if vars were configured.
Only available for **ACME_DEPLOY** events.
External Lua libraries External Lua libraries
====================== ======================

View file

@ -1,7 +1,7 @@
------------------------ ------------------------
HAProxy Management Guide HAProxy Management Guide
------------------------ ------------------------
version 3.5 version 3.4
This document describes how to start, stop, manage, and troubleshoot HAProxy, This document describes how to start, stop, manage, and troubleshoot HAProxy,
@ -215,18 +215,6 @@ list of options is :
in foreground and to show incoming and outgoing events. It must never be in foreground and to show incoming and outgoing events. It must never be
used in an init script. used in an init script.
-dA[file] : dump an archive of all dependencies detected at boot time in the
designated file in tar format, immediately after the configuration is done
loading. This is equivalent to "set-dumpable libs", but instead of keeping
the libs in memory, it dumps them into a file. This may be used after a
core dump, in order to provide all necessary libraries to developers to
permit them to exploit the core. This may not be available on all operating
systems. It is highly recommended to use this with the regular
configuration files, and optionally with "-c" when used manually, to make
haproxy immediately exit after the dump, without starting. Example:
$ haproxy -dA/tmp/libs.tar -c -f /etc/haproxy/haproxy.cfg
-dC[key] : dump the configuration file. It is performed after the lines are -dC[key] : dump the configuration file. It is performed after the lines are
tokenized, so comments are stripped and indenting is forced. If a non-zero tokenized, so comments are stripped and indenting is forced. If a non-zero
key is specified, lines are truncated before sensitive/confidential fields, key is specified, lines are truncated before sensitive/confidential fields,
@ -2469,8 +2457,7 @@ httpclient [--htx] <method> <URI>
name in the URL using the "default" resolvers section, which is populated name in the URL using the "default" resolvers section, which is populated
with the DNS servers of your /etc/resolv.conf by default. However it won't be with the DNS servers of your /etc/resolv.conf by default. However it won't be
able to resolve an host from /etc/hosts if you don't use a local dns daemon able to resolve an host from /etc/hosts if you don't use a local dns daemon
which can resolve those. If an internal error occurs (e.g. connection failure, which can resolve those.
timeout, etc.), the status code will be set to 0.
The --htx option allow to use the haproxy internal htx representation using The --htx option allow to use the haproxy internal htx representation using
the htx_dump() function, mainly used for debugging. the htx_dump() function, mainly used for debugging.

View file

@ -1,40 +0,0 @@
Reporting security issues in HAProxy
------------------------------------
Before reporting anything, please read doc/internals/threat-model.txt. It
defines precisely what is and is not considered a security vulnerability in
HAProxy. A fair number of suspected issues (and most automated or LLM-assisted
findings) fall outside that boundary: they are ordinary bugs, and are best
reported and fixed in public through the usual channels described in the
"Contacts" section of doc/intro.txt.
If, after reading the threat model, you are confident you have found a genuine
security issue that would put many users at risk if discussed in the open, the
security team can be reached at security@haproxy.org, a private list read by a
handful of security officers; anything shared there remains private. Please
include a reproducer, and ideally a proposed and tested patch, as well as a
valid name under which the report can be credited.
Auxiliary tools in dev/ and admin/ are not intended for production use and are
by nature out of the security scope. Please report bugs affecting them via the
regular channels.
We usually don't use embargoes: once a fix is available it simply gets merged.
In rare circumstances a release may be coordinated with software vendors, but
this disrupts everyone's work and rushed releases can introduce new bugs, so it
is avoided unless strictly necessary. As a result, reports that needlessly cause
such extra burden get little consideration, and the most effective and best
credited way to report an issue is to provide a working fix, which will appear
in the changelogs.
Findings produced with the help of AI MUST be accompanied by a working, tested
patch. Such tools routinely report issues that are out of scope (see the
threat model above) or simply not real, and reviewing them by hand wastes the
very time and trust this process depends on. A model-generated report that
arrives without a verified reproducer and a fix will generally not be
processed.
See also:
- doc/internals/threat-model.txt : what qualifies as a vulnerability
- doc/internals/core-principles.txt : the project's design principles
- doc/intro.txt : general contacts and bug reporting

View file

@ -1,162 +0,0 @@
-- ACME dns-01 automation via event_hdl callbacks using the Gandi LiveDNS API v5
--
-- HAProxy Configuration:
--
-- global
-- expose-experimental-directives
-- tune.lua.bool-sample-conversion normal
-- lua-load examples/lua/acme-gandi-livedns.lua
-- log stderr local0
--
-- acme LE
-- directory https://acme-staging-v02.api.letsencrypt.org/directory
-- contact foobar@example.com
-- challenge dns-01
-- challenge-ready cli,dns
--
-- crt-store
-- load crt foobar.pem acme LE domains *.foobar.example.com
--
-- Start HAProxy with the GANDI_API_KEY variable:
--
-- GANDI_API_KEY=fer89wf498w4f98we74f98wwiw787f8we4f8 ./haproxy -W -f haproxy.cfg
--
-- Gandi Personal Access Token (https://account.gandi.net -> Security -> Personal Access Tokens).
-- Set the GANDI_API_KEY environment variable before starting HAProxy.
local GANDI_API_KEY = os.getenv("GANDI_API_KEY") or error("GANDI_API_KEY environment variable is not set")
-- Gandi LiveDNS API base URL.
local GANDI_API_URL = "https://api.gandi.net/v5/livedns"
-- ---------------------------------------------------------------------------
-- Gandi LiveDNS helpers
-- ---------------------------------------------------------------------------
-- Try to set the _acme-challenge TXT record for <domain> to <txt_value>.
-- Probes each possible parent zone (longest first) until Gandi accepts one.
-- Returns the zone and record name on success, or nil on failure.
local function dns_set_txt(domain, txt_value)
local labels = {}
for label in domain:gmatch("[^.]+") do
labels[#labels + 1] = label
end
for i = 1, #labels - 1 do
local zone = table.concat(labels, ".", i + 1)
local name = "_acme-challenge." .. table.concat(labels, ".", 1, i)
local url = string.format("%s/domains/%s/records/%s/TXT", GANDI_API_URL, zone, name)
local body = string.format('{"rrset_values":["%s"],"rrset_ttl":300}', txt_value)
core.log(core.debug, string.format("acme: trying PUT %s", url))
-- Remove any stale TXT record first so the new value propagates cleanly.
local hc_del = core.httpclient()
hc_del:delete({
url = url,
headers = { ["Authorization"] = { "Bearer " .. GANDI_API_KEY } },
})
local hc = core.httpclient()
local res = hc:put({
url = url,
headers = {
["Authorization"] = { "Bearer " .. GANDI_API_KEY },
["Content-Type"] = { "application/json" },
},
body = body,
})
if res and (res.status == 200 or res.status == 201) then
core.log(core.notice, string.format(
"acme: TXT record set: %s in zone %s", name, zone))
return zone, name
end
end
core.log(core.alert, string.format(
"acme: failed to set TXT record for _acme-challenge.%s: no valid zone found", domain))
return nil, nil
end
-- Deletes the TXT record identified by <zone> and <name>.
local function dns_del_txt(zone, name)
local url = string.format("%s/domains/%s/records/%s/TXT", GANDI_API_URL, zone, name)
core.log(core.notice, string.format("acme: DELETE %s", url))
local hc = core.httpclient()
local res = hc:delete({
url = url,
headers = {
["Authorization"] = { "Bearer " .. GANDI_API_KEY },
},
})
if not res or res.status ~= 204 then
local status = res and res.status or "nil"
core.log(core.alert, string.format(
"acme: Gandi DELETE failed for %s/%s (status=%s)", zone, name, status))
return false
end
core.log(core.notice, string.format(
"acme: TXT record deleted: %s in zone %s", name, zone))
return true
end
-- ---------------------------------------------------------------------------
-- Tasks
-- ---------------------------------------------------------------------------
-- Track deployed TXT records per cert path so they can be cleaned up.
-- deployed[crt][domain] = { zone = ..., name = ... }
local deployed = {}
-- Spawn a background task per ACME_DEPLOY event to set the TXT record and
-- signal challenge readiness. Using register_task keeps HTTP calls in a
-- plain task context.
core.event_sub({"ACME_DEPLOY"}, function(event, data, sub, when)
local crt = data.crtname
local domain = data.domain
local record = data.dns_record
core.register_task(function()
local zone, name = dns_set_txt(domain, record)
if not zone then
core.log(core.alert, string.format(
"acme: aborting challenge for crt=%s domain=%s", crt, domain))
return
end
-- Remember this record for cleanup on ACME_NEWCERT.
if not deployed[crt] then deployed[crt] = {} end
deployed[crt][domain] = { zone = zone, name = name }
-- Signal HAProxy that the dns-01 challenge for this domain is ready.
local ok, ret = pcall(ACME.challenge_ready, crt, domain)
if not ok then
core.log(core.alert, string.format(
"acme: challenge_ready error for crt=%s domain=%s: %s", crt, domain, ret))
elseif ret == 0 then
core.log(core.notice, string.format(
"acme: all challenges ready for crt=%s, validation starting", crt))
else
core.log(core.info, string.format(
"acme: crt=%s domain=%s ready, %d challenge(s) still pending",
crt, domain, ret))
end
end)
end)
-- ACME_NEWCERT: remove the TXT records that were set for this certificate.
core.event_sub({"ACME_NEWCERT"}, function(event, data, sub, when)
local crt = data.crtname
if not deployed[crt] then return end
core.register_task(function()
for _, rec in pairs(deployed[crt]) do
dns_del_txt(rec.zone, rec.name)
end
deployed[crt] = nil
end)
end)

View file

@ -135,29 +135,6 @@ struct acme_ctx {
#define ACME_VERB_ADVANCED 4 #define ACME_VERB_ADVANCED 4
#define ACME_VERB_COMPLETE 5 #define ACME_VERB_COMPLETE 5
/* event data for EVENT_HDL_SUB_ACME_DEPLOY:
* published when a dns-01 challenge TXT record must be deployed externally.
*/
struct event_hdl_cb_data_acme_deploy {
struct {
char *crtname; /* certificate store name (heap-allocated) */
char *domain; /* domain being challenged (heap-allocated) */
char *thumbprint; /* account key JWK thumbprint (heap-allocated) */
char *dns_record; /* DNS TXT record value to set (heap-allocated) */
char *provider; /* DNS provider name (heap-allocated, may be NULL) */
char *vars; /* ACME vars string (heap-allocated, may be NULL) */
} safe;
};
/* event data for EVENT_HDL_SUB_ACME_NEWCERT:
* published when a new certificate was successfully installed.
*/
struct event_hdl_cb_data_acme_newcert {
struct {
char *crtname; /* certificate store name (heap-allocated) */
} safe;
};
#endif /* ! HAVE_ACME */ #endif /* ! HAVE_ACME */
#endif #endif

View file

@ -5,14 +5,8 @@
#include <haproxy/ssl_ckch-t.h> #include <haproxy/ssl_ckch-t.h>
int ckch_conf_acme_init(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err); int ckch_conf_acme_init(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err);
int acme_challenge_ready(const char *crt, const char *dns);
EVP_PKEY *acme_gen_tmp_pkey(); EVP_PKEY *acme_gen_tmp_pkey();
X509 *acme_gen_tmp_x509(); X509 *acme_gen_tmp_x509();
#if defined(USE_LUA)
#include <haproxy/hlua-t.h>
#include <haproxy/event_hdl-t.h>
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data);
#endif /* USE_LUA */
#endif #endif

View file

@ -422,7 +422,7 @@ static inline int channel_is_rewritable(const struct channel *chn)
*/ */
static inline int channel_may_send(const struct channel *chn) static inline int channel_may_send(const struct channel *chn)
{ {
return chn_cons(chn)->state >= SC_ST_REQ; return chn_cons(chn)->state == SC_ST_EST;
} }
/* HTX version of channel_may_recv(). Returns non-zero if the channel can still /* HTX version of channel_may_recv(). Returns non-zero if the channel can still

View file

@ -165,7 +165,9 @@ static forceinline struct buffer *alloc_small_trash_chunk(void)
*/ */
static forceinline struct buffer *alloc_trash_chunk_sz(size_t size) static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
{ {
if (size <= pool_head_trash->size) if (pool_head_small_trash && size <= pool_head_small_trash->size)
return alloc_small_trash_chunk();
else if (size <= pool_head_trash->size)
return alloc_trash_chunk(); return alloc_trash_chunk();
else if (pool_head_large_trash && size <= pool_head_large_trash->size) else if (pool_head_large_trash && size <= pool_head_large_trash->size)
return alloc_large_trash_chunk(); return alloc_large_trash_chunk();

View file

@ -179,8 +179,6 @@ enum {
/* below we have all handshake flags grouped into one */ /* below we have all handshake flags grouped into one */
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV, CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN, CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
/* handshake running on top of a layer6 */
CO_FL_WAIT_XPRT_L6 = CO_FL_QMUX_SEND | CO_FL_QMUX_RECV,
CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */ CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */

View file

@ -114,7 +114,6 @@ int conn_reverse(struct connection *conn);
const char *conn_err_code_name(struct connection *c); const char *conn_err_code_name(struct connection *c);
const char *conn_err_code_str(struct connection *c); const char *conn_err_code_str(struct connection *c);
int xprt_add_hs(struct connection *conn); int xprt_add_hs(struct connection *conn);
int xprt_add_l6hs(struct connection *conn, int xprt);
void register_mux_proto(struct mux_proto_list *list); void register_mux_proto(struct mux_proto_list *list);
static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type); static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type);

View file

@ -101,8 +101,6 @@ void ha_warning(const char *fmt, ...)
* These functions are reserved to output diagnostics on MODE_DIAG. * These functions are reserved to output diagnostics on MODE_DIAG.
* Use the underscore variants only if MODE_DIAG has already been checked. * Use the underscore variants only if MODE_DIAG has already been checked.
*/ */
void ha_diag_notice(const char *fmt, ...)
__attribute__ ((format(printf, 1 ,2)));
void _ha_vdiag_warning(const char *fmt, va_list argp); void _ha_vdiag_warning(const char *fmt, va_list argp);
void _ha_diag_warning(const char *fmt, ...); void _ha_diag_warning(const char *fmt, ...);
void ha_diag_warning(const char *fmt, ...) void ha_diag_warning(const char *fmt, ...)

View file

@ -290,16 +290,6 @@ struct event_hdl_sub {
#define EVENT_HDL_SUB_PAT_REF_COMMIT EVENT_HDL_SUB_TYPE(2,4) #define EVENT_HDL_SUB_PAT_REF_COMMIT EVENT_HDL_SUB_TYPE(2,4)
#define EVENT_HDL_SUB_PAT_REF_CLEAR EVENT_HDL_SUB_TYPE(2,5) #define EVENT_HDL_SUB_PAT_REF_CLEAR EVENT_HDL_SUB_TYPE(2,5)
/* ACME family, published in global subscription list.
* Provides event_hdl_cb_data_acme_deploy and event_hdl_cb_data_acme_newcert
* structs (defined in haproxy/acme-t.h).
*/
#define EVENT_HDL_SUB_ACME EVENT_HDL_SUB_FAMILY(3)
/* a new certificate was successfully installed */
#define EVENT_HDL_SUB_ACME_NEWCERT EVENT_HDL_SUB_TYPE(3,1)
/* dns-01 challenge must be deployed externally */
#define EVENT_HDL_SUB_ACME_DEPLOY EVENT_HDL_SUB_TYPE(3,2)
/* --------------------------------------- */ /* --------------------------------------- */
/* Please reflect changes above in event_hdl_sub_type_map defined /* Please reflect changes above in event_hdl_sub_type_map defined

View file

@ -61,7 +61,6 @@ extern struct cfgfile fileless_cfg;
/* storage for collected libs */ /* storage for collected libs */
extern void *lib_storage; extern void *lib_storage;
extern size_t lib_size; extern size_t lib_size;
extern char *lib_output_file;
struct proxy; struct proxy;
struct server; struct server;

View file

@ -98,7 +98,7 @@ enum h1m_state {
#define H1_MF_UPG_WEBSOCKET 0x00008000 // Set for a Websocket upgrade handshake #define H1_MF_UPG_WEBSOCKET 0x00008000 // Set for a Websocket upgrade handshake
#define H1_MF_TE_CHUNKED 0x00010000 // T-E "chunked" #define H1_MF_TE_CHUNKED 0x00010000 // T-E "chunked"
#define H1_MF_TE_OTHER 0x00020000 // T-E other than supported ones found (only "chunked" is supported for now) #define H1_MF_TE_OTHER 0x00020000 // T-E other than supported ones found (only "chunked" is supported for now)
#define H1_MF_UPG_HDR 0x00040000 // non-empty Upgrapde header found /* unused: 0x00040000 */
#define H1_MF_NOT_HTTP 0x00080000 // Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) #define H1_MF_NOT_HTTP 0x00080000 // Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted)
/* Mask to use to reset H1M flags when we restart headers parsing. /* Mask to use to reset H1M flags when we restart headers parsing.
* *

View file

@ -27,7 +27,7 @@
#include <haproxy/buf-t.h> #include <haproxy/buf-t.h>
#include <haproxy/mux_quic-t.h> #include <haproxy/mux_quic-t.h>
/* H3 unidirectional stream types /* H3 unidirecational stream types
* Emitted as the first byte on the stream to differentiate it. * Emitted as the first byte on the stream to differentiate it.
*/ */
#define H3_UNI_S_T_CTRL 0x00 #define H3_UNI_S_T_CTRL 0x00

View file

@ -47,7 +47,6 @@
#define CLASS_HTTP "HTTP" #define CLASS_HTTP "HTTP"
#define CLASS_HTTP_MSG "HTTPMessage" #define CLASS_HTTP_MSG "HTTPMessage"
#define CLASS_HTTPCLIENT "HTTPClient" #define CLASS_HTTPCLIENT "HTTPClient"
#define CLASS_HTTPCLIENT_REQ "HTTPClientRequest"
#define CLASS_MAP "Map" #define CLASS_MAP "Map"
#define CLASS_APPLET_TCP "AppletTCP" #define CLASS_APPLET_TCP "AppletTCP"
#define CLASS_APPLET_HTTP "AppletHTTP" #define CLASS_APPLET_HTTP "AppletHTTP"
@ -260,11 +259,6 @@ struct hlua_patref_iterator_context {
struct pat_ref_gen *gen; /* the generation we are iterating over */ struct pat_ref_gen *gen; /* the generation we are iterating over */
}; };
struct hlua_state_init_fct {
struct list list;
int (*fct)(lua_State *L, char **errmsg);
};
#else /* USE_LUA */ #else /* USE_LUA */
/************************ For use when Lua is disabled ********************/ /************************ For use when Lua is disabled ********************/

View file

@ -26,17 +26,6 @@
#ifdef USE_LUA #ifdef USE_LUA
/* Lua uses longjmp to perform yield or throwing errors. This
* macro is used only for identifying the function that can
* not return because a longjmp is executed.
* __LJMP marks a prototype of hlua file that can use longjmp.
* WILL_LJMP() marks an lua function that will use longjmp.
* MAY_LJMP() marks an lua function that may use longjmp.
*/
#define __LJMP
#define WILL_LJMP(func) do { func; my_unreachable(); } while(0)
#define MAY_LJMP(func) func
/* The following macros are used to set flags. */ /* The following macros are used to set flags. */
#define HLUA_SET_RUN(__hlua) do {(__hlua)->flags |= HLUA_RUN;} while(0) #define HLUA_SET_RUN(__hlua) do {(__hlua)->flags |= HLUA_RUN;} while(0)
#define HLUA_CLR_RUN(__hlua) do {(__hlua)->flags &= ~HLUA_RUN;} while(0) #define HLUA_CLR_RUN(__hlua) do {(__hlua)->flags &= ~HLUA_RUN;} while(0)
@ -62,11 +51,6 @@
/* Lua HAProxy integration functions. */ /* Lua HAProxy integration functions. */
void hlua_yield_asap(lua_State *L); void hlua_yield_asap(lua_State *L);
void hap_register_hlua_state_init(int (*fct)(lua_State *L, char **errmsg));
/* simplified way to register a lua_State init callback from any file */
#define REGISTER_HLUA_STATE_INIT(fct) \
INITCALL1(STG_REGISTER, hap_register_hlua_state_init, (fct))
const char *hlua_traceback(lua_State *L, const char* sep); const char *hlua_traceback(lua_State *L, const char* sep);
void hlua_ctx_destroy(struct hlua *lua); void hlua_ctx_destroy(struct hlua *lua);
void hlua_init(); void hlua_init();
@ -81,15 +65,6 @@ void hlua_pushref(lua_State *L, int ref);
void hlua_unref(lua_State *L, int ref); void hlua_unref(lua_State *L, int ref);
struct hlua *hlua_gethlua(lua_State *L); struct hlua *hlua_gethlua(lua_State *L);
void hlua_yieldk(lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k, int timeout, unsigned int flags); void hlua_yieldk(lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k, int timeout, unsigned int flags);
int hlua_pusherror(lua_State *L, const char *fmt, ...);
__LJMP static inline void hlua_check_args(lua_State *L, int nb, char *fcn)
{
if (lua_gettop(L) == nb)
return;
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
}
#define check_args hlua_check_args
#else /* USE_LUA */ #else /* USE_LUA */

View file

@ -326,50 +326,6 @@ static inline int is_immutable_header(struct ist hdr)
} }
} }
/* This function parses comma-separated values from <hv> and rewrite it in place,
* skip all occurrences of <value>. It is the caller responsibility to deal with
* empty header value.
*/
static inline void http_remove_header_value(struct ist *hv, struct ist value)
{
char *e, *n, *p;
struct ist word;
word.ptr = hv->ptr - 1; // -1 for next loop's pre-increment
p = hv->ptr;
e = hv->ptr + hv->len;
hv->len = 0;
while (++word.ptr < e) {
/* skip leading delimiter and blanks */
if (HTTP_IS_LWS(*word.ptr))
continue;
n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
word.len = n - word.ptr;
/* trim trailing blanks */
while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
word.len--;
if (isteqi(word, value))
goto skip_val;
if (hv->ptr + hv->len == p) {
/* no rewrite done till now */
hv->len = n - hv->ptr;
}
else {
if (hv->len)
hv->ptr[hv->len++] = ',';
istcat(hv, word, e - hv->ptr);
}
skip_val:
word.ptr = p = n;
}
}
#endif /* _HAPROXY_HTTP_H */ #endif /* _HAPROXY_HTTP_H */
/* /*

View file

@ -38,9 +38,4 @@ static inline int httpclient_started(struct httpclient *hc)
return !!(hc->flags & HTTPCLIENT_FS_STARTED); return !!(hc->flags & HTTPCLIENT_FS_STARTED);
} }
#ifdef USE_LUA
#include <haproxy/hlua-t.h>
void hlua_httpclient_destroy_all(struct hlua *hlua);
#endif
#endif /* !_HAPROXY_HTTPCLIENT_H */ #endif /* !_HAPROXY_HTTPCLIENT_H */

View file

@ -141,7 +141,6 @@
#define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */ #define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */
#define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */ #define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */
#define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */ #define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */
#define HTX_SL_F_UPG_HDR 0x00008000 /* non-empty Upgrapde header found */
/* This function is used to report flags in debugging tools. Please reflect /* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the * below any single-bit flag addition above in the same order via the
@ -158,8 +157,7 @@ static forceinline char *hsl_show_flags(char *buf, size_t len, const char *delim
_(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11, _(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11,
_(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP, _(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP,
_(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY, _(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY,
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG, _(HTX_SL_F_BODYLESS_RESP, _(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG)))))))))))));
_(HTX_SL_F_NOT_HTTP, _(HTX_SL_F_UPG_HDR))))))))))))))));
/* epilogue */ /* epilogue */
_(~0U); _(~0U);
return buf; return buf;

View file

@ -121,8 +121,8 @@ static inline size_t array_size_or_fail(size_t m, size_t n)
{ {
size_t size; size_t size;
if (unlikely(mulsz_overflow(m, n, &size))) if (mulsz_overflow(m, n, &size))
return DISGUISE(~(size_t)0); return ~(size_t)0;
return size; return size;
} }

View file

@ -286,11 +286,9 @@ static forceinline char *qcc_show_flags(char *buf, size_t len, const char *delim
/* flags */ /* flags */
_(QC_CF_ERRL, _(QC_CF_ERRL,
_(QC_CF_ERRL_DONE, _(QC_CF_ERRL_DONE,
_(QC_CF_IS_BACK,
_(QC_CF_CONN_FULL, _(QC_CF_CONN_FULL,
_(QC_CF_CONN_SHUT,
_(QC_CF_ERR_CONN, _(QC_CF_ERR_CONN,
_(QC_CF_WAIT_HS))))))); _(QC_CF_WAIT_HS)))));
/* epilogue */ /* epilogue */
_(~0U); _(~0U);
return buf; return buf;
@ -333,8 +331,7 @@ static forceinline char *qcs_show_flags(char *buf, size_t len, const char *delim
_(QC_SF_HREQ_RECV, _(QC_SF_HREQ_RECV,
_(QC_SF_TO_STOP_SENDING, _(QC_SF_TO_STOP_SENDING,
_(QC_SF_UNKNOWN_PL_LENGTH, _(QC_SF_UNKNOWN_PL_LENGTH,
_(QC_SF_RECV_RESET, _(QC_SF_RECV_RESET))))))))))));
_(QC_SF_EOI_SUSPENDED)))))))))))));
/* epilogue */ /* epilogue */
_(~0U); _(~0U);
return buf; return buf;

View file

@ -43,14 +43,6 @@
#define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation #define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation
#define QPACK_DEC_INST_SACK 0x80 // Section Acknowledgment #define QPACK_DEC_INST_SACK 0x80 // Section Acknowledgment
/* Encoded field line bitmasks (shared between encoder and decoder) */
#define QPACK_EFL_BITMASK 0xf0
#define QPACK_LFL_WPBNM 0x00 // Literal field line with post-base name reference
#define QPACK_IFL_WPBI 0x10 // Indexed field line with post-based index
#define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name
#define QPACK_LFL_WNR_BIT 0x40 // Literal field line with name reference
#define QPACK_IFL_BIT 0x80 // Indexed field line
/* RFC 9204 6. Error Handling */ /* RFC 9204 6. Error Handling */
enum qpack_err { enum qpack_err {
QPACK_ERR_DECOMPRESSION_FAILED = 0x200, QPACK_ERR_DECOMPRESSION_FAILED = 0x200,

View file

@ -93,30 +93,24 @@ static inline struct ist qpack_get_value(const struct qpack_dht *dht, const stru
return ret; return ret;
} }
/* takes an absolute idx (including static table offset), returns the associated name */ /* takes an idx, returns the associated name */
static inline struct ist qpack_idx_to_name(const struct qpack_dht *dht, uint32_t idx) static inline struct ist qpack_idx_to_name(const struct qpack_dht *dht, uint32_t idx)
{ {
const struct qpack_dte *dte; const struct qpack_dte *dte;
if (idx < QPACK_SHT_SIZE) dte = qpack_get_dte(dht, idx);
return ist("### ERR ###"); /* static table entries not accessible via dht */
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
if (!dte) if (!dte)
return ist("### ERR ###"); // error return ist("### ERR ###"); // error
return qpack_get_name(dht, dte); return qpack_get_name(dht, dte);
} }
/* takes an absolute idx (including static table offset), returns the associated value */ /* takes an idx, returns the associated value */
static inline struct ist qpack_idx_to_value(const struct qpack_dht *dht, uint32_t idx) static inline struct ist qpack_idx_to_value(const struct qpack_dht *dht, uint32_t idx)
{ {
const struct qpack_dte *dte; const struct qpack_dte *dte;
if (idx < QPACK_SHT_SIZE) dte = qpack_get_dte(dht, idx);
return ist("### ERR ###"); /* static table entries not accessible via dht */
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
if (!dte) if (!dte)
return ist("### ERR ###"); // error return ist("### ERR ###"); // error

View file

@ -135,8 +135,6 @@ static inline void in46un_to_addr(const union sockaddr_in46 *src,
in6->sin6_family = AF_INET6; in6->sin6_family = AF_INET6;
in6->sin6_addr = src->in6.sin6_addr; in6->sin6_addr = src->in6.sin6_addr;
in6->sin6_port = src->in6.sin6_port; in6->sin6_port = src->in6.sin6_port;
in6->sin6_flowinfo = src->in6.sin6_flowinfo;
in6->sin6_scope_id = src->in6.sin6_scope_id;
break; break;
default: default:

View file

@ -1,5 +1,5 @@
/* /*
* include/haproxy/resolvers-t.h * include/haproxy/dns-t.h
* This file provides structures and types for DNS. * This file provides structures and types for DNS.
* *
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com> * Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
@ -114,7 +114,7 @@ struct resolv_answer_item {
char name[DNS_MAX_NAME_SIZE+1]; /* answer name */ char name[DNS_MAX_NAME_SIZE+1]; /* answer name */
int16_t type; /* question type */ int16_t type; /* question type */
int16_t class; /* query class */ int16_t class; /* query class */
uint32_t ttl; /* response TTL */ int32_t ttl; /* response TTL */
int16_t priority; /* SRV type priority */ int16_t priority; /* SRV type priority */
uint16_t weight; /* SRV type weight */ uint16_t weight; /* SRV type weight */
uint16_t port; /* SRV type port */ uint16_t port; /* SRV type port */
@ -281,7 +281,7 @@ enum {
* matching preference was found. * matching preference was found.
*/ */
RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found
* OR provided IP found and preference is not matched and an IP * OR provided IP found and preference is not match and an IP
* matching preference was found. * matching preference was found.
*/ */
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */ RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */

View file

@ -1,5 +1,5 @@
/* /*
* include/haproxy/resolvers.h * include/haproxy/dns.h
* This file provides functions related to DNS protocol * This file provides functions related to DNS protocol
* *
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com> * Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>

View file

@ -279,8 +279,6 @@ struct srv_per_thread {
struct ceb_root *idle_conns; /* Shareable idle connections */ struct ceb_root *idle_conns; /* Shareable idle connections */
struct ceb_root *safe_conns; /* Safe idle connections */ struct ceb_root *safe_conns; /* Safe idle connections */
struct ceb_root *avail_conns; /* Connections in use, but with still new streams available */ struct ceb_root *avail_conns; /* Connections in use, but with still new streams available */
struct server *srv; /* Back-pointer to the server */
struct eb32_node idle_node; /* When to next do cleanup in the idle connections */
#ifdef USE_QUIC #ifdef USE_QUIC
struct ist quic_retry_token; struct ist quic_retry_token;
#endif #endif
@ -403,6 +401,7 @@ struct server {
* thread, and generally at the same time. * thread, and generally at the same time.
*/ */
THREAD_ALIGN(); THREAD_ALIGN();
struct eb32_node idle_node; /* When to next do cleanup in the idle connections */
unsigned int curr_idle_conns; /* Current number of orphan idling connections, both the idle and the safe lists */ unsigned int curr_idle_conns; /* Current number of orphan idling connections, both the idle and the safe lists */
unsigned int curr_idle_nb; /* Current number of connections in the idle list */ unsigned int curr_idle_nb; /* Current number of connections in the idle list */
unsigned int curr_safe_nb; /* Current number of connections in the safe list */ unsigned int curr_safe_nb; /* Current number of connections in the safe list */

View file

@ -41,9 +41,9 @@
#include <haproxy/tools.h> #include <haproxy/tools.h>
__decl_thread(extern HA_SPINLOCK_T idle_conn_srv_lock);
extern struct idle_conns idle_conns[MAX_THREADS]; extern struct idle_conns idle_conns[MAX_THREADS];
extern struct task *idle_conn_task[MAX_THREADS]; extern struct task *idle_conn_task;
extern struct eb_root idle_conn_srv[MAX_THREADS];
extern struct mt_list servers_list; extern struct mt_list servers_list;
extern struct dict server_key_dict; extern struct dict server_key_dict;

View file

@ -50,7 +50,7 @@ struct certificate_ocsp {
int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */ int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */ int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
struct buffer response; struct buffer response;
unsigned long expire; long expire;
X509 *issuer; X509 *issuer;
STACK_OF(X509) *chain; STACK_OF(X509) *chain;
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */ struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */

View file

@ -255,7 +255,6 @@ struct ssl_keylog {
#define SSL_SOCK_F_KTLS_RECV (1 << 3) /* kTLS receive is configure on that socket */ #define SSL_SOCK_F_KTLS_RECV (1 << 3) /* kTLS receive is configure on that socket */
#define SSL_SOCK_F_CTRL_SEND (1 << 4) /* We want to send a kTLS control message for that socket */ #define SSL_SOCK_F_CTRL_SEND (1 << 4) /* We want to send a kTLS control message for that socket */
#define SSL_SOCK_F_HAS_ALPN (1 << 5) /* An ALPN has been negotiated */ #define SSL_SOCK_F_HAS_ALPN (1 << 5) /* An ALPN has been negotiated */
#define SSL_SOCK_F_KTLS_ULP (1 << 6) /* TLS ULP is enabled on that socket */
struct ssl_sock_ctx { struct ssl_sock_ctx {
struct connection *conn; struct connection *conn;

View file

@ -53,7 +53,7 @@
/* use this to check a task state or to clean it up before queueing */ /* use this to check a task state or to clean it up before queueing */
#define TASK_WOKEN_ANY (TASK_WOKEN_OTHER|TASK_WOKEN_INIT|TASK_WOKEN_TIMER| \ #define TASK_WOKEN_ANY (TASK_WOKEN_OTHER|TASK_WOKEN_INIT|TASK_WOKEN_TIMER| \
TASK_WOKEN_IO|TASK_WOKEN_SIGNAL|TASK_WOKEN_MSG| \ TASK_WOKEN_IO|TASK_WOKEN_SIGNAL|TASK_WOKEN_MSG| \
TASK_WOKEN_RES|TASK_WOKEN_WQ) TASK_WOKEN_RES)
#define TASK_F_TASKLET 0x00008000 /* nature of this task: 0=task 1=tasklet */ #define TASK_F_TASKLET 0x00008000 /* nature of this task: 0=task 1=tasklet */
#define TASK_F_USR1 0x00010000 /* preserved user flag 1, application-specific, def:0 */ #define TASK_F_USR1 0x00010000 /* preserved user flag 1, application-specific, def:0 */
@ -61,8 +61,7 @@
#define TASK_F_UEVT2 0x00040000 /* one-shot user event type 2, application specific, def:0 */ #define TASK_F_UEVT2 0x00040000 /* one-shot user event type 2, application specific, def:0 */
#define TASK_F_WANTS_TIME 0x00080000 /* task/tasklet wants th_ctx->sched_call_date to be set */ #define TASK_F_WANTS_TIME 0x00080000 /* task/tasklet wants th_ctx->sched_call_date to be set */
#define TASK_F_UEVT3 0x00100000 /* one-shot user event type 3, application specific, def:0 */ #define TASK_F_UEVT3 0x00100000 /* one-shot user event type 3, application specific, def:0 */
#define TASK_WOKEN_WQ 0x00200000 /* The task has been waken up only to be put in the wait queue, because its expire changed */ /* unused: 0x200000..0x80000000 */
/* unused: 0x400000..0x80000000 */
/* These flags are persistent across scheduler calls */ /* These flags are persistent across scheduler calls */
#define TASK_PERSISTENT (TASK_SELF_WAKING | TASK_KILLED | \ #define TASK_PERSISTENT (TASK_SELF_WAKING | TASK_KILLED | \
@ -83,7 +82,7 @@ static forceinline char *task_show_state(char *buf, size_t len, const char *deli
_(TASK_KILLED, _(TASK_HEAVY, _(TASK_WOKEN_INIT, _(TASK_KILLED, _(TASK_HEAVY, _(TASK_WOKEN_INIT,
_(TASK_WOKEN_TIMER, _(TASK_WOKEN_IO, _(TASK_WOKEN_SIGNAL, _(TASK_WOKEN_TIMER, _(TASK_WOKEN_IO, _(TASK_WOKEN_SIGNAL,
_(TASK_WOKEN_MSG, _(TASK_WOKEN_RES, _(TASK_WOKEN_OTHER, _(TASK_WOKEN_MSG, _(TASK_WOKEN_RES, _(TASK_WOKEN_OTHER,
_(TASK_F_TASKLET, _(TASK_F_USR1, _(TASK_WOKEN_WQ))))))))))))))); _(TASK_F_TASKLET, _(TASK_F_USR1))))))))))))));
/* epilogue */ /* epilogue */
_(~0U); _(~0U);
return buf; return buf;

View file

@ -91,13 +91,14 @@ extern struct pool_head *pool_head_task;
extern struct pool_head *pool_head_tasklet; extern struct pool_head *pool_head_tasklet;
extern struct pool_head *pool_head_notification; extern struct pool_head *pool_head_notification;
__decl_thread(extern HA_RWLOCK_T wq_lock THREAD_ALIGNED());
void __tasklet_wakeup_on(struct tasklet *tl, int thr); void __tasklet_wakeup_on(struct tasklet *tl, int thr);
struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl); struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl);
void task_kill(struct task *t); void task_kill(struct task *t);
void tasklet_kill(struct tasklet *t); void tasklet_kill(struct tasklet *t);
void __task_wakeup(struct task *t); void __task_wakeup(struct task *t);
void __task_queue(struct task *task); void __task_queue(struct task *task, struct eb_root *wq);
static inline void _task_queue(struct task *task, const struct ha_caller *caller);
unsigned int run_tasks_from_lists(unsigned int budgets[]); unsigned int run_tasks_from_lists(unsigned int budgets[]);
@ -117,7 +118,7 @@ void process_runnable_tasks(void);
void wake_expired_tasks(void); void wake_expired_tasks(void);
/* Checks the next timer for the current thread by looking into its own timer /* Checks the next timer for the current thread by looking into its own timer
* list. It may return TICK_ETERNITY if no timer is present. * list and the global one. It may return TICK_ETERNITY if no timer is present.
* Note that the next timer might very well be slightly in the past. * Note that the next timer might very well be slightly in the past.
*/ */
int next_timer_expiry(void); int next_timer_expiry(void);
@ -204,77 +205,6 @@ static inline uint64_t task_mono_time(void)
return th_ctx->sched_call_date; return th_ctx->sched_call_date;
} }
#if !defined(HA_CAS_IS_8B) && !defined(HA_HAVE_CAS_DW)
__decl_thread(extern HA_SPINLOCK_T task_state_tid);
#endif
static inline int __task_set_state_and_tid(struct task *t, int expected_tid, int new_tid, unsigned int current, unsigned int wanted)
{
#if defined(HA_CAS_IS_8B) || defined(HA_HAVE_CAS_DW)
uint64_t expected_value;
uint64_t new_value;
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
expected_value = ((uint64_t)(current) << 32) | (uint32_t)expected_tid;
#else
expected_value = current | ((uint64_t)expected_tid << 32);
#endif
do {
int tid_seen;
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
tid_seen = (expected_value & 0xffffffff);
if (tid_seen != expected_tid)
return 0;
if ((expected_value >> 32) != current)
return 0;
new_value = ((uint64_t)wanted << 32) | (uint32_t)new_tid;
#else
tid_seen = (expected_value >> 32);
if (tid_seen != expected_tid)
return 0;
if ((expected_value & 0xffffffff) != current)
return 0;
new_value = wanted | ((uint64_t)new_tid << 32);
#endif
#if defined(HA_CAS_IS_8B)
} while (!HA_ATOMIC_CAS((uint64_t *)&t->state, &expected_value, new_value) && __ha_cpu_relax());
#elif defined(HA_HAVE_CAS_DW)
} while (!HA_ATOMIC_DWCAS((uint64_t *)&t->state, &expected_value, &new_value) && __ha_cpu_relax());
#endif
return 1;
#else /* !HA_CAS_IS_8B && !HA_HAVE_CAS_DW */
int old_state;
int ret = 0;
HA_SPIN_LOCK(OTHER_LOCK, &task_state_tid);
if (_HA_ATOMIC_LOAD(&t->tid) == expected_tid) {
old_state = _HA_ATOMIC_LOAD(&t->state);
if (old_state == current && HA_ATOMIC_CAS(&t->state, &old_state, wanted)) {
_HA_ATOMIC_STORE(&t->tid, new_tid);
ret = 1;
}
}
HA_SPIN_UNLOCK(OTHER_LOCK, &task_state_tid);
return ret;
#endif
}
static inline int __task_get_new_tid_field(int curtid)
{
if (curtid >= 0 || curtid < -1)
return curtid;
return -2 - tid;
}
static inline int __task_get_current_owner(int curtid)
{
if (curtid >= 0 || curtid == -1)
return curtid;
return ~(curtid + 1);
}
/* puts the task <t> in run queue with reason flags <f>, and returns <t> */ /* puts the task <t> in run queue with reason flags <f>, and returns <t> */
/* This will put the task in the local runqueue if the task is only runnable /* This will put the task in the local runqueue if the task is only runnable
* by the current thread, in the global runqueue otherwies. With DEBUG_TASK, * by the current thread, in the global runqueue otherwies. With DEBUG_TASK,
@ -290,9 +220,7 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
state = _HA_ATOMIC_OR_FETCH(&t->state, f); state = _HA_ATOMIC_OR_FETCH(&t->state, f);
while (!(state & (TASK_RUNNING | TASK_QUEUED))) { while (!(state & (TASK_RUNNING | TASK_QUEUED))) {
int expected_tid = _HA_ATOMIC_LOAD(&t->tid); if (_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED)) {
if (__task_set_state_and_tid(t, expected_tid, __task_get_new_tid_field(expected_tid), state, state | TASK_QUEUED)) {
if (likely(caller)) { if (likely(caller)) {
caller = HA_ATOMIC_XCHG(&t->caller, caller); caller = HA_ATOMIC_XCHG(&t->caller, caller);
BUG_ON((ulong)caller & 1); BUG_ON((ulong)caller & 1);
@ -303,7 +231,6 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
__task_wakeup(t); __task_wakeup(t);
break; break;
} }
state = _HA_ATOMIC_LOAD(&t->state);
} }
} }
@ -318,28 +245,14 @@ static inline void task_drop_running(struct task *t, unsigned int f)
{ {
unsigned int state, new_state; unsigned int state, new_state;
state = _HA_ATOMIC_LOAD(&t->state);
while (1) { while (1) {
int cur_tid, new_tid; new_state = state | f;
state = _HA_ATOMIC_LOAD(&t->state);
new_state = (state | f) &~ TASK_RUNNING;
cur_tid = t->tid;
if ((new_state & TASK_WOKEN_WQ) && __task_get_current_owner(cur_tid) == tid) {
_task_queue(t, NULL);
new_state &= ~TASK_WOKEN_WQ;
}
if (new_state & TASK_WOKEN_ANY) if (new_state & TASK_WOKEN_ANY)
new_state |= TASK_QUEUED; new_state |= TASK_QUEUED;
if (_HA_ATOMIC_CAS(&t->state, &state, new_state & ~TASK_RUNNING))
if ((new_state & TASK_QUEUED) || cur_tid >= 0 || task_in_wq(t) ||
__task_get_current_owner(cur_tid) != tid)
new_tid = cur_tid;
else
new_tid = -1;
if (__task_set_state_and_tid(t, cur_tid, new_tid, state, new_state))
break; break;
__ha_cpu_relax(); __ha_cpu_relax();
} }
@ -360,21 +273,31 @@ static inline struct task *__task_unlink_wq(struct task *t)
return t; return t;
} }
/* remove a task from its wait queue, which during normal operations will be /* remove a task from its wait queue. It may either be the local wait queue if
* the current thread's wait queue. * the task is bound to a single thread or the global queue. If the task uses a
* shared wait queue, the global wait queue lock is used.
*/ */
static inline struct task *task_unlink_wq(struct task *t) static inline struct task *task_unlink_wq(struct task *t)
{ {
unsigned long locked;
if (likely(task_in_wq(t))) { if (likely(task_in_wq(t))) {
BUG_ON(__task_get_current_owner(t->tid) != tid && !(global.mode & MODE_STOPPING)); locked = t->tid < 0;
BUG_ON(t->tid >= 0 && t->tid != tid && !(global.mode & MODE_STOPPING));
if (locked)
HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
__task_unlink_wq(t); __task_unlink_wq(t);
if (locked)
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
} }
return t; return t;
} }
/* Place <task> into the wait queue, where it may already be. If the expiration /* Place <task> into the wait queue, where it may already be. If the expiration
* timer is infinite, do nothing and rely on wake_expired_task to clean up. * timer is infinite, do nothing and rely on wake_expired_task to clean up.
* If the task uses a shared wait queue, it's queued into the global wait queue,
* protected by the global wq_lock, otherwise by it necessarily belongs to the
* current thread'sand is queued without locking.
*/ */
#define task_queue(t) \ #define task_queue(t) \
_task_queue(t, MK_CALLER(WAKEUP_TYPE_TASK_QUEUE, 0, 0)) _task_queue(t, MK_CALLER(WAKEUP_TYPE_TASK_QUEUE, 0, 0))
@ -393,17 +316,34 @@ static inline void _task_queue(struct task *task, const struct ha_caller *caller
if (!tick_isset(task->expire)) if (!tick_isset(task->expire))
return; return;
BUG_ON(task->tid >= 0 && task->tid != tid); #ifdef USE_THREAD
if (task->tid < 0) {
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) { HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
if (likely(caller)) { if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
caller = HA_ATOMIC_XCHG(&task->caller, caller); if (likely(caller)) {
BUG_ON((ulong)caller & 1); caller = HA_ATOMIC_XCHG(&task->caller, caller);
BUG_ON((ulong)caller & 1);
#ifdef DEBUG_TASK #ifdef DEBUG_TASK
HA_ATOMIC_STORE(&task->debug.prev_caller, caller); HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
#endif #endif
}
__task_queue(task, &tg_ctx->timers);
}
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
} else
#endif
{
BUG_ON(task->tid != tid);
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
if (likely(caller)) {
caller = HA_ATOMIC_XCHG(&task->caller, caller);
BUG_ON((ulong)caller & 1);
#ifdef DEBUG_TASK
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
#endif
}
__task_queue(task, &th_ctx->timers);
} }
__task_queue(task);
} }
} }
@ -423,11 +363,6 @@ static inline void task_set_thread(struct task *t, int thr)
/* no shared queue without threads */ /* no shared queue without threads */
thr = 0; thr = 0;
#endif #endif
/*
* Nothing to do, the task is only temporarily owned
*/
if (thr == -1 && t->tid == -2 - tid)
return;
if (unlikely(task_in_wq(t))) { if (unlikely(task_in_wq(t))) {
task_unlink_wq(t); task_unlink_wq(t);
t->tid = thr; t->tid = thr;
@ -505,30 +440,24 @@ static inline void _tasklet_wakeup_on(struct tasklet *tl, int thr, uint f, const
static inline void _task_instant_wakeup(struct task *t, unsigned int f, const struct ha_caller *caller) static inline void _task_instant_wakeup(struct task *t, unsigned int f, const struct ha_caller *caller)
{ {
int thr = t->tid; int thr = t->tid;
int newtid;
unsigned int state; unsigned int state;
if (thr < 0)
thr = tid;
/* first, let's update the task's state with the wakeup condition */ /* first, let's update the task's state with the wakeup condition */
state = _HA_ATOMIC_OR_FETCH(&t->state, f); state = _HA_ATOMIC_OR_FETCH(&t->state, f);
/* next we need to make sure the task was not/will not be added to the /* next we need to make sure the task was not/will not be added to the
* run queue because the tasklet list's mt_list uses the same storage * run queue because the tasklet list's mt_list uses the same storage
* as the task's run_queue. * as the task's run_queue.
*/ */
do { do {
state = _HA_ATOMIC_LOAD(&t->state);
thr = t->tid;
if (thr == -1)
newtid = -2 - tid;
else
newtid = thr;
/* do nothing if someone else already added it */ /* do nothing if someone else already added it */
if (state & (TASK_QUEUED|TASK_RUNNING)) if (state & (TASK_QUEUED|TASK_RUNNING))
return; return;
} while (!__task_set_state_and_tid(t, thr, newtid, state, state | TASK_QUEUED)); } while (!_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED));
if (newtid < 0)
thr = __task_get_current_owner(newtid);
BUG_ON_HOT(task_in_rq(t)); BUG_ON_HOT(task_in_rq(t));
/* at this point we're the first ones to add this task to the list */ /* at this point we're the first ones to add this task to the list */
@ -778,11 +707,11 @@ static inline void tasklet_set_tid(struct tasklet *tl, int tid)
static inline void _task_schedule(struct task *task, int when, const struct ha_caller *caller) static inline void _task_schedule(struct task *task, int when, const struct ha_caller *caller)
{ {
int did_lock = 0;
/* TODO: mthread, check if there is no task with this test */ /* TODO: mthread, check if there is no task with this test */
if (task_in_rq(task)) if (task_in_rq(task))
return; return;
#ifdef USE_THREAD
if (task->tid < 0) { if (task->tid < 0) {
/* /*
* If the task is already running, then just wake it up, just * If the task is already running, then just wake it up, just
@ -800,26 +729,44 @@ static inline void _task_schedule(struct task *task, int when, const struct ha_c
task_wakeup(task, TASK_WOKEN_OTHER); task_wakeup(task, TASK_WOKEN_OTHER);
return; return;
} }
did_lock = 1;
} else
BUG_ON(task->tid != tid);
if (task_in_wq(task)) /* FIXME: is it really needed to lock the WQ during the check ? */
when = tick_first(when, task->expire); HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
if (task_in_wq(task))
when = tick_first(when, task->expire);
task->expire = when; task->expire = when;
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) { if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
if (likely(caller)) { if (likely(caller)) {
caller = HA_ATOMIC_XCHG(&task->caller, caller); caller = HA_ATOMIC_XCHG(&task->caller, caller);
BUG_ON((ulong)caller & 1); BUG_ON((ulong)caller & 1);
#ifdef DEBUG_TASK #ifdef DEBUG_TASK
HA_ATOMIC_STORE(&task->debug.prev_caller, caller); HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
#endif #endif
}
__task_queue(task, &tg_ctx->timers);
} }
__task_queue(task);
}
if (did_lock)
task_drop_running(task, 0); task_drop_running(task, 0);
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
} else
#endif
{
BUG_ON(task->tid != tid);
if (task_in_wq(task))
when = tick_first(when, task->expire);
task->expire = when;
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
if (likely(caller)) {
caller = HA_ATOMIC_XCHG(&task->caller, caller);
BUG_ON((ulong)caller & 1);
#ifdef DEBUG_TASK
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
#endif
}
__task_queue(task, &th_ctx->timers);
}
}
} }
/* returns the string corresponding to a task type as found in the task caller /* returns the string corresponding to a task type as found in the task caller

View file

@ -178,6 +178,7 @@ struct ha_rwlock {
*/ */
enum lock_label { enum lock_label {
TASK_RQ_LOCK, TASK_RQ_LOCK,
TASK_WQ_LOCK,
LISTENER_LOCK, LISTENER_LOCK,
PROXY_LOCK, PROXY_LOCK,
SERVER_LOCK, SERVER_LOCK,

View file

@ -135,6 +135,8 @@ struct tgroup_ctx {
ulong threads_idle; /* mask of threads idling in the poller */ ulong threads_idle; /* mask of threads idling in the poller */
ulong stopping_threads; /* mask of threads currently stopping */ ulong stopping_threads; /* mask of threads currently stopping */
struct eb_root timers; /* wait queue (sorted timers tree, global, accessed under wq_lock) */
uint niced_tasks; /* number of niced tasks in this group's run queues */ uint niced_tasks; /* number of niced tasks in this group's run queues */
uint committed_extra_streams; /* sum of extra front streams committed by muxes in this group */ uint committed_extra_streams; /* sum of extra front streams committed by muxes in this group */

View file

@ -188,12 +188,4 @@ struct file_name_node {
char name[VAR_ARRAY]; /* storage, used with cebus_*() */ char name[VAR_ARRAY]; /* storage, used with cebus_*() */
}; };
/* a pair of uint64_t. It's purposely arranged in little endian to help
* being vectorized on modern processors.
*/
struct uint64_pair {
uint64_t l;
uint64_t h;
};
#endif /* _HAPROXY_TOOLS_T_H */ #endif /* _HAPROXY_TOOLS_T_H */

View file

@ -1154,8 +1154,6 @@ void *get_sym_curr_addr(const char *name);
void *get_sym_next_addr(const char *name); void *get_sym_next_addr(const char *name);
int dump_libs(struct buffer *output, int with_addr); int dump_libs(struct buffer *output, int with_addr);
void collect_libs(void); void collect_libs(void);
void free_collected_libs(void);
int copy_libs_to_file(void);
/* Note that this may result in opening libgcc() on first call, so it may need /* Note that this may result in opening libgcc() on first call, so it may need
* to have been called once before chrooting. * to have been called once before chrooting.
@ -1290,27 +1288,11 @@ static inline void _ha_aligned_free(void *ptr)
int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz); int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
/* PRNG */ /* PRNG */
struct uint64_pair _ha_random64_pair_hashed(void);
void ha_generate_uuid_v4(struct buffer *output); void ha_generate_uuid_v4(struct buffer *output);
void ha_generate_uuid_v7(struct buffer *output); void ha_generate_uuid_v7(struct buffer *output);
void ha_random_seed(const unsigned char *seed, size_t len); void ha_random_seed(const unsigned char *seed, size_t len);
void ha_random_seed_thread(void); void ha_random_jump96(uint32_t dist);
void ha_random_jump128(uint32_t dist);
void ha_random_jump192(uint32_t dist);
uint64_t ha_random64(void); uint64_t ha_random64(void);
uint64_t ha_random64_internal(void);
/* Returns a pair of uint64_t randoms hashed so as not to disclose the internal
* PRNG state.
*/
static inline void ha_random64_pair_hashed(uint64_t *l, uint64_t *h)
{
struct uint64_pair ret = _ha_random64_pair_hashed();
*l = ret.l;
*h = ret.h;
}
static inline uint32_t ha_random32() static inline uint32_t ha_random32()
{ {

View file

@ -33,7 +33,7 @@
#ifdef CONFIG_PRODUCT_BRANCH #ifdef CONFIG_PRODUCT_BRANCH
#define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH #define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH
#else #else
#define PRODUCT_BRANCH "3.5" #define PRODUCT_BRANCH "3.4"
#endif #endif
#ifdef CONFIG_PRODUCT_STATUS #ifdef CONFIG_PRODUCT_STATUS

View file

@ -1,77 +0,0 @@
varnishtest "Health-checks: some external check tests"
feature ignore_unknown_macro
#REGTEST_TYPE=slow
server s1 {
rxreq
expect req.method == GET
expect req.url == /health
expect req.proto == HTTP/1.1
txresp
} -start
syslog S1 -level notice {
recv
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded, reason: External check passed, code: 0"
recv
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded, reason: External check passed, code: 0"
} -start
syslog S2 -level notice {
recv
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
} -start
haproxy h1 -conf {
global
.if feature(THREAD)
thread-groups 1
.endif
external-check
insecure-fork-wanted
healthcheck http-health
type httpchk
http-check send meth GET uri /health ver HTTP/1.1
defaults
mode http
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
option log-health-checks
backend be1
log ${S1_addr}:${S1_port} len 2048 local0
option external-check
external-check command /bin/true
server srv ${h1_li1_addr}:${h1_li1_port} check inter 100ms rise 1 fall 1
defaults
mode http
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
option external-check
external-check command /bin/true
option log-health-checks
backend be2
log ${S1_addr}:${S1_port} len 2048 local0
server srv ${h1_li1_addr}:${h1_li1_port} check inter 100ms rise 1 fall 1
backend be3
log ${S2_addr}:${S2_port} len 2048 local0
option external-check
external-check command /bin/true
server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1 healthcheck http-health
listen li1
mode http
bind "fd@${li1}"
http-request return status 200
} -start
syslog S1 -wait
syslog S2 -wait

View file

@ -144,7 +144,7 @@ syslog S2 -level notice {
recv recv
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded" expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
recv recv
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded" expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9] succeeded"
recv recv
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded" expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
recv recv

View file

@ -9,7 +9,7 @@ haproxy h1 -conf {
thread-groups 1 thread-groups 1
.endif .endif
tune.lua.openlibs string tune.lua.openlibs none
tune.lua.bool-sample-conversion normal tune.lua.bool-sample-conversion normal
lua-load ${testdir}/h_txn_get_priv.lua lua-load ${testdir}/h_txn_get_priv.lua

View file

@ -18,8 +18,6 @@ core.register_service("fakeserv", "http", function(applet)
end) end)
local function cron() local function cron()
local httpclient = core.httpclient()
-- wait for until the correct port is set through the c0 request.. -- wait for until the correct port is set through the c0 request..
while vtc_port == 0 do while vtc_port == 0 do
core.msleep(1) core.msleep(1)
@ -32,16 +30,19 @@ local function cron()
body = body .. i .. ' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n' body = body .. i .. ' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n'
end end
core.Info("First httpclient request") core.Info("First httpclient request")
local httpclient = core.httpclient()
local response = httpclient:post{url="http://127.0.0.1:" .. vtc_port, body=body} local response = httpclient:post{url="http://127.0.0.1:" .. vtc_port, body=body}
core.Info("Received: " .. response.body) core.Info("Received: " .. response.body)
body = response.body body = response.body
core.Info("Second httpclient request") core.Info("Second httpclient request")
local response2 = httpclient:post{url="http://127.0.0.1:" .. vtc_port2, body=body} local httpclient2 = core.httpclient()
local response2 = httpclient2:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
core.Info("Third httpclient request") core.Info("Third httpclient request")
local response3 = httpclient:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }} local httpclient3 = core.httpclient()
local response3 = httpclient3:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
end end

View file

@ -42,7 +42,7 @@ haproxy h1 -conf {
thread-groups 1 thread-groups 1
.endif .endif
tune.lua.openlibs string tune.lua.openlibs none
tune.lua.bool-sample-conversion normal tune.lua.bool-sample-conversion normal
lua-load ${testdir}/lua_httpclient.lua lua-load ${testdir}/lua_httpclient.lua

View file

@ -14,7 +14,7 @@ haproxy h1 -conf {
thread-groups 1 thread-groups 1
.endif .endif
tune.lua.openlibs string tune.lua.openlibs none
tune.lua.bool-sample-conversion normal tune.lua.bool-sample-conversion normal
lua-load ${testdir}/lua_socket.lua lua-load ${testdir}/lua_socket.lua

View file

@ -10,7 +10,7 @@ haproxy h1 -conf {
thread-groups 1 thread-groups 1
.endif .endif
tune.lua.openlibs string,table tune.lua.openlibs none
tune.lua.bool-sample-conversion normal tune.lua.bool-sample-conversion normal
lua-load ${testdir}/txn_get_priv.lua lua-load ${testdir}/txn_get_priv.lua
lua-load ${testdir}/txn_get_priv-print_r.lua lua-load ${testdir}/txn_get_priv-print_r.lua

View file

@ -1 +0,0 @@
../ssl/certs

View file

@ -1,39 +0,0 @@
varnishtest "HTTP/3 over QMux"
feature ignore_unknown_macro
# TODO to adjust once QMux compilation is QUIC/SSL free
feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC) && !feature(QUIC_OPENSSL_COMPAT) && !feature(OPENSSL_WOLFSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
haproxy h1 -conf {
global
.if feature(THREAD)
thread-groups 1
.endif
expose-experimental-directives
ssl-server-verify none
defaults
mode http
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
frontend fterm
bind "fd@${fterm}" ssl crt ${testdir}/certs/common.pem alpn h3
http-request return status 200 hdr x-alpn %[ssl_fc_alpn] hdr x-ver %[req.ver]
frontend fpub
bind "fd@${fpub}" proto h1
use_backend be
backend be
server hap ${h1_fterm_addr}:${h1_fterm_port} ssl alpn h3
} -start
client c1 -connect ${h1_fpub_sock} {
txreq
rxresp
expect resp.status == 200
expect resp.http.x-alpn == "h3"
expect resp.http.x-ver == "3.0"
} -run

View file

@ -1,38 +0,0 @@
varnishtest "HTTP/3 over clear QMux"
feature ignore_unknown_macro
# TODO to adjust once QMux compilation is QUIC/SSL free
feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC) && !feature(QUIC_OPENSSL_COMPAT) && !feature(OPENSSL_WOLFSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(3.4-dev14)'"
haproxy h1 -conf {
global
.if feature(THREAD)
thread-groups 1
.endif
expose-experimental-directives
defaults
mode http
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
frontend fterm
bind "fd@${fterm}" proto qmux
http-request return status 200 hdr x-ver %[req.ver]
frontend fpub
bind "fd@${fpub}" proto h1
use_backend be
backend be
server hap ${h1_fterm_addr}:${h1_fterm_port} proto qmux
} -start
client c1 -connect ${h1_fpub_sock} {
txreq
rxresp
expect resp.status == 200
expect resp.http.x-ver == "3.0"
} -run

View file

@ -1,9 +1,5 @@
#REGTEST_TYPE=broken #REGTEST_TYPE=slow
# reg-test is around ~2.5s # reg-test is around ~2.5s
# It is incompatible with ssl/ocsp_auto_update.vtc running in parallel because
# both start a server on the same port, whose URL is specified in the test
# certificates. Given that the test is essentially about testing OCSP update,
# let's just use the more generic SSL one.
# broken with BoringSSL. # broken with BoringSSL.

View file

@ -19,7 +19,6 @@
#include <haproxy/acme-t.h> #include <haproxy/acme-t.h>
#include <haproxy/acme_resolvers.h> #include <haproxy/acme_resolvers.h>
#include <haproxy/event_hdl.h>
#include <haproxy/base64.h> #include <haproxy/base64.h>
#include <haproxy/intops.h> #include <haproxy/intops.h>
#include <haproxy/cfgparse.h> #include <haproxy/cfgparse.h>
@ -38,12 +37,6 @@
#include <haproxy/ssl_utils.h> #include <haproxy/ssl_utils.h>
#include <haproxy/tools.h> #include <haproxy/tools.h>
#include <haproxy/trace.h> #include <haproxy/trace.h>
#ifdef USE_LUA
#include <lua.h>
#include <lauxlib.h>
#include <haproxy/hlua.h>
#include <haproxy/hlua_fcn.h>
#endif
#define TRACE_SOURCE &trace_acme #define TRACE_SOURCE &trace_acme
@ -1457,27 +1450,6 @@ error:
return ret; return ret;
} }
/* mfree callback for EVENT_HDL_SUB_ACME_DEPLOY: frees heap-allocated fields */
static void acme_deploy_event_mfree(const void *data)
{
struct event_hdl_cb_data_acme_deploy *e = (struct event_hdl_cb_data_acme_deploy *)data;
ha_free(&e->safe.crtname);
ha_free(&e->safe.domain);
ha_free(&e->safe.thumbprint);
ha_free(&e->safe.dns_record);
ha_free(&e->safe.provider);
ha_free(&e->safe.vars);
}
/* mfree callback for EVENT_HDL_SUB_ACME_NEWCERT: frees the heap-allocated path */
static void acme_newcert_event_mfree(const void *data)
{
const struct event_hdl_cb_data_acme_newcert *e = data;
free(e->safe.crtname);
}
/* /*
* Update every certificate instances for the new store * Update every certificate instances for the new store
* *
@ -1531,15 +1503,6 @@ int acme_update_certificate(struct task *task, struct acme_ctx *ctx, char **errm
if (dpapi) if (dpapi)
sink_write(dpapi, LOG_HEADER_NONE, 0, line, 3); sink_write(dpapi, LOG_HEADER_NONE, 0, line, 3);
{
struct event_hdl_cb_data_acme_newcert cb_data = { };
cb_data.safe.crtname = strdup(ctx->store->path);
if (cb_data.safe.crtname)
event_hdl_publish(NULL, EVENT_HDL_SUB_ACME_NEWCERT,
EVENT_HDL_CB_DATA_DM(&cb_data, acme_newcert_event_mfree));
}
ctx->store = NULL; ctx->store = NULL;
ret = 0; ret = 0;
@ -1569,7 +1532,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -1599,16 +1562,6 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
key = ctx->store->data->key; key = ctx->store->data->key;
ctx->store->data->key = NULL; ctx->store->data->key = NULL;
/* OpenSSL's BIO_new_mem_buf() expects a NUL-terminated string when
* passed -1. The httpclient buffer lacks this, so manually terminate
* it here to prevent an out-of-bounds heap read during PEM parsing.
*/
if (b_room(&hc->res.buf) < 1) {
memprintf(errmsg, "ACME certificate response has no room for NUL terminator");
goto error;
}
hc->res.buf.area[hc->res.buf.data] = '\0';
/* XXX: might need a function dedicated to this, which does not read a private key */ /* XXX: might need a function dedicated to this, which does not read a private key */
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0) if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
goto error; goto error;
@ -1782,7 +1735,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -1883,7 +1836,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf); TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf);
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -2010,7 +1963,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -2065,7 +2018,6 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
/* if auth is already valid we need to skip solving challenges */ /* if auth is already valid we need to skip solving challenges */
if (strncasecmp("valid", trash.area, trash.data) == 0) { if (strncasecmp("valid", trash.area, trash.data) == 0) {
auth->validated = 1; auth->validated = 1;
auth->ready = ctx->cfg->cond_ready; /* no challenge needed, satisfy all readiness conditions */
goto out; goto out;
} }
@ -2219,22 +2171,6 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
dpapi = sink_find("dpapi"); dpapi = sink_find("dpapi");
if (dpapi) if (dpapi)
sink_write(dpapi, LOG_HEADER_NONE, 0, line, nmsg); sink_write(dpapi, LOG_HEADER_NONE, 0, line, nmsg);
{
struct event_hdl_cb_data_acme_deploy cb_data = { };
cb_data.safe.crtname = strdup(ctx->store->path);
cb_data.safe.domain = isttest(auth->dns) ? strndup(auth->dns.ptr, auth->dns.len) : NULL;
cb_data.safe.thumbprint = ctx->cfg->account.thumbprint ? strdup(ctx->cfg->account.thumbprint) : NULL;
cb_data.safe.dns_record = strndup(dns_record->area, dns_record->data);
cb_data.safe.provider = ctx->cfg->provider ? strdup(ctx->cfg->provider) : NULL;
cb_data.safe.vars = ctx->cfg->vars ? strdup(ctx->cfg->vars) : NULL;
if (cb_data.safe.crtname && cb_data.safe.dns_record)
event_hdl_publish(NULL, EVENT_HDL_SUB_ACME_DEPLOY,
EVENT_HDL_CB_DATA_DM(&cb_data, acme_deploy_event_mfree));
else
acme_deploy_event_mfree(&cb_data);
}
} }
else if (strcasecmp(ctx->cfg->challenge, "http-01") == 0) { else if (strcasecmp(ctx->cfg->challenge, "http-01") == 0) {
/* only useful for http-01 */ /* only useful for http-01 */
@ -2343,7 +2279,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -2522,7 +2458,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Location"))) { if (isteqi(hdr->n, ist("Location"))) {
istfree(&ctx->kid); istfree(&ctx->kid);
ctx->kid = istdup(hdr->v); ctx->kid = istdup(hdr->v);
@ -2589,7 +2525,7 @@ int acme_nonce(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -3559,64 +3495,16 @@ err:
return cli_dynerr(appctx, errmsg); return cli_dynerr(appctx, errmsg);
} }
/*
* Change the readiness of an ACME challenge per couple <crt>+<dns>
* Return:
* - -2 if the crt was not found
* - -1 if an non-ready couple crt+dns wasn't not found
* - 0 if the challenges are ready for the certificate
* - > 0 with the number of remaining challenge to enable
*/
int acme_challenge_ready(const char *crt, const char *dns)
{
struct ebmb_node *node = NULL;
struct acme_ctx *ctx = NULL;
struct acme_auth *auth = NULL;
int found = 0;
int remain = 0;
HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock);
node = ebst_lookup(&acme_tasks, crt);
if (!node) {
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
return -2;
}
ctx = ebmb_entry(node, struct acme_ctx, node);
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
auth = ctx->auths;
while (auth) {
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
if ((auth->ready & ACME_RDY_CLI) == 0) {
auth->ready |= ACME_RDY_CLI;
found++;
}
}
if ((auth->ready & ACME_RDY_CLI) == 0)
remain++;
auth = auth->next;
}
/* no remaining challenge to ready: wake the task only if it is
* currently suspended in ACME_CLI_WAIT, not in the middle of an
* HTTP exchange.
*/
if (!remain && ctx->state == ACME_CLI_WAIT)
task_wakeup(ctx->task, TASK_WOKEN_MSG);
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
if (!found)
return -1;
return remain;
}
static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx *appctx, void *private) static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx *appctx, void *private)
{ {
char *msg = NULL; char *msg = NULL;
const char *crt; const char *crt;
const char *dns; const char *dns;
int ret; struct acme_ctx *ctx = NULL;
struct acme_auth *auth = NULL;
int found = 0;
int remain = 0;
struct ebmb_node *node = NULL;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1; return 1;
@ -3629,13 +3517,39 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
crt = args[2]; crt = args[2];
dns = args[4]; dns = args[4];
ret = acme_challenge_ready(crt, dns); HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock);
if (ret < 0) { node = ebst_lookup(&acme_tasks, crt);
return cli_dynerr(appctx, memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns)); if (node) {
} else if (ret == 0) { ctx = ebmb_entry(node, struct acme_ctx, node);
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenges ready! All challenges ready, starting challenges validation!", crt)); if (ctx->cfg->cond_ready & ACME_RDY_CLI)
} else if (ret > 0) { auth = ctx->auths;
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenge(s) ready! Remaining challenges to deploy: %d", crt, ret)); while (auth) {
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
if (!(auth->ready & ACME_RDY_CLI)) {
auth->ready |= ACME_RDY_CLI;
found++;
} else {
memprintf(&msg, "ACME challenge for crt \"%s\" and dns \"%s\" was already READY !\n", crt, dns);
}
}
if ((auth->ready & ACME_RDY_CLI) == 0)
remain++;
auth = auth->next;
}
}
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
if (!found) {
if (!msg)
memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns);
goto err;
} else {
if (!remain) {
if (ctx)
task_wakeup(ctx->task, TASK_WOKEN_MSG);
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! All challenges ready, starting challenges validation!", found, dns));
} else {
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! Remaining challenges to deploy: %d", found, dns, remain));
}
} }
err: err:
@ -3745,127 +3659,6 @@ static void __acme_init(void)
} }
INITCALL0(STG_REGISTER, __acme_init); INITCALL0(STG_REGISTER, __acme_init);
#ifdef USE_LUA
#define CLASS_ACME_EVENT "AcmeEvent"
static int class_acme_event_ref;
/* Push a new AcmeEvent object for an ACME_DEPLOY event onto the Lua stack.
* The object exposes crtname, domain, thumbprint, dns_record fields, and
* optionally provider and vars if they were configured.
*/
static void hlua_fcn_new_acme_event_deploy(lua_State *L, const struct event_hdl_cb_data_acme_deploy *e)
{
lua_newtable(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, class_acme_event_ref);
lua_setmetatable(L, -2);
lua_pushstring(L, e->safe.crtname ? e->safe.crtname : "");
lua_setfield(L, -2, "crtname");
lua_pushstring(L, e->safe.domain ? e->safe.domain : "");
lua_setfield(L, -2, "domain");
lua_pushstring(L, e->safe.thumbprint ? e->safe.thumbprint : "");
lua_setfield(L, -2, "thumbprint");
lua_pushstring(L, e->safe.dns_record ? e->safe.dns_record : "");
lua_setfield(L, -2, "dns_record");
if (e->safe.provider) {
lua_pushstring(L, e->safe.provider);
lua_setfield(L, -2, "provider");
}
if (e->safe.vars) {
lua_pushstring(L, e->safe.vars);
lua_setfield(L, -2, "vars");
}
}
/* Push a new AcmeEvent object for an ACME_NEWCERT event onto the Lua stack.
* The object exposes a <crtname> field with the certificate store name.
*/
static void hlua_fcn_new_acme_event_newcert(lua_State *L, const char *crtname)
{
lua_newtable(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, class_acme_event_ref);
lua_setmetatable(L, -2);
lua_pushstring(L, crtname);
lua_setfield(L, -2, "crtname");
}
/*
* ACME.challenge_ready(crt, dns)
*
* Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
* Returns the number of remaining challenges, or 0 if all challenges are
* ready and validation has been triggered.
* Raises a Lua error if the certificate or domain is not found.
*/
__LJMP static int hlua_acme_challenge_ready(lua_State *L)
{
const char *crt;
const char *dns;
int ret;
if (lua_gettop(L) != 2)
WILL_LJMP(luaL_error(L, "'ACME.challenge_ready' needs 2 arguments."));
crt = MAY_LJMP(luaL_checkstring(L, 1));
dns = MAY_LJMP(luaL_checkstring(L, 2));
ret = acme_challenge_ready(crt, dns);
if (ret == -2)
WILL_LJMP(luaL_error(L, "ACME.challenge_ready: certificate '%s' not found", crt));
if (ret == -1)
WILL_LJMP(luaL_error(L, "ACME.challenge_ready: domain '%s' not found for certificate '%s'", dns, crt));
lua_pushinteger(L, ret);
return 1;
}
static int acme_hlua_init_state(lua_State *L, char **errmsg)
{
/* Register AcmeEvent class */
lua_newtable(L);
class_acme_event_ref = hlua_register_metatable(L, CLASS_ACME_EVENT);
lua_newtable(L);
hlua_class_function(L, "challenge_ready", hlua_acme_challenge_ready);
lua_setglobal(L, "ACME");
return ERR_NONE;
}
REGISTER_HLUA_STATE_INIT(acme_hlua_init_state);
/* Push ACME event data as a Lua table for core.event_sub() handlers.
* Called from hlua_event_hdl_cb_push_args() when the event family is ACME.
*/
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data)
{
if (!lua_checkstack(hlua->T, 3))
WILL_LJMP(luaL_error(hlua->T, "Lua out of memory error."));
if (event_hdl_sub_type_equal(EVENT_HDL_SUB_ACME_DEPLOY, event)) {
struct event_hdl_cb_data_acme_deploy *e_acme = data;
hlua->nargs += 1;
MAY_LJMP(hlua_fcn_new_acme_event_deploy(hlua->T, e_acme));
}
else if (event_hdl_sub_type_equal(EVENT_HDL_SUB_ACME_NEWCERT, event)) {
struct event_hdl_cb_data_acme_newcert *e_acme = data;
hlua->nargs += 1;
MAY_LJMP(hlua_fcn_new_acme_event_newcert(hlua->T, e_acme->safe.crtname));
}
}
#endif /* USE_LUA */
#endif /* ! HAVE_ACME */ #endif /* ! HAVE_ACME */
/* /*

View file

@ -80,10 +80,7 @@ static const char *const memprof_methods[MEMPROF_METH_METHODS] = {
struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1] = { }; struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1] = { };
/* used to detect recursive calls */ /* used to detect recursive calls */
#define MEMPROF_IN_INIT (1U << 0) static THREAD_LOCAL int in_memprof = 0;
#define MEMPROF_IN_HANDLER (1U << 1)
static THREAD_LOCAL uint in_memprof = 0; // arithmetic OR of MEMPROF_IN_*
/* These ones are used by glibc and will be called early. They are in charge of /* These ones are used by glibc and will be called early. They are in charge of
* initializing the handlers with the original functions. * initializing the handlers with the original functions.
@ -140,7 +137,7 @@ static __attribute__((noreturn)) void memprof_die(const char *msg)
*/ */
static void memprof_init() static void memprof_init()
{ {
in_memprof |= MEMPROF_IN_INIT; in_memprof++;
memprof_malloc_handler = get_sym_next_addr("malloc"); memprof_malloc_handler = get_sym_next_addr("malloc");
if (!memprof_malloc_handler) if (!memprof_malloc_handler)
memprof_die("FATAL: malloc() function not found.\n"); memprof_die("FATAL: malloc() function not found.\n");
@ -171,7 +168,7 @@ static void memprof_init()
memprof_aligned_alloc_handler = get_sym_next_addr("aligned_alloc"); memprof_aligned_alloc_handler = get_sym_next_addr("aligned_alloc");
memprof_posix_memalign_handler = get_sym_next_addr("posix_memalign"); memprof_posix_memalign_handler = get_sym_next_addr("posix_memalign");
in_memprof &= ~MEMPROF_IN_INIT; in_memprof--;
} }
/* the initial handlers will initialize all regular handlers and will call the /* the initial handlers will initialize all regular handlers and will call the
@ -180,7 +177,7 @@ static void memprof_init()
*/ */
static void *memprof_malloc_initial_handler(size_t size) static void *memprof_malloc_initial_handler(size_t size)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* it's likely that dlsym() needs malloc(), let's fail */ /* it's likely that dlsym() needs malloc(), let's fail */
return NULL; return NULL;
} }
@ -191,7 +188,7 @@ static void *memprof_malloc_initial_handler(size_t size)
static void *memprof_calloc_initial_handler(size_t nmemb, size_t size) static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* it's likely that dlsym() needs calloc(), let's fail */ /* it's likely that dlsym() needs calloc(), let's fail */
return NULL; return NULL;
} }
@ -201,7 +198,7 @@ static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
static void *memprof_realloc_initial_handler(void *ptr, size_t size) static void *memprof_realloc_initial_handler(void *ptr, size_t size)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* it's likely that dlsym() needs realloc(), let's fail */ /* it's likely that dlsym() needs realloc(), let's fail */
return NULL; return NULL;
} }
@ -212,7 +209,7 @@ static void *memprof_realloc_initial_handler(void *ptr, size_t size)
static char *memprof_strdup_initial_handler(const char *s) static char *memprof_strdup_initial_handler(const char *s)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* probably that dlsym() needs strdup(), let's fail */ /* probably that dlsym() needs strdup(), let's fail */
return NULL; return NULL;
} }
@ -231,7 +228,7 @@ static void memprof_free_initial_handler(void *ptr)
static char *memprof_strndup_initial_handler(const char *s, size_t n) static char *memprof_strndup_initial_handler(const char *s, size_t n)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* probably that dlsym() needs strndup(), let's fail */ /* probably that dlsym() needs strndup(), let's fail */
return NULL; return NULL;
} }
@ -242,7 +239,7 @@ static char *memprof_strndup_initial_handler(const char *s, size_t n)
static void *memprof_valloc_initial_handler(size_t sz) static void *memprof_valloc_initial_handler(size_t sz)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* probably that dlsym() needs valloc(), let's fail */ /* probably that dlsym() needs valloc(), let's fail */
return NULL; return NULL;
} }
@ -253,7 +250,7 @@ static void *memprof_valloc_initial_handler(size_t sz)
static void *memprof_pvalloc_initial_handler(size_t sz) static void *memprof_pvalloc_initial_handler(size_t sz)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* probably that dlsym() needs pvalloc(), let's fail */ /* probably that dlsym() needs pvalloc(), let's fail */
return NULL; return NULL;
} }
@ -264,7 +261,7 @@ static void *memprof_pvalloc_initial_handler(size_t sz)
static void *memprof_memalign_initial_handler(size_t al, size_t sz) static void *memprof_memalign_initial_handler(size_t al, size_t sz)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* probably that dlsym() needs memalign(), let's fail */ /* probably that dlsym() needs memalign(), let's fail */
return NULL; return NULL;
} }
@ -275,7 +272,7 @@ static void *memprof_memalign_initial_handler(size_t al, size_t sz)
static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz) static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* probably that dlsym() needs aligned_alloc(), let's fail */ /* probably that dlsym() needs aligned_alloc(), let's fail */
return NULL; return NULL;
} }
@ -286,7 +283,7 @@ static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz)
static int memprof_posix_memalign_initial_handler(void **ptr, size_t al, size_t sz) static int memprof_posix_memalign_initial_handler(void **ptr, size_t al, size_t sz)
{ {
if (in_memprof & MEMPROF_IN_INIT) { if (in_memprof) {
/* probably that dlsym() needs posix_memalign(), let's fail */ /* probably that dlsym() needs posix_memalign(), let's fail */
return ENOMEM; return ENOMEM;
} }
@ -347,13 +344,11 @@ void *malloc(size_t size)
struct memprof_stats *bin; struct memprof_stats *bin;
void *ret; void *ret;
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return memprof_malloc_handler(size); return memprof_malloc_handler(size);
in_memprof |= MEMPROF_IN_HANDLER;
ret = memprof_malloc_handler(size); ret = memprof_malloc_handler(size);
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MALLOC); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MALLOC);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
@ -376,13 +371,11 @@ void *calloc(size_t nmemb, size_t size)
struct memprof_stats *bin; struct memprof_stats *bin;
void *ret; void *ret;
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return memprof_calloc_handler(nmemb, size); return memprof_calloc_handler(nmemb, size);
in_memprof |= MEMPROF_IN_HANDLER;
ret = memprof_calloc_handler(nmemb, size); ret = memprof_calloc_handler(nmemb, size);
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_CALLOC); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_CALLOC);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
@ -408,14 +401,12 @@ void *realloc(void *ptr, size_t size)
size_t size_before; size_t size_before;
void *ret; void *ret;
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return memprof_realloc_handler(ptr, size); return memprof_realloc_handler(ptr, size);
in_memprof |= MEMPROF_IN_HANDLER;
size_before = malloc_usable_size(ptr); size_before = malloc_usable_size(ptr);
ret = memprof_realloc_handler(ptr, size); ret = memprof_realloc_handler(ptr, size);
size = malloc_usable_size(ret); size = malloc_usable_size(ret);
in_memprof &= ~MEMPROF_IN_HANDLER;
/* only count the extra link for new allocations */ /* only count the extra link for new allocations */
if (!ptr) if (!ptr)
@ -448,13 +439,11 @@ char *strdup(const char *s)
size_t size; size_t size;
char *ret; char *ret;
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return memprof_strdup_handler(s); return memprof_strdup_handler(s);
in_memprof |= MEMPROF_IN_HANDLER;
ret = memprof_strdup_handler(s); ret = memprof_strdup_handler(s);
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
@ -480,15 +469,13 @@ void free(void *ptr)
struct memprof_stats *bin; struct memprof_stats *bin;
size_t size_before; size_t size_before;
if (likely(!(profiling & HA_PROF_MEMORY) || !ptr || (in_memprof & MEMPROF_IN_HANDLER))) { if (likely(!(profiling & HA_PROF_MEMORY) || !ptr)) {
memprof_free_handler(ptr); memprof_free_handler(ptr);
return; return;
} }
in_memprof |= MEMPROF_IN_HANDLER;
size_before = malloc_usable_size(ptr) + sizeof(void *); size_before = malloc_usable_size(ptr) + sizeof(void *);
memprof_free_handler(ptr); memprof_free_handler(ptr);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_FREE); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_FREE);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
@ -508,13 +495,10 @@ char *strndup(const char *s, size_t size)
return NULL; return NULL;
ret = memprof_strndup_handler(s, size); ret = memprof_strndup_handler(s, size);
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return ret; return ret;
in_memprof |= MEMPROF_IN_HANDLER;
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRNDUP); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRNDUP);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
_HA_ATOMIC_ADD(&bin->locked_calls, 1); _HA_ATOMIC_ADD(&bin->locked_calls, 1);
@ -532,13 +516,10 @@ void *valloc(size_t size)
return NULL; return NULL;
ret = memprof_valloc_handler(size); ret = memprof_valloc_handler(size);
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return ret; return ret;
in_memprof |= MEMPROF_IN_HANDLER;
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_VALLOC); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_VALLOC);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
_HA_ATOMIC_ADD(&bin->locked_calls, 1); _HA_ATOMIC_ADD(&bin->locked_calls, 1);
@ -556,13 +537,10 @@ void *pvalloc(size_t size)
return NULL; return NULL;
ret = memprof_pvalloc_handler(size); ret = memprof_pvalloc_handler(size);
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return ret; return ret;
in_memprof |= MEMPROF_IN_HANDLER;
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_PVALLOC); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_PVALLOC);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
_HA_ATOMIC_ADD(&bin->locked_calls, 1); _HA_ATOMIC_ADD(&bin->locked_calls, 1);
@ -580,13 +558,10 @@ void *memalign(size_t align, size_t size)
return NULL; return NULL;
ret = memprof_memalign_handler(align, size); ret = memprof_memalign_handler(align, size);
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return ret; return ret;
in_memprof |= MEMPROF_IN_HANDLER;
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MEMALIGN); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MEMALIGN);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
_HA_ATOMIC_ADD(&bin->locked_calls, 1); _HA_ATOMIC_ADD(&bin->locked_calls, 1);
@ -604,13 +579,10 @@ void *aligned_alloc(size_t align, size_t size)
return NULL; return NULL;
ret = memprof_aligned_alloc_handler(align, size); ret = memprof_aligned_alloc_handler(align, size);
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return ret; return ret;
in_memprof |= MEMPROF_IN_HANDLER;
size = malloc_usable_size(ret) + sizeof(void *); size = malloc_usable_size(ret) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_ALIGNED_ALLOC); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_ALIGNED_ALLOC);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
_HA_ATOMIC_ADD(&bin->locked_calls, 1); _HA_ATOMIC_ADD(&bin->locked_calls, 1);
@ -628,16 +600,13 @@ int posix_memalign(void **ptr, size_t align, size_t size)
return ENOMEM; return ENOMEM;
ret = memprof_posix_memalign_handler(ptr, align, size); ret = memprof_posix_memalign_handler(ptr, align, size);
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER))) if (likely(!(profiling & HA_PROF_MEMORY)))
return ret; return ret;
if (ret != 0) // error if (ret != 0) // error
return ret; return ret;
in_memprof |= MEMPROF_IN_HANDLER;
size = malloc_usable_size(*ptr) + sizeof(void *); size = malloc_usable_size(*ptr) + sizeof(void *);
in_memprof &= ~MEMPROF_IN_HANDLER;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_POSIX_MEMALIGN); bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_POSIX_MEMALIGN);
if (unlikely(th_ctx->lock_level & 0x7F)) if (unlikely(th_ctx->lock_level & 0x7F))
_HA_ATOMIC_ADD(&bin->locked_calls, 1); _HA_ATOMIC_ADD(&bin->locked_calls, 1);

View file

@ -539,6 +539,9 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC)) if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC))
goto end; goto end;
if (!count)
goto end;
if (!appctx_get_buf(appctx, &appctx->outbuf)) { if (!appctx_get_buf(appctx, &appctx->outbuf)) {
TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx); TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx);
goto end; goto end;
@ -547,8 +550,7 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
if (flags & CO_RFL_BUF_FLUSH) if (flags & CO_RFL_BUF_FLUSH)
applet_fl_set(appctx, APPCTX_FL_FASTFWD); applet_fl_set(appctx, APPCTX_FL_FASTFWD);
if (count) ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
if (ret) if (ret)
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL); applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);
@ -611,7 +613,7 @@ size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t coun
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM); appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
} }
htx_to_buf(appctx_htx, &appctx->inbuf); htx_to_buf(appctx_htx, &appctx->outbuf);
htx_to_buf(buf_htx, buf); htx_to_buf(buf_htx, buf);
ret -= buf_htx->data; ret -= buf_htx->data;
end: end:

View file

@ -297,7 +297,7 @@ check_user(struct userlist *ul, const char *user, const char *pass)
fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : "")); fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
#endif #endif
if (ep && u->pass && strcmp(ep, u->pass) == 0) if (ep && strcmp(ep, u->pass) == 0)
return 1; return 1;
else else
return 0; return 0;

View file

@ -1818,7 +1818,7 @@ int connect_server(struct stream *s)
{ {
struct connection *cli_conn = objt_conn(strm_orig(s)); struct connection *cli_conn = objt_conn(strm_orig(s));
struct connection *srv_conn = NULL; struct connection *srv_conn = NULL;
const struct mux_proto_list *mux_proto = NULL; const struct mux_proto_list *mux_proto;
struct server *srv; struct server *srv;
struct ist name = IST_NULL; struct ist name = IST_NULL;
struct sample *name_smp; struct sample *name_smp;
@ -2139,10 +2139,12 @@ int connect_server(struct stream *s)
} }
if (may_start_mux_now) { if (may_start_mux_now) {
/* Delay MUX init if an XPRT handshake is required prior. */ /* Delay QMux MUX init to let xprt_qmux handshake runs first. */
mux_proto = conn_select_mux_be(srv_conn); mux_proto = conn_select_mux_be(srv_conn);
if (mux_proto && mux_proto->init_xprt) if (mux_proto && mux_proto->init_xprt == XPRT_QMUX) {
srv_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
may_start_mux_now = 0; may_start_mux_now = 0;
}
} }
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation) #if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
@ -2252,13 +2254,6 @@ int connect_server(struct stream *s)
} }
} }
} }
else if (mux_proto && mux_proto->init_xprt) {
/* Add handshake layer prior to MUX init if required. Does nothing if SSL layer is active though. */
if (xprt_add_l6hs(srv_conn, mux_proto->init_xprt)) {
conn_full_close(srv_conn);
return SF_ERR_INTERNAL;
}
}
/* /*
* Now that the mux may have been created, we can start the xprt. * Now that the mux may have been created, we can start the xprt.

View file

@ -125,9 +125,6 @@ int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
signed char b; signed char b;
int convlen = 0, i = 0, pad = 0; int convlen = 0, i = 0, pad = 0;
if (!ilen)
return 0;
if (ilen % 4) if (ilen % 4)
return -1; return -1;

View file

@ -374,7 +374,7 @@ static int secondary_key_cmp(const char *ref_key, const char *new_key)
* delete_expired==0, write otherwise. * delete_expired==0, write otherwise.
*/ */
struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry, struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry,
const char *primary_hash, const char *secondary_key, int delete_expired) const char *secondary_key, int delete_expired)
{ {
struct eb32_node *node = &entry->eb; struct eb32_node *node = &entry->eb;
@ -395,12 +395,6 @@ struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_e
entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL; entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL;
} }
/* Now verify the full primary hash matches: eb32 only compares 32 bits so
* we could have ended up on a different, unrelated entry.
*/
if (entry && primary_hash && memcmp(entry->hash, primary_hash, sizeof(entry->hash)))
entry = NULL;
/* Expired entry */ /* Expired entry */
if (entry && entry->expire <= date.tv_sec) { if (entry && entry->expire <= date.tv_sec) {
if (delete_expired) { if (delete_expired) {
@ -949,8 +943,8 @@ int http_calc_maxage(struct stream *s, struct cache *cache, int *true_maxage)
if (value) { if (value) {
struct buffer *chk = get_trash_chunk(); struct buffer *chk = get_trash_chunk();
chunk_memcat(chk, value, ctx.value.len - (8 + 1)); chunk_memcat(chk, value, ctx.value.len - 8 + 1);
*(b_tail(chk)) = '\0'; chunk_memcat(chk, "", 1);
offset = (*chk->area == '"') ? 1 : 0; offset = (*chk->area == '"') ? 1 : 0;
smaxage = strtol(chk->area + offset, &endptr, 10); smaxage = strtol(chk->area + offset, &endptr, 10);
if (unlikely(smaxage < 0 || endptr == chk->area + offset)) if (unlikely(smaxage < 0 || endptr == chk->area + offset))
@ -961,8 +955,8 @@ int http_calc_maxage(struct stream *s, struct cache *cache, int *true_maxage)
if (value) { if (value) {
struct buffer *chk = get_trash_chunk(); struct buffer *chk = get_trash_chunk();
chunk_memcat(chk, value, ctx.value.len - (7 + 1)); chunk_memcat(chk, value, ctx.value.len - 7 + 1);
*(b_tail(chk)) = '\0'; chunk_memcat(chk, "", 1);
offset = (*chk->area == '"') ? 1 : 0; offset = (*chk->area == '"') ? 1 : 0;
maxage = strtol(chk->area + offset, &endptr, 10); maxage = strtol(chk->area + offset, &endptr, 10);
if (unlikely(maxage < 0 || endptr == chk->area + offset)) if (unlikely(maxage < 0 || endptr == chk->area + offset))
@ -1309,7 +1303,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
if (old) { if (old) {
if (vary_signature) if (vary_signature)
old = get_secondary_entry(cache_tree, old, old = get_secondary_entry(cache_tree, old,
txn->cache_hash, txn->cache_secondary_hash, 1); txn->cache_secondary_hash, 1);
if (old) { if (old) {
if (!old->complete) { if (!old->complete) {
/* An entry with the same primary key is already being /* An entry with the same primary key is already being
@ -2184,20 +2178,9 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
if (!http_request_build_secondary_key(s, res->secondary_key_signature)) { if (!http_request_build_secondary_key(s, res->secondary_key_signature)) {
cache_rdlock(cache_tree); cache_rdlock(cache_tree);
sec_entry = get_secondary_entry(cache_tree, res, sec_entry = get_secondary_entry(cache_tree, res,
s->txn.http->cache_hash,
s->txn.http->cache_secondary_hash, s->txn.http->cache_secondary_hash,
0); 0);
if (!sec_entry) { if (sec_entry && sec_entry != res) {
/* Secondary key miss: release the retained primary entry
* and reattach the detached row before returning.
*/
release_entry(cache_tree, res, 0);
shctx_wrlock(shctx);
if (detached)
shctx_row_reattach(shctx, entry_block);
shctx_wrunlock(shctx);
}
else if (sec_entry != res) {
/* The wrong row was added to the hot list. */ /* The wrong row was added to the hot list. */
release_entry(cache_tree, res, 0); release_entry(cache_tree, res, 0);
retain_entry(sec_entry); retain_entry(sec_entry);
@ -3047,7 +3030,6 @@ static int cli_io_handler_show_cache(struct appctx *appctx)
node = eb32_lookup_ge(&cache_tree->entries, next_key); node = eb32_lookup_ge(&cache_tree->entries, next_key);
if (!node) { if (!node) {
ctx->next_key = 0; ctx->next_key = 0;
next_key = 0;
break; break;
} }

View file

@ -1629,6 +1629,11 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
struct proxy *curpx, const struct proxy *defpx, struct proxy *curpx, const struct proxy *defpx,
const char *file, int line, char **err) const char *file, int line, char **err)
{ {
if (!experimental_directives_allowed) {
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
return -1;
}
if (global.shm_stats_file != NULL) { if (global.shm_stats_file != NULL) {
memprintf(err, "'%s' already specified.\n", args[0]); memprintf(err, "'%s' already specified.\n", args[0]);
return -1; return -1;
@ -1639,6 +1644,7 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
return -1; return -1;
} }
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
global.shm_stats_file = strdup(args[1]); global.shm_stats_file = strdup(args[1]);
return 0; return 0;
} }
@ -1647,6 +1653,11 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
struct proxy *curpx, const struct proxy *defpx, struct proxy *curpx, const struct proxy *defpx,
const char *file, int line, char **err) const char *file, int line, char **err)
{ {
if (!experimental_directives_allowed) {
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
return -1;
}
if (shm_stats_file_max_objects != -1) { if (shm_stats_file_max_objects != -1) {
memprintf(err, "'%s' already specified.\n", args[0]); memprintf(err, "'%s' already specified.\n", args[0]);
return -1; return -1;
@ -1657,6 +1668,7 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
return -1; return -1;
} }
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
shm_stats_file_max_objects = atoi(args[1]); shm_stats_file_max_objects = atoi(args[1]);
return 0; return 0;
} }

View file

@ -2486,17 +2486,16 @@ init_proxies_list_stage1:
/* At this point, target names have already been resolved. */ /* At this point, target names have already been resolved. */
/***********************************************************/ /***********************************************************/
for (i = 0; i < global.nbthread; i++) { idle_conn_task = task_new_anywhere();
idle_conn_srv[i] = EB_ROOT; if (!idle_conn_task) {
idle_conn_task[i] = task_new_on(i); ha_alert("parsing : failed to allocate global idle connection task.\n");
if (!idle_conn_task[i]) { cfgerr++;
ha_alert("parsing : failed to allocate global idle connection task.\n"); }
cfgerr++; else {
} idle_conn_task->process = srv_cleanup_idle_conns;
else { idle_conn_task->context = NULL;
idle_conn_task[i]->process = srv_cleanup_idle_conns;
idle_conn_task[i]->context = NULL;
for (i = 0; i < global.nbthread; i++) {
idle_conns[i].cleanup_task = task_new_on(i); idle_conns[i].cleanup_task = task_new_on(i);
if (!idle_conns[i].cleanup_task) { if (!idle_conns[i].cleanup_task) {
ha_alert("parsing : failed to allocate idle connection tasks for thread '%d'.\n", i); ha_alert("parsing : failed to allocate idle connection tasks for thread '%d'.\n", i);

View file

@ -232,9 +232,6 @@ static void check_trace(enum trace_level level, uint64_t mask,
chunk_appendf(&trace_buf, " sc=%p(0x%08x)", check->sc, check->sc->flags); chunk_appendf(&trace_buf, " sc=%p(0x%08x)", check->sc, check->sc->flags);
} }
if (check->type != PR_O2_TCPCHK_CHK)
return;
if (mask & CHK_EV_TCPCHK) { if (mask & CHK_EV_TCPCHK) {
const char *type; const char *type;
@ -1407,20 +1404,7 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state)
check_release_buf(check, &check->bi); check_release_buf(check, &check->bi);
check_release_buf(check, &check->bo); check_release_buf(check, &check->bo);
_HA_ATOMIC_DEC(&th_ctx->running_checks);
if (unlikely(LIST_INLIST(&check->check_queue))) {
/*
* If that check is still queued, and we're about to
* purge it, then remove it from the queue, as it is
* about to be freed.
* This can happen if a server is deleted while the check
* is queued.
*/
if (check->state & CHK_ST_PURGE)
LIST_DEL_INIT(&check->check_queue);
}
else
_HA_ATOMIC_DEC(&th_ctx->running_checks);
_HA_ATOMIC_DEC(&th_ctx->active_checks); _HA_ATOMIC_DEC(&th_ctx->active_checks);
check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC); check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
check->state &= ~CHK_ST_READY; check->state &= ~CHK_ST_READY;
@ -1579,7 +1563,6 @@ void free_check(struct check *check)
ha_free(&check->tcpcheck); ha_free(&check->tcpcheck);
} }
LIST_DEL_INIT(&check->check_queue);
pool_free(pool_head_uniqueid, istptr(check->unique_id)); pool_free(pool_head_uniqueid, istptr(check->unique_id));
check->unique_id = IST_NULL; check->unique_id = IST_NULL;
ha_free(&check->pool_conn_name); ha_free(&check->pool_conn_name);
@ -1699,7 +1682,7 @@ static int start_checks()
for (px = proxies_list; px; px = px->next) { for (px = proxies_list; px; px = px->next) {
for (s = px->srv; s; s = s->next) { for (s = px->srv; s; s = s->next) {
if ((px->options2 & PR_O2_USE_SBUF_CHECK) && if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
(s->check.tcpcheck->rs && s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF)) (s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
s->check.state |= CHK_ST_USE_SMALL_BUFF; s->check.state |= CHK_ST_USE_SMALL_BUFF;
if (s->check.state & CHK_ST_CONFIGURED) { if (s->check.state & CHK_ST_CONFIGURED) {
@ -1816,9 +1799,6 @@ int init_srv_check(struct server *srv)
if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE)) if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
goto out; goto out;
if (!srv->check.type && (srv->proxy->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK)
goto init;
check_type = srv->check.tcpcheck->rs->flags & TCPCHK_RULES_PROTO_CHK; check_type = srv->check.tcpcheck->rs->flags & TCPCHK_RULES_PROTO_CHK;
if (!(srv->flags & SRV_F_DYNAMIC)) { if (!(srv->flags & SRV_F_DYNAMIC)) {
@ -1959,7 +1939,7 @@ int init_srv_check(struct server *srv)
} }
init: init:
err = init_check(&srv->check, srv->check.type ? srv->check.type : (srv->proxy->options2 & PR_O2_CHK_ANY)); err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
if (err) { if (err) {
ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n", ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err); proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);

View file

@ -145,15 +145,14 @@ struct buffer *get_small_trash_chunk(void)
/* Returns a trash chunk accordingly to the requested size. This function may /* Returns a trash chunk accordingly to the requested size. This function may
* fail if the requested size is too big or if the large chunks are not * fail if the requested size is too big or if the large chunks are not
* configured. Note that requesting a size larger than the largest available * configured.
* buffer will result in NULL being returned, so better be conservative when
* requesting the size and plan to use get_larger_trash_chunk() later if not
* sufficient.
*/ */
struct buffer *get_trash_chunk_sz(size_t size) struct buffer *get_trash_chunk_sz(size_t size)
{ {
if (likely(size <= trash_size)) if (likely(size > small_trash_size && size <= trash_size))
return get_trash_chunk(); return get_trash_chunk();
else if (small_trash_size && size <= small_trash_size)
return get_small_trash_chunk();
else if (large_trash_size && size <= large_trash_size) else if (large_trash_size && size <= large_trash_size)
return get_large_trash_chunk(); return get_large_trash_chunk();
else else

View file

@ -1151,13 +1151,8 @@ int cli_parse_cmdline(struct appctx *appctx)
*/ */
if (len-1 == strlen(appctx->cli_ctx.payload_pat)) { if (len-1 == strlen(appctx->cli_ctx.payload_pat)) {
if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) { if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) {
/* end of payload was reached, rewind before the previous \n, if any, and replace it by a \0 /* end of payload was reached, rewind before the previous \n and replace it by a \0 */
* Otherwise, the payload is empty, just b_sub(buf, strlen(appctx->cli_ctx.payload_pat) + 2);
*/
if (b_data(buf) > len)
b_sub(buf, len+1);
else
b_sub(buf, len);
*b_tail(buf) = '\0'; *b_tail(buf) = '\0';
appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD; appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
} }

View file

@ -196,7 +196,7 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
* information to create one, typically from the ALPN. If we're * information to create one, typically from the ALPN. If we're
* done with the handshake, attempt to create one. * done with the handshake, attempt to create one.
*/ */
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_WAIT_XPRT_L6))) { if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
ret = conn_create_mux(conn, NULL); ret = conn_create_mux(conn, NULL);
if (ret < 0) if (ret < 0)
goto done; goto done;
@ -847,43 +847,6 @@ int xprt_add_hs(struct connection *conn)
return 0; return 0;
} }
/* Activates an <xprt> layer on top of <conn> connection. This handshake layer
* should be designed to work on top of the layer 6. If SSL is active and its
* handshake still in progress, this function does nothing.
*
* Returns 0 on success else a negative error code.
*/
int xprt_add_l6hs(struct connection *conn, int xprt)
{
const struct xprt_ops *ops = xprt_get(xprt);
void *ops_ctx = NULL;
/* Only QMux is supported as handshake on top of layer6 for now. */
BUG_ON(xprt != XPRT_QMUX);
if (conn->flags & CO_FL_ERROR)
return -1;
/* Do nothing if SSL is in used but handshake still in progress. In
* this case, xprt layer will be added on handshake completion.
*/
if (conn->xprt == xprt_get(XPRT_SSL) &&
(conn->flags & CO_FL_WAIT_L6_CONN)) {
return 0;
}
if (ops->init(conn, &ops_ctx))
return -1;
ops->add_xprt(conn, ops_ctx, conn->xprt_ctx, conn->xprt, NULL, NULL);
conn->xprt = ops;
conn->xprt_ctx = ops_ctx;
/* Reset XPRT READY flag before the next conn_xprt_start(). */
conn->flags &= ~CO_FL_XPRT_READY;
return 0;
}
/* returns a short name for an error, typically the same as the enum name /* returns a short name for an error, typically the same as the enum name
* without the "CO_ER_" prefix, or an empty string for no error (better eye * without the "CO_ER_" prefix, or an empty string for no error (better eye
* catching in logs). This is more compact for some debug cases. * catching in logs). This is more compact for some debug cases.

View file

@ -19,7 +19,6 @@
/* cpu_policy_conf flags */ /* cpu_policy_conf flags */
#define CPU_POLICY_ONE_THREAD_PER_CORE (1 << 0) #define CPU_POLICY_ONE_THREAD_PER_CORE (1 << 0)
#define CPU_POLICY_SET_IN_CONFIG (1 << 1)
/* cpu_policy_conf affinities */ /* cpu_policy_conf affinities */
#define CPU_AFFINITY_PER_GROUP (1 << 0) #define CPU_AFFINITY_PER_GROUP (1 << 0)
@ -1107,23 +1106,14 @@ static int cpu_policy_first_usable_node(int policy, int tmin, int tmax, int gmin
int grp, thr; int grp, thr;
int thr_count = 0; int thr_count = 0;
if (!global.numa_cpu_mapping) { if (!global.numa_cpu_mapping)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when numa-cpu-mapping is set.\n");
return 0; return 0;
}
if (global.nbthread) { if (global.nbthread)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
return 0; return 0;
}
if (cpu_mask_forced) { if (cpu_mask_forced)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy first-numa-node is ignored when CPUs were externally restricted.\n");
return 0; return 0;
}
/* determine first and second nodes with usable CPUs */ /* determine first and second nodes with usable CPUs */
for (cpu = 0; cpu <= cpu_topo_lastcpu; cpu++) { for (cpu = 0; cpu <= cpu_topo_lastcpu; cpu++) {
@ -1279,7 +1269,7 @@ static int cpu_policy_first_usable_node(int policy, int tmin, int tmax, int gmin
if (tmin <= thr_count && thr_count < tmax) if (tmin <= thr_count && thr_count < tmax)
tmax = thr_count; tmax = thr_count;
ha_diag_notice("Multi-socket cpu detected, automatically binding on active CPUs of '%d' (%u active cpu(s))\n", first_node_id, cpu_count); ha_diag_warning("Multi-socket cpu detected, automatically binding on active CPUs of '%d' (%u active cpu(s))\n", first_node_id, cpu_count);
if (!global.nbthread) if (!global.nbthread)
global.nbthread = tmax; global.nbthread = tmax;
@ -1515,17 +1505,11 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
int cid; int cid;
int div; int div;
if (global.nbthread) { if (global.nbthread)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
return 0; return 0;
}
if (global.nbtgroups) { if (global.nbtgroups)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
return 0; return 0;
}
ha_cpuset_zero(&visited_cl_set); ha_cpuset_zero(&visited_cl_set);
@ -1536,8 +1520,7 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
div = ha_cpu_policy[policy].arg; div = ha_cpu_policy[policy].arg;
div = div ? div : 1; div = div ? div : 1;
while (global.nbtgroups < MAX_TGROUPS && (global.nbthread < MAX_THREADS) && while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
ha_cpuset_zero(&node_cpu_set); ha_cpuset_zero(&node_cpu_set);
ha_cpuset_zero(&touse_tsid); ha_cpuset_zero(&touse_tsid);
ha_cpuset_zero(&touse_ccx); ha_cpuset_zero(&touse_ccx);
@ -1567,10 +1550,6 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id); ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE)) } else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
thr_count++; thr_count++;
if (global.thread_limit != 0 &&
thr_count + global.nbthread >= global.thread_limit)
break;
} }
/* now cid = next cluster_id or -1 if none; cpu_count is the /* now cid = next cluster_id or -1 if none; cpu_count is the
@ -1586,9 +1565,9 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
} }
if (global.nbthread) if (global.nbthread)
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups); ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
else else
ha_diag_notice("Could not determine any CPU cluster\n"); ha_diag_warning("Could not determine any CPU cluster\n");
return 0; return 0;
} }
@ -1614,17 +1593,11 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
int l3id; int l3id;
int div; int div;
if (global.nbthread) { if (global.nbthread)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
return 0; return 0;
}
if (global.nbtgroups) { if (global.nbtgroups)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
return 0; return 0;
}
ha_cpuset_zero(&visited_ccx_set); ha_cpuset_zero(&visited_ccx_set);
@ -1635,8 +1608,7 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
div = ha_cpu_policy[policy].arg; div = ha_cpu_policy[policy].arg;
div = div ? div : 1; div = div ? div : 1;
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS && while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
ha_cpuset_zero(&node_cpu_set); ha_cpuset_zero(&node_cpu_set);
ha_cpuset_zero(&touse_tsid); ha_cpuset_zero(&touse_tsid);
ha_cpuset_zero(&touse_ccx); ha_cpuset_zero(&touse_ccx);
@ -1666,9 +1638,6 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id); ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE)) } else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
thr_count++; thr_count++;
if (global.thread_limit != 0 &&
global.nbthread + thr_count >= global.thread_limit)
break;
} }
/* now l3id = next L3 ID or -1 if none; cpu_count is the /* now l3id = next L3 ID or -1 if none; cpu_count is the
@ -1684,9 +1653,9 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
} }
if (global.nbthread) if (global.nbthread)
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups); ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
else else
ha_diag_notice("Could not determine any CPU cluster\n"); ha_diag_warning("Could not determine any CPU cluster\n");
return 0; return 0;
} }
@ -1703,17 +1672,8 @@ static int cpu_policy_performance(int policy, int tmin, int tmax, int gmin, int
int cpu, cluster; int cpu, cluster;
int capa; int capa;
if (global.nbthread) { if (global.nbthread || global.nbtgroups)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
return 0; return 0;
}
if (global.nbtgroups) {
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
return 0;
}
/* sort clusters by average reverse capacity */ /* sort clusters by average reverse capacity */
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus); cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
@ -1757,17 +1717,8 @@ static int cpu_policy_efficiency(int policy, int tmin, int tmax, int gmin, int g
int cpu, cluster; int cpu, cluster;
int capa; int capa;
if (global.nbthread) { if (global.nbthread || global.nbtgroups)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
return 0; return 0;
}
if (global.nbtgroups) {
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
return 0;
}
/* sort clusters by average reverse capacity */ /* sort clusters by average reverse capacity */
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus); cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
@ -1808,17 +1759,8 @@ static int cpu_policy_resource(int policy, int tmin, int tmax, int gmin, int gma
int cpu, cluster; int cpu, cluster;
int capa; int capa;
if (global.nbthread) { if (global.nbthread || global.nbtgroups)
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
return 0; return 0;
}
if (global.nbtgroups) {
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
return 0;
}
/* sort clusters by reverse capacity */ /* sort clusters by reverse capacity */
cpu_cluster_reorder_by_capa(ha_cpu_clusters, cpu_topo_maxcpus); cpu_cluster_reorder_by_capa(ha_cpu_clusters, cpu_topo_maxcpus);
@ -1853,8 +1795,6 @@ int cpu_apply_policy(int tmin, int tmax, int gmin, int gmax, char **err)
if (cpu_map_configured()) { if (cpu_map_configured()) {
/* nothing to do */ /* nothing to do */
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
ha_notice("cpu-policy is ignored when cpu-map is set.\n");
return 0; return 0;
} }
@ -2410,7 +2350,6 @@ static int cfg_parse_cpu_policy(char **args, int section_type, struct proxy *cur
for (i = 0; ha_cpu_policy[i].name; i++) { for (i = 0; ha_cpu_policy[i].name; i++) {
if (strcmp(args[1], ha_cpu_policy[i].name) == 0) { if (strcmp(args[1], ha_cpu_policy[i].name) == 0) {
cpu_policy_conf.cpu_policy = i; cpu_policy_conf.cpu_policy = i;
cpu_policy_conf.flags |= CPU_POLICY_SET_IN_CONFIG;
return 0; return 0;
} }
} }

View file

@ -79,7 +79,7 @@ static struct dict_entry *__dict_lookup(struct dict *d, const char *s)
*/ */
struct dict_entry *dict_insert(struct dict *d, char *s) struct dict_entry *dict_insert(struct dict *d, char *s)
{ {
struct dict_entry *de, *tree_de; struct dict_entry *de;
struct ebpt_node *n; struct ebpt_node *n;
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock); HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
@ -97,18 +97,13 @@ struct dict_entry *dict_insert(struct dict *d, char *s)
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock); HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
n = ebis_insert(&d->values, &de->value); n = ebis_insert(&d->values, &de->value);
tree_de = container_of(n, struct dict_entry, value); HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
if (tree_de == de) if (n != &de->value) {
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
else {
/* another entry was already there, we'll return it, kill
* ours and bump the other's refcount before returning it.
*/
HA_ATOMIC_INC(&tree_de->refcount);
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
free_dict_entry(de); free_dict_entry(de);
de = container_of(n, struct dict_entry, value);
} }
return tree_de;
return de;
} }
@ -122,11 +117,10 @@ void dict_entry_unref(struct dict *d, struct dict_entry *de)
if (!de) if (!de)
return; return;
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock); if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0)
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0) {
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
return; return;
}
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
ebpt_delete(&de->value); ebpt_delete(&de->value);
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock); HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);

View file

@ -386,20 +386,6 @@ void ha_diag_warning(const char *fmt, ...)
} }
} }
/*
* Displays the message on stderr with the pid if MODE_DIAG is set.
*/
void ha_diag_notice(const char *fmt, ...)
{
va_list argp;
if (global.mode & MODE_DIAG) {
va_start(argp, fmt);
print_message(1, "DIAG", fmt, argp);
va_end(argp);
}
}
/* /*
* Displays the message on stderr with the pid. * Displays the message on stderr with the pid.
*/ */

View file

@ -48,9 +48,6 @@ static struct event_hdl_sub_type_map event_hdl_sub_type_map[] = {
{"PAT_REF_SET", EVENT_HDL_SUB_PAT_REF_SET}, {"PAT_REF_SET", EVENT_HDL_SUB_PAT_REF_SET},
{"PAT_REF_COMMIT", EVENT_HDL_SUB_PAT_REF_COMMIT}, {"PAT_REF_COMMIT", EVENT_HDL_SUB_PAT_REF_COMMIT},
{"PAT_REF_CLEAR", EVENT_HDL_SUB_PAT_REF_CLEAR}, {"PAT_REF_CLEAR", EVENT_HDL_SUB_PAT_REF_CLEAR},
{"ACME", EVENT_HDL_SUB_ACME},
{"ACME_NEWCERT", EVENT_HDL_SUB_ACME_NEWCERT},
{"ACME_DEPLOY", EVENT_HDL_SUB_ACME_DEPLOY},
}; };
/* internal types (only used in this file) */ /* internal types (only used in this file) */

View file

@ -316,9 +316,6 @@ void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value)
skip_val: skip_val:
word.ptr = p = n; word.ptr = p = n;
} }
if (istlen(*value))
h1m->flags |= H1_MF_UPG_HDR;
} }
/* Macros used in the HTTP/1 parser, to check for the expected presence of /* Macros used in the HTTP/1 parser, to check for the expected presence of
@ -713,16 +710,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
case H1_MSG_RPCODE: case H1_MSG_RPCODE:
http_msg_rpcode: http_msg_rpcode:
if (likely(HTTP_IS_DIGIT(*ptr))) { if (likely(HTTP_IS_DIGIT(*ptr))) {
if (ptr - sl.st.c.ptr >= 3) {
/* more than 3 digits */
if (h1m->err_pos == -1) /* only capture the error pointer */
h1m->err_pos = ptr - start + skip;
else if (h1m->err_pos < -1 || sl.st.status >= ((uint16_t)~0 - 9) / 10) {
/* strict checks or risk of overflow */
state = H1_MSG_RPCODE;
goto http_msg_invalid;
}
}
sl.st.status = sl.st.status * 10 + *ptr - '0'; sl.st.status = sl.st.status * 10 + *ptr - '0';
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE); EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE);
} }
@ -965,20 +952,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
goto http_output_full; goto http_output_full;
} }
/* Skip headers whose names contain forbidden
* chars. When any is detected, h1m->err_pos >= 0,
* so we recheck the name only when an error was
* detected.
*/
if (unlikely(h1m->err_pos >= 0)) {
size_t i = 0;
while (i < n.len && HTTP_IS_TOKEN(n.ptr[i]))
i++;
if (i < n.len)
break;
}
if (isteqi(n, ist("transfer-encoding"))) { if (isteqi(n, ist("transfer-encoding"))) {
ret = h1_parse_xfer_enc_header(h1m, v); ret = h1_parse_xfer_enc_header(h1m, v);
if (ret < 0) { if (ret < 0) {
@ -1275,10 +1248,9 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
void h1_generate_random_ws_input_key(char key_out[25]) void h1_generate_random_ws_input_key(char key_out[25])
{ {
/* generate a random websocket key */ /* generate a random websocket key */
uint64_t rand1, rand2; const uint64_t rand1 = ha_random64(), rand2 = ha_random64();
char key[16]; char key[16];
ha_random64_pair_hashed(&rand1, &rand2);
memcpy(key, &rand1, 8); memcpy(key, &rand1, 8);
memcpy(&key[8], &rand2, 8); memcpy(&key[8], &rand2, 8);
a2base64(key, 16, key_out, 25); a2base64(key, 16, key_out, 25);

View file

@ -162,8 +162,6 @@ static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
} }
if (h1m->flags & H1_MF_CONN_UPG) if (h1m->flags & H1_MF_CONN_UPG)
flags |= HTX_SL_F_CONN_UPG; flags |= HTX_SL_F_CONN_UPG;
if (h1m->flags & H1_MF_UPG_HDR)
flags |= HTX_SL_F_UPG_HDR;
return flags; return flags;
} }
@ -215,31 +213,6 @@ static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
} }
} }
/* Remove Upgrade header if no 'connection: upgrade' found */
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_UPG_HDR) {
int i;
for (i = 0; hdrs[i].n.len; i++) {
if (isteqi(hdrs[i].n, ist("upgrade")))
hdrs[i].v = IST_NULL;
}
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR);
}
/* Remove 'Upgrade' value from connection header if not Upgrade header found */
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_CONN_UPG) {
int i;
for (i = 0; hdrs[i].n.len; i++) {
if (isteqi(hdrs[i].n, ist("connection"))) {
http_remove_header_value(&hdrs[i].v, ist("upgrade"));
if (!istlen(hdrs[i].v))
hdrs[i].v = IST_NULL;
}
}
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR);
}
flags |= h1m_htx_sl_flags(h1m); flags |= h1m_htx_sl_flags(h1m);
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn); sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);

193
src/h3.c
View file

@ -92,10 +92,6 @@ static const struct name_desc h3_trace_decoding[] = {
{ .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" }, { .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" },
#define H3_VERB_MINIMAL 2 #define H3_VERB_MINIMAL 2
{ .name="minimal", .desc="report only qcc/qcs state and flags, no real decoding" }, { .name="minimal", .desc="report only qcc/qcs state and flags, no real decoding" },
#define H3_VERB_SIMPLE 3
{ .name="simple", .desc="add request/response status line or frame info when available" },
#define H3_VERB_ADVANCED 4
{ .name="advanced", .desc="add header fields or frame decoding when available" },
{ /* end */ } { /* end */ }
}; };
@ -216,33 +212,8 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
break; break;
case H3_UNI_S_T_PUSH: case H3_UNI_S_T_PUSH:
if (!conn_is_back(qcs->qcc->conn)) { /* TODO not supported for the moment */
/* RFC 9114 6.2.2. Push Streams h3s->type = H3S_T_PUSH;
*
* Only servers can push; if a server receives a client-initiated push
* stream, this MUST be treated as a connection error of type
* H3_STREAM_CREATION_ERROR.
*/
TRACE_ERROR("reject push from client", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
else {
/* RFC 9114 4.6. Server Push
*
* A client MUST treat receipt of a push stream as a connection
* error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent or
* when the stream references a push ID that is greater than the maximum
* push ID.
*/
TRACE_ERROR("reject push from server outside of MAX_PUSH_ID", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
break; break;
case H3_UNI_S_T_QPACK_DEC: case H3_UNI_S_T_QPACK_DEC:
@ -394,6 +365,7 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
case H3_FT_CANCEL_PUSH: case H3_FT_CANCEL_PUSH:
case H3_FT_GOAWAY: case H3_FT_GOAWAY:
case H3_FT_MAX_PUSH_ID:
/* RFC 9114 7.2.3. CANCEL_PUSH /* RFC 9114 7.2.3. CANCEL_PUSH
* *
* A CANCEL_PUSH frame is sent on the control stream. Receiving a * A CANCEL_PUSH frame is sent on the control stream. Receiving a
@ -407,6 +379,13 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
* control stream as a connection error of type H3_FRAME_UNEXPECTED. * control stream as a connection error of type H3_FRAME_UNEXPECTED.
*/ */
/* RFC 9114 7.2.7. MAX_PUSH_ID
*
* The MAX_PUSH_ID frame is always sent on the control stream. Receipt
* of a MAX_PUSH_ID frame on any other stream MUST be treated as a
* connection error of type H3_FRAME_UNEXPECTED.
*/
if (h3s->type != H3S_T_CTRL) if (h3s->type != H3S_T_CTRL)
ret = H3_ERR_FRAME_UNEXPECTED; ret = H3_ERR_FRAME_UNEXPECTED;
else if (!(h3c->flags & H3_CF_SETTINGS_RECV)) else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
@ -433,33 +412,14 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
case H3_FT_PUSH_PROMISE: case H3_FT_PUSH_PROMISE:
/* RFC 9114 7.2.5. PUSH_PROMISE /* RFC 9114 7.2.5. PUSH_PROMISE
*
* If a PUSH_PROMISE frame is received on the control stream, the client
* MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
* *
* A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the * A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the
* receipt of a PUSH_PROMISE frame as a connection error of type * receipt of a PUSH_PROMISE frame as a connection error of type
* H3_FRAME_UNEXPECTED. * H3_FRAME_UNEXPECTED.
*/ */
if (h3s->type == H3S_T_CTRL || !conn_is_back(qcs->qcc->conn))
ret = H3_ERR_FRAME_UNEXPECTED;
break;
case H3_FT_MAX_PUSH_ID: /* TODO server-side only. */
/* RFC 9114 7.2.7. MAX_PUSH_ID ret = H3_ERR_FRAME_UNEXPECTED;
*
* The MAX_PUSH_ID frame is always sent on the control stream. Receipt
* of a MAX_PUSH_ID frame on any other stream MUST be treated as a
* connection error of type H3_FRAME_UNEXPECTED.
*
* A server MUST NOT send a MAX_PUSH_ID frame. A client MUST treat the
* receipt of a MAX_PUSH_ID frame as a connection error of type
* H3_FRAME_UNEXPECTED.
*/
if (h3s->type != H3S_T_CTRL || conn_is_back(qcs->qcc->conn))
ret = H3_ERR_FRAME_UNEXPECTED;
else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
ret = H3_ERR_MISSING_SETTINGS;
break; break;
default: default:
@ -634,51 +594,6 @@ static struct ist _h3_trim_header(struct ist value)
return v; return v;
} }
static void _h3_trace_header(const struct ist n, const struct ist v,
uint64_t mask, const struct ist trc_loc, const char *func,
const struct qcc *qcc, const struct qcs *qcs)
{
struct ist n_short, v_short;
const char *c_str __maybe_unused;
const char *s_str __maybe_unused;
chunk_reset(&trash);
c_str = chunk_newstr(&trash);
if (qcc)
chunk_appendf(&trash, "qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
s_str = chunk_newstr(&trash);
if (qcs)
chunk_appendf(&trash, " qcc=%p(%llu)", qcs, (ullong)qcs->id);
n_short = ist2(chunk_newstr(&trash), 0);
istscpy(&n_short, n, 256);
trash.data += n_short.len;
if (n_short.len != n.len)
chunk_appendf(&trash, " (... +%ld)", (long)(n.len - n_short.len));
v_short = ist2(chunk_newstr(&trash), 0);
istscpy(&v_short, v, 1024);
trash.data += v_short.len;
if (v_short.len != v.len)
chunk_appendf(&trash, " (... +%ld)", (long)(v.len - v_short.len));
TRACE_PRINTF_LOC(TRACE_LEVEL_USER, mask, trc_loc, func,
0, 0, 0, 0, "%s%s %s %s: %s", c_str, s_str,
mask & H3_EV_TX_HDR ? "sndh" : "rcvh",
istptr(n_short), istptr(v_short));
}
/* Output a trace for HTTP/3 header <n>:<v> if tracing is enabled. */
static void h3_trace_header(const struct ist n, const struct ist v,
uint64_t mask, const struct ist trc_loc, const char *func,
const struct qcc *qcc, const struct qcs *qcs)
{
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
TRACE_ENABLED(TRACE_LEVEL_USER, mask, qcc ? qcc->conn : 0, qcs, 0, 0))
_h3_trace_header(n, v, mask, trc_loc, func, qcc, qcs);
}
/* Parse from buffer <buf> a H3 HEADERS frame of length <len>. Data are copied /* Parse from buffer <buf> a H3 HEADERS frame of length <len>. Data are copied
* in a local HTX buffer and transfer to the stream connector layer. <fin> must be * in a local HTX buffer and transfer to the stream connector layer. <fin> must be
* set if this is the last data to transfer from this stream. * set if this is the last data to transfer from this stream.
@ -744,12 +659,6 @@ static ssize_t h3_req_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
goto out; goto out;
} }
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, 0, 0, 0)) {
for (i = 0; list[i].n.len; ++i)
h3_trace_header(list[i].n, list[i].v, H3_EV_RX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
}
if (!b_alloc(&htx_buf, DB_SE_RX)) { if (!b_alloc(&htx_buf, DB_SE_RX)) {
TRACE_ERROR("HTX buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs); TRACE_ERROR("HTX buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
len = -1; len = -1;
@ -1238,13 +1147,6 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
goto out; goto out;
} }
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, 0, 0, 0)) {
int i;
for (i = 0; list[i].n.len; ++i)
h3_trace_header(list[i].n, list[i].v, H3_EV_RX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
}
if (!(appbuf = qcc_get_stream_rxbuf(qcs))) { if (!(appbuf = qcc_get_stream_rxbuf(qcs))) {
TRACE_ERROR("buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs); TRACE_ERROR("buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
len = -1; len = -1;
@ -2028,25 +1930,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
h3s->st_req = H3S_ST_REQ_TRAILERS; h3s->st_req = H3S_ST_REQ_TRAILERS;
} }
break; break;
case H3_FT_CANCEL_PUSH:
if (!conn_is_back(qcs->qcc->conn)) {
/* RFC 9114 7.2.3. CANCEL_PUSH
*
* If a server receives a CANCEL_PUSH frame for a push ID
* that has not yet been mentioned by a PUSH_PROMISE frame, this MUST be
* treated as a connection error of type H3_ID_ERROR.
*/
TRACE_ERROR("reject CANCEL_PUSH from client", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
else {
/* Not supported */
ret = flen;
}
break;
case H3_FT_GOAWAY: case H3_FT_GOAWAY:
ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen); ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
if (ret < 0) { if (ret < 0) {
@ -2055,6 +1938,12 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
goto err; goto err;
} }
break; break;
case H3_FT_CANCEL_PUSH:
case H3_FT_PUSH_PROMISE:
case H3_FT_MAX_PUSH_ID:
/* Not supported */
ret = flen;
break;
case H3_FT_SETTINGS: case H3_FT_SETTINGS:
ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen); ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen);
if (ret < 0) { if (ret < 0) {
@ -2064,26 +1953,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
} }
h3c->flags |= H3_CF_SETTINGS_RECV; h3c->flags |= H3_CF_SETTINGS_RECV;
break; break;
case H3_FT_PUSH_PROMISE:
/* h3_check_frame_valid() must reject on server side. */
BUG_ON(!conn_is_back(qcs->qcc->conn));
/* RFC 9114 7.2.5. PUSH_PROMISE
*
* A client MUST treat
* receipt of a PUSH_PROMISE frame that contains a larger push ID than
* the client has advertised as a connection error of H3_ID_ERROR.
*/
TRACE_ERROR("Received unexpected PUSH_PROMISE frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1, muxc_tevt_type_proto_err);
goto err;
case H3_FT_MAX_PUSH_ID:
/* h3_check_frame_valid() must reject on client side. */
BUG_ON(conn_is_back(qcs->qcc->conn));
/* Not supported. */
ret = flen;
break;
default: default:
/* RFC 9114 Section 9. Extensions to HTTP/3 /* RFC 9114 Section 9. Extensions to HTTP/3
* *
@ -2322,7 +2191,6 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
if (qpack_encode_method(&headers_buf, sl->info.req.meth, meth)) if (qpack_encode_method(&headers_buf, sl->info.req.meth, meth))
goto err_full; goto err_full;
h3_trace_header(ist(":method"), meth, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
if (uri.ptr[0] != '/' && uri.ptr[0] != '*') { if (uri.ptr[0] != '/' && uri.ptr[0] != '*') {
int len = 1; int len = 1;
@ -2354,23 +2222,13 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
if (qpack_encode_scheme(&headers_buf, scheme)) if (qpack_encode_scheme(&headers_buf, scheme))
goto err_full; goto err_full;
h3_trace_header(ist(":scheme"), scheme, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
if (qpack_encode_path(&headers_buf, uri)) if (qpack_encode_path(&headers_buf, uri))
goto err_full; goto err_full;
h3_trace_header(ist(":path"), uri, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
if (istlen(auth)) { if (istlen(auth)) {
if (qpack_encode_auth(&headers_buf, auth)) if (qpack_encode_auth(&headers_buf, auth))
goto err_full; goto err_full;
h3_trace_header(ist(":authority"), auth, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
}
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, 0, 0, 0)) {
int i;
for (i = 0; list[i].n.len; ++i)
h3_trace_header(list[i].n, list[i].v, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
} }
if (!(sl->flags & HTX_SL_F_XFER_LEN)) { if (!(sl->flags & HTX_SL_F_XFER_LEN)) {
@ -2507,13 +2365,6 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
TRACE_USER("handling final HTX response", H3_EV_STRM_SEND, qcs->qcc->conn, qcs); TRACE_USER("handling final HTX response", H3_EV_STRM_SEND, qcs->qcc->conn, qcs);
h3s->flags &= ~H3_SF_SENT_INTERIM; h3s->flags &= ~H3_SF_SENT_INTERIM;
} }
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED) {
char sts[4];
h3_trace_header(ist(":status"), ist(ultoa_r(status, sts, sizeof(sts))),
H3_EV_TX_FRAME|H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__,
qcs->qcc, qcs);
}
} }
else if (type == HTX_BLK_HDR) { else if (type == HTX_BLK_HDR) {
if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) { if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) {
@ -2531,14 +2382,6 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
} }
} }
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, 0, 0, 0)) {
int i;
for (i = 0; list[i].n.len; ++i)
h3_trace_header(list[i].n, list[i].v, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
}
/* Current function expects HTX start-line to be present. This also /* Current function expects HTX start-line to be present. This also
* ensures <status> conformance has been checked prior to encoding it. * ensures <status> conformance has been checked prior to encoding it.
*/ */

View file

@ -210,6 +210,7 @@ struct global global = {
#endif #endif
/* by default allow clients which use a privileged port for TCP only */ /* by default allow clients which use a privileged port for TCP only */
.clt_privileged_ports = HA_PROTO_TCP, .clt_privileged_ports = HA_PROTO_TCP,
.maxthrpertgroup = DEF_MAX_THREADS_PER_GROUP,
/* others NULL OK */ /* others NULL OK */
}; };
@ -274,7 +275,6 @@ unsigned int deprecated_directives_allowed = 0;
/* mapped storage for collected libs */ /* mapped storage for collected libs */
void *lib_storage = NULL; void *lib_storage = NULL;
size_t lib_size = 0; size_t lib_size = 0;
char *lib_output_file = NULL;
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum, int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
char **errmsg) char **errmsg)
@ -785,9 +785,6 @@ static void usage(char *name)
#if defined(HA_HAVE_DUMP_LIBS) #if defined(HA_HAVE_DUMP_LIBS)
" -dL dumps loaded object files after config checks\n" " -dL dumps loaded object files after config checks\n"
#endif #endif
#if defined(HA_HAVE_DUMP_LIBS) && defined(HA_HAVE_DL_ITERATE_PHDR)
" -dA[file] collects libs into a tar file at <file>\n"
#endif
#if defined(USE_CPU_AFFINITY) #if defined(USE_CPU_AFFINITY)
" -dc dumps the list of selected and evicted CPUs\n" " -dc dumps the list of selected and evicted CPUs\n"
#endif #endif
@ -1631,16 +1628,6 @@ void haproxy_init_args(int argc, char **argv)
#if defined(HA_HAVE_DUMP_LIBS) #if defined(HA_HAVE_DUMP_LIBS)
else if (*flag == 'd' && flag[1] == 'L') else if (*flag == 'd' && flag[1] == 'L')
arg_mode |= MODE_DUMP_LIBS; arg_mode |= MODE_DUMP_LIBS;
# if defined(HA_HAVE_DL_ITERATE_PHDR)
else if (*flag == 'd' && flag[1] == 'A') {
lib_output_file = flag + 2;
if (!*lib_output_file) {
ha_alert("-dA: missing output file name\n");
exit(1);
}
arg_mode |= MODE_DUMP_LIBS; // stop on libs dump
}
# endif /* HA_HAVE_DL_ITERATE_PHDR */
#endif #endif
else if (*flag == 'd' && flag[1] == 'K') { else if (*flag == 'd' && flag[1] == 'K') {
arg_mode |= MODE_DUMP_KWD; arg_mode |= MODE_DUMP_KWD;
@ -1939,30 +1926,20 @@ static void dump_registered_keywords(void)
/* Generate a random cluster-secret in case the setting is not provided in the /* Generate a random cluster-secret in case the setting is not provided in the
* configuration. This allows to use features which rely on it albeit with some * configuration. This allows to use features which rely on it albeit with some
* limitations. The function prefers RAND_bytes() if available, otherwise falls * limitations.
* back to ha_random64_pair_hashed().
*/ */
static void generate_random_cluster_secret() static void generate_random_cluster_secret()
{ {
/* used as a default random cluster-secret if none defined. */ /* used as a default random cluster-secret if none defined. */
union { uint64_t rand;
uint64_t by64[2];
uchar by8[16];
} rand;
/* The caller must not overwrite an already defined secret. */ /* The caller must not overwrite an already defined secret. */
BUG_ON(cluster_secret_isset); BUG_ON(cluster_secret_isset);
BUG_ON(sizeof(global.cluster_secret) != sizeof(rand));
#ifdef USE_OPENSSL
if (RAND_bytes(rand.by8, sizeof(rand.by8)) != 1)
#endif
{
/* no SSL or not working, fall back to other sources */
ha_random64_pair_hashed(&rand.by64[0], &rand.by64[1]);
}
rand = ha_random64();
memcpy(global.cluster_secret, &rand, sizeof(rand)); memcpy(global.cluster_secret, &rand, sizeof(rand));
rand = ha_random64();
memcpy(global.cluster_secret + sizeof(rand), &rand, sizeof(rand));
cluster_secret_isset = 1; cluster_secret_isset = 1;
} }
@ -2152,6 +2129,9 @@ static void step_init_1()
if (init_acl() != 0) if (init_acl() != 0)
exit(1); exit(1);
/* Initialise lua. */
hlua_init();
/* set modes given from cmdline */ /* set modes given from cmdline */
global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE
| MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING | MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING
@ -2326,17 +2306,6 @@ static void step_init_2(int argc, char** argv)
#if defined(HA_HAVE_DUMP_LIBS) #if defined(HA_HAVE_DUMP_LIBS)
if (global.mode & MODE_DUMP_LIBS && !master) { if (global.mode & MODE_DUMP_LIBS && !master) {
# if defined(HA_HAVE_DL_ITERATE_PHDR)
if (lib_output_file) {
/* we'll dump everything to lib_output_file */
if (copy_libs_to_file() < 0)
deinit_and_exit(1);
/* release memory if no longer needed */
if ((global.tune.options & (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS)) !=
(GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS))
free_collected_libs();
}
# endif
qfprintf(stdout, "List of loaded object files:\n"); qfprintf(stdout, "List of loaded object files:\n");
chunk_reset(&trash); chunk_reset(&trash);
if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE))) if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE)))
@ -2810,7 +2779,6 @@ void deinit(void)
struct cfg_postparser *pprs, *pprsb; struct cfg_postparser *pprs, *pprsb;
char **tmp = init_env; char **tmp = init_env;
int cur_fd; int cur_fd;
int i;
/* the user may want to skip this phase */ /* the user may want to skip this phase */
if (global.tune.options & GTUNE_QUICK_EXIT) if (global.tune.options & GTUNE_QUICK_EXIT)
@ -2887,10 +2855,8 @@ void deinit(void)
ha_free(&global.server_state_base); ha_free(&global.server_state_base);
ha_free(&global.server_state_file); ha_free(&global.server_state_file);
ha_free(&global.stats_file); ha_free(&global.stats_file);
for (i = 0; i < global.nbthread; i++) { task_destroy(idle_conn_task);
task_destroy(idle_conn_task[i]); idle_conn_task = NULL;
idle_conn_task[i] = NULL;
}
list_for_each_entry_safe(log, logb, &global.loggers, list) { list_for_each_entry_safe(log, logb, &global.loggers, list) {
LIST_DEL_INIT(&log->list); LIST_DEL_INIT(&log->list);
@ -2984,7 +2950,7 @@ void deinit(void)
free(init_env); free(init_env);
} }
free(progname); free(progname);
free_collected_libs();
} /* end deinit() */ } /* end deinit() */
__attribute__((noreturn)) void deinit_and_exit(int status) __attribute__((noreturn)) void deinit_and_exit(int status)
@ -3122,7 +3088,6 @@ void *run_thread_poll_loop(void *data)
ha_set_thread(data); ha_set_thread(data);
set_thread_cpu_affinity(); set_thread_cpu_affinity();
clock_set_local_source(); clock_set_local_source();
ha_random_seed_thread();
#ifdef USE_THREAD #ifdef USE_THREAD
ha_thread_info[tid].pth_id = ha_get_pthread_id(tid); ha_thread_info[tid].pth_id = ha_get_pthread_id(tid);
@ -3364,18 +3329,16 @@ static void setup_user_ns(uid_t euid, gid_t egid)
static int do_chroot(const char *prog, const char *path) static int do_chroot(const char *prog, const char *path)
{ {
const char *dir, *chroot_dir; const char *chroot_dir = path;
int error, chroot_error; int error = 0;
error = chroot_error = 0;
dir = chroot_dir = path;
if (strcmp(path, "auto") == 0) { if (strcmp(path, "auto") == 0) {
/* When "chroot auto" is used, we attempt to chroot to an /* When "chroot auto" is used, we attempt to chroot to an
* anonymous and read-only directory. * anonymous and read-only directory.
*/ */
char tmpdir[] = "/tmp/haproxy.XXXXXX"; char tmpdir[] = "/tmp/haproxy.XXXXXX";
dir = mkdtemp(tmpdir); chroot_dir = mkdtemp(tmpdir);
if (dir == NULL) { if (chroot_dir == NULL) {
ha_alert("[%s.main()] Cannot create(%s) for chroot auto.\n", ha_alert("[%s.main()] Cannot create(%s) for chroot auto.\n",
prog, tmpdir); prog, tmpdir);
return -1; return -1;
@ -3386,32 +3349,16 @@ static int do_chroot(const char *prog, const char *path)
* want to remove the directory). * want to remove the directory).
*/ */
DISGUISE(rmdir(tmpdir)); DISGUISE(rmdir(tmpdir));
chroot_dir = ".";
if (!error) if (!error)
chroot_error = chroot("."); error = chroot(".");
} else if (strcmp(path, "/") != 0) { } else if (strcmp(path, "/") != 0) {
chroot_error = chroot(path); error = chroot(path);
} }
#ifdef CLONE_NEWUSER if (!error)
/* If the chroot failed because of insufficient privileges and
* unshare(CLONE_NEWUSER) is available, we attempt it to gain the
* abilty to chroot as an unprivileged user. If that worked, we
* try the chroot again.
*/
if (chroot_error && errno == EPERM) {
uid_t euid = geteuid();
gid_t egid = getegid();
if (unshare(CLONE_NEWUSER) == 0) {
setup_user_ns(euid, egid);
chroot_error = chroot(chroot_dir);
}
}
#endif
if (!error && !chroot_error)
error = chdir("/"); error = chdir("/");
if (error || chroot_error) { if (error) {
ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, dir); ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, chroot_dir);
return -1; return -1;
} }
@ -3755,6 +3702,20 @@ int main(int argc, char **argv)
} }
} }
#ifdef CLONE_NEWUSER
/* When we aren't root and intend to chroot, we try the Linux-only
* unshare(CLONE_NEWUSER) mechanism if available to allow chroot as an
* unprivileged user. If that doesn't work, we just let the subsequent
* chroot() fail as it would have previously.
*/
if (geteuid() != 0 && global.chroot != NULL) {
uid_t euid = geteuid();
gid_t egid = getegid();
if (unshare(CLONE_NEWUSER) == 0)
setup_user_ns(euid, egid);
}
#endif
/* Must chroot and setgid/setuid in the children */ /* Must chroot and setgid/setuid in the children */
/* chroot if needed */ /* chroot if needed */
if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) { if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) {

View file

@ -788,7 +788,7 @@ static void hstream_parse_uri(struct ist uri, struct hstream *hs)
} while (*next); } while (*next);
if (use_rand) if (use_rand)
result = ((long long)statistical_prng() * result) / 0xFFFFFFFFU; result = ((long long)ha_random64() * result) / ((long long)RAND_MAX + 1);
switch (*arg) { switch (*arg) {
case 's': case 's':

View file

@ -3,7 +3,6 @@
#include <haproxy/chunk.h> #include <haproxy/chunk.h>
#include <haproxy/errors.h> #include <haproxy/errors.h>
#include <haproxy/global.h> #include <haproxy/global.h>
#include <haproxy/openssl-compat.h>
#include <haproxy/version.h> #include <haproxy/version.h>
static int haterm_debug; static int haterm_debug;
@ -29,9 +28,7 @@ static void haterm_usage(char *name)
" -d : enable the traces for all http protocols\n" " -d : enable the traces for all http protocols\n"
" -dS : disables splice() usage even when available\n" " -dS : disables splice() usage even when available\n"
" -dZ : disable zero-copy forwarding\n" " -dZ : disable zero-copy forwarding\n"
#if defined(USE_QUIC)
" --" QUIC_BIND_LONG_OPT " <opts> : append options to QUIC \"bind\" lines\n" " --" QUIC_BIND_LONG_OPT " <opts> : append options to QUIC \"bind\" lines\n"
#endif
" --" TCP_BIND_LONG_OPT " <opts> : append options to TCP \"bind\" lines\n" " --" TCP_BIND_LONG_OPT " <opts> : append options to TCP \"bind\" lines\n"
, name); , name);
exit(1); exit(1);
@ -174,7 +171,7 @@ void haproxy_init_args(int argc, char **argv)
struct hbuf fbuf = HBUF_NULL; // "frontend" section struct hbuf fbuf = HBUF_NULL; // "frontend" section
struct hbuf tbuf = HBUF_NULL; // "traces" section struct hbuf tbuf = HBUF_NULL; // "traces" section
char *bits = NULL, *curves = NULL; char *bits = NULL, *curves = NULL;
char *quic_bind_opt __maybe_unused = NULL, *tcp_bind_opt = NULL; char *quic_bind_opt = NULL, *tcp_bind_opt = NULL;
int sargc; /* saved argc */ int sargc; /* saved argc */
char **sargv; /* saved argv */ char **sargv; /* saved argv */
@ -206,7 +203,6 @@ void haproxy_init_args(int argc, char **argv)
if (*opt == '-') { if (*opt == '-') {
/* long options */ /* long options */
opt++; opt++;
#if defined(USE_QUIC)
if (strcmp(opt, QUIC_BIND_LONG_OPT) == 0) { if (strcmp(opt, QUIC_BIND_LONG_OPT) == 0) {
argv++; argc--; argv++; argc--;
if (argc <= 0 || **argv == '-') if (argc <= 0 || **argv == '-')
@ -214,9 +210,7 @@ void haproxy_init_args(int argc, char **argv)
quic_bind_opt = *argv; quic_bind_opt = *argv;
} }
else else if (strcmp(opt, TCP_BIND_LONG_OPT) == 0) {
#endif
if (strcmp(opt, TCP_BIND_LONG_OPT) == 0) {
argv++; argc--; argv++; argc--;
if (argc <= 0 || **argv == '-') if (argc <= 0 || **argv == '-')
haterm_usage(progname); haterm_usage(progname);
@ -260,11 +254,6 @@ void haproxy_init_args(int argc, char **argv)
else if (*opt == 'd' && *(opt+1) == 'S') { else if (*opt == 'd' && *(opt+1) == 'S') {
global.tune.options &= ~GTUNE_USE_SPLICE; global.tune.options &= ~GTUNE_USE_SPLICE;
} }
# if defined(HA_USE_KTLS)
else if (*opt == 'd' && *(opt+1) == 'T') {
global.tune.options |= GTUNE_NO_KTLS;
}
# endif
#endif #endif
else if (*opt == 'd' && *(opt+1) == 'Z') { else if (*opt == 'd' && *(opt+1) == 'Z') {
global.tune.no_zero_copy_fwd |= NO_ZERO_COPY_FWD; global.tune.no_zero_copy_fwd |= NO_ZERO_COPY_FWD;
@ -405,30 +394,20 @@ void haproxy_init_args(int argc, char **argv)
} }
/* clear HTTP */ /* clear HTTP */
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread%s%s\n", ip, port1, hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread\n", ip, port1);
tcp_bind_opt ? " " : "",
tcp_bind_opt ? tcp_bind_opt : "");
has_bind = 1; has_bind = 1;
if (port2) { if (port2) {
#if defined(USE_OPENSSL)
has_ssl = 1; has_ssl = 1;
/* SSL/TCP binding */ /* SSL/TCP binding */
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread ssl " hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread ssl "
"alpn h3,h2,http1.1,http1.0" "alpn h2,http1.1,http1.0"
" crt " HATERM_RSA_CERT_NAME " crt " HATERM_RSA_CERT_NAME
" crt " HATERM_ECDSA_CERT_NAME "%s%s%s\n", " crt " HATERM_ECDSA_CERT_NAME "%s%s\n",
ip, port2, ip, port2,
tcp_bind_opt ? " " : "", tcp_bind_opt ? " " : "",
tcp_bind_opt ? tcp_bind_opt : "", tcp_bind_opt ? tcp_bind_opt : "");
# if defined(USE_LINUX_SPLICE) && defined(HA_USE_KTLS)
" ktls on"
# else
"" /* no ktls */
# endif
);
# if defined(USE_QUIC)
/* QUIC binding */ /* QUIC binding */
hbuf_appendf(&fbuf, "\tbind %s@%s:%s shards by-thread ssl" hbuf_appendf(&fbuf, "\tbind %s@%s:%s shards by-thread ssl"
" crt " HATERM_RSA_CERT_NAME " crt " HATERM_RSA_CERT_NAME
@ -436,11 +415,6 @@ void haproxy_init_args(int argc, char **argv)
ipv6 ? "quic6" : "quic4", ip, port2, ipv6 ? "quic6" : "quic4", ip, port2,
quic_bind_opt ? " " : "", quic_bind_opt ? " " : "",
quic_bind_opt ? quic_bind_opt : ""); quic_bind_opt ? quic_bind_opt : "");
# endif /* USE_QUIC */
#else /* !USE_OPENSSL */
ha_alert("SSL support not compiled in. Rebuild with USE_OPENSSL=1.\n");
goto leave;
#endif /* USE_OPENSSL */
} }
} }
else else
@ -464,12 +438,6 @@ void haproxy_init_args(int argc, char **argv)
} }
hbuf_appendf(&gbuf, "global\n"); hbuf_appendf(&gbuf, "global\n");
hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n"); hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n");
if (has_ssl)
hbuf_appendf(&gbuf, "\texpose-experimental-directives\n");
#if defined(USE_LINUX_SPLICE) && defined(HA_USE_KTLS)
if (has_ssl)
hbuf_appendf(&gbuf, "\ttune.pipesize 262144\n");
#endif
} }
/* "global" section */ /* "global" section */

View file

@ -73,9 +73,6 @@
#include <haproxy/event_hdl.h> #include <haproxy/event_hdl.h>
#include <haproxy/check.h> #include <haproxy/check.h>
#include <haproxy/mailers.h> #include <haproxy/mailers.h>
#if defined(HAVE_ACME)
#include <haproxy/acme.h>
#endif /* HAVE_ACME */
/* Global LUA flags */ /* Global LUA flags */
@ -108,6 +105,16 @@ static uint8_t hlua_body = 1;
*/ */
static uint8_t hlua_bool_sample_conversion = HLUA_BOOL_SAMPLE_CONVERSION_UNK; static uint8_t hlua_bool_sample_conversion = HLUA_BOOL_SAMPLE_CONVERSION_UNK;
/* Lua uses longjmp to perform yield or throwing errors. This
* macro is used only for identifying the function that can
* not return because a longjmp is executed.
* __LJMP marks a prototype of hlua file that can use longjmp.
* WILL_LJMP() marks an lua function that will use longjmp.
* MAY_LJMP() marks an lua function that may use longjmp.
*/
#define __LJMP
#define WILL_LJMP(func) do { func; my_unreachable(); } while(0)
#define MAY_LJMP(func) func
/* This couple of function executes securely some Lua calls outside of /* This couple of function executes securely some Lua calls outside of
* the lua runtime environment. Each Lua call can return a longjmp * the lua runtime environment. Each Lua call can return a longjmp
@ -169,24 +176,6 @@ static int hlua_panic_ljmp(lua_State *L) { WILL_LJMP(longjmp(safe_ljmp_env, 1));
*/ */
static struct list referenced_functions = LIST_HEAD_INIT(referenced_functions); static struct list referenced_functions = LIST_HEAD_INIT(referenced_functions);
/* List of callbacks registered via hap_register_hlua_state_init(), called
* for each new lua_State created in hlua_init_state().
*/
static struct list hlua_state_init_list = LIST_HEAD_INIT(hlua_state_init_list);
void hap_register_hlua_state_init(int (*fct)(lua_State *L, char **errmsg))
{
struct hlua_state_init_fct *entry;
entry = calloc(1, sizeof(*entry));
if (!entry) {
ha_alert("hlua: out of memory registering state init callback\n");
exit(1);
}
entry->fct = fct;
LIST_APPEND(&hlua_state_init_list, &entry->list);
}
/* This variable is used only during initialization to identify the Lua state /* This variable is used only during initialization to identify the Lua state
* currently being initialized. 0 is the common lua state, 1 to n are the Lua * currently being initialized. 0 is the common lua state, 1 to n are the Lua
* states dedicated to each thread (in this case hlua_state_id==tid+1). * states dedicated to each thread (in this case hlua_state_id==tid+1).
@ -510,6 +499,7 @@ static int class_fetches_ref;
static int class_converters_ref; static int class_converters_ref;
static int class_http_ref; static int class_http_ref;
static int class_http_msg_ref; static int class_http_msg_ref;
static int class_httpclient_ref;
static int class_map_ref; static int class_map_ref;
static int class_applet_tcp_ref; static int class_applet_tcp_ref;
static int class_applet_http_ref; static int class_applet_http_ref;
@ -998,6 +988,13 @@ const char *hlua_traceback(lua_State *L, const char* sep)
* stack. If the number of arguments available is not the same * stack. If the number of arguments available is not the same
* then <nb> an error is thrown. * then <nb> an error is thrown.
*/ */
__LJMP static inline void check_args(lua_State *L, int nb, char *fcn)
{
if (lua_gettop(L) == nb)
return;
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
}
/* This function pushes an error string prefixed by the file name /* This function pushes an error string prefixed by the file name
* and the line number where the error is encountered. * and the line number where the error is encountered.
* *
@ -1014,7 +1011,7 @@ __LJMP static int _hlua_pusherror(lua_State *L)
return 1; return 1;
} }
int hlua_pusherror(lua_State *L, const char *fmt, ...) static int hlua_pusherror(lua_State *L, const char *fmt, ...)
{ {
va_list argp; va_list argp;
int ret = 1; int ret = 1;
@ -1786,6 +1783,34 @@ int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task)
return 1; return 1;
} }
/* kill all associated httpclient to this hlua task
* We must take extra precautions as we're manipulating lua-exposed
* objects without the main lua lock.
*/
static void hlua_httpclient_destroy_all(struct hlua *hlua)
{
struct hlua_httpclient *hlua_hc;
/* use thread-safe accessors for hc_list since GC cycle initiated by
* another thread sharing the same main lua stack (lua coroutine)
* could execute hlua_httpclient_gc() on the hlua->hc_list items
* in parallel: Lua GC applies on the main stack, it is not limited to
* a single coroutine stack, see Github issue #2037 for reference.
* Remember, coroutines created using lua_newthread() are not meant to
* be thread safe in Lua. (From lua co-author:
* http://lua-users.org/lists/lua-l/2011-07/msg00072.html)
*
* This security measure is superfluous when 'lua-load-per-thread' is used
* since in this case coroutines exclusively run on the same thread
* (main stack is not shared between OS threads).
*/
while ((hlua_hc = MT_LIST_POP(&hlua->hc_list, typeof(hlua_hc), by_hlua))) {
httpclient_stop_and_destroy(hlua_hc->hc);
hlua_hc->hc = NULL;
}
}
/* Used to destroy the Lua coroutine when the attached stream or task /* Used to destroy the Lua coroutine when the attached stream or task
* is destroyed. The destroy also the memory context. The struct "lua" * is destroyed. The destroy also the memory context. The struct "lua"
* will be freed. * will be freed.
@ -2924,20 +2949,20 @@ __LJMP static int hlua_socket_receive_yield(struct lua_State *L, int status, lua
/* remove final \r\n. */ /* remove final \r\n. */
if (nblk == 1) { if (nblk == 1) {
if (len1 && blk1[len1-1] == '\n') { if (blk1[len1-1] == '\n') {
len1--; len1--;
skip_at_end++; skip_at_end++;
if (len1 && blk1[len1-1] == '\r') { if (blk1[len1-1] == '\r') {
len1--; len1--;
skip_at_end++; skip_at_end++;
} }
} }
} }
else { else {
if (len2 && blk2[len2-1] == '\n') { if (blk2[len2-1] == '\n') {
len2--; len2--;
skip_at_end++; skip_at_end++;
if (len2 && blk2[len2-1] == '\r') { if (blk2[len2-1] == '\r') {
len2--; len2--;
skip_at_end++; skip_at_end++;
} }
@ -6684,20 +6709,6 @@ __LJMP static inline int hlua_http_add_hdr(lua_State *L, struct http_msg *msg)
size_t value_len; size_t value_len;
const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len)); const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
struct htx *htx = htxbuf(&msg->chn->buf); struct htx *htx = htxbuf(&msg->chn->buf);
size_t i;
/* Reject header values containing CR/LF/NUL to prevent HTTP header
* injection on HTTP/1 output.
*/
for (i = 0; i < name_len; i++) {
if (name[i] == 0 || name[i] == '\r' || name[i] == '\n')
WILL_LJMP(lua_error(L));
}
for (i = 0; i < value_len; i++) {
if (value[i] == 0 || value[i] == '\r' || value[i] == '\n')
WILL_LJMP(lua_error(L));
}
lua_pushboolean(L, http_add_header(htx, ist2(name, name_len), lua_pushboolean(L, http_add_header(htx, ist2(name, name_len),
ist2(value, value_len), 1)); ist2(value, value_len), 1));
@ -7925,6 +7936,488 @@ __LJMP static int hlua_http_msg_unset_eom(lua_State *L)
return 0; return 0;
} }
/*
*
*
* Class HTTPClient
*
*
*/
__LJMP static struct hlua_httpclient *hlua_checkhttpclient(lua_State *L, int ud)
{
return MAY_LJMP(hlua_checkudata(L, ud, class_httpclient_ref));
}
/* stops the httpclient and ask it to kill itself */
__LJMP static int hlua_httpclient_gc(lua_State *L)
{
struct hlua_httpclient *hlua_hc;
MAY_LJMP(check_args(L, 1, "__gc"));
hlua_hc = MAY_LJMP(hlua_checkhttpclient(L, 1));
if (MT_LIST_DELETE(&hlua_hc->by_hlua)) {
/* we won the race against hlua_httpclient_destroy_all() */
httpclient_stop_and_destroy(hlua_hc->hc);
hlua_hc->hc = NULL;
}
return 0;
}
__LJMP static int hlua_httpclient_new(lua_State *L)
{
struct hlua_httpclient *hlua_hc;
struct hlua *hlua;
/* Get hlua struct, or NULL if we execute from main lua state */
hlua = hlua_gethlua(L);
if (!hlua)
return 0;
/* Check stack size. */
if (!lua_checkstack(L, 3)) {
hlua_pusherror(L, "httpclient: full stack");
goto err;
}
/* Create the object: obj[0] = userdata. */
lua_newtable(L);
hlua_hc = MAY_LJMP(lua_newuserdata(L, sizeof(*hlua_hc)));
lua_rawseti(L, -2, 0);
memset(hlua_hc, 0, sizeof(*hlua_hc));
hlua_hc->hc = httpclient_new(hlua, 0, IST_NULL);
if (!hlua_hc->hc)
goto err;
MT_LIST_APPEND(&hlua->hc_list, &hlua_hc->by_hlua);
/* Pop a class stream metatable and affect it to the userdata. */
lua_rawgeti(L, LUA_REGISTRYINDEX, class_httpclient_ref);
lua_setmetatable(L, -2);
return 1;
err:
WILL_LJMP(lua_error(L));
return 0;
}
/*
* Callback of the httpclient, this callback wakes the lua task up, once the
* httpclient receives some data
*
*/
static void hlua_httpclient_cb(struct httpclient *hc)
{
struct hlua *hlua = hc->caller;
if (!hlua || !hlua->task)
return;
task_wakeup(hlua->task, TASK_WOKEN_MSG);
}
/*
* Fill the lua stack with headers from the httpclient response
* This works the same way as the hlua_http_get_headers() function
*/
__LJMP static int hlua_httpclient_get_headers(lua_State *L, struct hlua_httpclient *hlua_hc)
{
struct http_hdr *hdr;
lua_newtable(L);
for (hdr = hlua_hc->hc->res.hdrs; hdr && isttest(hdr->n); hdr++) {
struct ist n, v;
int len;
n = hdr->n;
v = hdr->v;
/* Check for existing entry:
* assume that the table is on the top of the stack, and
* push the key in the stack, the function lua_gettable()
* perform the lookup.
*/
lua_pushlstring(L, n.ptr, n.len);
lua_gettable(L, -2);
switch (lua_type(L, -1)) {
case LUA_TNIL:
/* Table not found, create it. */
lua_pop(L, 1); /* remove the nil value. */
lua_pushlstring(L, n.ptr, n.len); /* push the header name as key. */
lua_newtable(L); /* create and push empty table. */
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
lua_rawseti(L, -2, 0); /* index header value (pop it). */
lua_rawset(L, -3); /* index new table with header name (pop the values). */
break;
case LUA_TTABLE:
/* Entry found: push the value in the table. */
len = lua_rawlen(L, -1);
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
lua_rawseti(L, -2, len+1); /* index header value (pop it). */
lua_pop(L, 1); /* remove the table (it is stored in the main table). */
break;
default:
/* Other cases are errors. */
hlua_pusherror(L, "internal error during the parsing of headers.");
WILL_LJMP(lua_error(L));
}
}
return 1;
}
/*
* Allocate and return an array of http_hdr ist extracted from the <headers> lua table
*
* Caller must free the result
*/
struct http_hdr *hlua_httpclient_table_to_hdrs(lua_State *L)
{
struct http_hdr hdrs[global.tune.max_http_hdr];
struct http_hdr *result = NULL;
uint32_t hdr_num = 0;
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
struct ist name, value;
const char *n, *v;
size_t nlen, vlen;
if (!lua_isstring(L, -2) || !lua_istable(L, -1)) {
/* Skip element if the key is not a string or if the value is not a table */
goto next_hdr;
}
n = lua_tolstring(L, -2, &nlen);
name = ist2(n, nlen);
/* Loop on header's values */
lua_pushnil(L);
while (lua_next(L, -2)) {
if (!lua_isstring(L, -1)) {
/* Skip the value if it is not a string */
goto next_value;
}
if (hdr_num >= global.tune.max_http_hdr) {
lua_pop(L, 2);
goto skip_headers;
}
v = lua_tolstring(L, -1, &vlen);
value = ist2(v, vlen);
name = ist2(n, nlen);
hdrs[hdr_num].n = istdup(name);
hdrs[hdr_num].v = istdup(value);
hdr_num++;
next_value:
lua_pop(L, 1);
}
next_hdr:
lua_pop(L, 1);
}
if (hdr_num) {
/* alloc and copy the headers in the httpclient struct */
result = calloc((hdr_num + 1), sizeof(*result));
if (!result)
goto skip_headers;
memcpy(result, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
result[hdr_num].n = IST_NULL;
result[hdr_num].v = IST_NULL;
}
skip_headers:
return result;
}
/*
* For each yield, checks if there is some data in the httpclient and push them
* in the lua buffer, once the httpclient finished its job, push the result on
* the stack
*/
__LJMP static int hlua_httpclient_rcv_yield(lua_State *L, int status, lua_KContext ctx)
{
struct buffer *tr;
int res;
struct hlua *hlua = hlua_gethlua(L);
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
tr = get_trash_chunk();
res = httpclient_res_xfer(hlua_hc->hc, tr);
luaL_addlstring(&hlua_hc->b, b_orig(tr), res);
if (!httpclient_data(hlua_hc->hc) && httpclient_ended(hlua_hc->hc)) {
luaL_pushresult(&hlua_hc->b);
lua_settable(L, -3);
lua_pushstring(L, "status");
lua_pushinteger(L, hlua_hc->hc->res.status);
lua_settable(L, -3);
lua_pushstring(L, "reason");
lua_pushlstring(L, hlua_hc->hc->res.reason.ptr, hlua_hc->hc->res.reason.len);
lua_settable(L, -3);
lua_pushstring(L, "headers");
hlua_httpclient_get_headers(L, hlua_hc);
lua_settable(L, -3);
return 1;
}
if (httpclient_data(hlua_hc->hc))
task_wakeup(hlua->task, TASK_WOKEN_MSG);
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
return 0;
}
/*
* Call this when trying to stream a body during a request
*/
__LJMP static int hlua_httpclient_snd_yield(lua_State *L, int status, lua_KContext ctx)
{
struct hlua *hlua;
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
const char *body_str = NULL;
int ret;
int end = 0;
size_t buf_len;
size_t to_send = 0;
hlua = hlua_gethlua(L);
if (!hlua || !hlua->task)
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
"'frontend', 'backend' or 'task'"));
ret = lua_getfield(L, -1, "body");
if (ret != LUA_TSTRING)
goto rcv;
body_str = lua_tolstring(L, -1, &buf_len);
lua_pop(L, 1);
to_send = buf_len - hlua_hc->sent;
if ((hlua_hc->sent + to_send) >= buf_len)
end = 1;
/* the end flag is always set since we are using the whole remaining size */
hlua_hc->sent += httpclient_req_xfer(hlua_hc->hc, ist2(body_str + hlua_hc->sent, to_send), end);
if (buf_len > hlua_hc->sent) {
/* still need to process the buffer */
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
} else {
goto rcv;
/* we sent the whole request buffer we can recv */
}
return 0;
rcv:
/* we return a "res" object */
lua_newtable(L);
lua_pushstring(L, "body");
luaL_buffinit(L, &hlua_hc->b);
task_wakeup(hlua->task, TASK_WOKEN_MSG);
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
return 1;
}
/*
* Send an HTTP request and wait for a response
*/
__LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
{
struct hlua_httpclient *hlua_hc;
struct http_hdr *hdrs = NULL;
struct http_hdr *hdrs_i = NULL;
struct hlua *hlua;
const char *url_str = NULL;
const char *body_str = NULL;
size_t buf_len = 0;
int ret;
hlua = hlua_gethlua(L);
if (!hlua || !hlua->task)
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
"'frontend', 'backend' or 'task'"));
if (lua_gettop(L) != 2 || lua_type(L, -1) != LUA_TTABLE)
WILL_LJMP(luaL_error(L, "'get' needs a table as argument"));
hlua_hc = hlua_checkhttpclient(L, 1);
/* An HTTPclient instance must never process more that one request. So
* at this stage, it must never have been started.
*/
if (httpclient_started(hlua_hc->hc))
WILL_LJMP(luaL_error(L, "httpclient instance cannot be reused. It must process at most one request"));
lua_pushnil(L); /* first key */
while (lua_next(L, 2)) {
if (strcmp(lua_tostring(L, -2), "dst") == 0) {
if (httpclient_set_dst(hlua_hc->hc, lua_tostring(L, -1)) < 0)
WILL_LJMP(luaL_error(L, "Can't use the 'dst' argument"));
} else if (strcmp(lua_tostring(L, -2), "url") == 0) {
if (lua_type(L, -1) != LUA_TSTRING)
WILL_LJMP(luaL_error(L, "invalid parameter in 'url', must be a string"));
url_str = lua_tostring(L, -1);
} else if (strcmp(lua_tostring(L, -2), "timeout") == 0) {
if (lua_type(L, -1) != LUA_TNUMBER)
WILL_LJMP(luaL_error(L, "invalid parameter in 'timeout', must be a number"));
httpclient_set_timeout(hlua_hc->hc, lua_tointeger(L, -1));
} else if (strcmp(lua_tostring(L, -2), "headers") == 0) {
if (lua_type(L, -1) != LUA_TTABLE)
WILL_LJMP(luaL_error(L, "invalid parameter in 'headers', must be a table"));
hdrs = hlua_httpclient_table_to_hdrs(L);
} else if (strcmp(lua_tostring(L, -2), "body") == 0) {
if (lua_type(L, -1) != LUA_TSTRING)
WILL_LJMP(luaL_error(L, "invalid parameter in 'body', must be a string"));
body_str = lua_tolstring(L, -1, &buf_len);
} else {
WILL_LJMP(luaL_error(L, "'%s' invalid parameter name", lua_tostring(L, -2)));
}
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(L, 1);
}
if (!url_str) {
WILL_LJMP(luaL_error(L, "'get' need a 'url' argument"));
return 0;
}
hlua_hc->sent = 0;
istfree(&hlua_hc->hc->req.url);
hlua_hc->hc->req.url = istdup(ist(url_str));
hlua_hc->hc->req.meth = meth;
/* update the httpclient callbacks */
hlua_hc->hc->ops.res_stline = hlua_httpclient_cb;
hlua_hc->hc->ops.res_headers = hlua_httpclient_cb;
hlua_hc->hc->ops.res_payload = hlua_httpclient_cb;
hlua_hc->hc->ops.res_end = hlua_httpclient_cb;
/* a body is available, it will use the request callback */
if (body_str && buf_len) {
hlua_hc->hc->ops.req_payload = hlua_httpclient_cb;
}
ret = httpclient_req_gen(hlua_hc->hc, hlua_hc->hc->req.url, meth, hdrs, IST_NULL);
/* free the temporary headers array */
hdrs_i = hdrs;
while (hdrs_i && isttest(hdrs_i->n)) {
istfree(&hdrs_i->n);
istfree(&hdrs_i->v);
hdrs_i++;
}
ha_free(&hdrs);
if (ret != ERR_NONE) {
WILL_LJMP(luaL_error(L, "Can't generate the HTTP request"));
return 0;
}
if (!httpclient_start(hlua_hc->hc))
WILL_LJMP(luaL_error(L, "couldn't start the httpclient"));
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
return 0;
}
/*
* Sends an HTTP HEAD request and wait for a response
*
* httpclient:head(url, headers, payload)
*/
__LJMP static int hlua_httpclient_head(lua_State *L)
{
return hlua_httpclient_send(L, HTTP_METH_HEAD);
}
/*
* Send an HTTP GET request and wait for a response
*
* httpclient:get(url, headers, payload)
*/
__LJMP static int hlua_httpclient_get(lua_State *L)
{
return hlua_httpclient_send(L, HTTP_METH_GET);
}
/*
* Sends an HTTP PUT request and wait for a response
*
* httpclient:put(url, headers, payload)
*/
__LJMP static int hlua_httpclient_put(lua_State *L)
{
return hlua_httpclient_send(L, HTTP_METH_PUT);
}
/*
* Send an HTTP POST request and wait for a response
*
* httpclient:post(url, headers, payload)
*/
__LJMP static int hlua_httpclient_post(lua_State *L)
{
return hlua_httpclient_send(L, HTTP_METH_POST);
}
/*
* Sends an HTTP DELETE request and wait for a response
*
* httpclient:delete(url, headers, payload)
*/
__LJMP static int hlua_httpclient_delete(lua_State *L)
{
return hlua_httpclient_send(L, HTTP_METH_DELETE);
}
/* /*
* *
* *
@ -9556,11 +10049,6 @@ __LJMP static void hlua_event_hdl_cb_push_args(struct hlua_event_sub *hlua_sub,
lua_settable(hlua->T, -3); lua_settable(hlua->T, -3);
} }
} }
#if defined(HAVE_ACME)
else if (event_hdl_sub_family_equal(EVENT_HDL_SUB_ACME, event)) {
MAY_LJMP(acme_hlua_event_push_args(hlua, event, data));
}
#endif /* HAVE_ACME */
/* sub mgmt */ /* sub mgmt */
hlua->nargs += 1; hlua->nargs += 1;
hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub); hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub);
@ -12890,16 +13378,6 @@ static int hlua_cfg_parse_openlibs(char **args, int section_type, struct proxy *
return -1; return -1;
} }
/* Reject a non-default restriction if the Lua VM is already initialised,
* which happens when lua-load, lua-load-per-thread or lua-prepend-path
* appeared before this directive.
*/
if (flags != HLUA_OPENLIBS_ALL && hlua_states[0]) {
memprintf(err, "'%s' must appear before any 'lua-load', 'lua-load-per-thread' or 'lua-prepend-path' directive",
args[0]);
return -1;
}
hlua_openlibs_flags = flags; hlua_openlibs_flags = flags;
return 0; return 0;
} }
@ -13000,8 +13478,6 @@ static int hlua_load(char **args, int section_type, struct proxy *curpx,
return -1; return -1;
} }
hlua_init();
/* loading for global state */ /* loading for global state */
hlua_state_id = 0; hlua_state_id = 0;
ha_set_thread(NULL); ha_set_thread(NULL);
@ -13020,8 +13496,6 @@ static int hlua_load_per_thread(char **args, int section_type, struct proxy *cur
return -1; return -1;
} }
hlua_init();
if (per_thread_load == NULL) { if (per_thread_load == NULL) {
/* allocate the first entry large enough to store the final NULL */ /* allocate the first entry large enough to store the final NULL */
per_thread_load = calloc(1, sizeof(*per_thread_load)); per_thread_load = calloc(1, sizeof(*per_thread_load));
@ -13110,8 +13584,6 @@ static int hlua_config_prepend_path(char **args, int section_type, struct proxy
struct prepend_path *p = NULL; struct prepend_path *p = NULL;
size_t i; size_t i;
hlua_init();
if (too_many_args(2, args, err, NULL)) { if (too_many_args(2, args, err, NULL)) {
goto err; goto err;
} }
@ -13520,7 +13992,6 @@ int hlua_post_init()
hlua_body = 0; hlua_body = 0;
#if defined(USE_OPENSSL) #if defined(USE_OPENSSL)
/* Initialize SSL server. */ /* Initialize SSL server. */
if (socket_ssl->xprt->prepare_srv) { if (socket_ssl->xprt->prepare_srv) {
@ -13831,6 +14302,7 @@ lua_State *hlua_init_state(int thread_num)
hlua_class_function(L, "get_patref", hlua_get_patref); hlua_class_function(L, "get_patref", hlua_get_patref);
hlua_class_function(L, "get_var", hlua_core_get_var); hlua_class_function(L, "get_var", hlua_core_get_var);
hlua_class_function(L, "tcp", hlua_socket_new); hlua_class_function(L, "tcp", hlua_socket_new);
hlua_class_function(L, "httpclient", hlua_httpclient_new);
hlua_class_function(L, "event_sub", hlua_event_global_sub); hlua_class_function(L, "event_sub", hlua_event_global_sub);
hlua_class_function(L, "log", hlua_log); hlua_class_function(L, "log", hlua_log);
hlua_class_function(L, "Debug", hlua_log_debug); hlua_class_function(L, "Debug", hlua_log_debug);
@ -14152,6 +14624,30 @@ lua_State *hlua_init_state(int thread_num)
/* Register previous table in the registry with reference and named entry. */ /* Register previous table in the registry with reference and named entry. */
class_http_msg_ref = hlua_register_metatable(L, CLASS_HTTP_MSG); class_http_msg_ref = hlua_register_metatable(L, CLASS_HTTP_MSG);
/*
*
* Register class HTTPClient
*
*/
/* Create and fill the metatable. */
lua_newtable(L);
lua_pushstring(L, "__index");
lua_newtable(L);
hlua_class_function(L, "get", hlua_httpclient_get);
hlua_class_function(L, "head", hlua_httpclient_head);
hlua_class_function(L, "put", hlua_httpclient_put);
hlua_class_function(L, "post", hlua_httpclient_post);
hlua_class_function(L, "delete", hlua_httpclient_delete);
lua_settable(L, -3); /* Sets the __index entry. */
/* Register the garbage collector entry. */
lua_pushstring(L, "__gc");
lua_pushcclosure(L, hlua_httpclient_gc, 0);
lua_settable(L, -3); /* Push the last 2 entries in the table at index -3 */
class_httpclient_ref = hlua_register_metatable(L, CLASS_HTTPCLIENT);
/* /*
* *
* Register class AppletTCP * Register class AppletTCP
@ -14303,28 +14799,6 @@ lua_State *hlua_init_state(int thread_num)
/* Register previous table in the registry with reference and named entry. */ /* Register previous table in the registry with reference and named entry. */
class_socket_ref = hlua_register_metatable(L, CLASS_SOCKET); class_socket_ref = hlua_register_metatable(L, CLASS_SOCKET);
/* Call all registered state init callbacks. */
{
struct hlua_state_init_fct *e;
char *errmsg = NULL;
int err_code;
list_for_each_entry(e, &hlua_state_init_list, list) {
err_code = e->fct(L, &errmsg);
if (errmsg) {
if (err_code & ERR_ALERT)
ha_alert("Lua: %s\n", errmsg);
else if (err_code & ERR_WARN)
ha_warning("Lua: %s\n", errmsg);
else
ha_notice("Lua: %s\n", errmsg);
ha_free(&errmsg);
}
if (err_code & (ERR_ABORT|ERR_FATAL))
exit(1);
}
}
lua_atpanic(L, hlua_panic_safe); lua_atpanic(L, hlua_panic_safe);
return L; return L;
@ -14345,9 +14819,6 @@ void hlua_init(void) {
}; };
#endif #endif
if (hlua_states[0])
return; /* already initialised */
/* Init post init function list head */ /* Init post init function list head */
for (i = 0; i < MAX_THREADS + 1; i++) for (i = 0; i < MAX_THREADS + 1; i++)
LIST_INIT(&hlua_init_functions[i]); LIST_INIT(&hlua_init_functions[i]);
@ -14413,16 +14884,10 @@ static void hlua_deinit()
{ {
int thr; int thr;
struct hlua_reg_filter *reg_flt, *reg_flt_bck; struct hlua_reg_filter *reg_flt, *reg_flt_bck;
struct hlua_state_init_fct *e, *eb;
list_for_each_entry_safe(reg_flt, reg_flt_bck, &referenced_filters, l) list_for_each_entry_safe(reg_flt, reg_flt_bck, &referenced_filters, l)
release_hlua_reg_filter(reg_flt); release_hlua_reg_filter(reg_flt);
list_for_each_entry_safe(e, eb, &hlua_state_init_list, list) {
LIST_DELETE(&e->list);
free(e);
}
for (thr = 0; thr < MAX_THREADS+1; thr++) { for (thr = 0; thr < MAX_THREADS+1; thr++) {
if (hlua_states[thr]) if (hlua_states[thr])
lua_close(hlua_states[thr]); lua_close(hlua_states[thr]);
@ -14442,14 +14907,3 @@ static void hlua_register_build_options(void)
} }
INITCALL0(STG_REGISTER, hlua_register_build_options); INITCALL0(STG_REGISTER, hlua_register_build_options);
/* Ensure the Lua VM is initialised even if no Lua directive appeared
* in the configuration (e.g. no global section at all).
*/
static int hlua_pre_check(void)
{
hlua_init();
return ERR_NONE;
}
REGISTER_PRE_CHECK(hlua_pre_check);

View file

@ -351,8 +351,6 @@ int hpack_dht_insert(struct hpack_dht *dht, struct ist name, struct ist value)
else { else {
/* need to defragment the table before inserting upfront */ /* need to defragment the table before inserting upfront */
dht = hpack_dht_defrag(dht); dht = hpack_dht_defrag(dht);
if (!dht)
return -1;
wrap = dht->wrap + 1; wrap = dht->wrap + 1;
head = dht->head + 1; head = dht->head + 1;
dht->dte[head].addr = dht->dte[dht->front].addr - (name.len + value.len); dht->dte[head].addr = dht->dte[dht->front].addr - (name.len + value.len);

Some files were not shown because too many files have changed in this diff Show more