mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-03-27 11:01:05 +01:00
Compare commits
189 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1c7e56585 | ||
|
|
4e99cddde4 | ||
|
|
0e36267aac | ||
|
|
1b0dfff552 | ||
|
|
d3ad730d5f | ||
|
|
cca9245416 | ||
|
|
07edaed191 | ||
|
|
1c1d9d2500 | ||
|
|
47987ccbd9 | ||
|
|
33041fe91f | ||
|
|
8e250bba8f | ||
|
|
c7564c19a2 | ||
|
|
efbf0f8ed1 | ||
|
|
52d8ee85e7 | ||
|
|
38a7d8599d | ||
|
|
82afd36b6c | ||
|
|
ada33006ef | ||
|
|
163eba5c8c | ||
|
|
61d68f14b2 | ||
|
|
125cbecfa9 | ||
|
|
a61ea0f414 | ||
|
|
cd363e0246 | ||
|
|
d257dd4563 | ||
|
|
39121ceca6 | ||
|
|
c9a9fa813b | ||
|
|
181cd8ba8a | ||
|
|
5acdda4eed | ||
|
|
92a24a4e87 | ||
|
|
467f911cea | ||
|
|
0213dd70c9 | ||
|
|
5ead611cc2 | ||
|
|
41c89e4fb6 | ||
|
|
b71f70d548 | ||
|
|
01b9b67d5c | ||
|
|
f8c96bf9cb | ||
|
|
4d6cba03f2 | ||
|
|
1c379cad88 | ||
|
|
3d9865a12c | ||
|
|
d72be950bd | ||
|
|
5a0fbbf1ca | ||
|
|
c6fc53aa99 | ||
|
|
2ca7601c2d | ||
|
|
d250b381dc | ||
|
|
5b184e4178 | ||
|
|
fedaf054c4 | ||
|
|
8e469ebf2e | ||
|
|
ff7b06badb | ||
|
|
5d0f5f8168 | ||
|
|
ed6a4bc807 | ||
|
|
282b9b7d16 | ||
|
|
6982c2539f | ||
|
|
9852d5be26 | ||
|
|
7d40b3134a | ||
|
|
8f6cb8f452 | ||
|
|
60c9e2975b | ||
|
|
5617e47f91 | ||
|
|
042b7ab763 | ||
|
|
7466f64c56 | ||
|
|
15b005fd1e | ||
|
|
f1e8173a43 | ||
|
|
4c61e9028c | ||
|
|
932d77e287 | ||
|
|
c238965b27 | ||
|
|
b63492e4f4 | ||
|
|
b3a84800b4 | ||
|
|
eec60f14dd | ||
|
|
fc38ebb079 | ||
|
|
10e78d9246 | ||
|
|
4e937e0391 | ||
|
|
05a295441c | ||
|
|
4e57516c9a | ||
|
|
e31640368a | ||
|
|
0e231bbd7c | ||
|
|
1696cfaa19 | ||
|
|
c6221db375 | ||
|
|
b93137ce67 | ||
|
|
e07c9ee575 | ||
|
|
de4f7eaeed | ||
|
|
e1738b665d | ||
|
|
6152a4eef5 | ||
|
|
94a4578ccf | ||
|
|
c1dfea3ab3 | ||
|
|
15cdcab1fc | ||
|
|
7a1382da79 | ||
|
|
8fe0950511 | ||
|
|
4319c20363 | ||
|
|
29592cb330 | ||
|
|
8dae4f7c0b | ||
|
|
ef2a292585 | ||
|
|
ba7dc46a92 | ||
|
|
fbdb0a991a | ||
|
|
3250ec6e9c | ||
|
|
9c0aeb3af4 | ||
|
|
cd91838042 | ||
|
|
b3be3b94a0 | ||
|
|
d10fc3d265 | ||
|
|
00bea05a14 | ||
|
|
ab7acdcc3a | ||
|
|
a3bf0de651 | ||
|
|
51d6f1ca4f | ||
|
|
cb51c8729d | ||
|
|
a390daaee4 | ||
|
|
d172f7b923 | ||
|
|
4e8cf26ab6 | ||
|
|
f7820bcbaa | ||
|
|
892adf3cc1 | ||
|
|
17cbec485a | ||
|
|
5cd71f69ba | ||
|
|
8139795c64 | ||
|
|
9cb11d0859 | ||
|
|
c0bf395cde | ||
|
|
ec7b07b650 | ||
|
|
e8e4449985 | ||
|
|
3fb8659d04 | ||
|
|
43b56c22c7 | ||
|
|
861d1111c3 | ||
|
|
261cae3b6d | ||
|
|
aa4d5dd217 | ||
|
|
6e819dc4fa | ||
|
|
2cd0cd84c6 | ||
|
|
6e75da7a91 | ||
|
|
33c928c745 | ||
|
|
3f3a0609e3 | ||
|
|
998ed00729 | ||
|
|
5d3246205b | ||
|
|
13c89bf20d | ||
|
|
2dfc8417cf | ||
|
|
b7c8fab507 | ||
|
|
fb7e5e1696 | ||
|
|
3b4275b072 | ||
|
|
825e5611ba | ||
|
|
01457979b6 | ||
|
|
07655da068 | ||
|
|
ed44adc3ca | ||
|
|
4d5a91b8af | ||
|
|
4102461dd6 | ||
|
|
13d13691b5 | ||
|
|
76ba026548 | ||
|
|
f24ed2a5d1 | ||
|
|
b837b2b86c | ||
|
|
b6e28bb4d7 | ||
|
|
760fef1fc0 | ||
|
|
73732abfb2 | ||
|
|
e82f03dd88 | ||
|
|
6d14fd0b29 | ||
|
|
00563233b7 | ||
|
|
b2b0d1a8be | ||
|
|
fb1bc592f5 | ||
|
|
990456462f | ||
|
|
c65526ad57 | ||
|
|
9dfff87b69 | ||
|
|
0fc6884bc7 | ||
|
|
0c9741b70a | ||
|
|
e33dfc4f26 | ||
|
|
5aa67f0587 | ||
|
|
aef7afbe65 | ||
|
|
7c895092a7 | ||
|
|
aaa97c4441 | ||
|
|
d491329de9 | ||
|
|
9c7c669d7a | ||
|
|
a33b42035b | ||
|
|
7be95eb892 | ||
|
|
64d997ebfc | ||
|
|
26a0817c1a | ||
|
|
376487cca9 | ||
|
|
aea0d38fdd | ||
|
|
7abb7c4c79 | ||
|
|
924a92200f | ||
|
|
31bbc1f0f1 | ||
|
|
3925bb8efc | ||
|
|
32d9af559f | ||
|
|
026652a7eb | ||
|
|
3d9764f4c3 | ||
|
|
e34b633be3 | ||
|
|
1babe8cb1b | ||
|
|
1cbd1163f0 | ||
|
|
bd3983b595 | ||
|
|
520faedda0 | ||
|
|
459835d535 | ||
|
|
9b3345237a | ||
|
|
2a0cf52cfc | ||
|
|
551e5f5fd4 | ||
|
|
2a2989bb23 | ||
|
|
cbebdb4ba8 | ||
|
|
9549b05b94 | ||
|
|
5e14904fef | ||
|
|
af6b9a0967 | ||
|
|
bfe5a2c3d7 | ||
|
|
b48c9a1465 |
11
.github/matrix.py
vendored
11
.github/matrix.py
vendored
@ -19,9 +19,10 @@ from packaging import version
|
||||
#
|
||||
# this CI is used for both development and stable branches of HAProxy
|
||||
#
|
||||
# naming convention used, if branch name matches:
|
||||
# naming convention used, if branch/tag name matches:
|
||||
#
|
||||
# "haproxy-" - stable branches
|
||||
# "vX.Y.Z" - release tags
|
||||
# otherwise - development branch (i.e. "latest" ssl variants, "latest" github images)
|
||||
#
|
||||
|
||||
@ -120,11 +121,13 @@ def clean_compression(compression):
|
||||
def main(ref_name):
|
||||
print("Generating matrix for branch '{}'.".format(ref_name))
|
||||
|
||||
is_stable = "haproxy-" in ref_name or re.match(r'^v\d+\.\d+\.\d+$', ref_name)
|
||||
|
||||
matrix = []
|
||||
|
||||
# Ubuntu
|
||||
|
||||
if "haproxy-" in ref_name:
|
||||
if is_stable:
|
||||
os = "ubuntu-24.04" # stable branch
|
||||
os_arm = "ubuntu-24.04-arm" # stable branch
|
||||
else:
|
||||
@ -228,7 +231,7 @@ def main(ref_name):
|
||||
# "BORINGSSL=yes",
|
||||
]
|
||||
|
||||
if "haproxy-" not in ref_name: # development branch
|
||||
if not is_stable: # development branch
|
||||
ssl_versions = ssl_versions + [
|
||||
"OPENSSL_VERSION=latest",
|
||||
"LIBRESSL_VERSION=latest",
|
||||
@ -276,7 +279,7 @@ def main(ref_name):
|
||||
)
|
||||
|
||||
# macOS on dev branches
|
||||
if "haproxy-" not in ref_name:
|
||||
if not is_stable:
|
||||
os = "macos-26" # development branch
|
||||
|
||||
TARGET = "osx"
|
||||
|
||||
132
CHANGELOG
132
CHANGELOG
@ -1,6 +1,138 @@
|
||||
ChangeLog :
|
||||
===========
|
||||
|
||||
2026/03/20 : 3.4-dev7
|
||||
- BUG/MINOR: stconn: Increase SC bytes_out value in se_done_ff()
|
||||
- BUG/MINOR: ssl-sample: Fix sample_conv_sha2() by checking EVP_Digest* failures
|
||||
- BUG/MINOR: backend: Don't get proto to use for webscoket if there is no server
|
||||
- BUG/MINOR: jwt: Missing 'jwt_tokenize' return value check
|
||||
- MINOR: flt_http_comp: define and use proxy_get_comp() helper function
|
||||
- MEDIUM: flt_http_comp: split "compression" filter in 2 distinct filters
|
||||
- CLEANUP: flt_http_comp: comp_state doesn't bother about the direction anymore
|
||||
- BUG/MINOR: admin: haproxy-reload use explicit socat address type
|
||||
- MEDIUM: admin: haproxy-reload conversion to POSIX sh
|
||||
- BUG/MINOR: admin: haproxy-reload rename -vv long option
|
||||
- SCRIPTS: git-show-backports: hide the common ancestor warning in quiet mode
|
||||
- SCRIPTS: git-show-backports: add a restart-from-last option
|
||||
- MINOR: mworker: add a BUG_ON() on mproxy_li in _send_status
|
||||
- BUG/MINOR: mworker: don't set the PROC_O_LEAVING flag on master process
|
||||
- Revert "BUG/MINOR: jwt: Missing 'jwt_tokenize' return value check"
|
||||
- MINOR: jwt: Improve 'jwt_tokenize' function
|
||||
- MINOR: jwt: Convert EC JWK to EVP_PKEY
|
||||
- MINOR: jwt: Parse ec-specific fields in jose header
|
||||
- MINOR: jwt: Manage ECDH-ES algorithm in jwt_decrypt_jwk function
|
||||
- MINOR: jwt: Add ecdh-es+axxxkw support in jwt_decrypt_jwk converter
|
||||
- MINOR: jwt: Manage ec certificates in jwt_decrypt_cert
|
||||
- DOC: jwt: Add ECDH support in jwt_decrypt converters
|
||||
- MINOR: stconn: Call sc_conn_process from the I/O callback if TASK_WOKEN_MSG state was set
|
||||
- MINOR: mux-h2: Rely on h2s_notify_send() when resuming h2s for sending
|
||||
- MINOR: mux-spop: Rely on spop_strm_notify_send() when resuming streams for sending
|
||||
- MINOR: muxes: Wakup the data layer from a mux stream with TASK_WOKEN_IO state
|
||||
- MAJOR: muxes: No longer use app_ops .wake() callback function from muxes
|
||||
- MINOR: applet: Call sc_applet_process() instead of .wake() callback function
|
||||
- MINOR: connection: Call sc_conn_process() instead of .wake() callback function
|
||||
- MEDIUM: stconn: Remove .wake() callback function from app_ops
|
||||
- MINOR: check: Remove wake_srv_chk() function
|
||||
- MINOR: haterm: Remove hstream_wake() function
|
||||
- MINOR: stconn: Wakup the SC with TASK_WOKEN_IO state from opposite side
|
||||
- MEDIUM: stconn: Merge all .chk_rcv() callback functions in sc_chk_rcv()
|
||||
- MINOR: stconn: Remove .chk_rcv() callback functions
|
||||
- MEDIUM: stconn: Merge all .chk_snd() callback functions in sc_chk_snd()
|
||||
- MINOR: stconn: Remove .chk_snd() callback functions
|
||||
- MEDIUM: stconn: Merge all .abort() callback functions in sc_abort()
|
||||
- MINOR: stconn: Remove .abort() callback functions
|
||||
- MEDIUM: stconn: Merge all .shutdown() callback functions in sc_shutdown()
|
||||
- MINOR: stconn: Remove .shutdown() callback functions
|
||||
- MINOR: stconn: Totally app_ops from the stconns
|
||||
- MINOR: stconn: Simplify sc_abort/sc_shutdown by merging calls to se_shutdown
|
||||
- DEBUG: stconn: Add a CHECK_IF() when I/O are performed on a orphan SC
|
||||
- MEDIUM: mworker: exiting when couldn't find the master mworker_proc element
|
||||
- BUILD: ssl: use ASN1_STRING accessors for OpenSSL 4.0 compatibility
|
||||
- BUILD: ssl: make X509_NAME usage OpenSSL 4.0 ready
|
||||
- BUG/MINOR: tcpcheck: Fix typo in error error message for `http-check expect`
|
||||
- BUG/MINOR: jws: fix memory leak in jws_b64_signature
|
||||
- DOC: configuration: http-check expect example typo
|
||||
- DOC/CLEANUP: config: update mentions of the old "Global parameters" section
|
||||
- BUG/MEDIUM: ssl: Handle receiving early data with BoringSSL/AWS-LC
|
||||
- BUG/MINOR: mworker: always stop the receiving listener
|
||||
- BUG/MEDIUM: ssl: Don't report read data as early data with AWS-LC
|
||||
- BUILD: makefile: fix range build without test command
|
||||
- BUG/MINOR: memprof: avoid a small memory leak in "show profiling"
|
||||
- BUG/MINOR: proxy: do not forget to validate quic-initial rules
|
||||
- MINOR: activity: use dynamic allocation for "show profiling" entries
|
||||
- MINOR: tools: extend the pointer hashing code to ease manipulations
|
||||
- MINOR: tools: add a new pointer hash function that also takes an argument
|
||||
- MINOR: memprof: attempt different retry slots for different hashes on collision
|
||||
- MINOR: tinfo: start to add basic thread_exec_ctx
|
||||
- MINOR: memprof: prepare to consider exec_ctx in reporting
|
||||
- MINOR: memprof: also permit to sort output by calling context
|
||||
- MINOR: tools: add a function to write a thread execution context.
|
||||
- MINOR: debug: report the execution context on thread dumps
|
||||
- MINOR: memprof: report the execution context on profiling output
|
||||
- MINOR: initcall: record the file and line declaration of an INITCALL
|
||||
- MINOR: tools: decode execution context TH_EX_CTX_INITCALL
|
||||
- MINOR: tools: support decoding ha_caller type exec context
|
||||
- MINOR: sample: store location for fetch/conv via initcalls
|
||||
- MINOR: sample: also report contexts registered directly
|
||||
- MINOR: tools: support an execution context that is just a function
|
||||
- MINOR: actions: store the location of keywords registered via initcalls
|
||||
- MINOR: actions: also report execution contexts registered directly
|
||||
- MINOR: filters: set the exec context to the current filter config
|
||||
- MINOR: ssl: set the thread execution context during message callbacks
|
||||
- MINOR: connection: track mux calls to report their allocation context
|
||||
- MINOR: task: set execution context on task/tasklet calls
|
||||
- MINOR: applet: set execution context on applet calls
|
||||
- MINOR: cli: keep the info of the current keyword being processed in the appctx
|
||||
- MINOR: cli: keep track of the initcall context since kw registration
|
||||
- MINOR: cli: implement execution context for manually registered keywords
|
||||
- MINOR: activity: support aggregating by caller also for memprofile
|
||||
- MINOR: activity: raise the default number of memprofile buckets to 4k
|
||||
- DOC: internals: short explanation on how thread_exec_ctx works
|
||||
- BUG/MINOR: mworker: only match worker processes when looking for unspawned proc
|
||||
- MINOR: traces: defer processing of "-dt" options
|
||||
- BUG/MINOR: mworker: fix typo &= instead of & in proc list serialization
|
||||
- BUG/MINOR: mworker: set a timeout on the worker socketpair read at startup
|
||||
- BUG/MINOR: mworker: avoid passing NULL version in proc list serialization
|
||||
- BUG/MINOR: sockpair: set FD_CLOEXEC on fd received via SCM_RIGHTS
|
||||
- BUG/MEDIUM: stconn: Don't forget to wakeup applets on shutdown
|
||||
- BUG/MINOR: spoe: Properly switch SPOE filter to WAITING_ACK state
|
||||
- BUG/MEDIUM: spoe: Properly abort processing on client abort
|
||||
- BUG/MEDIUM: stconn: Fix abort on close when a large buffer is used
|
||||
- BUG/MEDIUM: stconn: Don't perform L7 retries with large buffer
|
||||
- BUG/MINOR: h2/h3: Only test number of trailers inserted in HTX message
|
||||
- MINOR: htx: Add function to truncate all blocks after a specific block
|
||||
- BUG/MINOR: h2/h3: Never insert partial headers/trailers in an HTX message
|
||||
- BUG/MINOR: http-ana: Swap L7 buffer with request buffer by hand
|
||||
- BUG/MINOR: stream: Fix crash in stream dump if the current rule has no keyword
|
||||
- BUG/MINOR: mjson: make mystrtod() length-aware to prevent out-of-bounds reads
|
||||
- MEDIUM: stats-file/clock: automatically update now_offset based on shared clock
|
||||
- MINOR: promex: export "haproxy_sticktable_local_updates" metric
|
||||
- BUG/MINOR: spoe: Fix condition to abort processing on client abort
|
||||
- BUILD: spoe: Remove unsused variable
|
||||
- MINOR: tools: add a function to create a tar file header
|
||||
- MINOR: tools: add a function to load a file into a tar archive
|
||||
- MINOR: config: support explicit "on" and "off" for "set-dumpable"
|
||||
- MINOR: debug: read all libs in memory when set-dumpable=libs
|
||||
- DEV: gdb: add a new utility to extract libs from a core dump: libs-from-core
|
||||
- MINOR: debug: copy debug symbols from /usr/lib/debug when present
|
||||
- MINOR: debug: opportunistically load libthread_db.so.1 with set-dumpable=libs
|
||||
- BUG/MINOR: mworker: don't try to access an initializing process
|
||||
- BUG/MEDIUM: peers: enforce check on incoming table key type
|
||||
- BUG/MINOR: mux-h2: properly ignore R bit in GOAWAY stream ID
|
||||
- BUG/MINOR: mux-h2: properly ignore R bit in WINDOW_UPDATE increments
|
||||
- OPTIM: haterm: use chunk builders for generated response headers
|
||||
- BUG/MAJOR: h3: check body size with content-length on empty FIN
|
||||
- BUG/MEDIUM: h3: reject unaligned frames except DATA
|
||||
- BUG/MINOR: mworker/cli: fix show proc pagination losing entries on resume
|
||||
- CI: github: treat vX.Y.Z release tags as stable like haproxy-* branches
|
||||
- MINOR: freq_ctr: add a function to add values with a peak
|
||||
- MINOR: task: maintain a per-thread indicator of the peak run-queue size
|
||||
- MINOR: mux-h2: store the concurrent streams hard limit in the h2c
|
||||
- MINOR: mux-h2: permit to moderate the advertised streams limit depending on load
|
||||
- MINOR: mux-h2: permit to fix a minimum value for the advertised streams limit
|
||||
- BUG/MINOR: mworker: fix sort order of mworker_proc in 'show proc'
|
||||
- CLEANUP: mworker: fix tab/space mess in mworker_env_to_proc_list()
|
||||
|
||||
2026/03/05 : 3.4-dev6
|
||||
- CLEANUP: acme: remove duplicate includes
|
||||
- BUG/MINOR: proxy: detect strdup error on server auto SNI
|
||||
|
||||
10
Makefile
10
Makefile
@ -1043,7 +1043,7 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
|
||||
uninstall clean tags cscope tar git-tar version update-version \
|
||||
opts reg-tests reg-tests-help unit-tests admin/halog/halog dev/flags/flags \
|
||||
dev/haring/haring dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop \
|
||||
dev/term_events/term_events dev/gdb/pm-from-core
|
||||
dev/term_events/term_events dev/gdb/pm-from-core dev/gdb/libs-from-core
|
||||
|
||||
ifneq ($(TARGET),)
|
||||
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
|
||||
@ -1077,6 +1077,9 @@ admin/dyncookie/dyncookie: admin/dyncookie/dyncookie.o
|
||||
dev/flags/flags: dev/flags/flags.o
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
dev/gdb/libs-from-core: dev/gdb/libs-from-core.o
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
dev/gdb/pm-from-core: dev/gdb/pm-from-core.o
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
@ -1178,7 +1181,7 @@ distclean: clean
|
||||
$(Q)rm -f admin/dyncookie/dyncookie
|
||||
$(Q)rm -f dev/haring/haring dev/ncpu/ncpu{,.so} dev/poll/poll dev/tcploop/tcploop
|
||||
$(Q)rm -f dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
||||
$(Q)rm -f dev/qpack/decode dev/gdb/pm-from-core
|
||||
$(Q)rm -f dev/qpack/decode dev/gdb/pm-from-core dev/gdb/libs-from-core
|
||||
|
||||
tags:
|
||||
$(Q)find src include \( -name '*.c' -o -name '*.h' \) -print0 | \
|
||||
@ -1332,7 +1335,8 @@ range:
|
||||
echo "[ $$index/$$count ] $$commit #############################"; \
|
||||
git checkout -q $$commit || die 1; \
|
||||
$(MAKE) all || die 1; \
|
||||
[ -z "$(TEST_CMD)" ] || $(TEST_CMD) || die 1; \
|
||||
set -- $(TEST_CMD); \
|
||||
[ "$$#" -eq 0 ] || "$$@" || die 1; \
|
||||
index=$$((index + 1)); \
|
||||
done; \
|
||||
echo;echo "Done! $${count} commit(s) built successfully for RANGE $${RANGE}" ; \
|
||||
|
||||
@ -407,6 +407,7 @@ listed below. Metrics from extra counters are not listed.
|
||||
+----------------------------------------------------+
|
||||
| haproxy_sticktable_size |
|
||||
| haproxy_sticktable_used |
|
||||
| haproxy_sticktable_local_updates |
|
||||
+----------------------------------------------------+
|
||||
|
||||
* Resolvers metrics
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
export VERBOSE=1
|
||||
export TIMEOUT=90
|
||||
export MASTER_SOCKET=${MASTER_SOCKET:-/var/run/haproxy-master.sock}
|
||||
export RET=
|
||||
export MASTER_SOCKET="${MASTER_SOCKET:-/var/run/haproxy-master.sock}"
|
||||
|
||||
alert() {
|
||||
if [ "$VERBOSE" -ge "1" ]; then
|
||||
@ -15,32 +14,38 @@ alert() {
|
||||
|
||||
|
||||
reload() {
|
||||
while read -r line; do
|
||||
|
||||
if [ "$line" = "Success=0" ]; then
|
||||
RET=1
|
||||
elif [ "$line" = "Success=1" ]; then
|
||||
RET=0
|
||||
elif [ "$line" = "Another reload is still in progress." ]; then
|
||||
alert "$line"
|
||||
elif [ "$line" = "--" ]; then
|
||||
continue;
|
||||
else
|
||||
if [ "$RET" = 1 ] && [ "$VERBOSE" = "2" ]; then
|
||||
echo "$line" >&2
|
||||
elif [ "$VERBOSE" = "3" ]; then
|
||||
echo "$line" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
done < <(echo "reload" | socat -t"${TIMEOUT}" "${MASTER_SOCKET}" -)
|
||||
|
||||
if [ -z "$RET" ]; then
|
||||
alert "Couldn't finish the reload before the timeout (${TIMEOUT})."
|
||||
return 1
|
||||
if [ -S "$MASTER_SOCKET" ]; then
|
||||
socat_addr="UNIX-CONNECT:${MASTER_SOCKET}"
|
||||
else
|
||||
case "$MASTER_SOCKET" in
|
||||
*:[0-9]*)
|
||||
socat_addr="TCP:${MASTER_SOCKET}"
|
||||
;;
|
||||
*)
|
||||
alert "Invalid master socket address '${MASTER_SOCKET}': expected a UNIX socket file or <host>:<port>"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
return "$RET"
|
||||
echo "reload" | socat -t"${TIMEOUT}" "$socat_addr" - | {
|
||||
read -r status || { alert "No status received (connection error or timeout after ${TIMEOUT}s)."; exit 1; }
|
||||
case "$status" in
|
||||
"Success=1") ret=0 ;;
|
||||
"Success=0") ret=1 ;;
|
||||
*) alert "Unexpected response: '$status'"; exit 1 ;;
|
||||
esac
|
||||
|
||||
read -r _ # consume "--"
|
||||
|
||||
if [ "$VERBOSE" -ge 3 ] || { [ "$ret" = 1 ] && [ "$VERBOSE" -ge 2 ]; }; then
|
||||
cat >&2
|
||||
else
|
||||
cat >/dev/null
|
||||
fi
|
||||
|
||||
exit "$ret"
|
||||
}
|
||||
}
|
||||
|
||||
usage() {
|
||||
@ -52,12 +57,12 @@ usage() {
|
||||
echo " EXPERIMENTAL script!"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${MASTER_SOCKET})"
|
||||
echo " -S, --master-socket <addr> Unix socket path or <host>:<port> (default: ${MASTER_SOCKET})"
|
||||
echo " -d, --debug Debug mode, set -x"
|
||||
echo " -t, --timeout Timeout (socat -t) (default: ${TIMEOUT})"
|
||||
echo " -s, --silent Silent mode (no output)"
|
||||
echo " -v, --verbose Verbose output (output from haproxy on failure)"
|
||||
echo " -vv Even more verbose output (output from haproxy on success and failure)"
|
||||
echo " -vv --verbose=all Very verbose output (output from haproxy on success and failure)"
|
||||
echo " -h, --help This help"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
@ -84,7 +89,7 @@ main() {
|
||||
VERBOSE=2
|
||||
shift
|
||||
;;
|
||||
-vv|--verbose)
|
||||
-vv|--verbose=all)
|
||||
VERBOSE=3
|
||||
shift
|
||||
;;
|
||||
|
||||
162
dev/gdb/libs-from-core.c
Normal file
162
dev/gdb/libs-from-core.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Extracts the libs archives from a core dump
|
||||
*
|
||||
* Copyright (C) 2026 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Note: builds with no option under glibc, and can be built as a minimal
|
||||
* uploadable static executable using nolibc as well:
|
||||
gcc -o libs-from-core -nostdinc -nostdlib -s -Os -static -fno-ident \
|
||||
-fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables \
|
||||
-Wl,--gc-sections,--orphan-handling=discard,-znoseparate-code \
|
||||
-I /path/to/nolibc-sysroot/include libs-from-core.c
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void usage(const char *progname)
|
||||
{
|
||||
const char *slash = strrchr(progname, '/');
|
||||
|
||||
if (slash)
|
||||
progname = slash + 1;
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s [-q] <core_file>\n"
|
||||
"Locate a libs archive from an haproxy core dump and dump it to stdout.\n"
|
||||
"Arguments:\n"
|
||||
" -q Query mode: only report offset and length, do not dump\n"
|
||||
" core_file Core dump produced by haproxy\n",
|
||||
progname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Elf64_Ehdr *ehdr;
|
||||
Elf64_Phdr *phdr;
|
||||
struct stat st;
|
||||
uint8_t *mem;
|
||||
int i, fd;
|
||||
const char *fname;
|
||||
int quiet = 0;
|
||||
int arg;
|
||||
|
||||
for (arg = 1; arg < argc; arg++) {
|
||||
if (*argv[arg] != '-')
|
||||
break;
|
||||
|
||||
if (strcmp(argv[arg], "-q") == 0)
|
||||
quiet = 1;
|
||||
else if (strcmp(argv[arg], "--") == 0) {
|
||||
arg++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg < argc) {
|
||||
fname = argv[arg];
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fd = open(fname, O_RDONLY);
|
||||
|
||||
/* Let's just map the core dump as an ELF header */
|
||||
fstat(fd, &st);
|
||||
mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (mem == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* get the program headers */
|
||||
ehdr = (Elf64_Ehdr *)mem;
|
||||
|
||||
/* check that it's really a core. Should be "\x7fELF" */
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
|
||||
fprintf(stderr, "ELF magic not found.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
fprintf(stderr, "Only 64-bit ELF supported.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ehdr->e_type != ET_CORE) {
|
||||
fprintf(stderr, "ELF type %d, not a core dump.\n", ehdr->e_type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* OK we can safely go with program headers */
|
||||
phdr = (Elf64_Phdr *)(mem + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
uint64_t size = phdr[i].p_filesz;
|
||||
uint64_t offset = phdr[i].p_offset;
|
||||
int ret = 0;
|
||||
|
||||
if (phdr[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
//fprintf(stderr, "Scanning segment %d...\n", ehdr->e_phnum);
|
||||
//fprintf(stderr, "\r%-5d: off=%lx va=%lx sz=%lx ", i, (long)offset, (long)phdr[i].p_vaddr, (long)size);
|
||||
if (!size)
|
||||
continue;
|
||||
|
||||
if (size < 512) // minimum for a tar header
|
||||
continue;
|
||||
|
||||
/* tar magic */
|
||||
if (memcmp(mem + offset + 257, "ustar\0""00", 8) != 0)
|
||||
continue;
|
||||
|
||||
/* uid, gid */
|
||||
if (memcmp(mem + offset + 108, "0000000\0""0000000\0", 16) != 0)
|
||||
continue;
|
||||
|
||||
/* link name */
|
||||
if (memcmp(mem + offset + 157, "haproxy-libs-dump\0", 18) != 0)
|
||||
continue;
|
||||
|
||||
/* OK that's really it */
|
||||
|
||||
if (quiet)
|
||||
printf("offset=%#lx size=%#lx\n", offset, size);
|
||||
else
|
||||
ret = (write(1, mem + offset, size) == size) ? 0 : 1;
|
||||
return ret;
|
||||
}
|
||||
//fprintf(stderr, "\r%75s\n", "\r");
|
||||
fprintf(stderr, "libs archive not found. Was 'set-dumpable' set to 'libs' ?\n");
|
||||
return 1;
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
Configuration Manual
|
||||
----------------------
|
||||
version 3.4
|
||||
2026/03/05
|
||||
2026/03/20
|
||||
|
||||
|
||||
This document covers the configuration language as implemented in the version
|
||||
@ -1725,7 +1725,7 @@ Assuming haproxy is in $PATH, test these configurations in a shell with:
|
||||
$ sudo haproxy -f configuration.conf -c
|
||||
|
||||
|
||||
3. Global parameters
|
||||
3. Global section
|
||||
--------------------
|
||||
|
||||
Parameters in the "global" section are process-wide and often OS-specific. They
|
||||
@ -1886,10 +1886,13 @@ The following keywords are supported in the "global" section :
|
||||
- tune.h2.be.glitches-threshold
|
||||
- tune.h2.be.initial-window-size
|
||||
- tune.h2.be.max-concurrent-streams
|
||||
- tune.h2.be.max-frames-at-once
|
||||
- tune.h2.be.rxbuf
|
||||
- tune.h2.fe.glitches-threshold
|
||||
- tune.h2.fe.initial-window-size
|
||||
- tune.h2.fe.max-concurrent-streams
|
||||
- tune.h2.fe.max-frames-at-once
|
||||
- tune.h2.fe.max-rst-at-once
|
||||
- tune.h2.fe.max-total-streams
|
||||
- tune.h2.fe.rxbuf
|
||||
- tune.h2.header-table-size
|
||||
@ -3148,10 +3151,29 @@ server-state-file <file>
|
||||
configuration. See also "server-state-base" and "show servers state",
|
||||
"load-server-state-from-file" and "server-state-file-name"
|
||||
|
||||
set-dumpable
|
||||
set-dumpable [ on | off | libs ]
|
||||
This option helps choose the core dump behavior in case of process crash.
|
||||
Available options are:
|
||||
|
||||
- on this enables core dumping at the process level if it was
|
||||
previously disabled.
|
||||
|
||||
- off this disables a previously enabled core dumping.
|
||||
|
||||
- libs this enables core dumping with an embedded copy of the binaries and
|
||||
libraries that are required for debugging. This may be requested by
|
||||
developers. In this case haproxy will try to load the libraries it
|
||||
depends on into memory and keep them preciously. If the process
|
||||
crashes, they will be dumped into the core so there is no need for
|
||||
retrieving them from the file system anymore and no risk that they
|
||||
do not match the core. This takes a few megabytes to a few tens of
|
||||
megabytes of additional RAM, so it is better not to use it on small
|
||||
systems.
|
||||
|
||||
This option is better left disabled by default and enabled only upon a
|
||||
developer's request. If it has been enabled, it may still be forcibly
|
||||
disabled by prefixing it with the "no" keyword. It has no impact on
|
||||
developer's request. By default it is disabled. Without argument, it defaults
|
||||
to "on". If it has been enabled, it may still be forcibly disabled by prefixing
|
||||
it with the "no" keyword or by setting it to "off". It has no impact on
|
||||
performance nor stability but will try hard to re-enable core dumps that were
|
||||
possibly disabled by file size limitations (ulimit -f), core size limitations
|
||||
(ulimit -c), or "dumpability" of a process after changing its UID/GID (such
|
||||
@ -4143,8 +4165,11 @@ tune.bufsize.small <size>
|
||||
If however a small buffer is not sufficient, a reallocation is automatically
|
||||
done to switch to a standard size buffer.
|
||||
|
||||
For the moment, it is used only by HTTP/3 protocol to emit the response
|
||||
headers.
|
||||
For the moment, it is automatically used only by HTTP/3 protocol to emit the
|
||||
response headers. Otherwise, small buffers support can be enabled for
|
||||
specific proxies via the "use-small-buffers" option.
|
||||
|
||||
See also: option use-small-buffers
|
||||
|
||||
tune.comp.maxlevel <number>
|
||||
Sets the maximum compression level. The compression level affects CPU
|
||||
@ -4349,6 +4374,13 @@ tune.h2.be.max-concurrent-streams <number>
|
||||
case). It is highly recommended not to increase this value; some might find
|
||||
it optimal to run at low values (1..5 typically).
|
||||
|
||||
tune.h2.be.max-frames-at-once <number>
|
||||
Sets the maximum number of HTTP/2 incoming frames that will be processed at
|
||||
once on a backend connection. It can be useful to set this to a low value
|
||||
(a few tens to a few hundreds) when dealing with very large buffers in order
|
||||
to maintain a low latency and a better fairness between multiple connections.
|
||||
The default value is zero, which means that no limitation is enforced.
|
||||
|
||||
tune.h2.be.rxbuf <size>
|
||||
Sets the HTTP/2 receive buffer size for outgoing connections, in bytes. This
|
||||
size will be rounded up to the next multiple of tune.bufsize and will be
|
||||
@ -4399,7 +4431,7 @@ tune.h2.fe.initial-window-size <number>
|
||||
|
||||
See also: tune.h2.initial-window-size.
|
||||
|
||||
tune.h2.fe.max-concurrent-streams <number>
|
||||
tune.h2.fe.max-concurrent-streams <number> [args...]
|
||||
Sets the HTTP/2 maximum number of concurrent streams per incoming connection
|
||||
(i.e. the number of outstanding requests on a single connection from a
|
||||
client). When not set, the default set by tune.h2.max-concurrent-streams
|
||||
@ -4407,7 +4439,56 @@ tune.h2.fe.max-concurrent-streams <number>
|
||||
the page load time for complex sites with lots of small objects over high
|
||||
latency networks but can also result in using more memory by allowing a
|
||||
client to allocate more resources at once. The default value of 100 is
|
||||
generally good and it is recommended not to change this value.
|
||||
generally good and it is recommended not to change this value. A larger
|
||||
concurrency also has an impact on the processing load and latency when
|
||||
dealing with large numbers of connections which are themselves using many
|
||||
streams, and it may lower the barrier to denial of service attacks. The
|
||||
command supports the following optional arguments after the number:
|
||||
|
||||
- rq-load { <number> | auto | ignore }:
|
||||
The optional argument "rq-load" permits to dynamically adjust the
|
||||
advertised concurrency based on the executing thread's run-queue load:
|
||||
as long as the thread's load remains below the indicated threshold, the
|
||||
configured streams limit will be advertised. When the thread's load
|
||||
increases beyond the configured limit, the advertised streams limit will be
|
||||
decreased proportionally to the square of the excess ratio. Target load
|
||||
levels between 50 and 100 generally show very good moderation under heavy
|
||||
loads. Alternately, instead of specifying an explicit number, the keyword
|
||||
accepts "ignore", which is the default and means that the thread's
|
||||
run-queue load will not be considered to moderate the advertised streams
|
||||
limit, and "auto", which sets the limit to the "tune.runqueue-depth"
|
||||
value, which generally provides good results without having to tweak
|
||||
the configuration any further.
|
||||
|
||||
- min <number>:
|
||||
This sets the minimum advertised concurrency level when rq-load is used,
|
||||
even if this results in a higher load than the configured target. This
|
||||
allows to maintain a good level of interactivity on a site under very
|
||||
heavy load. The minimum and default value is 1, but values between 5
|
||||
and 15 can improve user experience.
|
||||
|
||||
Example:
|
||||
|
||||
tune.h2.fe.max-concurrent-streams 100 rq-load auto min 15
|
||||
|
||||
tune.h2.fe.max-frames-at-once <number>
|
||||
Sets the maximum number of HTTP/2 incoming frames that will be processed at
|
||||
once on a frontend connection. It can be useful to set this to a low value
|
||||
(a few tens to a few hundreds) when dealing with very large buffers in order
|
||||
to maintain a low latency and a better fairness between multiple connections.
|
||||
The default value is zero, which means that no limitation is enforced.
|
||||
|
||||
tune.h2.fe.max-rst-at-once <number>
|
||||
Sets the maximum number of HTTP/2 incoming RST_STREAM that will be processed
|
||||
at once on a frontend connection. Once the specified number of RST_STREAM
|
||||
frames are received, the connection handler will be placed in a low priority
|
||||
queue and be processed after all other tasks. It can be useful to set this to
|
||||
a very low value (1 or a few units) to significantly reduce the impacts of
|
||||
RST_STREAM floods. RST_STREAM do happen when a user clicks on the Stop button
|
||||
in their browser, but the few extra milliseconds caused by this requeuing are
|
||||
generally unnoticeable, however they are generally effective at significantly
|
||||
lowering the load caused from such floods. The default value is zero, which
|
||||
means that no limitation is enforced.
|
||||
|
||||
tune.h2.fe.max-total-streams <number>
|
||||
Sets the HTTP/2 maximum number of total streams processed per incoming
|
||||
@ -5893,6 +5974,8 @@ errorloc302 X X X X
|
||||
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
||||
errorloc303 X X X X
|
||||
error-log-format X X X -
|
||||
external-check command X - X X
|
||||
external-check path X - X X
|
||||
force-persist - - X X
|
||||
force-be-switch - X X -
|
||||
filter - X X X
|
||||
@ -5938,6 +6021,7 @@ option disable-h2-upgrade (*) X X X -
|
||||
option dontlog-normal (*) X X X -
|
||||
option dontlognull (*) X X X -
|
||||
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
||||
option external-check X - X X
|
||||
option forwardfor X X X X
|
||||
option forwarded (*) X - X X
|
||||
option h1-case-adjust-bogus-client (*) X X X -
|
||||
@ -5956,9 +6040,9 @@ option httpchk X - X X
|
||||
option httpclose (*) X X X X
|
||||
option httplog X X X -
|
||||
option httpslog X X X -
|
||||
option idle-close-on-response (*) X X X -
|
||||
option independent-streams (*) X X X X
|
||||
option ldap-check X - X X
|
||||
option external-check X - X X
|
||||
option log-health-checks (*) X - X X
|
||||
option log-separate-errors (*) X X X -
|
||||
option logasap (*) X X X -
|
||||
@ -5985,9 +6069,7 @@ option tcp-smart-connect (*) X - X X
|
||||
option tcpka X X X X
|
||||
option tcplog X X X -
|
||||
option transparent (deprecated) (*) X - X X
|
||||
option idle-close-on-response (*) X X X -
|
||||
external-check command X - X X
|
||||
external-check path X - X X
|
||||
option use-small-buffers (*) X - X X
|
||||
persist rdp-cookie X - X X
|
||||
quic-initial X (!) X X -
|
||||
rate-limit sessions X X X -
|
||||
@ -6935,12 +7017,16 @@ compression offload
|
||||
|
||||
See also : "compression type", "compression algo", "compression direction"
|
||||
|
||||
compression direction <direction>
|
||||
compression direction <direction> (deprecated)
|
||||
Makes haproxy able to compress both requests and responses.
|
||||
Valid values are "request", to compress only requests, "response", to
|
||||
compress only responses, or "both", when you want to compress both.
|
||||
The default value is "response".
|
||||
|
||||
This directive is only relevant when legacy "filter compression" was
|
||||
enabled, as with explicit comp-req and comp-res filters compression
|
||||
direction is redundant.
|
||||
|
||||
May be used in the following contexts: http
|
||||
|
||||
See also : "compression type", "compression algo", "compression offload"
|
||||
@ -7646,6 +7732,96 @@ force-persist { if | unless } <condition>
|
||||
and section 7 about ACL usage.
|
||||
|
||||
|
||||
external-check command <command>
|
||||
Executable to run when performing an external-check
|
||||
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
|
||||
Arguments :
|
||||
<command> is the external command to run
|
||||
|
||||
The arguments passed to the to the command are:
|
||||
|
||||
<proxy_address> <proxy_port> <server_address> <server_port>
|
||||
|
||||
The <proxy_address> and <proxy_port> are derived from the first listener
|
||||
that is either IPv4, IPv6 or a UNIX socket. In the case of a UNIX socket
|
||||
listener the proxy_address will be the path of the socket and the
|
||||
<proxy_port> will be the string "NOT_USED". In a backend section, it's not
|
||||
possible to determine a listener, and both <proxy_address> and <proxy_port>
|
||||
will have the string value "NOT_USED".
|
||||
|
||||
Some values are also provided through environment variables.
|
||||
|
||||
Environment variables :
|
||||
HAPROXY_PROXY_ADDR The first bind address if available (or empty if not
|
||||
applicable, for example in a "backend" section).
|
||||
|
||||
HAPROXY_PROXY_ID The backend id.
|
||||
|
||||
HAPROXY_PROXY_NAME The backend name.
|
||||
|
||||
HAPROXY_PROXY_PORT The first bind port if available (or empty if not
|
||||
applicable, for example in a "backend" section or
|
||||
for a UNIX socket).
|
||||
|
||||
HAPROXY_SERVER_ADDR The server address.
|
||||
|
||||
HAPROXY_SERVER_CURCONN The current number of connections on the server.
|
||||
|
||||
HAPROXY_SERVER_ID The server id.
|
||||
|
||||
HAPROXY_SERVER_MAXCONN The server max connections.
|
||||
|
||||
HAPROXY_SERVER_NAME The server name.
|
||||
|
||||
HAPROXY_SERVER_PORT The server port if available (or empty for a UNIX
|
||||
socket).
|
||||
|
||||
HAPROXY_SERVER_SSL "0" when SSL is not used, "1" when it is used
|
||||
|
||||
HAPROXY_SERVER_PROTO The protocol used by this server, which can be one
|
||||
of "cli" (the haproxy CLI), "syslog" (syslog TCP
|
||||
server), "peers" (peers TCP server), "h1" (HTTP/1.x
|
||||
server), "h2" (HTTP/2 server), or "tcp" (any other
|
||||
TCP server).
|
||||
|
||||
PATH The PATH environment variable used when executing
|
||||
the command may be set using "external-check path".
|
||||
|
||||
If the command executed and exits with a zero status then the check is
|
||||
considered to have passed, otherwise the check is considered to have
|
||||
failed.
|
||||
|
||||
Example :
|
||||
external-check command /bin/true
|
||||
|
||||
See also : "external-check", "option external-check", "external-check path"
|
||||
|
||||
|
||||
external-check path <path>
|
||||
The value of the PATH environment variable used when running an external-check
|
||||
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
|
||||
Arguments :
|
||||
<path> is the path used when executing external command to run
|
||||
|
||||
The default path is "".
|
||||
|
||||
Example :
|
||||
external-check path "/usr/bin:/bin"
|
||||
|
||||
See also : "external-check", "option external-check",
|
||||
"external-check command"
|
||||
|
||||
|
||||
force-be-switch { if | unless } <condition>
|
||||
Allow content switching to select a backend instance even if it is disabled
|
||||
or unpublished. This rule can be used by admins to test traffic to services
|
||||
@ -8150,6 +8326,11 @@ http-check expect [min-recv <int>] [comment <msg>]
|
||||
occurred during the expect rule evaluation. <fmt> is a
|
||||
Custom log format string (see section 8.2.6).
|
||||
|
||||
status-code <expr> is optional and can be used to set the check status code
|
||||
reported in logs, on success or on error. <expr> is a
|
||||
standard HAProxy expression formed by a sample-fetch
|
||||
followed by some converters.
|
||||
|
||||
<match> is a keyword indicating how to look for a specific pattern in the
|
||||
response. The keyword may be one of "status", "rstatus", "hdr",
|
||||
"fhdr", "string", or "rstring". The keyword may be preceded by an
|
||||
@ -8259,7 +8440,7 @@ http-check expect [min-recv <int>] [comment <msg>]
|
||||
http-check expect status 200,201,300-310
|
||||
|
||||
# be sure a sessid coookie is set
|
||||
http-check expect header name "set-cookie" value -m beg "sessid="
|
||||
http-check expect hdr name "set-cookie" value -m beg "sessid="
|
||||
|
||||
# consider SQL errors as errors
|
||||
http-check expect ! string SQL\ Error
|
||||
@ -9867,6 +10048,24 @@ no option dontlognull
|
||||
See also : "log", "http-ignore-probes", "monitor-uri", and
|
||||
section 8 about logging.
|
||||
|
||||
|
||||
option external-check
|
||||
Use external processes for server health checks
|
||||
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
|
||||
It is possible to test the health of a server using an external command.
|
||||
This is achieved by running the executable set using "external-check
|
||||
command".
|
||||
|
||||
Requires the "external-check" global to be set.
|
||||
|
||||
See also : "external-check", "external-check command", "external-check path"
|
||||
|
||||
|
||||
option forwarded [ proto ]
|
||||
[ host | host-expr <host_expr> ]
|
||||
[ by | by-expr <by_expr> ] [ by_port | by_port-expr <by_port_expr>]
|
||||
@ -10657,6 +10856,39 @@ option httpslog
|
||||
See also : section 8 about logging.
|
||||
|
||||
|
||||
option idle-close-on-response
|
||||
no option idle-close-on-response
|
||||
Avoid closing idle frontend connections if a soft stop is in progress
|
||||
|
||||
May be used in the following contexts: http
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | yes | yes | no
|
||||
|
||||
Arguments : none
|
||||
|
||||
By default, idle connections will be closed during a soft stop. In some
|
||||
environments, a client talking to the proxy may have prepared some idle
|
||||
connections in order to send requests later. If there is no proper retry on
|
||||
write errors, this can result in errors while haproxy is reloading. Even
|
||||
though a proper implementation should retry on connection/write errors, this
|
||||
option was introduced to support backwards compatibility with haproxy prior
|
||||
to version 2.4. Indeed before v2.4, haproxy used to wait for a last request
|
||||
and response to add a "connection: close" header before closing, thus
|
||||
notifying the client that the connection would not be reusable.
|
||||
|
||||
In a real life example, this behavior was seen in AWS using the ALB in front
|
||||
of a haproxy. The end result was ALB sending 502 during haproxy reloads.
|
||||
|
||||
Users are warned that using this option may increase the number of old
|
||||
processes if connections remain idle for too long. Adjusting the client
|
||||
timeouts and/or the "hard-stop-after" parameter accordingly might be
|
||||
needed in case of frequent reloads.
|
||||
|
||||
See also: "timeout client", "timeout client-fin", "timeout http-request",
|
||||
"hard-stop-after"
|
||||
|
||||
|
||||
option independent-streams
|
||||
no option independent-streams
|
||||
Enable or disable independent timeout processing for both directions
|
||||
@ -10721,56 +10953,6 @@ option ldap-check
|
||||
See also : "option httpchk"
|
||||
|
||||
|
||||
option external-check
|
||||
Use external processes for server health checks
|
||||
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
|
||||
It is possible to test the health of a server using an external command.
|
||||
This is achieved by running the executable set using "external-check
|
||||
command".
|
||||
|
||||
Requires the "external-check" global to be set.
|
||||
|
||||
See also : "external-check", "external-check command", "external-check path"
|
||||
|
||||
|
||||
option idle-close-on-response
|
||||
no option idle-close-on-response
|
||||
Avoid closing idle frontend connections if a soft stop is in progress
|
||||
|
||||
May be used in the following contexts: http
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | yes | yes | no
|
||||
|
||||
Arguments : none
|
||||
|
||||
By default, idle connections will be closed during a soft stop. In some
|
||||
environments, a client talking to the proxy may have prepared some idle
|
||||
connections in order to send requests later. If there is no proper retry on
|
||||
write errors, this can result in errors while haproxy is reloading. Even
|
||||
though a proper implementation should retry on connection/write errors, this
|
||||
option was introduced to support backwards compatibility with haproxy prior
|
||||
to version 2.4. Indeed before v2.4, haproxy used to wait for a last request
|
||||
and response to add a "connection: close" header before closing, thus
|
||||
notifying the client that the connection would not be reusable.
|
||||
|
||||
In a real life example, this behavior was seen in AWS using the ALB in front
|
||||
of a haproxy. The end result was ALB sending 502 during haproxy reloads.
|
||||
|
||||
Users are warned that using this option may increase the number of old
|
||||
processes if connections remain idle for too long. Adjusting the client
|
||||
timeouts and/or the "hard-stop-after" parameter accordingly might be
|
||||
needed in case of frequent reloads.
|
||||
|
||||
See also: "timeout client", "timeout client-fin", "timeout http-request",
|
||||
"hard-stop-after"
|
||||
|
||||
|
||||
option log-health-checks
|
||||
no option log-health-checks
|
||||
Enable or disable logging of health checks status updates
|
||||
@ -11732,95 +11914,35 @@ no option transparent (deprecated)
|
||||
"transparent" option of the "bind" keyword.
|
||||
|
||||
|
||||
external-check command <command>
|
||||
Executable to run when performing an external-check
|
||||
option use-small-buffers [ queue | l7-retries | check ]*
|
||||
|
||||
May be used in the following contexts: tcp, http, log
|
||||
Enable support for small buffers for the given categories.
|
||||
|
||||
May be used in the following contexts: tcp, http
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
|
||||
Arguments :
|
||||
<command> is the external command to run
|
||||
This option can be used to enable the small buffers support at diffent places
|
||||
to save memory. By default, with no parameter, small buffers are used as far
|
||||
as possible at all possible places. Otherwise, it is possible to limit it to
|
||||
following the places:
|
||||
|
||||
The arguments passed to the to the command are:
|
||||
- queue: When set, small buffers will be used to store the requests, if
|
||||
small enough, when the connection is queued.
|
||||
- l7-retries: When set, small buffers will be used to save the requests
|
||||
when L7 retries are enabled.
|
||||
- check: When set, small buffers will be used for the health-checks
|
||||
requests.
|
||||
|
||||
<proxy_address> <proxy_port> <server_address> <server_port>
|
||||
When enabled, small buffers are used, but only if it is possible. Otherwise,
|
||||
when data are too large, a regular buffer is automtically used. The size of
|
||||
small buffers is configurable via the "tune.bufsize.small" global setting.
|
||||
|
||||
The <proxy_address> and <proxy_port> are derived from the first listener
|
||||
that is either IPv4, IPv6 or a UNIX socket. In the case of a UNIX socket
|
||||
listener the proxy_address will be the path of the socket and the
|
||||
<proxy_port> will be the string "NOT_USED". In a backend section, it's not
|
||||
possible to determine a listener, and both <proxy_address> and <proxy_port>
|
||||
will have the string value "NOT_USED".
|
||||
|
||||
Some values are also provided through environment variables.
|
||||
|
||||
Environment variables :
|
||||
HAPROXY_PROXY_ADDR The first bind address if available (or empty if not
|
||||
applicable, for example in a "backend" section).
|
||||
|
||||
HAPROXY_PROXY_ID The backend id.
|
||||
|
||||
HAPROXY_PROXY_NAME The backend name.
|
||||
|
||||
HAPROXY_PROXY_PORT The first bind port if available (or empty if not
|
||||
applicable, for example in a "backend" section or
|
||||
for a UNIX socket).
|
||||
|
||||
HAPROXY_SERVER_ADDR The server address.
|
||||
|
||||
HAPROXY_SERVER_CURCONN The current number of connections on the server.
|
||||
|
||||
HAPROXY_SERVER_ID The server id.
|
||||
|
||||
HAPROXY_SERVER_MAXCONN The server max connections.
|
||||
|
||||
HAPROXY_SERVER_NAME The server name.
|
||||
|
||||
HAPROXY_SERVER_PORT The server port if available (or empty for a UNIX
|
||||
socket).
|
||||
|
||||
HAPROXY_SERVER_SSL "0" when SSL is not used, "1" when it is used
|
||||
|
||||
HAPROXY_SERVER_PROTO The protocol used by this server, which can be one
|
||||
of "cli" (the haproxy CLI), "syslog" (syslog TCP
|
||||
server), "peers" (peers TCP server), "h1" (HTTP/1.x
|
||||
server), "h2" (HTTP/2 server), or "tcp" (any other
|
||||
TCP server).
|
||||
|
||||
PATH The PATH environment variable used when executing
|
||||
the command may be set using "external-check path".
|
||||
|
||||
If the command executed and exits with a zero status then the check is
|
||||
considered to have passed, otherwise the check is considered to have
|
||||
failed.
|
||||
|
||||
Example :
|
||||
external-check command /bin/true
|
||||
|
||||
See also : "external-check", "option external-check", "external-check path"
|
||||
|
||||
|
||||
external-check path <path>
|
||||
The value of the PATH environment variable used when running an external-check
|
||||
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
|
||||
Arguments :
|
||||
<path> is the path used when executing external command to run
|
||||
|
||||
The default path is "".
|
||||
|
||||
Example :
|
||||
external-check path "/usr/bin:/bin"
|
||||
|
||||
See also : "external-check", "option external-check",
|
||||
"external-check command"
|
||||
If this option has been enabled in a "defaults" section, it can be disabled
|
||||
in a specific instance by prepending the "no" keyword before it.
|
||||
|
||||
See also: tune.bufsize.small
|
||||
|
||||
persist rdp-cookie
|
||||
persist rdp-cookie(<name>)
|
||||
@ -13579,13 +13701,6 @@ tcp-check expect [min-recv <int>] [comment <msg>]
|
||||
does not match, the check will wait for more data. If set to 0,
|
||||
the evaluation result is always conclusive.
|
||||
|
||||
<match> is a keyword indicating how to look for a specific pattern in the
|
||||
response. The keyword may be one of "string", "rstring", "binary" or
|
||||
"rbinary".
|
||||
The keyword may be preceded by an exclamation mark ("!") to negate
|
||||
the match. Spaces are allowed between the exclamation mark and the
|
||||
keyword. See below for more details on the supported keywords.
|
||||
|
||||
ok-status <st> is optional and can be used to set the check status if
|
||||
the expect rule is successfully evaluated and if it is
|
||||
the last rule in the tcp-check ruleset. "L7OK", "L7OKC",
|
||||
@ -13633,6 +13748,13 @@ tcp-check expect [min-recv <int>] [comment <msg>]
|
||||
standard HAProxy expression formed by a sample-fetch
|
||||
followed by some converters.
|
||||
|
||||
<match> is a keyword indicating how to look for a specific pattern in the
|
||||
response. The keyword may be one of "string", "rstring", "binary" or
|
||||
"rbinary".
|
||||
The keyword may be preceded by an exclamation mark ("!") to negate
|
||||
the match. Spaces are allowed between the exclamation mark and the
|
||||
keyword. See below for more details on the supported keywords.
|
||||
|
||||
<pattern> is the pattern to look for. It may be a string or a regular
|
||||
expression. If the pattern contains spaces, they must be escaped
|
||||
with the usual backslash ('\').
|
||||
@ -15329,15 +15451,14 @@ disable-l7-retry
|
||||
reason than a connection failure. This can be useful for example to make
|
||||
sure POST requests aren't retried on failure.
|
||||
|
||||
do-log
|
||||
do-log [profile <log_profile>]
|
||||
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||
X | X | X | X | X | X | X | X
|
||||
|
||||
This action manually triggers a log emission on the proxy. This means
|
||||
log options on the proxy will be considered (including formatting options
|
||||
such as "log-format"), but it will not interfere with the logs automatically
|
||||
generated by the proxy during transaction handling. It currently doesn't
|
||||
support any argument, though extensions may appear in future versions.
|
||||
generated by the proxy during transaction handling.
|
||||
|
||||
Using "log-profile", it is possible to precisely describe how the log should
|
||||
be emitted for each of the available contexts where the action may be used.
|
||||
@ -15347,15 +15468,28 @@ do-log
|
||||
|
||||
Also, they will be properly reported when using "%OG" logformat alias.
|
||||
|
||||
Optional "profile" argument may be used to specify the name of a log-profile
|
||||
section that should be used for this do-log action specifically instead of
|
||||
the one associated to the current logger that applies by default.
|
||||
|
||||
Example:
|
||||
log-profile myprof
|
||||
log-profile my-dft-prof
|
||||
on tcp-req-conn format "Connect: %ci"
|
||||
|
||||
log-profile my-local-prof
|
||||
on tcp-req-conn format "Local Connect: %ci"
|
||||
|
||||
frontend myfront
|
||||
log stdout format rfc5424 profile myprof local0
|
||||
log stdout format rfc5424 profile my-dft-prof local0
|
||||
log-format "log generated using proxy logformat, from '%OG'"
|
||||
tcp-request connection do-log #uses special log-profile format
|
||||
tcp-request content do-log #uses proxy logformat
|
||||
acl local src 127.0.0.1
|
||||
# on connection use either log-profile from the logger (my-dft-prof) or
|
||||
# explicit my-local-prof if source ip is localhost
|
||||
tcp-request connection do-log if !local
|
||||
tcp-request connection do-log profile my-local-prof if local
|
||||
# on content use proxy logformat, since no override was specified
|
||||
# in my-dft-prof
|
||||
tcp-request content do-log
|
||||
|
||||
do-resolve(<var>,<resolvers>[,ipv4|ipv6]) <expr>
|
||||
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||
@ -21499,7 +21633,8 @@ jwt_decrypt_cert(<cert>)
|
||||
format (five dot-separated base64-url encoded strings).
|
||||
|
||||
This converter can be used for tokens that have an algorithm ("alg" field of
|
||||
the JOSE header) among the following: RSA1_5, RSA-OAEP or RSA-OAEP-256.
|
||||
the JOSE header) among the following: RSA1_5, RSA-OAEP, RSA-OAEP-256,
|
||||
ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW or ECDH-ES+A256KW.
|
||||
|
||||
The JWE token must be provided base64url-encoded and the output will be
|
||||
provided "raw". If an error happens during token parsing, signature
|
||||
@ -21515,8 +21650,9 @@ jwt_decrypt_jwk(<jwk>)
|
||||
Performs a signature validation of a JSON Web Token following the JSON Web
|
||||
Encryption format (see RFC 7516) given in input and return its content
|
||||
decrypted thanks to the provided JSON Web Key (RFC7517).
|
||||
The <jwk> parameter must be a valid JWK of type 'oct' or 'RSA' ('kty' field
|
||||
of the JSON key) that can be provided either as a string or via a variable.
|
||||
The <jwk> parameter must be a valid JWK of type 'oct', 'EC' or 'RSA' ('kty'
|
||||
field of the JSON key) that can be provided either as a string or via a
|
||||
variable.
|
||||
|
||||
The only tokens managed yet are the ones using the Compact Serialization
|
||||
format (five dot-separated base64-url encoded strings).
|
||||
@ -21524,11 +21660,16 @@ jwt_decrypt_jwk(<jwk>)
|
||||
This converter can be used to decode token that have a symmetric-type
|
||||
algorithm ("alg" field of the JOSE header) among the following: A128KW,
|
||||
A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW, dir. In this case, we expect
|
||||
the provided JWK to be of the 'oct' type. Please note that the A128KW and
|
||||
A192KW algorithms are not available on AWS-LC and decryption will not work.
|
||||
This converter also manages tokens that have an algorithm ("alg" field of
|
||||
the JOSE header) among the following: RSA1_5, RSA-OAEP or RSA-OAEP-256. In
|
||||
such a case an 'RSA' type JWK representing a private key must be provided.
|
||||
the provided JWK to be of the 'oct' type.
|
||||
|
||||
This converter also manages tokens that have an algorithm ("alg" field of the
|
||||
JOSE header) in the RSA family (RSA1_5, RSA-OAEP or RSA-OAEP-256) when
|
||||
provided an 'RSA' JWK, or in the ECDH family (ECDH-ES, ECDH-ES+A128KW,
|
||||
ECDH-ES+A192KW or ECDH-ES+A256KW) when provided an 'EC' JWK.
|
||||
|
||||
Please note that the A128KW and A192KW algorithms are not available on AWS-LC
|
||||
so the A128KW, A192KW, ECDH-ES+A128KW and ECDH-ES+A192KW algorithms won't
|
||||
work.
|
||||
|
||||
The JWE token must be provided base64url-encoded and the output will be
|
||||
provided "raw". If an error happens during token parsing, signature
|
||||
@ -21542,7 +21683,7 @@ jwt_decrypt_jwk(<jwk>)
|
||||
# Get a JWT from the authorization header, put its decrypted content in an
|
||||
# HTTP header
|
||||
http-request set-var(txn.bearer) http_auth_bearer
|
||||
http-request set-header X-Decrypted %[var(txn.bearer),jwt_decrypt_secret(\'{\"kty\":\"oct\",\"k\":\"wAsgsg\"}\')
|
||||
http-request set-header X-Decrypted %[var(txn.bearer),jwt_decrypt_jwk(\'{\"kty\":\"oct\",\"k\":\"wAsgsg\"}\')
|
||||
|
||||
# or via a variable
|
||||
http-request set-var(txn.bearer) http_auth_bearer
|
||||
@ -28053,7 +28194,7 @@ Detailed fields description :
|
||||
limits have been reached. For instance, if actconn is close to 512 when
|
||||
multiple connection errors occur, chances are high that the system limits
|
||||
the process to use a maximum of 1024 file descriptors and that all of them
|
||||
are used. See section 3 "Global parameters" to find how to tune the system.
|
||||
are used. See section 3 "Global section" to find how to tune the system.
|
||||
|
||||
- "feconn" is the total number of concurrent connections on the frontend when
|
||||
the session was logged. It is useful to estimate the amount of resource
|
||||
@ -28293,7 +28434,7 @@ Detailed fields description :
|
||||
limits have been reached. For instance, if actconn is close to 512 or 1024
|
||||
when multiple connection errors occur, chances are high that the system
|
||||
limits the process to use a maximum of 1024 file descriptors and that all
|
||||
of them are used. See section 3 "Global parameters" to find how to tune the
|
||||
of them are used. See section 3 "Global section" to find how to tune the
|
||||
system.
|
||||
|
||||
- "feconn" is the total number of concurrent connections on the frontend when
|
||||
@ -29904,7 +30045,21 @@ a server by adding some latencies in the processing.
|
||||
9.2. HTTP compression
|
||||
---------------------
|
||||
|
||||
filter compression
|
||||
filter comp-req
|
||||
|
||||
Enables filter that explicitly tries to compress HTTP requests according to
|
||||
"compression" settings. Implicitly sets "compression direction request".
|
||||
|
||||
filter comp-res
|
||||
|
||||
Enables filter that explicitly tries to compress HTTP responses according to
|
||||
"compression" settings. Implicitly sets "compression direction response"
|
||||
|
||||
filter compression (deprecated)
|
||||
|
||||
Alias for backward compatibility purposes that is functionnally equivalent to
|
||||
enabling both "comp-req" and "comp-res" filter. "compression" keyword must be
|
||||
used to configure appropriate behavior:
|
||||
|
||||
The HTTP compression has been moved in a filter in HAProxy 1.7. "compression"
|
||||
keyword must still be used to enable and configure the HTTP compression. And
|
||||
|
||||
@ -539,10 +539,22 @@ message. These functions are used by HTX analyzers or by multiplexers.
|
||||
with the first block not removed, or NULL if everything was removed, and
|
||||
the amount of data drained.
|
||||
|
||||
- htx_xfer_blks() transfers HTX blocks from an HTX message to another,
|
||||
stopping after the first block of a specified type is transferred or when
|
||||
a specific amount of bytes, including meta-data, was moved. If the tail
|
||||
block is a DATA block, it may be partially moved. All other block are
|
||||
- htx_xfer() transfers HTX blocks from an HTX message to another, stopping
|
||||
when a specific amount of bytes, including meta-data, was copied. If the
|
||||
tail block is a DATA block, it may be partially copied. All other block
|
||||
are transferred at once. By default, copied blocks are removed from the
|
||||
original HTX message and headers and trailers parts cannot be partially
|
||||
copied. But flags can be set to change the default behavior:
|
||||
|
||||
- HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||
- HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers
|
||||
part can be xferred
|
||||
- HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||
|
||||
- htx_xfer_blks() [DEPRECATED] transfers HTX blocks from an HTX message to
|
||||
another, stopping after the first block of a specified type is transferred
|
||||
or when a specific amount of bytes, including meta-data, was moved. If the
|
||||
tail block is a DATA block, it may be partially moved. All other block are
|
||||
transferred at once or kept. This function returns a mixed value, with the
|
||||
last block moved, or NULL if nothing was moved, and the amount of data
|
||||
transferred. When HEADERS or TRAILERS blocks must be transferred, this
|
||||
|
||||
50
doc/internals/thread-exec-ctx.txt
Normal file
50
doc/internals/thread-exec-ctx.txt
Normal file
@ -0,0 +1,50 @@
|
||||
2026-03-12 - thread execution context
|
||||
|
||||
Thread execution context (thread_exec_ctx) is a combination of type and pointer
|
||||
that are set in the current running thread at th_ctx->exec_ctx when entering
|
||||
certain processing (tasks, sample fetch functions, actions, CLI keywords etc).
|
||||
They're refined along execution, so that a task such as process_stream could
|
||||
temporarily switch to a converter while evaluating an expression and switch
|
||||
back to process_stream. They are reported in thread dumps and are mixed with
|
||||
caller locations for memory profiling. As such they are intentionally not too
|
||||
precise in order to avoid an explosion of the number of buckets. At the moment,
|
||||
the level of granularity it provides is sufficient to try to narrow a
|
||||
misbehaving origin down to a list of keywords. The context types can currently
|
||||
be:
|
||||
|
||||
- something registered via an initcall, with the initcall's location
|
||||
- something registered via an ha_caller, with the caller's location
|
||||
- an explicit sample fetch / converter / action / CLI keyword list
|
||||
- an explicit function (mainly used for actions without keywords)
|
||||
- a task / tasklet (no distinction is made), using the ->process pointer
|
||||
- a filter (e.g. compression), via flt_conf, reporting name
|
||||
- a mux (via the mux_ops, reporting the name)
|
||||
- an applet (e.g. cache, stats, CLI)
|
||||
|
||||
A macro EXEC_CTX_MAKE(type, pointer) makes a thread_exec_ctx from such
|
||||
values.
|
||||
|
||||
A macro EXEC_CTX_NO_RET(ctx, statement) calls a void statement under the
|
||||
specified context.
|
||||
|
||||
A macro EXEC_CTX_WITH_RET(ctx, expr) calls an expression under the specified
|
||||
context.
|
||||
|
||||
Most locations were modified to directly use these macros on the fly, by
|
||||
retrieving the context from where it was set on the element being evaluated
|
||||
(e.g. an action rule contains the context inherited by the action keyword
|
||||
that was used to create it).
|
||||
|
||||
In tools.c, chunk_append_thread_ctx() tries to decode the given exec_ctx and
|
||||
appends it into the provided buffer. It's used by ha_thread_dump_one() and
|
||||
cli_io_handler_show_activity() for memory profiling. In this latter case,
|
||||
the detected thread_ctx are reported in the output under brackets prefixed
|
||||
with "[via ...]" to distinguish call paths to the same allocators.
|
||||
|
||||
A good way to test if a context is properly reported is to place a bleeding
|
||||
malloc() call into one of the monitored functions, e.g.:
|
||||
|
||||
DISGUISE(malloc(8));
|
||||
|
||||
and issue "show profiling memory" after stressing the function. Its context
|
||||
must appear on the right with the number of calls.
|
||||
@ -1740,10 +1740,7 @@ add backend <name> from <defproxy> [mode <mode>] [guid <guid>] [ EXPERIMENTAL ]
|
||||
|
||||
All named default proxies can be used, given that they validate the same
|
||||
inheritance rules applied during configuration parsing. There is some
|
||||
exceptions though, for example when the mode is neither TCP nor HTTP. Another
|
||||
exception is that it is not yet possible to use a default proxies which
|
||||
reference custom HTTP errors, for example via the errorfiles or http-rules
|
||||
keywords.
|
||||
exceptions though, for example when the mode is neither TCP nor HTTP.
|
||||
|
||||
This command is restricted and can only be issued on sockets configured for
|
||||
level "admin". Moreover, this feature is still considered in development so it
|
||||
@ -3359,7 +3356,7 @@ show pools [byname|bysize|byusage] [detailed] [match <pfx>] [<nb>]
|
||||
- Pool quic_conn_c (152 bytes) : 1337 allocated (203224 bytes), ...
|
||||
Total: 15 pools, 109578176 bytes allocated, 109578176 used ...
|
||||
|
||||
show profiling [{all | status | tasks | memory}] [byaddr|bytime|aggr|<max_lines>]*
|
||||
show profiling [{all | status | tasks | memory}] [byaddr|bytime|byctx|aggr|<max_lines>]*
|
||||
Dumps the current profiling settings, one per line, as well as the command
|
||||
needed to change them. When tasks profiling is enabled, some per-function
|
||||
statistics collected by the scheduler will also be emitted, with a summary
|
||||
@ -3368,14 +3365,15 @@ show profiling [{all | status | tasks | memory}] [byaddr|bytime|aggr|<max_lines>
|
||||
allocations/releases and their sizes will be reported. It is possible to
|
||||
limit the dump to only the profiling status, the tasks, or the memory
|
||||
profiling by specifying the respective keywords; by default all profiling
|
||||
information are dumped. It is also possible to limit the number of lines
|
||||
information are dumped. It is also possible to limit the number of lines of
|
||||
of output of each category by specifying a numeric limit. If is possible to
|
||||
request that the output is sorted by address or by total execution time
|
||||
instead of usage, e.g. to ease comparisons between subsequent calls or to
|
||||
check what needs to be optimized, and to aggregate task activity by called
|
||||
function instead of seeing the details. Please note that profiling is
|
||||
essentially aimed at developers since it gives hints about where CPU cycles
|
||||
or memory are wasted in the code. There is nothing useful to monitor there.
|
||||
request that the output is sorted by address, by total execution time, or by
|
||||
calling context instead of usage, e.g. to ease comparisons between subsequent
|
||||
calls or to check what needs to be optimized, and to aggregate task activity
|
||||
by called function instead of seeing the details. Please note that profiling
|
||||
is essentially aimed at developers since it gives hints about where CPU
|
||||
cycles or memory are wasted in the code. There is nothing useful to monitor
|
||||
there.
|
||||
|
||||
show resolvers [<resolvers section id>]
|
||||
Dump statistics for the given resolvers section, or all resolvers sections
|
||||
|
||||
@ -198,6 +198,11 @@ struct act_rule {
|
||||
struct server *srv; /* target server to attach the connection */
|
||||
struct sample_expr *name; /* used to differentiate idle connections */
|
||||
} attach_srv; /* 'attach-srv' rule */
|
||||
struct {
|
||||
enum log_orig_id orig;
|
||||
char *profile_name;
|
||||
struct log_profile *profile;
|
||||
} do_log; /* 'do-log' action */
|
||||
struct {
|
||||
int value;
|
||||
struct sample_expr *expr;
|
||||
@ -206,6 +211,7 @@ struct act_rule {
|
||||
void *p[4];
|
||||
} act; /* generic pointers to be used by custom actions */
|
||||
} arg; /* arguments used by some actions */
|
||||
struct thread_exec_ctx exec_ctx; /* execution context */
|
||||
struct {
|
||||
char *file; /* file name where the rule appears (or NULL) */
|
||||
int line; /* line number where the rule appears */
|
||||
@ -217,7 +223,9 @@ struct action_kw {
|
||||
enum act_parse_ret (*parse)(const char **args, int *cur_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err);
|
||||
int flags;
|
||||
/* 4 bytes here */
|
||||
void *private;
|
||||
struct thread_exec_ctx exec_ctx; /* execution context */
|
||||
};
|
||||
|
||||
struct action_kw_list {
|
||||
|
||||
@ -35,6 +35,7 @@ int act_resolution_cb(struct resolv_requester *requester, struct dns_counters *c
|
||||
int act_resolution_error_cb(struct resolv_requester *requester, int error_code);
|
||||
const char *action_suggest(const char *word, const struct list *keywords, const char **extra);
|
||||
void free_act_rule(struct act_rule *rule);
|
||||
void act_add_list(struct list *head, struct action_kw_list *kw_list);
|
||||
|
||||
static inline struct action_kw *action_lookup(struct list *keywords, const char *kw)
|
||||
{
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include <haproxy/api-t.h>
|
||||
#include <haproxy/freq_ctr-t.h>
|
||||
#include <haproxy/tinfo-t.h>
|
||||
|
||||
/* bit fields for the "profiling" global variable */
|
||||
#define HA_PROF_TASKS_OFF 0x00000000 /* per-task CPU profiling forced disabled */
|
||||
@ -84,6 +85,7 @@ struct memprof_stats {
|
||||
unsigned long long alloc_tot;
|
||||
unsigned long long free_tot;
|
||||
void *info; // for pools, ptr to the pool
|
||||
struct thread_exec_ctx exec_ctx;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@ -130,6 +130,7 @@ struct appctx {
|
||||
int (*io_handler)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK */
|
||||
void (*io_release)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK,
|
||||
if the command is terminated or the session released */
|
||||
struct cli_kw *kw; /* the keyword being processed */
|
||||
} cli_ctx; /* context dedicated to the CLI applet */
|
||||
|
||||
struct buffer_wait buffer_wait; /* position in the list of objects waiting for a buffer */
|
||||
|
||||
@ -62,6 +62,13 @@ ssize_t applet_append_line(void *ctx, struct ist v1, struct ist v2, size_t ofs,
|
||||
static forceinline void applet_fl_set(struct appctx *appctx, uint on);
|
||||
static forceinline void applet_fl_clr(struct appctx *appctx, uint off);
|
||||
|
||||
/* macros to switch the calling context to the applet during a call. There's
|
||||
* one with a return value for most calls, and one without for the few like
|
||||
* fct(), shut(), or release() with no return.
|
||||
*/
|
||||
#define CALL_APPLET_WITH_RET(applet, func) EXEC_CTX_WITH_RET(EXEC_CTX_MAKE(TH_EX_CTX_APPLET, (applet)), (applet)->func)
|
||||
#define CALL_APPLET_NO_RET(applet, func) EXEC_CTX_NO_RET(EXEC_CTX_MAKE(TH_EX_CTX_APPLET, (applet)), (applet)->func)
|
||||
|
||||
|
||||
static forceinline uint appctx_app_test(const struct appctx *appctx, uint test)
|
||||
{
|
||||
@ -126,7 +133,7 @@ static inline int appctx_init(struct appctx *appctx)
|
||||
task_set_thread(appctx->t, tid);
|
||||
|
||||
if (appctx->applet->init)
|
||||
return appctx->applet->init(appctx);
|
||||
return CALL_APPLET_WITH_RET(appctx->applet, init(appctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -59,6 +59,7 @@ enum chk_result {
|
||||
#define CHK_ST_FASTINTER 0x0400 /* force fastinter check */
|
||||
#define CHK_ST_READY 0x0800 /* check ready to migrate or run, see below */
|
||||
#define CHK_ST_SLEEPING 0x1000 /* check was sleeping, i.e. not currently bound to a thread, see below */
|
||||
#define CHK_ST_USE_SMALL_BUFF 0x2000 /* Use small buffers if possible for the request */
|
||||
|
||||
/* 4 possible states for CHK_ST_SLEEPING and CHK_ST_READY:
|
||||
* SLP RDY State Description
|
||||
@ -188,6 +189,7 @@ struct check {
|
||||
char **envp; /* the environment to use if running a process-based check */
|
||||
struct pid_list *curpid; /* entry in pid_list used for current process-based test, or -1 if not in test */
|
||||
struct sockaddr_storage addr; /* the address to check */
|
||||
struct protocol *proto; /* protocol used for check, may be different from the server's one */
|
||||
char *pool_conn_name; /* conn name used on reuse */
|
||||
char *sni; /* Server name */
|
||||
char *alpn_str; /* ALPN to use for checks */
|
||||
|
||||
@ -78,12 +78,11 @@ struct task *process_chk(struct task *t, void *context, unsigned int state);
|
||||
struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state);
|
||||
|
||||
int check_buf_available(void *target);
|
||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr);
|
||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer);
|
||||
void check_release_buf(struct check *check, struct buffer *bptr);
|
||||
const char *init_check(struct check *check, int type);
|
||||
void free_check(struct check *check);
|
||||
void check_purge(struct check *check);
|
||||
int wake_srv_chk(struct stconn *sc);
|
||||
|
||||
int init_srv_check(struct server *srv);
|
||||
int init_srv_agent_check(struct server *srv);
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
|
||||
extern struct pool_head *pool_head_trash;
|
||||
extern struct pool_head *pool_head_large_trash;
|
||||
extern struct pool_head *pool_head_small_trash;
|
||||
|
||||
/* function prototypes */
|
||||
|
||||
@ -48,6 +49,7 @@ int chunk_strcmp(const struct buffer *chk, const char *str);
|
||||
int chunk_strcasecmp(const struct buffer *chk, const char *str);
|
||||
struct buffer *get_trash_chunk(void);
|
||||
struct buffer *get_large_trash_chunk(void);
|
||||
struct buffer *get_small_trash_chunk(void);
|
||||
struct buffer *get_trash_chunk_sz(size_t size);
|
||||
struct buffer *get_larger_trash_chunk(struct buffer *chunk);
|
||||
int init_trash_buffers(int first);
|
||||
@ -133,6 +135,29 @@ static forceinline struct buffer *alloc_large_trash_chunk(void)
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a small trash chunk from the reentrant pool. The buffer starts at
|
||||
* the end of the chunk. This chunk must be freed using free_trash_chunk(). This
|
||||
* call may fail and the caller is responsible for checking that the returned
|
||||
* pointer is not NULL.
|
||||
*/
|
||||
static forceinline struct buffer *alloc_small_trash_chunk(void)
|
||||
{
|
||||
struct buffer *chunk;
|
||||
|
||||
if (!pool_head_small_trash)
|
||||
return NULL;
|
||||
|
||||
chunk = pool_alloc(pool_head_small_trash);
|
||||
if (chunk) {
|
||||
char *buf = (char *)chunk + sizeof(struct buffer);
|
||||
*buf = 0;
|
||||
chunk_init(chunk, buf,
|
||||
pool_head_small_trash->size - sizeof(struct buffer));
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a trash chunk accordingly to the requested size. This chunk must be
|
||||
* freed using free_trash_chunk(). This call may fail and the caller is
|
||||
@ -140,7 +165,9 @@ static forceinline struct buffer *alloc_large_trash_chunk(void)
|
||||
*/
|
||||
static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
||||
{
|
||||
if (likely(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();
|
||||
else if (pool_head_large_trash && size <= pool_head_large_trash->size)
|
||||
return alloc_large_trash_chunk();
|
||||
@ -153,10 +180,12 @@ static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
||||
*/
|
||||
static forceinline void free_trash_chunk(struct buffer *chunk)
|
||||
{
|
||||
if (likely(chunk && chunk->size == pool_head_trash->size - sizeof(struct buffer)))
|
||||
pool_free(pool_head_trash, chunk);
|
||||
else
|
||||
if (pool_head_small_trash && chunk && chunk->size == pool_head_small_trash->size - sizeof(struct buffer))
|
||||
pool_free(pool_head_small_trash, chunk);
|
||||
else if (pool_head_large_trash && chunk && chunk->size == pool_head_large_trash->size - sizeof(struct buffer))
|
||||
pool_free(pool_head_large_trash, chunk);
|
||||
else
|
||||
pool_free(pool_head_trash, chunk);
|
||||
}
|
||||
|
||||
/* copies chunk <src> into <chk>. Returns 0 in case of failure. */
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#define _HAPROXY_CLI_T_H
|
||||
|
||||
#include <haproxy/applet-t.h>
|
||||
#include <haproxy/tinfo-t.h>
|
||||
|
||||
/* Access level for a stats socket (appctx->cli_ctx.level) */
|
||||
#define ACCESS_LVL_NONE 0x0000
|
||||
@ -120,6 +121,8 @@ struct cli_kw {
|
||||
void (*io_release)(struct appctx *appctx);
|
||||
void *private;
|
||||
int level; /* this is the level needed to show the keyword usage and to use it */
|
||||
/* 4-byte hole here */
|
||||
struct thread_exec_ctx exec_ctx; /* execution context */
|
||||
};
|
||||
|
||||
struct cli_kw_list {
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
#include <haproxy/listener-t.h>
|
||||
#include <haproxy/obj_type.h>
|
||||
#include <haproxy/pool-t.h>
|
||||
#include <haproxy/protocol.h>
|
||||
#include <haproxy/server.h>
|
||||
#include <haproxy/session-t.h>
|
||||
#include <haproxy/task-t.h>
|
||||
@ -49,6 +50,13 @@ extern struct mux_stopping_data mux_stopping_data[MAX_THREADS];
|
||||
|
||||
#define IS_HTX_CONN(conn) ((conn)->mux && ((conn)->mux->flags & MX_FL_HTX))
|
||||
|
||||
/* macros to switch the calling context to the mux during a call. There's one
|
||||
* with a return value for most calls, and one without for the few like shut(),
|
||||
* detach() or destroy() with no return.
|
||||
*/
|
||||
#define CALL_MUX_WITH_RET(mux, func) EXEC_CTX_WITH_RET(EXEC_CTX_MAKE(TH_EX_CTX_MUX, (mux)), (mux)->func)
|
||||
#define CALL_MUX_NO_RET(mux, func) EXEC_CTX_NO_RET(EXEC_CTX_MAKE(TH_EX_CTX_MUX, (mux)), (mux)->func)
|
||||
|
||||
/* receive a PROXY protocol header over a connection */
|
||||
int conn_recv_proxy(struct connection *conn, int flag);
|
||||
int conn_send_proxy(struct connection *conn, unsigned int flag);
|
||||
@ -480,7 +488,7 @@ static inline int conn_install_mux(struct connection *conn, const struct mux_ops
|
||||
|
||||
conn->mux = mux;
|
||||
conn->ctx = ctx;
|
||||
ret = mux->init ? mux->init(conn, prx, sess, &BUF_NULL) : 0;
|
||||
ret = mux->init ? CALL_MUX_WITH_RET(mux, init(conn, prx, sess, &BUF_NULL)) : 0;
|
||||
if (ret < 0) {
|
||||
conn->mux = NULL;
|
||||
conn->ctx = NULL;
|
||||
@ -602,13 +610,13 @@ void list_mux_proto(FILE *out);
|
||||
*/
|
||||
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
||||
const struct ist mux_proto,
|
||||
int proto_side, int proto_mode)
|
||||
int proto_side, int proto_is_quic, int proto_mode)
|
||||
{
|
||||
struct mux_proto_list *item;
|
||||
struct mux_proto_list *fallback = NULL;
|
||||
|
||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||
if (!(item->side & proto_side) || !(item->mode & proto_mode))
|
||||
if (!(item->side & proto_side) || !(item->mode & proto_mode) || (proto_is_quic && !(item->mux->flags & MX_FL_FRAMED)))
|
||||
continue;
|
||||
if (istlen(mux_proto) && isteq(mux_proto, item->token))
|
||||
return item;
|
||||
@ -633,7 +641,7 @@ static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
|
||||
{
|
||||
const struct mux_proto_list *item;
|
||||
|
||||
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_mode);
|
||||
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_is_quic(conn->ctrl), proto_mode);
|
||||
|
||||
return item ? item->mux : NULL;
|
||||
}
|
||||
|
||||
@ -536,6 +536,11 @@
|
||||
#define TIME_STATS_SAMPLES 512
|
||||
#endif
|
||||
|
||||
/* number of samples used to measure the load in the run queue */
|
||||
#ifndef RQ_LOAD_SAMPLES
|
||||
#define RQ_LOAD_SAMPLES 512
|
||||
#endif
|
||||
|
||||
/* max ocsp cert id asn1 encoded length */
|
||||
#ifndef OCSP_MAX_CERTID_ASN1_LENGTH
|
||||
#define OCSP_MAX_CERTID_ASN1_LENGTH 128
|
||||
@ -601,7 +606,7 @@
|
||||
* store stats.
|
||||
*/
|
||||
#ifndef MEMPROF_HASH_BITS
|
||||
# define MEMPROF_HASH_BITS 10
|
||||
# define MEMPROF_HASH_BITS 12
|
||||
#endif
|
||||
#define MEMPROF_HASH_BUCKETS (1U << MEMPROF_HASH_BITS)
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
|
||||
extern struct pool_head *pool_head_buffer;
|
||||
extern struct pool_head *pool_head_large_buffer;
|
||||
extern struct pool_head *pool_head_small_buffer;
|
||||
|
||||
int init_buffer(void);
|
||||
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
||||
@ -66,6 +67,12 @@ static inline int b_is_large_sz(size_t sz)
|
||||
return (pool_head_large_buffer && sz == pool_head_large_buffer->size);
|
||||
}
|
||||
|
||||
/* Return 1 if <sz> is the size of a small buffer */
|
||||
static inline int b_is_small_sz(size_t sz)
|
||||
{
|
||||
return (pool_head_small_buffer && sz == pool_head_small_buffer->size);
|
||||
}
|
||||
|
||||
/* Return 1 if <bug> is a default buffer */
|
||||
static inline int b_is_default(struct buffer *buf)
|
||||
{
|
||||
@ -78,6 +85,12 @@ static inline int b_is_large(struct buffer *buf)
|
||||
return b_is_large_sz(b_size(buf));
|
||||
}
|
||||
|
||||
/* Return 1 if <buf> is a small buffer */
|
||||
static inline int b_is_small(struct buffer *buf)
|
||||
{
|
||||
return b_is_small_sz(b_size(buf));
|
||||
}
|
||||
|
||||
/**************************************************/
|
||||
/* Functions below are used for buffer allocation */
|
||||
/**************************************************/
|
||||
@ -172,6 +185,8 @@ static inline char *__b_get_emergency_buf(void)
|
||||
* than the default buffers */ \
|
||||
if (unlikely(b_is_large_sz(sz))) \
|
||||
pool_free(pool_head_large_buffer, area); \
|
||||
else if (unlikely(b_is_small_sz(sz))) \
|
||||
pool_free(pool_head_small_buffer, area); \
|
||||
else if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \
|
||||
th_ctx->emergency_bufs[th_ctx->emergency_bufs_left++] = area; \
|
||||
else \
|
||||
@ -185,6 +200,35 @@ static inline char *__b_get_emergency_buf(void)
|
||||
__b_free((_buf)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
static inline struct buffer *b_alloc_small(struct buffer *buf)
|
||||
{
|
||||
char *area = NULL;
|
||||
|
||||
if (!buf->size) {
|
||||
area = pool_alloc(pool_head_small_buffer);
|
||||
if (!area)
|
||||
return NULL;
|
||||
buf->area = area;
|
||||
buf->size = global.tune.bufsize_small;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline struct buffer *b_alloc_large(struct buffer *buf)
|
||||
{
|
||||
char *area = NULL;
|
||||
|
||||
if (!buf->size) {
|
||||
area = pool_alloc(pool_head_large_buffer);
|
||||
if (!area)
|
||||
return NULL;
|
||||
buf->area = area;
|
||||
buf->size = global.tune.bufsize_large;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Offer one or multiple buffer currently belonging to target <from> to whoever
|
||||
* needs one. Any pointer is valid for <from>, including NULL. Its purpose is
|
||||
* to avoid passing a buffer to oneself in case of failed allocations (e.g.
|
||||
|
||||
@ -28,7 +28,9 @@
|
||||
#include <haproxy/stream-t.h>
|
||||
|
||||
extern const char *trace_flt_id;
|
||||
extern const char *http_comp_flt_id;
|
||||
extern const char *http_comp_req_flt_id;
|
||||
extern const char *http_comp_res_flt_id;
|
||||
|
||||
extern const char *cache_store_flt_id;
|
||||
extern const char *spoe_filter_id;
|
||||
extern const char *fcgi_flt_id;
|
||||
|
||||
@ -403,6 +403,25 @@ static inline uint swrate_add_scaled_opportunistic(uint *sum, uint n, uint v, ui
|
||||
return new_sum;
|
||||
}
|
||||
|
||||
/* Like swrate_add() except that if <v> is beyond the current average, the
|
||||
* average is replaced by the peak. This is essentially used to measure peak
|
||||
* loads in the scheduler, reason why it is provided as a local variant that
|
||||
* does not involve atomic operations.
|
||||
*/
|
||||
static inline uint swrate_add_peak_local(uint *sum, uint n, uint v)
|
||||
{
|
||||
uint old_sum, new_sum;
|
||||
|
||||
old_sum = *sum;
|
||||
if (v * n > old_sum)
|
||||
new_sum = v * n;
|
||||
else
|
||||
new_sum = old_sum - (old_sum + n - 1) / n + v;
|
||||
|
||||
*sum = new_sum;
|
||||
return new_sum;
|
||||
}
|
||||
|
||||
/* Returns the average sample value for the sum <sum> over a sliding window of
|
||||
* <n> samples. Better if <n> is a power of two. It must be the same <n> as the
|
||||
* one used above in all additions.
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
#define GTUNE_DISABLE_H2_WEBSOCKET (1<<21)
|
||||
#define GTUNE_DISABLE_ACTIVE_CLOSE (1<<22)
|
||||
#define GTUNE_QUICK_EXIT (1<<23)
|
||||
/* (1<<24) unused */
|
||||
#define GTUNE_COLLECT_LIBS (1<<24)
|
||||
/* (1<<25) unused */
|
||||
#define GTUNE_USE_FAST_FWD (1<<26)
|
||||
#define GTUNE_LISTENER_MQ_FAIR (1<<27)
|
||||
|
||||
@ -58,6 +58,10 @@ extern int devnullfd;
|
||||
extern int fileless_mode;
|
||||
extern struct cfgfile fileless_cfg;
|
||||
|
||||
/* storage for collected libs */
|
||||
extern void *lib_storage;
|
||||
extern size_t lib_size;
|
||||
|
||||
struct proxy;
|
||||
struct server;
|
||||
int main(int argc, char **argv);
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include <haproxy/hstream-t.h>
|
||||
|
||||
struct task *sc_hstream_io_cb(struct task *t, void *ctx, unsigned int state);
|
||||
int hstream_wake(struct stconn *sc);
|
||||
void hstream_shutdown(struct stconn *sc);
|
||||
void *hstream_new(struct session *sess, struct stconn *sc, struct buffer *input);
|
||||
|
||||
|
||||
@ -93,4 +93,22 @@ struct http_errors {
|
||||
struct list list; /* http-errors list */
|
||||
};
|
||||
|
||||
/* Indicates the keyword origin of an http-error definition. This is used in
|
||||
* <conf_errors> type to indicate which part of the internal union should be
|
||||
* manipulated.
|
||||
*/
|
||||
enum http_err_directive {
|
||||
HTTP_ERR_DIRECTIVE_SECTION = 0, /* "errorfiles" keyword referencing a http-errors section */
|
||||
HTTP_ERR_DIRECTIVE_INLINE, /* "errorfile" keyword with inline error definition */
|
||||
};
|
||||
|
||||
/* Used with "errorfiles" directives. It indicates for each known HTTP error
|
||||
* status codes if they are defined in the target http-errors section.
|
||||
*/
|
||||
enum http_err_import {
|
||||
HTTP_ERR_IMPORT_NO = 0,
|
||||
HTTP_ERR_IMPORT_IMPLICIT, /* import every errcode defined in a section */
|
||||
HTTP_ERR_IMPORT_EXPLICIT, /* import a specific errcode from a section */
|
||||
};
|
||||
|
||||
#endif /* _HAPROXY_HTTP_HTX_T_H */
|
||||
|
||||
@ -78,6 +78,7 @@ struct buffer *http_load_errorfile(const char *file, char **errmsg);
|
||||
struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg);
|
||||
struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg);
|
||||
struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg);
|
||||
int proxy_check_http_errors(struct proxy *px);
|
||||
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg);
|
||||
void proxy_release_conf_errors(struct proxy *px);
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t bl
|
||||
struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk);
|
||||
struct htx_ret htx_find_offset(struct htx *htx, uint32_t offset);
|
||||
void htx_truncate(struct htx *htx, uint32_t offset);
|
||||
void htx_truncate_blk(struct htx *htx, struct htx_blk *blk);
|
||||
struct htx_ret htx_drain(struct htx *htx, uint32_t max);
|
||||
|
||||
struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
|
||||
@ -56,6 +57,16 @@ size_t htx_add_data(struct htx *htx, const struct ist data);
|
||||
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data);
|
||||
void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref);
|
||||
int htx_append_msg(struct htx *dst, const struct htx *src);
|
||||
struct buffer *htx_move_to_small_buffer(struct buffer *dst, struct buffer *src);
|
||||
struct buffer *htx_move_to_large_buffer(struct buffer *dst, struct buffer *src);
|
||||
struct buffer *htx_copy_to_small_buffer(struct buffer *dst, struct buffer *src);
|
||||
struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src);
|
||||
|
||||
#define HTX_XFER_DEFAULT 0x00000000 /* Default XFER: no partial xfer / remove blocks from source */
|
||||
#define HTX_XFER_KEEP_SRC_BLKS 0x00000001 /* Don't remove xfer blocks from source messages during xfer */
|
||||
#define HTX_XFER_PARTIAL_HDRS_COPY 0x00000002 /* Allow partial copy of headers and trailers part */
|
||||
#define HTX_XFER_HDRS_ONLY 0x00000003 /* Only Transfert header blocks (start-line, header and EOH) */
|
||||
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags);
|
||||
|
||||
/* Functions and macros to get parts of the start-line or length of these
|
||||
* parts. Request and response start-lines are both composed of 3 parts.
|
||||
|
||||
@ -20,6 +20,11 @@ extern struct list server_deinit_list;
|
||||
extern struct list per_thread_free_list;
|
||||
extern struct list per_thread_deinit_list;
|
||||
|
||||
/* initcall caller location */
|
||||
extern const struct initcall *caller_initcall;
|
||||
extern const char *caller_file;
|
||||
extern int caller_line;
|
||||
|
||||
void hap_register_pre_check(int (*fct)());
|
||||
void hap_register_post_check(int (*fct)());
|
||||
void hap_register_post_proxy_check(int (*fct)(struct proxy *));
|
||||
|
||||
@ -77,6 +77,8 @@ struct initcall {
|
||||
void *arg1;
|
||||
void *arg2;
|
||||
void *arg3;
|
||||
const char *loc_file; /* file where the call is declared, or NULL */
|
||||
int loc_line; /* line where the call is declared, or NULL */
|
||||
#if defined(USE_OBSOLETE_LINKER)
|
||||
void *next;
|
||||
#endif
|
||||
@ -107,6 +109,8 @@ struct initcall {
|
||||
.arg1 = (void *)(a1), \
|
||||
.arg2 = (void *)(a2), \
|
||||
.arg3 = (void *)(a3), \
|
||||
.loc_file = __FILE__, \
|
||||
.loc_line = linenum, \
|
||||
} : NULL
|
||||
|
||||
|
||||
@ -131,6 +135,8 @@ __attribute__((constructor)) static void __initcb_##linenum() \
|
||||
.arg1 = (void *)(a1), \
|
||||
.arg2 = (void *)(a2), \
|
||||
.arg3 = (void *)(a3), \
|
||||
.loc_file = __FILE__, \
|
||||
.loc_line = linenum, \
|
||||
}; \
|
||||
if (stg < STG_SIZE) { \
|
||||
entry.next = __initstg[stg]; \
|
||||
@ -229,8 +235,15 @@ extern struct initcall *__initstg[STG_SIZE];
|
||||
const struct initcall **ptr; \
|
||||
if (stg >= STG_SIZE) \
|
||||
break; \
|
||||
FOREACH_INITCALL(ptr, stg) \
|
||||
FOREACH_INITCALL(ptr, stg) { \
|
||||
caller_initcall = *ptr; \
|
||||
caller_file = (*ptr)->loc_file; \
|
||||
caller_line = (*ptr)->loc_line; \
|
||||
(*ptr)->fct((*ptr)->arg1, (*ptr)->arg2, (*ptr)->arg3); \
|
||||
caller_initcall = NULL; \
|
||||
caller_file = NULL; \
|
||||
caller_line = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else // USE_OBSOLETE_LINKER
|
||||
@ -243,8 +256,15 @@ extern struct initcall *__initstg[STG_SIZE];
|
||||
const struct initcall *ptr; \
|
||||
if (stg >= STG_SIZE) \
|
||||
break; \
|
||||
FOREACH_INITCALL(ptr, stg) \
|
||||
FOREACH_INITCALL(ptr, stg) { \
|
||||
caller_initcall = ptr; \
|
||||
caller_file = (ptr)->loc_file; \
|
||||
caller_line = (ptr)->loc_line; \
|
||||
(ptr)->fct((ptr)->arg1, (ptr)->arg2, (ptr)->arg3); \
|
||||
caller_initcall = NULL; \
|
||||
caller_file = NULL; \
|
||||
caller_line = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // USE_OBSOLETE_LINKER
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
enum jwt_alg jwt_parse_alg(const char *alg_str, unsigned int alg_len);
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int *item_num);
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int item_num);
|
||||
int jwt_tree_load_cert(char *path, int pathlen, int tryload_cert, const char *file, int line, char **err);
|
||||
|
||||
enum jwt_vrfy_status jwt_verify(const struct buffer *token, const struct buffer *alg,
|
||||
|
||||
@ -394,6 +394,12 @@ static inline unsigned long ERR_peek_error_func(const char **func)
|
||||
#define __OPENSSL_110_CONST__
|
||||
#endif
|
||||
|
||||
#if (HA_OPENSSL_VERSION_NUMBER >= 0x40000000L) && (!defined(USE_OPENSSL_WOLFSSL))
|
||||
#define __X509_NAME_CONST__ const
|
||||
#else
|
||||
#define __X509_NAME_CONST__
|
||||
#endif
|
||||
|
||||
/* ERR_remove_state() was deprecated in 1.0.0 in favor of
|
||||
* ERR_remove_thread_state(), which was in turn deprecated in
|
||||
* 1.1.0 and does nothing anymore. Let's simply silently kill
|
||||
|
||||
@ -124,6 +124,12 @@ static inline int real_family(int ss_family)
|
||||
return fam ? fam->real_family : AF_UNSPEC;
|
||||
}
|
||||
|
||||
static inline int proto_is_quic(const struct protocol *proto)
|
||||
{
|
||||
return (proto->proto_type == PROTO_TYPE_DGRAM &&
|
||||
proto->xprt_type == PROTO_TYPE_STREAM);
|
||||
}
|
||||
|
||||
#endif /* _HAPROXY_PROTOCOL_H */
|
||||
|
||||
/*
|
||||
|
||||
@ -156,14 +156,17 @@ enum PR_SRV_STATE_FILE {
|
||||
#define PR_O2_RSTRICT_REQ_HDR_NAMES_NOOP 0x01000000 /* preserve request header names containing chars outside of [0-9a-zA-Z-] charset */
|
||||
#define PR_O2_RSTRICT_REQ_HDR_NAMES_MASK 0x01c00000 /* mask for restrict-http-header-names option */
|
||||
|
||||
/* unused : 0x02000000 ... 0x08000000 */
|
||||
|
||||
/* server health checks */
|
||||
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
||||
#define PR_O2_TCPCHK_CHK 0x90000000 /* use TCPCHK check for server health */
|
||||
#define PR_O2_EXT_CHK 0xA0000000 /* use external command for server health */
|
||||
/* unused: 0xB0000000 to 0xF000000, reserved for health checks */
|
||||
#define PR_O2_CHK_ANY 0xF0000000 /* Mask to cover any check */
|
||||
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
||||
#define PR_O2_TCPCHK_CHK 0x02000000 /* use TCPCHK check for server health */
|
||||
#define PR_O2_EXT_CHK 0x04000000 /* use external command for server health */
|
||||
#define PR_O2_CHK_ANY 0x06000000 /* Mask to cover any check */
|
||||
|
||||
#define PR_O2_USE_SBUF_QUEUE 0x08000000 /* use small buffer for request when stream are queued*/
|
||||
#define PR_O2_USE_SBUF_L7_RETRY 0x10000000 /* use small buffer for request when L7 retires are enabled */
|
||||
#define PR_O2_USE_SBUF_CHECK 0x20000000 /* use small buffer for request's healthchecks */
|
||||
#define PR_O2_USE_SBUF_ALL 0x38000000 /* all flags for use-large-buffer option */
|
||||
/* unused : 0x40000000 ... 0x80000000 */
|
||||
/* end of proxy->options2 */
|
||||
|
||||
/* bits for proxy->options3 */
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#define _HAPROXY_SAMPLE_T_H
|
||||
|
||||
#include <haproxy/api-t.h>
|
||||
#include <haproxy/tinfo-t.h>
|
||||
#include <haproxy/sample_data-t.h>
|
||||
|
||||
/* input and output sample types
|
||||
@ -265,6 +266,7 @@ struct sample_conv {
|
||||
unsigned int in_type; /* expected input sample type */
|
||||
unsigned int out_type; /* output sample type */
|
||||
void *private; /* private values. only used by maps and Lua */
|
||||
struct thread_exec_ctx exec_ctx; /* execution context */
|
||||
};
|
||||
|
||||
/* sample conversion expression */
|
||||
@ -288,6 +290,7 @@ struct sample_fetch {
|
||||
unsigned int use; /* fetch source (SMP_USE_*) */
|
||||
unsigned int val; /* fetch validity (SMP_VAL_*) */
|
||||
void *private; /* private values. only used by Lua */
|
||||
struct thread_exec_ctx exec_ctx; /* execution context */
|
||||
};
|
||||
|
||||
/* sample expression */
|
||||
|
||||
@ -36,6 +36,10 @@
|
||||
void sc_update_rx(struct stconn *sc);
|
||||
void sc_update_tx(struct stconn *sc);
|
||||
|
||||
void sc_abort(struct stconn *sc);
|
||||
void sc_shutdown(struct stconn *sc);
|
||||
void sc_chk_rcv(struct stconn *sc);
|
||||
|
||||
struct task *sc_conn_io_cb(struct task *t, void *ctx, unsigned int state);
|
||||
int sc_conn_sync_recv(struct stconn *sc);
|
||||
int sc_conn_sync_send(struct stconn *sc);
|
||||
@ -360,38 +364,6 @@ static inline int sc_is_recv_allowed(const struct stconn *sc)
|
||||
return !(sc->flags & (SC_FL_WONT_READ|SC_FL_NEED_BUFF|SC_FL_NEED_ROOM));
|
||||
}
|
||||
|
||||
/* This is to be used after making some room available in a channel. It will
|
||||
* return without doing anything if the stream connector's RX path is blocked.
|
||||
* It will automatically mark the stream connector as busy processing the end
|
||||
* point in order to avoid useless repeated wakeups.
|
||||
* It will then call ->chk_rcv() to enable receipt of new data.
|
||||
*/
|
||||
static inline void sc_chk_rcv(struct stconn *sc)
|
||||
{
|
||||
if (sc_ep_test(sc, SE_FL_APPLET_NEED_CONN) &&
|
||||
sc_state_in(sc_opposite(sc)->state, SC_SB_RDY|SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) {
|
||||
sc_ep_clr(sc, SE_FL_APPLET_NEED_CONN);
|
||||
sc_ep_report_read_activity(sc);
|
||||
}
|
||||
|
||||
if (!sc_is_recv_allowed(sc))
|
||||
return;
|
||||
|
||||
if (!sc_state_in(sc->state, SC_SB_RDY|SC_SB_EST))
|
||||
return;
|
||||
|
||||
sc_ep_set(sc, SE_FL_HAVE_NO_DATA);
|
||||
if (likely(sc->app_ops->chk_rcv))
|
||||
sc->app_ops->chk_rcv(sc);
|
||||
}
|
||||
|
||||
/* Calls chk_snd on the endpoint using the data layer */
|
||||
static inline void sc_chk_snd(struct stconn *sc)
|
||||
{
|
||||
if (likely(sc->app_ops->chk_snd))
|
||||
sc->app_ops->chk_snd(sc);
|
||||
}
|
||||
|
||||
|
||||
/* Perform a synchronous receive using the right version, depending the endpoing
|
||||
* is a connection or an applet.
|
||||
@ -536,24 +508,10 @@ static inline void sc_schedule_abort(struct stconn *sc)
|
||||
sc->flags |= SC_FL_ABRT_WANTED;
|
||||
}
|
||||
|
||||
/* Abort the SC and notify the endpoint using the data layer */
|
||||
static inline void sc_abort(struct stconn *sc)
|
||||
{
|
||||
if (likely(sc->app_ops->abort))
|
||||
sc->app_ops->abort(sc);
|
||||
}
|
||||
|
||||
/* Schedule a shutdown for the SC */
|
||||
static inline void sc_schedule_shutdown(struct stconn *sc)
|
||||
{
|
||||
sc->flags |= SC_FL_SHUT_WANTED;
|
||||
}
|
||||
|
||||
/* Shutdown the SC and notify the endpoint using the data layer */
|
||||
static inline void sc_shutdown(struct stconn *sc)
|
||||
{
|
||||
if (likely(sc->app_ops->shutdown))
|
||||
sc->app_ops->shutdown(sc);
|
||||
}
|
||||
|
||||
#endif /* _HAPROXY_SC_STRM_H */
|
||||
|
||||
@ -34,10 +34,10 @@ int cert_get_pkey_algo(X509 *crt, struct buffer *out);
|
||||
int ssl_sock_get_serial(X509 *crt, struct buffer *out);
|
||||
int ssl_sock_crt2der(X509 *crt, struct buffer *out);
|
||||
int ssl_sock_get_time(ASN1_TIME *tm, struct buffer *out);
|
||||
int ssl_sock_get_dn_entry(X509_NAME *a, const struct buffer *entry, int pos,
|
||||
int ssl_sock_get_dn_entry(__X509_NAME_CONST__ X509_NAME *a, const struct buffer *entry, int pos,
|
||||
struct buffer *out);
|
||||
int ssl_sock_get_dn_formatted(X509_NAME *a, const struct buffer *format, struct buffer *out);
|
||||
int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out);
|
||||
int ssl_sock_get_dn_formatted(__X509_NAME_CONST__ X509_NAME *a, const struct buffer *format, struct buffer *out);
|
||||
int ssl_sock_get_dn_oneline(__X509_NAME_CONST__ X509_NAME *a, struct buffer *out);
|
||||
X509* ssl_sock_get_peer_certificate(SSL *ssl);
|
||||
X509* ssl_sock_get_verified_chain_root(SSL *ssl);
|
||||
unsigned int openssl_version_parser(const char *version);
|
||||
|
||||
@ -36,7 +36,7 @@ struct shm_stats_file_hdr {
|
||||
/* 2 bytes hole */
|
||||
uint global_now_ms; /* global monotonic date (ms) common to all processes using the shm */
|
||||
ullong global_now_ns; /* global monotonic date (ns) common to all processes using the shm */
|
||||
llong now_offset; /* offset applied to global monotonic date on startup */
|
||||
ALWAYS_PAD(8); // 8 bytes hole
|
||||
/* each process uses one slot and is identified using its pid, max 64 in order
|
||||
* to be able to use bitmask to refer to a process and then look its pid in the
|
||||
* "slots.pid" map
|
||||
|
||||
@ -349,19 +349,6 @@ struct sedesc {
|
||||
unsigned long long kop; /* Known outgoing payload length (see above) */
|
||||
};
|
||||
|
||||
/* sc_app_ops describes the application layer's operations and notification
|
||||
* callbacks when I/O activity is reported and to use to perform shutr/shutw.
|
||||
* There are very few combinations in practice (strm/chk <-> none/mux/applet).
|
||||
*/
|
||||
struct sc_app_ops {
|
||||
void (*chk_rcv)(struct stconn *); /* chk_rcv function, may not be null */
|
||||
void (*chk_snd)(struct stconn *); /* chk_snd function, may not be null */
|
||||
void (*abort)(struct stconn *); /* abort function, may not be null */
|
||||
void (*shutdown)(struct stconn *); /* shutdown function, may not be null */
|
||||
int (*wake)(struct stconn *); /* data-layer callback to report activity */
|
||||
char name[8]; /* data layer name, zero-terminated */
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure describes the elements of a connection relevant to a stream
|
||||
*/
|
||||
@ -383,7 +370,6 @@ struct stconn {
|
||||
struct wait_event wait_event; /* We're in a wait list */
|
||||
struct sedesc *sedesc; /* points to the stream endpoint descriptor */
|
||||
enum obj_type *app; /* points to the applicative point (stream or check) */
|
||||
const struct sc_app_ops *app_ops; /* general operations used at the app layer */
|
||||
struct sockaddr_storage *src; /* source address (pool), when known, otherwise NULL */
|
||||
struct sockaddr_storage *dst; /* destination address (pool), when known, otherwise NULL */
|
||||
};
|
||||
|
||||
@ -57,6 +57,8 @@ void sc_destroy(struct stconn *sc);
|
||||
int sc_reset_endp(struct stconn *sc);
|
||||
|
||||
struct appctx *sc_applet_create(struct stconn *sc, struct applet *app);
|
||||
int sc_applet_process(struct stconn *sc);
|
||||
int sc_conn_process(struct stconn *sc);
|
||||
|
||||
void sc_conn_prepare_endp_upgrade(struct stconn *sc);
|
||||
void sc_conn_abort_endp_upgrade(struct stconn *sc);
|
||||
@ -349,16 +351,6 @@ static inline struct hstream *sc_hstream(const struct stconn *sc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns the name of the application layer's name for the stconn,
|
||||
* or "NONE" when none is attached.
|
||||
*/
|
||||
static inline const char *sc_get_data_name(const struct stconn *sc)
|
||||
{
|
||||
if (!sc->app_ops)
|
||||
return "NONE";
|
||||
return sc->app_ops->name;
|
||||
}
|
||||
|
||||
/* Returns non-zero if the stream connector's Rx path is blocked because of
|
||||
* lack of room in the input buffer. This usually happens after applets failed
|
||||
* to deliver data into the channel's buffer and reported it via sc_need_room().
|
||||
@ -460,7 +452,7 @@ static inline size_t se_nego_ff(struct sedesc *se, struct buffer *input, size_t
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = mux->nego_fastfwd(se->sc, input, count, flags);
|
||||
ret = CALL_MUX_WITH_RET(mux, nego_fastfwd(se->sc, input, count, flags));
|
||||
if (se->iobuf.flags & IOBUF_FL_FF_BLOCKED) {
|
||||
sc_ep_report_blocked_send(se->sc, 0);
|
||||
|
||||
@ -493,7 +485,7 @@ static inline size_t se_done_ff(struct sedesc *se)
|
||||
size_t to_send = se_ff_data(se);
|
||||
|
||||
BUG_ON(!mux->done_fastfwd);
|
||||
ret = mux->done_fastfwd(se->sc);
|
||||
ret = CALL_MUX_WITH_RET(mux, done_fastfwd(se->sc));
|
||||
if (ret) {
|
||||
/* Something was forwarded, unblock the zero-copy forwarding.
|
||||
* If all data was sent, report and send activity.
|
||||
@ -525,7 +517,7 @@ static inline size_t se_done_ff(struct sedesc *se)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
se->sc->bytes_out += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -130,20 +130,22 @@ struct notification {
|
||||
* on return.
|
||||
*/
|
||||
#define TASK_COMMON \
|
||||
struct { \
|
||||
unsigned int state; /* task state : bitfield of TASK_ */ \
|
||||
int tid; /* tid of task/tasklet. <0 = local for tasklet, unbound for task */ \
|
||||
struct task *(*process)(struct task *t, void *ctx, unsigned int state); /* the function which processes the task */ \
|
||||
void *context; /* the task's context */ \
|
||||
const struct ha_caller *caller; /* call place of last wakeup(); 0 on init, -1 on free */ \
|
||||
uint32_t wake_date; /* date of the last task wakeup */ \
|
||||
unsigned int calls; /* number of times process was called */ \
|
||||
TASK_DEBUG_STORAGE; \
|
||||
}
|
||||
unsigned int state; /* task state : bitfield of TASK_ */ \
|
||||
int tid; /* tid of task/tasklet. <0 = local for tasklet, unbound for task */ \
|
||||
struct task *(*process)(struct task *t, void *ctx, unsigned int state); /* the function which processes the task */ \
|
||||
void *context; /* the task's context */ \
|
||||
const struct ha_caller *caller; /* call place of last wakeup(); 0 on init, -1 on free */ \
|
||||
uint32_t wake_date; /* date of the last task wakeup */ \
|
||||
unsigned int calls; /* number of times process was called */ \
|
||||
TASK_DEBUG_STORAGE; \
|
||||
short last_run; /* 16-bit now_ms of last run */
|
||||
/* a 16- or 48-bit hole remains here and is used by task */
|
||||
|
||||
/* The base for all tasks */
|
||||
struct task {
|
||||
TASK_COMMON; /* must be at the beginning! */
|
||||
short nice; /* task prio from -1024 to +1024 */
|
||||
int expire; /* next expiration date for this task, in ticks */
|
||||
struct eb32_node rq; /* ebtree node used to hold the task in the run queue */
|
||||
/* WARNING: the struct task is often aliased as a struct tasklet when
|
||||
* it is NOT in the run queue. The tasklet has its struct list here
|
||||
@ -151,14 +153,12 @@ struct task {
|
||||
* ever reorder these fields without taking this into account!
|
||||
*/
|
||||
struct eb32_node wq; /* ebtree node used to hold the task in the wait queue */
|
||||
int expire; /* next expiration date for this task, in ticks */
|
||||
short nice; /* task prio from -1024 to +1024 */
|
||||
/* 16-bit hole here */
|
||||
};
|
||||
|
||||
/* lightweight tasks, without priority, mainly used for I/Os */
|
||||
struct tasklet {
|
||||
TASK_COMMON; /* must be at the beginning! */
|
||||
/* 48-bit hole here */
|
||||
struct list list;
|
||||
/* WARNING: the struct task is often aliased as a struct tasklet when
|
||||
* it is not in the run queue. The task has its struct rq here where
|
||||
|
||||
@ -121,6 +121,7 @@ enum tcpcheck_rule_type {
|
||||
/* Unused 0x000000A0..0x00000FF0 (reserved for future proto) */
|
||||
#define TCPCHK_RULES_TCP_CHK 0x00000FF0
|
||||
#define TCPCHK_RULES_PROTO_CHK 0x00000FF0 /* Mask to cover protocol check */
|
||||
#define TCPCHK_RULES_MAY_USE_SBUF 0x00001000 /* checks may try to use small buffers if possible for the request */
|
||||
|
||||
struct check;
|
||||
struct tcpcheck_connect {
|
||||
|
||||
@ -75,6 +75,41 @@ enum {
|
||||
/* we have 4 buffer-wait queues, in highest to lowest emergency order */
|
||||
#define DYNBUF_NBQ 4
|
||||
|
||||
/* execution context, for tracing resource usage or warning origins */
|
||||
enum thread_exec_ctx_type {
|
||||
TH_EX_CTX_NONE = 0, /* context not filled */
|
||||
TH_EX_CTX_OTHER, /* context only known by a generic pointer */
|
||||
TH_EX_CTX_INITCALL, /* the pointer is an initcall providing file:line */
|
||||
TH_EX_CTX_CALLER, /* the pointer is an ha_caller of the caller providing file:line etc */
|
||||
TH_EX_CTX_SMPF, /* directly registered sample fetch function, using .smpf_kwl */
|
||||
TH_EX_CTX_CONV, /* directly registered converter function, using .conv_kwl */
|
||||
TH_EX_CTX_FUNC, /* hopefully recognizable function/callback, using .pointer */
|
||||
TH_EX_CTX_ACTION, /* directly registered action function, using .action_kwl */
|
||||
TH_EX_CTX_FLT, /* filter whose config is in .flt_conf */
|
||||
TH_EX_CTX_MUX, /* mux whose mux_ops is in .mux_ops */
|
||||
TH_EX_CTX_TASK, /* task or tasklet whose function is in .task */
|
||||
TH_EX_CTX_APPLET, /* applet whose applet is in .applet */
|
||||
TH_EX_CTX_CLI_KWL, /* CLI keyword list, using .cli_kwl */
|
||||
};
|
||||
|
||||
struct thread_exec_ctx {
|
||||
enum thread_exec_ctx_type type;
|
||||
/* 32-bit hole here on 64-bit platforms */
|
||||
union {
|
||||
const void *pointer; /* generic pointer (for other) */
|
||||
const struct initcall *initcall; /* used with TH_EX_CTX_INITCALL */
|
||||
const struct ha_caller *ha_caller; /* used with TH_EX_CTX_CALLER */
|
||||
const struct sample_fetch_kw_list *smpf_kwl; /* used with TH_EX_CTX_SMPF */
|
||||
const struct sample_conv_kw_list *conv_kwl; /* used with TH_EX_CTX_CONV */
|
||||
const struct action_kw_list *action_kwl; /* used with TH_EX_CTX_ACTION */
|
||||
const struct flt_conf *flt_conf; /* used with TH_EX_CTX_FLTCONF */
|
||||
const struct mux_ops *mux_ops; /* used with TH_EX_CTX_MUX */
|
||||
const struct task *(*task)(struct task *, void *, unsigned int); /* used with TH_EX_CTX_TASK */
|
||||
const struct applet *applet; /* used with TH_EX_CTX_APPLET */
|
||||
const struct cli_kw_list *cli_kwl; /* used with TH_EX_CTX_CLI_KWL */
|
||||
};
|
||||
};
|
||||
|
||||
/* Thread group information. This defines a base and a count of global thread
|
||||
* IDs which belong to it, and which can be looked up into thread_info/ctx. It
|
||||
* is set up during parsing and is stable during operation. Thread groups start
|
||||
@ -172,8 +207,7 @@ struct thread_ctx {
|
||||
uint64_t curr_mono_time; /* latest system wide monotonic time (leaving poll) */
|
||||
|
||||
ulong lock_history; /* history of used locks, see thread.h for more details */
|
||||
|
||||
/* around 56 unused bytes here */
|
||||
struct thread_exec_ctx exec_ctx; /* current execution context when known, or NULL */
|
||||
|
||||
// fourth cache line here on 64 bits: accessed mostly using atomic ops
|
||||
ALWAYS_ALIGN(64);
|
||||
@ -199,6 +233,7 @@ struct thread_ctx {
|
||||
struct buffer *last_dump_buffer; /* Copy of last buffer used for a dump; may be NULL or invalid; for post-mortem only */
|
||||
unsigned long long total_streams; /* Total number of streams created on this thread */
|
||||
unsigned int stream_cnt; /* Number of streams attached to this thread */
|
||||
unsigned int rq_tot_peak; /* total run queue size last call */
|
||||
|
||||
// around 68 bytes here for shared variables
|
||||
|
||||
|
||||
@ -117,4 +117,42 @@ static inline void thread_set_pin_grp1(struct thread_set *ts, ulong mask)
|
||||
ts->rel[i] = 0;
|
||||
}
|
||||
|
||||
/* switches the current execution context to <ctx> and returns the previous one
|
||||
* so that this may even be used to save and restore. Setting EXEC_CTX_NONE
|
||||
* resets it. It's efficient because it uses a pair of registers on input and
|
||||
* output.
|
||||
*/
|
||||
static inline struct thread_exec_ctx switch_exec_ctx(const struct thread_exec_ctx ctx)
|
||||
{
|
||||
const struct thread_exec_ctx prev = th_ctx->exec_ctx;
|
||||
|
||||
th_ctx->exec_ctx = ctx;
|
||||
return prev;
|
||||
}
|
||||
|
||||
/* used to reset the execution context */
|
||||
#define EXEC_CTX_NONE ((struct thread_exec_ctx){ .type = 0, .pointer = NULL })
|
||||
|
||||
/* make an execution context from a type and a pointer */
|
||||
#define EXEC_CTX_MAKE(_type, _pointer) ((struct thread_exec_ctx){ .type = (_type), .pointer = (_pointer) })
|
||||
|
||||
/* execute expression <expr> under context <new_ctx> then restore the previous
|
||||
* one, and return the expression's return value.
|
||||
*/
|
||||
#define EXEC_CTX_WITH_RET(new_ctx, expr) ({ \
|
||||
const struct thread_exec_ctx __prev_ctx = switch_exec_ctx(new_ctx); \
|
||||
typeof(expr) __ret = (expr); \
|
||||
switch_exec_ctx(__prev_ctx); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
/* execute expression <expr> under context <new_ctx> then restore the previous
|
||||
* one. This one has no return value.
|
||||
*/
|
||||
#define EXEC_CTX_NO_RET(new_ctx, expr) do { \
|
||||
const struct thread_exec_ctx __prev_ctx = switch_exec_ctx(new_ctx); \
|
||||
do { expr; } while (0); \
|
||||
switch_exec_ctx(__prev_ctx); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _HAPROXY_TINFO_H */
|
||||
|
||||
@ -1147,10 +1147,13 @@ void dump_hex(struct buffer *out, const char *pfx, const void *buf, int len, int
|
||||
int may_access(const void *ptr);
|
||||
const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *addr);
|
||||
const void *resolve_dso_name(struct buffer *buf, const char *pfx, const void *addr);
|
||||
void make_tar_header(char *output, const char *pfx, const char *fname, const char *link, size_t size, mode_t mode);
|
||||
int load_file_into_tar(char **storage, size_t *size, const char *pfx, const char *fname, const char *input, const char *link);
|
||||
const char *get_exec_path(void);
|
||||
void *get_sym_curr_addr(const char *name);
|
||||
void *get_sym_next_addr(const char *name);
|
||||
int dump_libs(struct buffer *output, int with_addr);
|
||||
void collect_libs(void);
|
||||
|
||||
/* Note that this may result in opening libgcc() on first call, so it may need
|
||||
* to have been called once before chrooting.
|
||||
@ -1324,6 +1327,62 @@ static inline uint statistical_prng_range(uint range)
|
||||
return mul32hi(statistical_prng(), range ? range - 1 : 0);
|
||||
}
|
||||
|
||||
/* The functions below are used to hash one or two pointers together and reduce
|
||||
* the result to fit into a given number of bits. The first part is made of a
|
||||
* multiplication (and possibly an addition) by one or two prime numbers giving
|
||||
* a 64-bit number whose center bits are the most distributed, and the second
|
||||
* part will reuse this value and return a mix of the most variable bits that
|
||||
* fits in the requested size. The most convenient approach is to directly
|
||||
* call ptr_hash() / ptr2_hash(), though for some specific use cases where a
|
||||
* second value could be useful, one may prefer to call the lower level
|
||||
* operations instead.
|
||||
*/
|
||||
|
||||
/* reduce a 64-bit pointer hash to <bits> bits */
|
||||
static forceinline uint _ptr_hash_reduce(unsigned long long x, const int bits)
|
||||
{
|
||||
if (!bits)
|
||||
return 0;
|
||||
|
||||
if (sizeof(long) == 4)
|
||||
x ^= x >> 32;
|
||||
else
|
||||
x >>= 31 - (bits + 1) / 2;
|
||||
return x & (~0U >> (-bits & 31));
|
||||
}
|
||||
|
||||
/* single-pointer version, low-level, use ptr_hash() instead */
|
||||
static forceinline ullong _ptr_hash(const void *p)
|
||||
{
|
||||
unsigned long long x = (unsigned long)p;
|
||||
|
||||
x *= 0xacd1be85U;
|
||||
return x;
|
||||
}
|
||||
|
||||
/* two-pointer version, low-level, use ptr2_hash() instead */
|
||||
static forceinline ullong _ptr2_hash(const void *p1, const void *p2)
|
||||
{
|
||||
unsigned long long x = (unsigned long)p1;
|
||||
unsigned long long y = (unsigned long)p2;
|
||||
|
||||
x *= 0xacd1be85U;
|
||||
y *= 0x9d28e4e9U;
|
||||
return x ^ y;
|
||||
}
|
||||
|
||||
/* two-pointer plus arg version, low-level, use ptr2_hash_arg() instead */
|
||||
static forceinline ullong _ptr2_hash_arg(const void *p1, const void *p2, ulong arg)
|
||||
{
|
||||
unsigned long long x = (unsigned long)p1;
|
||||
unsigned long long y = (unsigned long)p2;
|
||||
|
||||
x *= 0xacd1be85U;
|
||||
x += arg;
|
||||
y *= 0x9d28e4e9U;
|
||||
return x ^ y;
|
||||
}
|
||||
|
||||
/* returns a hash on <bits> bits of pointer <p> that is suitable for being used
|
||||
* to compute statistic buckets, in that it's fast and reasonably distributed
|
||||
* thanks to mixing the bits via a multiplication by a prime number and using
|
||||
@ -1337,17 +1396,7 @@ static inline uint statistical_prng_range(uint range)
|
||||
*/
|
||||
static forceinline uint ptr_hash(const void *p, const int bits)
|
||||
{
|
||||
unsigned long long x = (unsigned long)p;
|
||||
|
||||
if (!bits)
|
||||
return 0;
|
||||
|
||||
x *= 0xacd1be85U;
|
||||
if (sizeof(long) == 4)
|
||||
x ^= x >> 32;
|
||||
else
|
||||
x >>= 31 - (bits + 1) / 2;
|
||||
return x & (~0U >> (-bits & 31));
|
||||
return _ptr_hash_reduce(_ptr_hash(p), bits);
|
||||
}
|
||||
|
||||
/* Same as above but works on two pointers. It will return the same values
|
||||
@ -1355,20 +1404,15 @@ static forceinline uint ptr_hash(const void *p, const int bits)
|
||||
*/
|
||||
static forceinline uint ptr2_hash(const void *p1, const void *p2, const int bits)
|
||||
{
|
||||
unsigned long long x = (unsigned long)p1;
|
||||
unsigned long long y = (unsigned long)p2;
|
||||
return _ptr_hash_reduce(_ptr2_hash(p1, p2), bits);
|
||||
}
|
||||
|
||||
if (!bits)
|
||||
return 0;
|
||||
|
||||
x *= 0xacd1be85U;
|
||||
y *= 0x9d28e4e9U;
|
||||
x ^= y;
|
||||
if (sizeof(long) == 4)
|
||||
x ^= x >> 32;
|
||||
else
|
||||
x >>= 33 - bits / 2;
|
||||
return x & (~0U >> (-bits & 31));
|
||||
/* Same as above but works on two pointers and a long argument. It will return
|
||||
* the same values if the second pointer is NULL.
|
||||
*/
|
||||
static forceinline uint ptr2_hash_arg(const void *p1, const void *p2, ulong arg, const int bits)
|
||||
{
|
||||
return _ptr_hash_reduce(_ptr2_hash_arg(p1, p2, arg), bits);
|
||||
}
|
||||
|
||||
|
||||
@ -1499,4 +1543,6 @@ void ha_freearray(char ***array);
|
||||
|
||||
void ha_memset_s(void *s, int c, size_t n);
|
||||
|
||||
void chunk_append_thread_ctx(struct buffer *output, const struct thread_exec_ctx *ctx, const char *pfx, const char *sfx);
|
||||
|
||||
#endif /* _HAPROXY_TOOLS_H */
|
||||
|
||||
@ -190,7 +190,8 @@ void trace_no_cb(enum trace_level level, uint64_t mask, const struct trace_sourc
|
||||
|
||||
void trace_register_source(struct trace_source *source);
|
||||
|
||||
int trace_parse_cmd(const char *arg_src, char **errmsg);
|
||||
int trace_add_cmd(const char *arg_src, char **errmsg);
|
||||
void trace_parse_cmds(void);
|
||||
|
||||
/* return a single char to describe a trace state */
|
||||
static inline char trace_state_char(enum trace_state st)
|
||||
|
||||
13
reg-tests/jwt/ec_decrypt.crt
Normal file
13
reg-tests/jwt/ec_decrypt.crt
Normal file
@ -0,0 +1,13 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICBTCCAaugAwIBAgIUN+Ne3W00v5RwrlIBqhub+WHgq3kwCgYIKoZIzj0EAwIw
|
||||
VzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
|
||||
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEQMA4GA1UEAwwHZm9vLmJhcjAgFw0yNjAy
|
||||
MjYxMDM5MjhaGA8yMDUzMDcxNDEwMzkyOFowVzELMAkGA1UEBhMCQVUxEzARBgNV
|
||||
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
|
||||
ZDEQMA4GA1UEAwwHZm9vLmJhcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGwE
|
||||
ope2KbYUXi5bSLGiQmkaxO17SwkVbTqRrXTztIx99xj9qfSrVKFqN3lnaNDXAclG
|
||||
GnfmU/j7xsEocZdYmPujUzBRMB0GA1UdDgQWBBQZSL9UUhRofXo5X9BoS0XBug4i
|
||||
DzAfBgNVHSMEGDAWgBQZSL9UUhRofXo5X9BoS0XBug4iDzAPBgNVHRMBAf8EBTAD
|
||||
AQH/MAoGCCqGSM49BAMCA0gAMEUCIQDFDrvj5p9R7wmMRoJGUuEJu7I2xYtXDcOP
|
||||
lLE0quJtvwIgWW7vuM3B+ruCslhIrMMqD+DYeguxAxi+aHRVMnBig/c=
|
||||
-----END CERTIFICATE-----
|
||||
5
reg-tests/jwt/ec_decrypt.key
Normal file
5
reg-tests/jwt/ec_decrypt.key
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6qbbYYII1zqqmlDH
|
||||
hTwJt+JYBe+ELI02yAecAx+nD4yhRANCAARsBKKXtim2FF4uW0ixokJpGsTte0sJ
|
||||
FW06ka1087SMffcY/an0q1Shajd5Z2jQ1wHJRhp35lP4+8bBKHGXWJj7
|
||||
-----END PRIVATE KEY-----
|
||||
@ -16,7 +16,7 @@ feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
|
||||
feature cmd "command -v socat"
|
||||
feature ignore_unknown_macro
|
||||
|
||||
server s1 -repeat 27 {
|
||||
server s1 -repeat 40 {
|
||||
rxreq
|
||||
txresp
|
||||
} -start
|
||||
@ -542,3 +542,44 @@ client c27 -connect ${h1_mainfe_sock} {
|
||||
expect resp.http.x-jwt-verify-RS256-var2 == "1"
|
||||
|
||||
} -run
|
||||
|
||||
client c28 -connect ${h1_mainfe_sock} {
|
||||
# Token content : {"alg":"none"}
|
||||
# {"iss":"joe", "exp":1300819380, "http://example.com/is_root":true}
|
||||
txreq -url "/none" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "1"
|
||||
} -run
|
||||
|
||||
client c29 -connect ${h1_mainfe_sock} {
|
||||
# Invalid Token : too many subparts
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.aa.aa.aa"
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
|
||||
# Invalid Token : too many subparts
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.aa.aa."
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
|
||||
# Invalid Token : too few subparts
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.aa"
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
|
||||
# Invalid Token : no signature but alg different than "none"
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "RS256"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
} -run
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(3.4-dev2)'"
|
||||
feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && openssl_version_atleast(1.1.1)'"
|
||||
feature ignore_unknown_macro
|
||||
|
||||
server s1 -repeat 20 {
|
||||
server s1 -repeat 30 {
|
||||
rxreq
|
||||
txresp
|
||||
} -start
|
||||
@ -53,6 +53,10 @@ haproxy h1 -conf {
|
||||
# { "kty": "RSA", "e": "AQAB", "n": "wsqJbopx18NQFYLYOq4ZeMSE89yGiEankUpf25yV8QqroKUGrASj_OeqTWUjwPGKTN1vGFFuHYxiJeAUQH2qQPmg9Oqk6-ATBEKn9COKYniQ5459UxCwmZA2RL6ufhrNyq0JF3GfXkjLDBfhU9zJJEOhknsA0L_c-X4AI3d_NbFdMqxNe1V_UWAlLcbKdwO6iC9fAvwUmDQxgy6R0DC1CMouQpenMRcALaSHar1cm4K-syoNobv3HEuqgZ3s6-hOOSqauqAO0GUozPpaIA7OeruyRl5sTWT0r-iz39bchID2bIKtcqLiFcSYPLBcxmsaQCqRlGhmv6stjTCLV1yT9w", "kid": "ff3c5c96-392e-46ef-a839-6ff16027af78", "d": "b9hXfQ8lOtw8mX1dpqPcoElGhbczz_-xq2znCXQpbBPSZBUddZvchRSH5pSSKPEHlgb3CSGIdpLqsBCv0C_XmCM9ViN8uqsYgDO9uCLIDK5plWttbkqA_EufvW03R9UgIKWmOL3W4g4t-C2mBb8aByaGGVNjLnlb6i186uBsPGkvaeLHbQcRQKAvhOUTeNiyiiCbUGJwCm4avMiZrsz1r81Y1Z5izo0ERxdZymxM3FRZ9vjTB-6DtitvTXXnaAm1JTu6TIpj38u2mnNLkGMbflOpgelMNKBZVxSmfobIbFN8CHVc1UqLK2ElsZ9RCQANgkMHlMkOMj-XT0wHa3VBUQ", "p": "8mgriveKJAp1S7SHqirQAfZafxVuAK_A2QBYPsAUhikfBOvN0HtZjgurPXSJSdgR8KbWV7ZjdJM_eOivIb_XiuAaUdIOXbLRet7t9a_NJtmX9iybhoa9VOJFMBq_rbnbbte2kq0-FnXmv3cukbC2LaEw3aEcDgyURLCgWFqt7M0", "q": "zbbTv5421GowOfKVEuVoA35CEWgl8mdasnEZac2LWxMwKExikKU5LLacLQlcOt7A6n1ZGUC2wyH8mstO5tV34Eug3fnNrbnxFUEE_ZB_njs_rtZnwz57AoUXOXVnd194seIZF9PjdzZcuwXwXbrZ2RSVW8if_ZH5OVYEM1EsA9M", "dp": "1BaIYmIKn1X3InGlcSFcNRtSOnaJdFhRpotCqkRssKUx2qBlxs7ln_5dqLtZkx5VM_UE_GE7yzc6BZOwBxtOftdsr8HVh-14ksSR9rAGEsO2zVBiEuW4qZf_aQM-ScWfU--wcczZ0dT-Ou8P87Bk9K9fjcn0PeaLoz3WTPepzNE", "dq": "kYw2u4_UmWvcXVOeV_VKJ5aQZkJ6_sxTpodRBMPyQmkMHKcW4eKU1mcJju_deqWadw5jGPPpm5yTXm5UkAwfOeookoWpGa7CvVf4kPNI6Aphn3GBjunJHNpPuU6w-wvomGsxd-NqQDGNYKHuFFMcyXO_zWXglQdP_1o1tJ1M-BM", "qi": "j94Ens784M8zsfwWoJhYq9prcSZOGgNbtFWQZO8HP8pcNM9ls7YA4snTtAS_B4peWWFAFZ0LSKPCxAvJnrq69ocmEKEk7ss1Jo062f9pLTQ6cnhMjev3IqLocIFt5Vbsg_PWYpFSR7re6FRbF9EYOM7F2-HRv1idxKCWoyQfBqk" }
|
||||
load crt rsa_oeap.pem key rsa_oeap.key jwt on
|
||||
|
||||
# Private key built out of the following JWK:
|
||||
# {"crv":"P-256","d":"6qbbYYII1zqqmlDHhTwJt-JYBe-ELI02yAecAx-nD4w","kty":"EC","x":"bASil7YpthReLltIsaJCaRrE7XtLCRVtOpGtdPO0jH0","y":"9xj9qfSrVKFqN3lnaNDXAclGGnfmU_j7xsEocZdYmPs"}
|
||||
load crt ec_decrypt.crt key ec_decrypt.key jwt on
|
||||
|
||||
listen main-fe
|
||||
bind "fd@${mainfe}"
|
||||
|
||||
@ -84,6 +88,11 @@ haproxy h1 -conf {
|
||||
|
||||
http-request set-var(txn.decrypted) var(txn.jwe),jwt_decrypt_cert(txn.pem)
|
||||
|
||||
.if ssllib_name_startswith(AWS-LC)
|
||||
acl aws_unmanaged var(txn.jwe),jwt_header_query('$.alg') -m end "A128KW" -m end "A192KW"
|
||||
http-request set-var(txn.decrypted) str("AWS-LC UNMANAGED") if aws_unmanaged
|
||||
.endif
|
||||
|
||||
http-after-response set-header X-Decrypted %[var(txn.decrypted)]
|
||||
server s1 ${s1_addr}:${s1_port}
|
||||
|
||||
@ -95,7 +104,7 @@ haproxy h1 -conf {
|
||||
http-request set-var(txn.decrypted) var(txn.jwe),jwt_decrypt_jwk(txn.jwk)
|
||||
|
||||
.if ssllib_name_startswith(AWS-LC)
|
||||
acl aws_unmanaged var(txn.jwe),jwt_header_query('$.alg') -m str "A128KW"
|
||||
acl aws_unmanaged var(txn.jwe),jwt_header_query('$.alg') -m end "A128KW" -m end "A192KW"
|
||||
http-request set-var(txn.decrypted) str("AWS-LC UNMANAGED") if aws_unmanaged
|
||||
.endif
|
||||
|
||||
@ -262,3 +271,53 @@ client c8 -connect ${h1_mainfe_sock} {
|
||||
expect resp.http.x-decrypted == ""
|
||||
} -run
|
||||
|
||||
|
||||
# ECDH-ES
|
||||
client c9 -connect ${h1_mainfe_sock} {
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiAiRUNESC1FUyIsICJlbmMiOiAiQTI1NkdDTSIsICJlcGsiOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0yNTYiLCAieCI6ICJZVUg1VlJweURCb3FiUjVCUWlSMGV4anJ0bDdLb24yeTZkejFnM3NuNzZJIiwgInkiOiAiZld2RHFHb3pYNjJnMnRTS19oSkctWkVKNkFCcFhYTS1Tc1hPeE5KUXFtUSJ9fQ..0tN70AQ3P_4uEV4t.zkv7KfnUlDTKjJ82zKCMK_z7OEFk_euXGuJemShf8mnOeEUE4UN8wS5cRJzMQWxcY9d3dIvUCYx0HhzeoXnKqnkEU6be659IVtKpqtceLYKcIkpjj0XiaEalVqIKKXTU2NG2ldNsYwnEDN_XxMnIUPFOy3yJqpOfjf8v98ABYuTWfJVwk3tK9vYCj-ScCf2NK7cEIti_09VCsxMg7z0kvco5UaTXvDjEbPhj_EVfHoPlmDE6EuaO5OX5t3reOoJ1vsM2PEpADiYfmvSZxeWAmmtAH7cvrRIUCcy4Q5pNczh1Pmt0y-uJKtme16YWq8PxVtnb7lY9HDTuPeaMVqvMV6PlQ9vnfsirjpz72qx3ArAeXkIGJsPOGKfgCoW6sAWHQxCzvq8ek7zOaqTAo169PSdtxfBL4MJWxoLg38pODy4cjEGR71YYirthejEMgRs7G1A8ksxgs2bkYGInunUD_iAWkQzxYZhFlLRntWP1ikOKmx9gbqR6K9UiqCK1UG4NXF3o4OV34m-jw-cXMDF2JkekVK2-rhxTbXmqP-VhDrkQ2ANdk7fTW9elFYNisVzE1QjdClMKGhO1fdKiSJ9xSPo3W6pMuquYYN-XT1fLiu3GDtO4ELZWVdwmiucsxv9H2jzPwbhvbvlXwXsmyCBtvumcEUbiYCOIYvlddhTGjZHplvDU73O5SkxUYJTYh7H0DcSiZ-6tcWdRCs605xVZMJ_X91_gZ1tb2_df73lYT_tVo39kw78m3GVFBeK2Zy4JeLheo0fHE7n8lg13uwG77SHwrWSV61KKWhBPZR0bWGi8YvVHnqX0GWklIjpqjbIjYAk4baFv4MO4OvEkPxnGm64NNZWrGEA0U8eEHCgjF1ZagQFNb674Crgd-tRA0QPEAOc9NsnlK1Q-47KIgqNbwoc3VpbpHNLVJT4aKWV5q187YNxarbpeDqguh75M9AgbpT5bSDFhjF83f1kiEDgLdNTkAd-CPAzgtzaEAfxD1K4ViZZZ2DqXgw0PFTFZAWrWqv8Ydi61r5MJ.Srleju8Bifrc_6bqFPUF_w" \
|
||||
-hdr "X-JWK: {\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"DxaAKzwruXJh4IkdieycIJER6w8M1TYMCV3qOa-l9CM\",\"y\":\"_kRI1aD7-PMFwhUpXmcRzw6hALF_xdKwADuKOM-xsak\",\"d\":\"SOu5eRc40yn5yVrg069VjWNH4wsoErN8_AxmH4cI88s\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted == "Sed ut perspiciatis unde omnis iste natus error sit voluptatem doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. porro quisquam est, qui dolorem ipsum quia dolor sit amet, adipisci velit, sed quia non numquam eius modi tempora incidunt ut dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in voluptate velit esse quam nihil molestiae consequatur, vel illum qui eum fugiat quo voluptas nulla pariatur?"
|
||||
|
||||
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImVwayI6eyJjcnYiOiJQLTUyMSIsImt0eSI6IkVDIiwieCI6IkFYelVIT0hFdXU1RzlxSHBQOGdXelBub3FQNUpSVmNrYWZGb1VxV0FUQVd6b3FUN0tmX3V5WW5HSElaNkVXdUx0U0NrUnREUHE4WDJlcV9lSDc0QVRQZGwiLCJ5IjoiQVRxelFNWV9PUE9lWUZYNlpGN0l4ZkgwQ2x6RlRjZjVhaE1UTERmMHJYRkczNmdHN1lDMjR1Q2hrR2ZoZHlBT1RRY09kN1ZyQlM4clNZeC03R0hLbzNWNSJ9fQ..kTaw9v3MWCN78jq5OXTWZA.w4o_19dlHEFEhQ0GXI08x-vJnImL_mtZ_oHXTCvfCj_aCEDL4UuiaAU7-yvtM60G3HjNO6TTvmCdvHOTz6Ynrg.H-EbBpTyi5YXNT5DHSFiNBeBcdjmClR_LDARvak4qng" \
|
||||
-hdr "X-JWK: {\"alg\":\"ECDH-ES\",\"crv\":\"P-521\",\"d\":\"AVBp1yn67_t0C8WfJnrhZsgy4TDkA9XktZnwAHcCTUMWTBCURXOjCNCIaCyE65xzIQbZUc9rO-B93XKFO81u8myd\",\"key_ops\":[\"wrapKey\",\"unwrapKey\"],\"kty\":\"EC\",\"x\":\"AByuEl5P9ledNRyj4EjTtQwDcsIYpbNzUqjri5o8GPGLzeTWUzjDBVt1ZyxKfK8VMVQbj8sIrBHncYUqM1Re3pSA\",\"y\":\"AW171IiyQSWx95A9uT1m76XPcAss3jeE7lHgw8mU7yIxSi_SItDYFixJ5Xtf2Vu2BLlmpR0on6VV1UUNIyPk6qwb\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted == "Random test message for ECDH-ES encrypted tokens"
|
||||
|
||||
txreq -url "/pem" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6InhEcUZveF9oR3Q5VjZWSWZjRUpaU1VVTm1uT0V5Qk1BYzZybHlOV09lcjgiLCJ5IjoiUVZmdkstcVJ0V0J1Uk9XVzRnMmlVampqMFN3U1BzYjB6ZE10R0c2czBFUSJ9fQ..0ykoqdP2WMKra2VugMQMzg.dyCI6QGNIf-x4n0DIaXgVnGtoSCOD3sOX7I01djrFdNRRSmPnITcQiJn1lw1LbiZyqZxOLf_mJHw7BRrcgPxBG6gsP3oFBnLeXllcD6kuLtllVofaPDEKdr66W9dp6Cr.002j4NUlGTYz8d_0mTM38A" \
|
||||
-hdr "X-PEM: ${testdir}/ec_decrypt.crt"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted == "Random test message for ECDH-ES encrypted token (with some extra padding for good measure)"
|
||||
|
||||
} -run
|
||||
|
||||
# ECDH-ES+A___KW
|
||||
client c10 -connect ${h1_mainfe_sock} {
|
||||
|
||||
# ECDH-ES+A128KW
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJtc2poQktWNW5oNnBjdjhoRnR0UDlFVXRzaURzWG83T3RCekVZYkVJM1EwIiwieSI6IloxQ3FPQlEya1RNR1lENWdMUWJCaHB0MzRKRkR3dW5TX2ZzSmhsMlc1OWcifX0.5l7YaATvAWFJnWK_HsBPmawJ0RMqrkiwyZ9xAuiYCFSiqWWSr8D82A.0sa1s5V2RcDf0FW6hA1lig.z2DVLxtHeY1fPp6dJHiHEuHLVIQHQ10GfYXeFxwNE7JGyto-D3K1elHQn0Yq4Pitaheja21gnXkJajXhOA0rwQ.YmpToFWmj8XQrXMeXTa9eQ" \
|
||||
-hdr "X-JWK: {\"crv\":\"P-256\",\"d\":\"6qbbYYII1zqqmlDHhTwJt-JYBe-ELI02yAecAx-nD4w\",\"kty\":\"EC\",\"x\":\"bASil7YpthReLltIsaJCaRrE7XtLCRVtOpGtdPO0jH0\",\"y\":\"9xj9qfSrVKFqN3lnaNDXAclGGnfmU_j7xsEocZdYmPs\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted ~ "(Random test message for ECDH-ES encrypted tokens|AWS-LC UNMANAGED)"
|
||||
|
||||
txreq -url "/pem" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJKeUJzcDZtZjVCMWROYUk0ZGppX2pnMm9NdFRRQUd4akxTekdGbGJ0dXZZIiwieSI6IlFfZnlBUmZiMGpjWXFtSTZUNEdXTXA1U2dGYXZiQ3lGUGF3OHhab1BIYzAifX0.JKrKKRF9QdxUyv0KX-MV11eHpP2Vz8Amdh8j3ipd_QP57jkN-OWRCQ.CjpnSVRVV51C10cUwCTaXA.bliaBk7mGYIOGdvgiMg481iC8GiOarRrjIkUgEBuqiSJENmOi90IXgnoVp4qQdi70bJVBNuCYP7Q9sLzZc4X2g.C_TCuAfAH5020v-NdR91BA" \
|
||||
-hdr "X-PEM: ${testdir}/ec_decrypt.crt"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted ~ "(Random test message for ECDH-ES\\+A128KW encrypted token|AWS-LC UNMANAGED)"
|
||||
|
||||
|
||||
# ECDH-ES+A192KW
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsiY3J2IjoiUC0zODQiLCJrdHkiOiJFQyIsIngiOiJDcTd3Y0MzUm92VFRZSTMzLU9DcXBocjFlN1NzeEZWY0dOQXhEOEpWZHBRQmROaGg3Z2dLNTJKVkJ1RF9uZXVHIiwieSI6IjlaLU1MV09TQ3VZd0JZVTEtcTd2YUREWUZ1WFhqc1EwSmxpWllLVmdOU0dqVHVLY3VXQnJHemV2RzZEeGgyRHQifX0.75lt6Ixq6UhlN8uiaEphy8SiqEVsuD4Rc3QbFcmP7MJUTyt15LcZ3y-M7TJeNBh3Ajy_6K2WooU.cO9tUaQ2eVo0tIuOqb5_Bw.HQ6DqnLhW2Ad0c78WFGgwCStefYdL37xmh2Fa2mCsVNW5q0K3-xeDHYuIP9Q5xBYEY70U6wV5a0iVN87ii_iMA.feLteQh1ickYVJ2ZZ2whoVzNGRHgUpjp" \
|
||||
-hdr "X-JWK: {\"alg\":\"ECDH-ES+A192KW\",\"crv\":\"P-384\",\"d\":\"pj6xIezfwtUakkkLtbRQ9FmN6uN1YJ-TSBkWn4awuDfWiHgqpQHA7_L95Hjks1cK\",\"key_ops\":[\"wrapKey\",\"unwrapKey\"],\"kty\":\"EC\",\"x\":\"JO3ojbUYOzoSb-7lAy-c7VhDIjhEtg4zrPn_NJKuGhat-cuI1c4LvOj3n8p3j4bn\",\"y\":\"CA3i4pN7t6liWxQXyxdDp9t79B8uWuubGADJuGn_2_yl6pufhnQ30OBA590fOtEm\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted ~ "(Random test message for ECDH-ES\\+A192KW encrypted tokens|AWS-LC UNMANAGED)"
|
||||
|
||||
|
||||
# ECDH-ES+A256KW
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsiY3J2IjoiUC01MjEiLCJrdHkiOiJFQyIsIngiOiJBTFZuZXN6Tl93WVJSWVYtblp3dy1sSkVDTXB2eE1iSENXX3BjY3EyWlF2eFdsNzVKdm5TM3lKbjgzcTE1MlpnWU4zTTB4SUhzQmw1empWZS02OGR4TThwIiwieSI6IkFUX2pGel94RGt0VFY4WWYzZlo1MnRvbE5QWkwwNXlwa0dVTThPWFRNZTBaaVNfYnIzaS0xNHFlWG1OcjA3TFFjNUZMX1VTQkE5WmlyWGRaZkVLUnFqNmEifX0.MqGFvMzpIlwQHeXgPucBkXmS2BaXr2ByUugzD31XrPtxwlWw96vOmfcjSHvda2FGJ1u6InaMMVZMMp75P6AF0kvk8vuM7QF2.kHYblcqwHgXv0xRQrLHwoA.gwFUyTx3RRHWvmqyUL5N6W8HcwbNc1hPTImQPoCNPv6rkhzV1obikVj7sNuTh3Po0nBu2QCKrt-GjJTlD4Q5kw.Q_YZWSkVVxv1rcpySgENN3ZPp-chIYoCGC070kkqiXc" \
|
||||
-hdr "X-JWK: {\"alg\":\"ECDH-ES+A256KW\",\"crv\":\"P-521\",\"d\":\"AGGLpIzSL1jE34wGa-owWCVt2rgk8j3jqh33QQFKwYCJ9abp3vROyQ-dNv6j6PjrnF1EFyY9dDzChNpWmzoOZAp3\",\"key_ops\":[\"wrapKey\",\"unwrapKey\"],\"kty\":\"EC\",\"x\":\"AD0EIUE6Bt_TDcyOPM6VchRocp7AFSeVd6XkVALWf8AFebeMgKIvJsCsGeRdPTO3vWWrR5AOvvpiBfurb9M9Tus-\",\"y\":\"AOeI5d0iF463g3DolhmVFn6MWk764ONuXRexLApjN-Q6_RkcnCieRSZzqqSPMYuEn-N3i4aYfiEPZV0jk8oZKQMQ\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted == "Random test message for ECDH-ES+A256KW encrypted tokens"
|
||||
|
||||
} -run
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
# show-backports -q -m -r hapee-r2 hapee-r1
|
||||
|
||||
|
||||
USAGE="Usage: ${0##*/} [-q] [-H] [-m] [-u] [-r reference] [-l logexpr] [-s subject] [-b base] {branch|range} [...] [-- file*]"
|
||||
USAGE="Usage: ${0##*/} [-q] [-H] [-m] [-u] [-L] [-r reference] [-l logexpr] [-s subject] [-b base] {branch|range} [...] [-- file*]"
|
||||
BASES=( )
|
||||
BRANCHES=( )
|
||||
REF=
|
||||
@ -39,6 +39,7 @@ SUBJECT=
|
||||
MISSING=
|
||||
UPSTREAM=
|
||||
BODYHASH=
|
||||
SINCELAST=
|
||||
|
||||
die() {
|
||||
[ "$#" -eq 0 ] || echo "$*" >&2
|
||||
@ -70,7 +71,7 @@ dump_commit_matrix() {
|
||||
count=0
|
||||
# now look up commits
|
||||
while read ref subject; do
|
||||
if [ -n "$MISSING" -a "${subject:0:9}" = "[RELEASE]" ]; then
|
||||
if [ -n "$MISSING" -o -n "$SINCELAST" ] && [ "${subject:0:9}" = "[RELEASE]" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
@ -153,6 +154,7 @@ while [ -n "$1" -a -z "${1##-*}" ]; do
|
||||
-m) MISSING=1 ; shift ;;
|
||||
-u) UPSTREAM=1 ; shift ;;
|
||||
-H) BODYHASH=1 ; shift ;;
|
||||
-L) SINCELAST=1 ; shift ;;
|
||||
-h|--help) quit "$USAGE" ;;
|
||||
*) die "$USAGE" ;;
|
||||
esac
|
||||
@ -255,7 +257,7 @@ if [ -z "$BASE" -a -n "$MISSING" ]; then
|
||||
fi
|
||||
|
||||
if [ -z "$BASE" ]; then
|
||||
err "Warning! No base specified, looking for common ancestor."
|
||||
[ "$QUIET" != "" ] || err "Warning! No base specified, looking for common ancestor."
|
||||
BASE=$(git merge-base --all "$REF" "${BRANCHES[@]}")
|
||||
if [ -z "$BASE" ]; then
|
||||
die "Couldn't find a common ancestor between these branches"
|
||||
@ -297,9 +299,23 @@ dump_commit_matrix | column -t | \
|
||||
(
|
||||
left_commits=( )
|
||||
right_commits=( )
|
||||
since_last=( )
|
||||
last_bkp=$BASE
|
||||
while read line; do
|
||||
# append the subject at the end of the line
|
||||
set -- $line
|
||||
if [ -n "$SINCELAST" ]; then
|
||||
if [ "${line::1}" = ":" ]; then
|
||||
continue
|
||||
fi
|
||||
if [ "$2" != "-" ]; then
|
||||
last_bkp="$1"
|
||||
since_last=( )
|
||||
else
|
||||
since_last[${#since_last[@]}]="$1"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
echo -n "$line "
|
||||
if [ "${line::1}" = ":" ]; then
|
||||
echo "---- Subject ----"
|
||||
@ -315,7 +331,14 @@ dump_commit_matrix | column -t | \
|
||||
right_commits[${#right_commits[@]}]="$comm"
|
||||
fi
|
||||
done
|
||||
if [ -n "$MISSING" -a ${#left_commits[@]} -eq 0 ]; then
|
||||
if [ -n "$SINCELAST" -a ${#since_last[@]} -eq 0 ]; then
|
||||
echo "No new commit upstream since last commit $last_bkp."
|
||||
elif [ -n "$SINCELAST" ]; then
|
||||
echo "Found ${#since_last[@]} commit(s) added to branch $REF since last backported commit $last_bkp:"
|
||||
echo
|
||||
echo " git cherry-pick -sx ${since_last[@]}"
|
||||
echo
|
||||
elif [ -n "$MISSING" -a ${#left_commits[@]} -eq 0 ]; then
|
||||
echo "No missing commit to apply."
|
||||
elif [ -n "$MISSING" ]; then
|
||||
echo
|
||||
|
||||
60
src/acme.c
60
src/acme.c
@ -15,6 +15,7 @@
|
||||
#include <haproxy/acme-t.h>
|
||||
|
||||
#include <haproxy/base64.h>
|
||||
#include <haproxy/intops.h>
|
||||
#include <haproxy/cfgparse.h>
|
||||
#include <haproxy/cli.h>
|
||||
#include <haproxy/errors.h>
|
||||
@ -266,7 +267,6 @@ static int cfg_parse_acme(const char *file, int linenum, char **args, int kwm)
|
||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||
|
||||
if (strcmp(args[0], "acme") == 0) {
|
||||
struct acme_cfg *tmp_acme = acme_cfgs;
|
||||
|
||||
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
||||
goto out;
|
||||
@ -292,7 +292,7 @@ static int cfg_parse_acme(const char *file, int linenum, char **args, int kwm)
|
||||
* name */
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
ha_alert("parsing [%s:%d]: acme section '%s' already exists (%s:%d).\n",
|
||||
file, linenum, args[1], tmp_acme->filename, tmp_acme->linenum);
|
||||
file, linenum, args[1], cur_acme->filename, cur_acme->linenum);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1188,7 +1188,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
}
|
||||
/* get the next retry timing */
|
||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||
ctx->retryafter = atol(hdr->v.ptr);
|
||||
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1261,7 +1261,7 @@ int acme_res_chkorder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
}
|
||||
/* get the next retry timing */
|
||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||
ctx->retryafter = atol(hdr->v.ptr);
|
||||
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1344,7 +1344,6 @@ int acme_req_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
csr->data = ret;
|
||||
|
||||
chunk_printf(req_in, "{ \"csr\": \"%.*s\" }", (int)csr->data, csr->area);
|
||||
OPENSSL_free(data);
|
||||
|
||||
|
||||
if (acme_jws_payload(req_in, ctx->nonce, ctx->finalize, ctx->cfg->account.pkey, ctx->kid, req_out, errmsg) != 0)
|
||||
@ -1358,6 +1357,7 @@ int acme_req_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
error:
|
||||
memprintf(errmsg, "couldn't request the finalize URL");
|
||||
out:
|
||||
OPENSSL_free(data);
|
||||
free_trash_chunk(req_in);
|
||||
free_trash_chunk(req_out);
|
||||
free_trash_chunk(csr);
|
||||
@ -1391,7 +1391,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
}
|
||||
/* get the next retry timing */
|
||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||
ctx->retryafter = atol(hdr->v.ptr);
|
||||
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1492,7 +1492,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
|
||||
}
|
||||
/* get the next retry timing */
|
||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||
ctx->retryafter = atol(hdr->v.ptr);
|
||||
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1618,7 +1618,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
||||
}
|
||||
/* get the next retry timing */
|
||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||
ctx->retryafter = atol(hdr->v.ptr);
|
||||
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1849,7 +1849,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
}
|
||||
/* get the next retry timing */
|
||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||
ctx->retryafter = atol(hdr->v.ptr);
|
||||
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||
}
|
||||
/* get the order URL */
|
||||
if (isteqi(hdr->n, ist("Location"))) {
|
||||
@ -2009,7 +2009,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
|
||||
}
|
||||
/* get the next retry timing */
|
||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||
ctx->retryafter = atol(hdr->v.ptr);
|
||||
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||
}
|
||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||
istfree(&ctx->nonce);
|
||||
@ -2526,9 +2526,9 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
||||
{
|
||||
struct buffer *san_trash = NULL;
|
||||
X509_REQ *x = NULL;
|
||||
X509_NAME *nm;
|
||||
X509_NAME *nm = NULL;
|
||||
STACK_OF(X509_EXTENSION) *exts = NULL;
|
||||
X509_EXTENSION *ext_san;
|
||||
X509_EXTENSION *ext_san = NULL;
|
||||
char *str_san = NULL;
|
||||
int i = 0;
|
||||
|
||||
@ -2559,26 +2559,36 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
||||
for (i = 0; san[i]; i++) {
|
||||
chunk_appendf(san_trash, "%sDNS:%s", i ? "," : "", san[i]);
|
||||
}
|
||||
str_san = my_strndup(san_trash->area, san_trash->data);
|
||||
if ((str_san = my_strndup(san_trash->area, san_trash->data)) == NULL)
|
||||
goto error;
|
||||
|
||||
if ((ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, str_san)) == NULL)
|
||||
goto error;
|
||||
|
||||
if (!sk_X509_EXTENSION_push(exts, ext_san))
|
||||
goto error;
|
||||
|
||||
ext_san = NULL; /* handle double-free upon error */
|
||||
|
||||
if (!X509_REQ_add_extensions(x, exts))
|
||||
goto error;
|
||||
|
||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||
|
||||
if (!X509_REQ_sign(x, pkey, EVP_sha256()))
|
||||
goto error;
|
||||
|
||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||
X509_NAME_free(nm);
|
||||
free(str_san);
|
||||
free_trash_chunk(san_trash);
|
||||
|
||||
return x;
|
||||
|
||||
error:
|
||||
X509_EXTENSION_free(ext_san);
|
||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||
X509_REQ_free(x);
|
||||
X509_NAME_free(nm);
|
||||
free(str_san);
|
||||
free_trash_chunk(san_trash);
|
||||
return NULL;
|
||||
|
||||
@ -2721,7 +2731,10 @@ static int cli_acme_renew_parse(char **args, char *payload, struct appctx *appct
|
||||
struct ckch_store *store = NULL;
|
||||
char *errmsg = NULL;
|
||||
|
||||
if (!*args[1]) {
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
if (!*args[2]) {
|
||||
memprintf(&errmsg, ": not enough parameters\n");
|
||||
goto err;
|
||||
}
|
||||
@ -2760,8 +2773,11 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
||||
int remain = 0;
|
||||
struct ebmb_node *node = NULL;
|
||||
|
||||
if (!*args[2] && !*args[3] && !*args[4]) {
|
||||
memprintf(&msg, ": not enough parameters\n");
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
if (!*args[2] || !*args[3] || !*args[4]) {
|
||||
memprintf(&msg, "Not enough parameters: \"acme challenge_ready <certfile> domain <domain>\"\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -2882,8 +2898,12 @@ end:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cli_acme_ps(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
static int cli_acme_parse_status(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
{
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2891,7 +2911,7 @@ static int cli_acme_ps(char **args, char *payload, struct appctx *appctx, void *
|
||||
|
||||
static struct cli_kw_list cli_kws = {{ },{
|
||||
{ { "acme", "renew", NULL }, "acme renew <certfile> : renew a certificate using the ACME protocol", cli_acme_renew_parse, NULL, NULL, NULL, 0 },
|
||||
{ { "acme", "status", NULL }, "acme status : show status of certificates configured with ACME", cli_acme_ps, cli_acme_status_io_handler, NULL, NULL, 0 },
|
||||
{ { "acme", "status", NULL }, "acme status : show status of certificates configured with ACME", cli_acme_parse_status, cli_acme_status_io_handler, NULL, NULL, 0 },
|
||||
{ { "acme", "challenge_ready", NULL }, "acme challenge_ready <certfile> domain <domain> : notify HAProxy that the ACME challenge is ready", cli_acme_chall_ready_parse, NULL, NULL, NULL, 0 },
|
||||
{ { NULL }, NULL, NULL, NULL }
|
||||
}};
|
||||
|
||||
31
src/action.c
31
src/action.c
@ -25,7 +25,8 @@
|
||||
|
||||
|
||||
/* Check an action ruleset validity. It returns the number of error encountered
|
||||
* and err_code is updated if a warning is emitted.
|
||||
* and err_code is updated if a warning is emitted. It also takes this
|
||||
* opportunity for filling the execution context based on available info.
|
||||
*/
|
||||
int check_action_rules(struct list *rules, struct proxy *px, int *err_code)
|
||||
{
|
||||
@ -40,6 +41,13 @@ int check_action_rules(struct list *rules, struct proxy *px, int *err_code)
|
||||
}
|
||||
*err_code |= warnif_tcp_http_cond(px, rule->cond);
|
||||
ha_free(&errmsg);
|
||||
|
||||
if (!rule->exec_ctx.type) {
|
||||
if (rule->kw && rule->kw->exec_ctx.type)
|
||||
rule->exec_ctx = rule->kw->exec_ctx;
|
||||
else if (rule->action_ptr)
|
||||
rule->exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FUNC, rule->action_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -378,3 +386,24 @@ void dump_act_rules(const struct list *rules, const char *pfx)
|
||||
(akwn->flags & KWF_MATCH_PREFIX) ? "*" : "");
|
||||
}
|
||||
}
|
||||
|
||||
/* adds the keyword list kw_list to the head <head> */
|
||||
void act_add_list(struct list *head, struct action_kw_list *kw_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; kw_list->kw[i].kw != NULL; i++) {
|
||||
/* store declaration file/line if known */
|
||||
if (kw_list->kw[i].exec_ctx.type)
|
||||
continue;
|
||||
|
||||
if (caller_initcall) {
|
||||
kw_list->kw[i].exec_ctx.type = TH_EX_CTX_INITCALL;
|
||||
kw_list->kw[i].exec_ctx.initcall = caller_initcall;
|
||||
} else {
|
||||
kw_list->kw[i].exec_ctx.type = TH_EX_CTX_ACTION;
|
||||
kw_list->kw[i].exec_ctx.action_kwl = kw_list;
|
||||
}
|
||||
}
|
||||
LIST_APPEND(head, &kw_list->list);
|
||||
}
|
||||
|
||||
156
src/activity.c
156
src/activity.c
@ -29,8 +29,11 @@ struct show_prof_ctx {
|
||||
int dump_step; /* 0,1,2,4,5,6; see cli_iohandler_show_profiling() */
|
||||
int linenum; /* next line to be dumped (starts at 0) */
|
||||
int maxcnt; /* max line count per step (0=not set) */
|
||||
int by_what; /* 0=sort by usage, 1=sort by address, 2=sort by time */
|
||||
int by_what; /* 0=sort by usage, 1=sort by address, 2=sort by time, 3=sort by ctx */
|
||||
int aggr; /* 0=dump raw, 1=aggregate on callee */
|
||||
/* 4-byte hole here */
|
||||
struct sched_activity *tmp_activity; /* dynamically allocated during dumps */
|
||||
struct memprof_stats *tmp_memstats; /* dynamically allocated during dumps */
|
||||
};
|
||||
|
||||
/* CLI context for the "show activity" command */
|
||||
@ -299,13 +302,18 @@ struct memprof_stats *memprof_get_bin(const void *ra, enum memprof_method meth)
|
||||
int retries = 16; // up to 16 consecutive entries may be tested.
|
||||
const void *old;
|
||||
unsigned int bin;
|
||||
ullong hash;
|
||||
|
||||
if (unlikely(!ra)) {
|
||||
bin = MEMPROF_HASH_BUCKETS;
|
||||
goto leave;
|
||||
}
|
||||
bin = ptr_hash(ra, MEMPROF_HASH_BITS);
|
||||
for (; memprof_stats[bin].caller != ra; bin = (bin + 1) & (MEMPROF_HASH_BUCKETS - 1)) {
|
||||
hash = _ptr2_hash_arg(ra, th_ctx->exec_ctx.pointer, th_ctx->exec_ctx.type);
|
||||
for (bin = _ptr_hash_reduce(hash, MEMPROF_HASH_BITS);
|
||||
memprof_stats[bin].caller != ra ||
|
||||
memprof_stats[bin].exec_ctx.type != th_ctx->exec_ctx.type ||
|
||||
memprof_stats[bin].exec_ctx.pointer != th_ctx->exec_ctx.pointer;
|
||||
bin = (bin + (hash | 1)) & (MEMPROF_HASH_BUCKETS - 1)) {
|
||||
if (!--retries) {
|
||||
bin = MEMPROF_HASH_BUCKETS;
|
||||
break;
|
||||
@ -314,6 +322,7 @@ struct memprof_stats *memprof_get_bin(const void *ra, enum memprof_method meth)
|
||||
old = NULL;
|
||||
if (!memprof_stats[bin].caller &&
|
||||
HA_ATOMIC_CAS(&memprof_stats[bin].caller, &old, ra)) {
|
||||
memprof_stats[bin].exec_ctx = th_ctx->exec_ctx;
|
||||
memprof_stats[bin].method = meth;
|
||||
break;
|
||||
}
|
||||
@ -918,6 +927,14 @@ static int cmp_memprof_stats(const void *a, const void *b)
|
||||
return -1;
|
||||
else if (l->alloc_tot + l->free_tot < r->alloc_tot + r->free_tot)
|
||||
return 1;
|
||||
else if (l->exec_ctx.type > r->exec_ctx.type)
|
||||
return -1;
|
||||
else if (l->exec_ctx.type < r->exec_ctx.type)
|
||||
return 1;
|
||||
else if (l->exec_ctx.pointer > r->exec_ctx.pointer)
|
||||
return -1;
|
||||
else if (l->exec_ctx.pointer < r->exec_ctx.pointer)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@ -931,6 +948,47 @@ static int cmp_memprof_addr(const void *a, const void *b)
|
||||
return -1;
|
||||
else if (l->caller < r->caller)
|
||||
return 1;
|
||||
else if (l->exec_ctx.type > r->exec_ctx.type)
|
||||
return -1;
|
||||
else if (l->exec_ctx.type < r->exec_ctx.type)
|
||||
return 1;
|
||||
else if (l->exec_ctx.pointer > r->exec_ctx.pointer)
|
||||
return -1;
|
||||
else if (l->exec_ctx.pointer < r->exec_ctx.pointer)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmp_memprof_ctx(const void *a, const void *b)
|
||||
{
|
||||
const struct memprof_stats *l = (const struct memprof_stats *)a;
|
||||
const struct memprof_stats *r = (const struct memprof_stats *)b;
|
||||
const void *ptrl = l->exec_ctx.pointer;
|
||||
const void *ptrr = r->exec_ctx.pointer;
|
||||
|
||||
/* in case of a mux, we'll use the always-present ->subscribe()
|
||||
* function as a sorting key so that mux-ops and other mux functions
|
||||
* appear grouped together.
|
||||
*/
|
||||
if (l->exec_ctx.type == TH_EX_CTX_MUX)
|
||||
ptrl = l->exec_ctx.mux_ops->subscribe;
|
||||
|
||||
if (r->exec_ctx.type == TH_EX_CTX_MUX)
|
||||
ptrr = r->exec_ctx.mux_ops->subscribe;
|
||||
|
||||
if (ptrl > ptrr)
|
||||
return -1;
|
||||
else if (ptrl < ptrr)
|
||||
return 1;
|
||||
else if (l->exec_ctx.type > r->exec_ctx.type)
|
||||
return -1;
|
||||
else if (l->exec_ctx.type < r->exec_ctx.type)
|
||||
return 1;
|
||||
else if (l->caller > r->caller)
|
||||
return -1;
|
||||
else if (l->caller < r->caller)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@ -992,9 +1050,9 @@ struct sched_activity *sched_activity_entry(struct sched_activity *array, const
|
||||
static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
{
|
||||
struct show_prof_ctx *ctx = appctx->svcctx;
|
||||
struct sched_activity tmp_activity[SCHED_ACT_HASH_BUCKETS];
|
||||
struct sched_activity *tmp_activity = ctx->tmp_activity;
|
||||
#ifdef USE_MEMORY_PROFILING
|
||||
struct memprof_stats tmp_memstats[MEMPROF_HASH_BUCKETS + 1];
|
||||
struct memprof_stats *tmp_memstats = ctx->tmp_memstats;
|
||||
unsigned long long tot_alloc_calls, tot_free_calls;
|
||||
unsigned long long tot_alloc_bytes, tot_free_bytes;
|
||||
#endif
|
||||
@ -1035,7 +1093,20 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
if ((ctx->dump_step & 3) != 1)
|
||||
goto skip_tasks;
|
||||
|
||||
memcpy(tmp_activity, sched_activity, sizeof(tmp_activity));
|
||||
if (tmp_activity)
|
||||
goto tasks_resume;
|
||||
|
||||
/* first call for show profiling tasks: we have to allocate a tmp
|
||||
* array for sorting and processing, and possibly perform some
|
||||
* sorting and aggregation.
|
||||
*/
|
||||
tmp_activity = ha_aligned_alloc(__alignof__(*tmp_activity), sizeof(sched_activity));
|
||||
if (!tmp_activity)
|
||||
goto end_tasks;
|
||||
|
||||
ctx->tmp_activity = tmp_activity;
|
||||
memcpy(tmp_activity, sched_activity, sizeof(sched_activity));
|
||||
|
||||
/* for addr sort and for callee aggregation we have to first sort by address */
|
||||
if (ctx->aggr || ctx->by_what == 1) // sort by addr
|
||||
qsort(tmp_activity, SCHED_ACT_HASH_BUCKETS, sizeof(tmp_activity[0]), cmp_sched_activity_addr);
|
||||
@ -1060,6 +1131,7 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
else if (ctx->by_what == 2) // by cpu_tot
|
||||
qsort(tmp_activity, SCHED_ACT_HASH_BUCKETS, sizeof(tmp_activity[0]), cmp_sched_activity_cpu);
|
||||
|
||||
tasks_resume:
|
||||
if (!ctx->linenum)
|
||||
chunk_appendf(&trash, "Tasks activity over %.3f sec till %.3f sec ago:\n"
|
||||
" function calls cpu_tot cpu_avg lkw_avg lkd_avg mem_avg lat_avg\n",
|
||||
@ -1123,6 +1195,8 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
end_tasks:
|
||||
ha_free(&ctx->tmp_activity);
|
||||
ctx->linenum = 0; // reset first line to dump
|
||||
if ((ctx->dump_step & 4) == 0)
|
||||
ctx->dump_step++; // next step
|
||||
@ -1133,16 +1207,57 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
if ((ctx->dump_step & 3) != 2)
|
||||
goto skip_mem;
|
||||
|
||||
memcpy(tmp_memstats, memprof_stats, sizeof(tmp_memstats));
|
||||
if (ctx->by_what)
|
||||
if (tmp_memstats)
|
||||
goto memstats_resume;
|
||||
|
||||
/* first call for show profiling memory: we have to allocate a tmp
|
||||
* array for sorting and processing, and possibly perform some sorting
|
||||
* and aggregation.
|
||||
*/
|
||||
tmp_memstats = ha_aligned_alloc(__alignof__(*tmp_memstats), sizeof(memprof_stats));
|
||||
if (!tmp_memstats)
|
||||
goto end_memstats;
|
||||
|
||||
ctx->tmp_memstats = tmp_memstats;
|
||||
memcpy(tmp_memstats, memprof_stats, sizeof(memprof_stats));
|
||||
|
||||
if (ctx->by_what == 1)
|
||||
qsort(tmp_memstats, MEMPROF_HASH_BUCKETS+1, sizeof(tmp_memstats[0]), cmp_memprof_addr);
|
||||
else if (ctx->by_what == 3)
|
||||
qsort(tmp_memstats, MEMPROF_HASH_BUCKETS+1, sizeof(tmp_memstats[0]), cmp_memprof_ctx);
|
||||
else
|
||||
qsort(tmp_memstats, MEMPROF_HASH_BUCKETS+1, sizeof(tmp_memstats[0]), cmp_memprof_stats);
|
||||
|
||||
if (ctx->aggr) {
|
||||
/* merge entries for the same caller and reset the exec_ctx */
|
||||
for (i = j = 0; i < MEMPROF_HASH_BUCKETS; i++) {
|
||||
if ((tmp_memstats[i].alloc_calls | tmp_memstats[i].free_calls) == 0)
|
||||
continue;
|
||||
for (j = i + 1; j < MEMPROF_HASH_BUCKETS; j++) {
|
||||
if ((tmp_memstats[j].alloc_calls | tmp_memstats[j].free_calls) == 0)
|
||||
continue;
|
||||
if (tmp_memstats[j].caller != tmp_memstats[i].caller ||
|
||||
tmp_memstats[j].method != tmp_memstats[i].method ||
|
||||
tmp_memstats[j].info != tmp_memstats[i].info)
|
||||
continue;
|
||||
tmp_memstats[i].locked_calls += tmp_memstats[j].locked_calls;
|
||||
tmp_memstats[i].alloc_calls += tmp_memstats[j].alloc_calls;
|
||||
tmp_memstats[i].free_calls += tmp_memstats[j].free_calls;
|
||||
tmp_memstats[i].alloc_tot += tmp_memstats[j].alloc_tot;
|
||||
tmp_memstats[i].free_tot += tmp_memstats[j].free_tot;
|
||||
/* don't dump the ctx */
|
||||
tmp_memstats[i].exec_ctx.type = 0;
|
||||
/* don't dump the merged entry */
|
||||
tmp_memstats[j].alloc_calls = tmp_memstats[j].free_calls = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memstats_resume:
|
||||
if (!ctx->linenum)
|
||||
chunk_appendf(&trash,
|
||||
"Alloc/Free statistics by call place over %.3f sec till %.3f sec ago:\n"
|
||||
" Calls | Tot Bytes | Caller and method\n"
|
||||
" Calls | Tot Bytes | Caller, method, extra info\n"
|
||||
"<- alloc -> <- free ->|<-- alloc ---> <-- free ---->|\n",
|
||||
(prof_mem_start_ns ? (prof_mem_stop_ns ? prof_mem_stop_ns : now_ns) - prof_mem_start_ns : 0) / 1000000000.0,
|
||||
(prof_mem_stop_ns ? now_ns - prof_mem_stop_ns : 0) / 1000000000.0);
|
||||
@ -1200,6 +1315,7 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
(int)((1000ULL * entry->locked_calls / tot_calls) % 10));
|
||||
}
|
||||
|
||||
chunk_append_thread_ctx(&trash, &entry->exec_ctx, " [via ", "]");
|
||||
chunk_appendf(&trash, "\n");
|
||||
|
||||
if (applet_putchk(appctx, &trash) == -1)
|
||||
@ -1309,9 +1425,15 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
tot_alloc_calls - tot_free_calls,
|
||||
tot_alloc_bytes - tot_free_bytes);
|
||||
|
||||
/* release optional buffer name */
|
||||
for (i = 0; i < max; i++)
|
||||
ha_free(&tmp_memstats[i].info);
|
||||
|
||||
if (applet_putchk(appctx, &trash) == -1)
|
||||
return 0;
|
||||
|
||||
end_memstats:
|
||||
ha_free(&ctx->tmp_memstats);
|
||||
ctx->linenum = 0; // reset first line to dump
|
||||
if ((ctx->dump_step & 4) == 0)
|
||||
ctx->dump_step++; // next step
|
||||
@ -1322,6 +1444,15 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* release structs allocated by "show profiling" */
|
||||
static void cli_release_show_profiling(struct appctx *appctx)
|
||||
{
|
||||
struct show_prof_ctx *ctx = appctx->svcctx;
|
||||
|
||||
ha_free(&ctx->tmp_activity);
|
||||
ha_free(&ctx->tmp_memstats);
|
||||
}
|
||||
|
||||
/* parse a "show profiling" command. It returns 1 on failure, 0 if it starts to dump.
|
||||
* - cli.i0 is set to the first state (0=all, 4=status, 5=tasks, 6=memory)
|
||||
* - cli.o1 is set to 1 if the output must be sorted by addr instead of usage
|
||||
@ -1354,6 +1485,9 @@ static int cli_parse_show_profiling(char **args, char *payload, struct appctx *a
|
||||
else if (strcmp(args[arg], "bytime") == 0) {
|
||||
ctx->by_what = 2; // sort output by total time instead of usage
|
||||
}
|
||||
else if (strcmp(args[arg], "byctx") == 0) {
|
||||
ctx->by_what = 3; // sort output by caller context instead of usage
|
||||
}
|
||||
else if (strcmp(args[arg], "aggr") == 0) {
|
||||
ctx->aggr = 1; // aggregate output by callee
|
||||
}
|
||||
@ -1361,7 +1495,7 @@ static int cli_parse_show_profiling(char **args, char *payload, struct appctx *a
|
||||
ctx->maxcnt = atoi(args[arg]); // number of entries to dump
|
||||
}
|
||||
else
|
||||
return cli_err(appctx, "Expects either 'all', 'status', 'tasks', 'memory', 'byaddr', 'bytime', 'aggr' or a max number of output lines.\n");
|
||||
return cli_err(appctx, "Expects either 'all', 'status', 'tasks', 'memory', 'byaddr', 'bytime', 'byctx', 'aggr' or a max number of output lines.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1705,7 +1839,7 @@ INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||
static struct cli_kw_list cli_kws = {{ },{
|
||||
{ { "set", "profiling", NULL }, "set profiling <what> {auto|on|off} : enable/disable resource profiling (tasks,memory)", cli_parse_set_profiling, NULL },
|
||||
{ { "show", "activity", NULL }, "show activity [-1|0|thread_num] : show per-thread activity stats (for support/developers)", cli_parse_show_activity, cli_io_handler_show_activity, NULL },
|
||||
{ { "show", "profiling", NULL }, "show profiling [<what>|<#lines>|<opts>]*: show profiling state (all,status,tasks,memory)", cli_parse_show_profiling, cli_io_handler_show_profiling, NULL },
|
||||
{ { "show", "profiling", NULL }, "show profiling [<what>|<#lines>|<opts>]*: show profiling state (all,status,tasks,memory)", cli_parse_show_profiling, cli_io_handler_show_profiling, cli_release_show_profiling },
|
||||
{ { "show", "tasks", NULL }, "show tasks : show running tasks", NULL, cli_io_handler_show_tasks, NULL },
|
||||
{{},}
|
||||
}};
|
||||
|
||||
21
src/applet.c
21
src/applet.c
@ -31,7 +31,6 @@ unsigned int nb_applets = 0;
|
||||
|
||||
DECLARE_TYPED_POOL(pool_head_appctx, "appctx", struct appctx);
|
||||
|
||||
|
||||
/* trace source and events */
|
||||
static void applet_trace(enum trace_level level, uint64_t mask,
|
||||
const struct trace_source *src,
|
||||
@ -417,7 +416,7 @@ void appctx_shut(struct appctx *appctx)
|
||||
TRACE_ENTER(APPLET_EV_RELEASE, appctx);
|
||||
|
||||
if (appctx->applet->release)
|
||||
appctx->applet->release(appctx);
|
||||
CALL_APPLET_NO_RET(appctx->applet, release(appctx));
|
||||
applet_fl_set(appctx, APPCTX_FL_SHUTDOWN);
|
||||
|
||||
b_dequeue(&appctx->buffer_wait);
|
||||
@ -512,7 +511,7 @@ size_t appctx_htx_rcv_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
||||
goto out;
|
||||
}
|
||||
|
||||
htx_xfer_blks(buf_htx, appctx_htx, count, HTX_BLK_UNUSED);
|
||||
htx_xfer(buf_htx, appctx_htx, count, HTX_XFER_DEFAULT);
|
||||
buf_htx->flags |= (appctx_htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
||||
if (htx_is_empty(appctx_htx)) {
|
||||
buf_htx->flags |= (appctx_htx->flags & HTX_FL_EOM);
|
||||
@ -551,7 +550,7 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
|
||||
if (flags & CO_RFL_BUF_FLUSH)
|
||||
applet_fl_set(appctx, APPCTX_FL_FASTFWD);
|
||||
|
||||
ret = appctx->applet->rcv_buf(appctx, buf, count, flags);
|
||||
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
|
||||
if (ret)
|
||||
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);
|
||||
|
||||
@ -609,7 +608,7 @@ size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
||||
goto end;
|
||||
}
|
||||
|
||||
htx_xfer_blks(appctx_htx, buf_htx, count, HTX_BLK_UNUSED);
|
||||
htx_xfer(appctx_htx, buf_htx, count, HTX_XFER_DEFAULT);
|
||||
if (htx_is_empty(buf_htx)) {
|
||||
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
||||
}
|
||||
@ -659,7 +658,7 @@ size_t appctx_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = appctx->applet->snd_buf(appctx, buf, count, flags);
|
||||
ret = CALL_APPLET_WITH_RET(appctx->applet, snd_buf(appctx, buf, count, flags));
|
||||
|
||||
if (applet_fl_test(appctx, (APPCTX_FL_ERROR|APPCTX_FL_ERR_PENDING)))
|
||||
se_report_term_evt(appctx->sedesc, se_tevt_type_snd_err);
|
||||
@ -716,7 +715,7 @@ int appctx_fastfwd(struct stconn *sc, unsigned int count, unsigned int flags)
|
||||
}
|
||||
|
||||
b_add(sdo->iobuf.buf, sdo->iobuf.offset);
|
||||
ret = appctx->applet->fastfwd(appctx, sdo->iobuf.buf, len, 0);
|
||||
ret = CALL_APPLET_WITH_RET(appctx->applet, fastfwd(appctx, sdo->iobuf.buf, len, 0));
|
||||
b_sub(sdo->iobuf.buf, sdo->iobuf.offset);
|
||||
sdo->iobuf.data += ret;
|
||||
|
||||
@ -853,7 +852,7 @@ struct task *task_run_applet(struct task *t, void *context, unsigned int state)
|
||||
* already called)
|
||||
*/
|
||||
if (!se_fl_test(app->sedesc, SE_FL_SHR) || !se_fl_test(app->sedesc, SE_FL_SHW))
|
||||
app->applet->fct(app);
|
||||
CALL_APPLET_NO_RET(app->applet, fct(app));
|
||||
|
||||
TRACE_POINT(APPLET_EV_PROCESS, app);
|
||||
|
||||
@ -900,7 +899,7 @@ struct task *task_run_applet(struct task *t, void *context, unsigned int state)
|
||||
stream_dump_and_crash(&app->obj_type, read_freq_ctr(&app->call_rate));
|
||||
}
|
||||
|
||||
sc->app_ops->wake(sc);
|
||||
sc_applet_process(sc);
|
||||
channel_release_buffer(ic, &app->buffer_wait);
|
||||
TRACE_LEAVE(APPLET_EV_PROCESS, app);
|
||||
return t;
|
||||
@ -954,7 +953,7 @@ struct task *task_process_applet(struct task *t, void *context, unsigned int sta
|
||||
* already called)
|
||||
*/
|
||||
if (!applet_fl_test(app, APPCTX_FL_SHUTDOWN))
|
||||
app->applet->fct(app);
|
||||
CALL_APPLET_NO_RET(app->applet, fct(app));
|
||||
|
||||
TRACE_POINT(APPLET_EV_PROCESS, app);
|
||||
|
||||
@ -993,7 +992,7 @@ struct task *task_process_applet(struct task *t, void *context, unsigned int sta
|
||||
stream_dump_and_crash(&app->obj_type, read_freq_ctr(&app->call_rate));
|
||||
}
|
||||
|
||||
sc->app_ops->wake(sc);
|
||||
sc_applet_process(sc);
|
||||
appctx_release_buffers(app);
|
||||
TRACE_LEAVE(APPLET_EV_PROCESS, app);
|
||||
return t;
|
||||
|
||||
@ -1396,7 +1396,7 @@ check_tgid:
|
||||
tree = search_tree ? &srv->per_thr[i].safe_conns : &srv->per_thr[i].idle_conns;
|
||||
conn = srv_lookup_conn(tree, hash);
|
||||
while (conn) {
|
||||
if (conn->mux->takeover && conn->mux->takeover(conn, i, 0) == 0) {
|
||||
if (conn->mux->takeover && CALL_MUX_WITH_RET(conn->mux, takeover(conn, i, 0)) == 0) {
|
||||
conn_delete_from_tree(conn, i);
|
||||
_HA_ATOMIC_INC(&activity[tid].fd_takeover);
|
||||
found = 1;
|
||||
@ -1498,7 +1498,7 @@ takeover_random_idle_conn(struct ceb_root **root, int curtid)
|
||||
|
||||
conn = ceb64_item_first(root, hash_node.node, hash_node.key, struct connection);
|
||||
while (conn) {
|
||||
if (conn->mux->takeover && conn->mux->takeover(conn, curtid, 1) == 0) {
|
||||
if (conn->mux->takeover && CALL_MUX_WITH_RET(conn->mux, takeover(conn, curtid, 1)) == 0) {
|
||||
conn_delete_from_tree(conn, curtid);
|
||||
return conn;
|
||||
}
|
||||
@ -1555,7 +1555,7 @@ kill_random_idle_conn(struct server *srv)
|
||||
*/
|
||||
_HA_ATOMIC_INC(&srv->curr_used_conns);
|
||||
}
|
||||
conn->mux->destroy(conn->ctx);
|
||||
CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -1765,7 +1765,7 @@ int be_reuse_connection(int64_t hash, struct session *sess,
|
||||
}
|
||||
|
||||
if (avail >= 1) {
|
||||
if (srv_conn->mux->attach(srv_conn, sc->sedesc, sess) == -1) {
|
||||
if (CALL_MUX_WITH_RET(srv_conn->mux, attach(srv_conn, sc->sedesc, sess)) == -1) {
|
||||
if (sc_reset_endp(sc) < 0)
|
||||
goto err;
|
||||
sc_ep_clr(sc, ~SE_FL_DETACHED);
|
||||
@ -1879,7 +1879,7 @@ int connect_server(struct stream *s)
|
||||
* It will in turn call srv_release_conn through
|
||||
* conn_free which also uses it.
|
||||
*/
|
||||
tokill_conn->mux->destroy(tokill_conn->ctx);
|
||||
CALL_MUX_NO_RET(tokill_conn->mux, destroy(tokill_conn->ctx));
|
||||
}
|
||||
else {
|
||||
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
||||
@ -2204,7 +2204,7 @@ int connect_server(struct stream *s)
|
||||
*/
|
||||
if (may_start_mux_now) {
|
||||
const struct mux_ops *alt_mux =
|
||||
likely(!(s->flags & SF_WEBSOCKET)) ? NULL : srv_get_ws_proto(srv);
|
||||
likely(!(s->flags & SF_WEBSOCKET) || !srv) ? NULL : srv_get_ws_proto(srv);
|
||||
if (conn_install_mux_be(srv_conn, s->scb, s->sess, alt_mux) < 0) {
|
||||
conn_full_close(srv_conn);
|
||||
return SF_ERR_INTERNAL;
|
||||
|
||||
@ -626,7 +626,7 @@ cache_store_check(struct proxy *px, struct flt_conf *fconf)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (f->id == http_comp_flt_id)
|
||||
else if (f->id == http_comp_req_flt_id || f->id == http_comp_res_flt_id)
|
||||
comp = 1;
|
||||
else if (f->id == fcgi_flt_id)
|
||||
continue;
|
||||
|
||||
@ -89,12 +89,23 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
|
||||
global.tune.options |= GTUNE_BUSY_POLLING;
|
||||
}
|
||||
else if (strcmp(args[0], "set-dumpable") == 0) { /* "no set-dumpable" or "set-dumpable" */
|
||||
if (alertif_too_many_args(0, file, linenum, args, &err_code))
|
||||
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
||||
goto out;
|
||||
if (kwm == KWM_NO)
|
||||
if (kwm == KWM_NO) {
|
||||
global.tune.options &= ~GTUNE_SET_DUMPABLE;
|
||||
else
|
||||
global.tune.options |= GTUNE_SET_DUMPABLE;
|
||||
goto out;
|
||||
}
|
||||
if (!*args[1] || strcmp(args[1], "on") == 0)
|
||||
global.tune.options |= GTUNE_SET_DUMPABLE;
|
||||
else if (strcmp(args[1], "libs") == 0)
|
||||
global.tune.options |= GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS;
|
||||
else if (strcmp(args[1], "off") == 0)
|
||||
global.tune.options &= ~GTUNE_SET_DUMPABLE;
|
||||
else {
|
||||
ha_alert("parsing [%s:%d] : '%s' only supports 'on' and 'off' as an argument, found '%s'.\n", file, linenum, args[0], args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[0], "h2-workaround-bogus-websocket-clients") == 0) { /* "no h2-workaround-bogus-websocket-clients" or "h2-workaround-bogus-websocket-clients" */
|
||||
if (alertif_too_many_args(0, file, linenum, args, &err_code))
|
||||
|
||||
@ -1358,14 +1358,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err_code |= warnif_misplaced_http_req(curproxy, file, linenum, args[0], NULL);
|
||||
if (warnif_misplaced_http_req(curproxy, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
|
||||
if (curproxy->cap & PR_CAP_FE)
|
||||
where |= SMP_VAL_FE_HRQ_HDR;
|
||||
if (curproxy->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRQ_HDR;
|
||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
|
||||
LIST_APPEND(&curproxy->http_req_rules, &rule->list);
|
||||
@ -1400,7 +1401,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
if (curproxy->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRS_HDR;
|
||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
|
||||
LIST_APPEND(&curproxy->http_res_rules, &rule->list);
|
||||
@ -1434,7 +1435,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
if (curproxy->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRS_HDR;
|
||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
|
||||
LIST_APPEND(&curproxy->http_after_res_rules, &rule->list);
|
||||
@ -1491,14 +1492,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
}
|
||||
|
||||
LIST_APPEND(&curproxy->redirect_rules, &rule->list);
|
||||
err_code |= warnif_misplaced_redirect(curproxy, file, linenum, args[0], NULL);
|
||||
if (warnif_misplaced_redirect(curproxy, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
|
||||
if (curproxy->cap & PR_CAP_FE)
|
||||
where |= SMP_VAL_FE_HRQ_HDR;
|
||||
if (curproxy->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRQ_HDR;
|
||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
}
|
||||
else if (strcmp(args[0], "use_backend") == 0) {
|
||||
@ -1528,7 +1530,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
}
|
||||
|
||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_FE_SET_BCK, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
}
|
||||
else if (*args[2]) {
|
||||
@ -1591,7 +1593,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
}
|
||||
|
||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
|
||||
rule = calloc(1, sizeof(*rule));
|
||||
@ -1646,7 +1648,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
* where force-persist is applied.
|
||||
*/
|
||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_REQ_CNT, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
|
||||
rule = calloc(1, sizeof(*rule));
|
||||
@ -1814,7 +1816,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_STO_RUL, &errmsg);
|
||||
else
|
||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
|
||||
rule = calloc(1, sizeof(*rule));
|
||||
@ -1872,7 +1874,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
if (curproxy->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRQ_HDR;
|
||||
err_code |= warnif_cond_conflicts(cond, where, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
|
||||
rule = calloc(1, sizeof(*rule));
|
||||
@ -1952,7 +1954,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
if (curproxy->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRQ_HDR;
|
||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||
if (err_code)
|
||||
if (errmsg && *errmsg)
|
||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||
LIST_APPEND(&curproxy->uri_auth->http_req_rules, &rule->list);
|
||||
|
||||
@ -2200,6 +2202,42 @@ stats_error_parsing:
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
else if (strcmp(args[1], "use-small-buffers") == 0) {
|
||||
unsigned int flags = PR_O2_USE_SBUF_ALL;
|
||||
|
||||
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL)) {
|
||||
err_code |= ERR_WARN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*(args[2])) {
|
||||
int cur_arg;
|
||||
|
||||
flags = 0;
|
||||
for (cur_arg = 2; *(args[cur_arg]); cur_arg++) {
|
||||
if (strcmp(args[cur_arg], "queue") == 0)
|
||||
flags |= PR_O2_USE_SBUF_QUEUE;
|
||||
else if (strcmp(args[cur_arg], "l7-retries") == 0)
|
||||
flags |= PR_O2_USE_SBUF_L7_RETRY;
|
||||
else if (strcmp(args[cur_arg], "check") == 0)
|
||||
flags |= PR_O2_USE_SBUF_CHECK;
|
||||
else {
|
||||
ha_alert("parsing [%s:%d] : invalid parameter '%s'. option '%s' expects 'queue', 'l7-retries' or 'check' value.\n",
|
||||
file, linenum, args[cur_arg], args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kwm == KWM_STD) {
|
||||
curproxy->options2 &= ~PR_O2_USE_SBUF_ALL;
|
||||
curproxy->options2 |= flags;
|
||||
}
|
||||
else if (kwm == KWM_NO) {
|
||||
curproxy->options2 &= ~flags;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kwm != KWM_STD) {
|
||||
ha_alert("parsing [%s:%d]: negation/default is not supported for option '%s'.\n",
|
||||
@ -2557,7 +2595,8 @@ stats_error_parsing:
|
||||
goto out;
|
||||
}
|
||||
|
||||
err_code |= warnif_misplaced_monitor(curproxy, file, linenum, args[0], args[1]);
|
||||
if (warnif_misplaced_monitor(curproxy, file, linenum, args[0], args[1]))
|
||||
err_code |= ERR_WARN;
|
||||
if ((cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + 2, &errmsg)) == NULL) {
|
||||
ha_alert("parsing [%s:%d] : error detected while parsing a '%s %s' condition : %s.\n",
|
||||
file, linenum, args[0], args[1], errmsg);
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
#include <haproxy/global.h>
|
||||
#include <haproxy/http_ana.h>
|
||||
#include <haproxy/http_rules.h>
|
||||
#include <haproxy/http_htx.h>
|
||||
#include <haproxy/lb_chash.h>
|
||||
#include <haproxy/lb_fas.h>
|
||||
#include <haproxy/lb_fwlc.h>
|
||||
@ -2318,6 +2319,18 @@ int check_config_validity()
|
||||
"Please fix either value to remove this warning.\n",
|
||||
global.tune.bufsize_large, global.tune.bufsize);
|
||||
global.tune.bufsize_large = 0;
|
||||
err_code |= ERR_WARN;
|
||||
}
|
||||
}
|
||||
|
||||
if (global.tune.bufsize_small > 0) {
|
||||
if (global.tune.bufsize_small == global.tune.bufsize)
|
||||
global.tune.bufsize_small = 0;
|
||||
else if (global.tune.bufsize_small > global.tune.bufsize) {
|
||||
ha_warning("invalid small buffer size %d bytes which is greater to default bufsize %d bytes.\n",
|
||||
global.tune.bufsize_small, global.tune.bufsize);
|
||||
global.tune.bufsize_small = 0;
|
||||
err_code |= ERR_WARN;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2377,6 +2390,9 @@ int check_config_validity()
|
||||
cfgerr += check_action_rules(&defpx->http_req_rules, defpx, &err_code);
|
||||
cfgerr += check_action_rules(&defpx->http_res_rules, defpx, &err_code);
|
||||
cfgerr += check_action_rules(&defpx->http_after_res_rules, defpx, &err_code);
|
||||
#ifdef USE_QUIC
|
||||
cfgerr += check_action_rules(&defpx->quic_init_rules, defpx, &err_code);
|
||||
#endif
|
||||
|
||||
err = NULL;
|
||||
i = smp_resolve_args(defpx, &err);
|
||||
@ -2389,6 +2405,8 @@ int check_config_validity()
|
||||
else {
|
||||
cfgerr += acl_find_targets(defpx);
|
||||
}
|
||||
|
||||
err_code |= proxy_check_http_errors(defpx);
|
||||
}
|
||||
|
||||
/* starting to initialize the main proxies list */
|
||||
|
||||
58
src/check.c
58
src/check.c
@ -1045,13 +1045,12 @@ int httpchk_build_status_header(struct server *s, struct buffer *buf)
|
||||
/**************************************************************************/
|
||||
/***************** Health-checks based on connections *********************/
|
||||
/**************************************************************************/
|
||||
/* This function is used only for server health-checks. It handles connection
|
||||
* status updates including errors. If necessary, it wakes the check task up.
|
||||
* It returns 0 on normal cases, <0 if at least one close() has happened on the
|
||||
* connection (eg: reconnect). It relies on tcpcheck_main().
|
||||
/* This function handles connection status updates including errors. If
|
||||
* necessary, it wakes the check task up.
|
||||
*/
|
||||
int wake_srv_chk(struct stconn *sc)
|
||||
struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state)
|
||||
{
|
||||
struct stconn *sc = ctx;
|
||||
struct connection *conn;
|
||||
struct check *check = __sc_check(sc);
|
||||
int ret = 0;
|
||||
@ -1098,15 +1097,6 @@ int wake_srv_chk(struct stconn *sc)
|
||||
|
||||
end:
|
||||
TRACE_LEAVE(CHK_EV_HCHK_WAKE, check);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function checks if any I/O is wanted, and if so, attempts to do so */
|
||||
struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state)
|
||||
{
|
||||
struct stconn *sc = ctx;
|
||||
|
||||
wake_srv_chk(sc);
|
||||
return t;
|
||||
}
|
||||
|
||||
@ -1525,13 +1515,15 @@ int check_buf_available(void *target)
|
||||
/*
|
||||
* Allocate a buffer. If it fails, it adds the check in buffer wait queue.
|
||||
*/
|
||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
|
||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer)
|
||||
{
|
||||
struct buffer *buf = NULL;
|
||||
|
||||
if (likely(!LIST_INLIST(&check->buf_wait.list)) &&
|
||||
unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) {
|
||||
b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available);
|
||||
if (small_buffer == 0 || (buf = b_alloc_small(bptr)) == NULL) {
|
||||
if (likely(!LIST_INLIST(&check->buf_wait.list)) &&
|
||||
unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) {
|
||||
b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@ -1543,8 +1535,11 @@ struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
|
||||
void check_release_buf(struct check *check, struct buffer *bptr)
|
||||
{
|
||||
if (bptr->size) {
|
||||
int defbuf = b_is_default(bptr);
|
||||
|
||||
b_free(bptr);
|
||||
offer_buffers(check->buf_wait.target, 1);
|
||||
if (defbuf)
|
||||
offer_buffers(check->buf_wait.target, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1664,7 +1659,6 @@ int start_check_task(struct check *check, int mininter,
|
||||
*/
|
||||
static int start_checks()
|
||||
{
|
||||
|
||||
struct proxy *px;
|
||||
struct server *s;
|
||||
char *errmsg = NULL;
|
||||
@ -1691,6 +1685,10 @@ static int start_checks()
|
||||
*/
|
||||
for (px = proxies_list; px; px = px->next) {
|
||||
for (s = px->srv; s; s = s->next) {
|
||||
if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
|
||||
(s->check.tcpcheck_rules->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||
s->check.state |= CHK_ST_USE_SMALL_BUFF;
|
||||
|
||||
if (s->check.state & CHK_ST_CONFIGURED) {
|
||||
nbcheck++;
|
||||
if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
|
||||
@ -1815,7 +1813,15 @@ int init_srv_check(struct server *srv)
|
||||
* specified.
|
||||
*/
|
||||
if (!srv->check.port && !is_addr(&srv->check.addr)) {
|
||||
if (!srv->check.use_ssl && srv->use_ssl != -1)
|
||||
/*
|
||||
* If any setting is set for the check, then we can't
|
||||
* assume we'll use the same XPRT as the server, the
|
||||
* server may be QUIC, but we want a TCP check.
|
||||
*/
|
||||
if (!srv->check.use_ssl && srv->use_ssl != -1 &&
|
||||
!srv->check.via_socks4 && !srv->check.send_proxy &&
|
||||
(!srv->check.alpn_len || (srv->check.alpn_len == srv->ssl_ctx.alpn_len && !strncmp(srv->check.alpn_str, srv->ssl_ctx.alpn_str, srv->check.alpn_len))) &&
|
||||
(!srv->check.mux_proto || srv->check.mux_proto != srv->mux_proto))
|
||||
srv->check.xprt = srv->xprt;
|
||||
else if (srv->check.use_ssl == 1)
|
||||
srv->check.xprt = xprt_get(XPRT_SSL);
|
||||
@ -2066,6 +2072,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
||||
char **errmsg)
|
||||
{
|
||||
struct sockaddr_storage *sk;
|
||||
struct protocol *proto;
|
||||
int port1, port2, err_code = 0;
|
||||
|
||||
|
||||
@ -2074,7 +2081,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
||||
goto error;
|
||||
}
|
||||
|
||||
sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, NULL, errmsg, NULL, NULL, NULL,
|
||||
sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, &proto, NULL, errmsg, NULL, NULL, NULL,
|
||||
PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
|
||||
if (!sk) {
|
||||
memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
|
||||
@ -2082,6 +2089,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
||||
}
|
||||
|
||||
srv->check.addr = *sk;
|
||||
srv->check.proto = proto;
|
||||
/* if agentaddr was never set, we can use addr */
|
||||
if (!(srv->flags & SRV_F_AGENTADDR))
|
||||
srv->agent.addr = *sk;
|
||||
@ -2111,7 +2119,11 @@ static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx,
|
||||
goto error;
|
||||
}
|
||||
set_srv_agent_addr(srv, &sk);
|
||||
|
||||
/* Agent currently only uses TCP */
|
||||
if (sk.ss_family == AF_INET)
|
||||
srv->agent.proto = &proto_tcpv4;
|
||||
else
|
||||
srv->agent.proto = &proto_tcpv6;
|
||||
out:
|
||||
return err_code;
|
||||
|
||||
|
||||
99
src/chunk.c
99
src/chunk.c
@ -53,6 +53,22 @@ struct pool_head *pool_head_large_trash __read_mostly = NULL;
|
||||
/* this is used to drain data, and as a temporary large buffer */
|
||||
THREAD_LOCAL struct buffer trash_large = { };
|
||||
|
||||
/* small trash chunks used for various conversions */
|
||||
static THREAD_LOCAL struct buffer *small_trash_chunk;
|
||||
static THREAD_LOCAL struct buffer small_trash_chunk1;
|
||||
static THREAD_LOCAL struct buffer small_trash_chunk2;
|
||||
|
||||
/* small trash buffers used for various conversions */
|
||||
static int small_trash_size __read_mostly = 0;
|
||||
static THREAD_LOCAL char *small_trash_buf1 = NULL;
|
||||
static THREAD_LOCAL char *small_trash_buf2 = NULL;
|
||||
|
||||
/* the trash pool for reentrant allocations */
|
||||
struct pool_head *pool_head_small_trash __read_mostly = NULL;
|
||||
|
||||
/* this is used to drain data, and as a temporary small buffer */
|
||||
THREAD_LOCAL struct buffer trash_small = { };
|
||||
|
||||
/*
|
||||
* Returns a pre-allocated and initialized trash chunk that can be used for any
|
||||
* type of conversion. Two chunks and their respective buffers are alternatively
|
||||
@ -103,14 +119,40 @@ struct buffer *get_large_trash_chunk(void)
|
||||
return large_trash_chunk;
|
||||
}
|
||||
|
||||
/* Similar to get_trash_chunk() but return a pre-allocated small chunk
|
||||
* instead. Becasuse small buffers are not enabled by default, this function may
|
||||
* return NULL.
|
||||
*/
|
||||
struct buffer *get_small_trash_chunk(void)
|
||||
{
|
||||
char *small_trash_buf;
|
||||
|
||||
if (!small_trash_size)
|
||||
return NULL;
|
||||
|
||||
if (small_trash_chunk == &small_trash_chunk1) {
|
||||
small_trash_chunk = &small_trash_chunk2;
|
||||
small_trash_buf = small_trash_buf2;
|
||||
}
|
||||
else {
|
||||
small_trash_chunk = &small_trash_chunk1;
|
||||
small_trash_buf = small_trash_buf1;
|
||||
}
|
||||
*small_trash_buf = 0;
|
||||
chunk_init(small_trash_chunk, small_trash_buf, small_trash_size);
|
||||
return small_trash_chunk;
|
||||
}
|
||||
|
||||
/* Returns a trash chunk accordingly to the requested size. This function may
|
||||
* fail if the requested size is too big or if the large chubks are not
|
||||
* configured.
|
||||
*/
|
||||
struct buffer *get_trash_chunk_sz(size_t size)
|
||||
{
|
||||
if (likely(size <= trash_size))
|
||||
return get_trash_chunk();
|
||||
if (likely(size > small_trash_size && size <= trash_size))
|
||||
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)
|
||||
return get_large_trash_chunk();
|
||||
else
|
||||
@ -122,17 +164,20 @@ struct buffer *get_trash_chunk_sz(size_t size)
|
||||
*/
|
||||
struct buffer *get_larger_trash_chunk(struct buffer *chk)
|
||||
{
|
||||
struct buffer *chunk;
|
||||
struct buffer *chunk = NULL;
|
||||
|
||||
if (!chk)
|
||||
return get_trash_chunk();
|
||||
if (!chk || chk->size == small_trash_size) {
|
||||
/* no chunk or a small one, use a regular buffer */
|
||||
chunk = get_trash_chunk();
|
||||
}
|
||||
else if (large_trash_size && chk->size <= large_trash_size) {
|
||||
/* a regular byffer, use a large buffer if possible */
|
||||
chunk = get_large_trash_chunk();
|
||||
}
|
||||
|
||||
/* No large buffers or current chunk is alread a large trash chunk */
|
||||
if (!large_trash_size || chk->size == large_trash_size)
|
||||
return NULL;
|
||||
if (chk && chunk)
|
||||
b_xfer(chunk, chk, b_data(chk));
|
||||
|
||||
chunk = get_large_trash_chunk();
|
||||
b_xfer(chunk, chk, b_data(chk));
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@ -166,9 +211,29 @@ static int alloc_large_trash_buffers(int bufsize)
|
||||
return trash_large.area && large_trash_buf1 && large_trash_buf2;
|
||||
}
|
||||
|
||||
/* allocates the trash small buffers if necessary. Returns 0 in case of
|
||||
* failure. Unlike alloc_trash_buffers(), It is unexpected to call this function
|
||||
* multiple times. Small buffers are not used during configuration parsing.
|
||||
*/
|
||||
static int alloc_small_trash_buffers(int bufsize)
|
||||
{
|
||||
small_trash_size = bufsize;
|
||||
if (!small_trash_size)
|
||||
return 1;
|
||||
|
||||
BUG_ON(trash_small.area && small_trash_buf1 && small_trash_buf2);
|
||||
|
||||
chunk_init(&trash_small, my_realloc2(trash_small.area, bufsize), bufsize);
|
||||
small_trash_buf1 = (char *)my_realloc2(small_trash_buf1, bufsize);
|
||||
small_trash_buf2 = (char *)my_realloc2(small_trash_buf2, bufsize);
|
||||
return trash_small.area && small_trash_buf1 && small_trash_buf2;
|
||||
}
|
||||
|
||||
static int alloc_trash_buffers_per_thread()
|
||||
{
|
||||
return alloc_trash_buffers(global.tune.bufsize) && alloc_large_trash_buffers(global.tune.bufsize_large);
|
||||
return (alloc_trash_buffers(global.tune.bufsize) &&
|
||||
alloc_large_trash_buffers(global.tune.bufsize_large) &&
|
||||
alloc_small_trash_buffers(global.tune.bufsize_large));
|
||||
}
|
||||
|
||||
static void free_trash_buffers_per_thread()
|
||||
@ -180,6 +245,10 @@ static void free_trash_buffers_per_thread()
|
||||
chunk_destroy(&trash_large);
|
||||
ha_free(&large_trash_buf2);
|
||||
ha_free(&large_trash_buf1);
|
||||
|
||||
chunk_destroy(&trash_small);
|
||||
ha_free(&small_trash_buf2);
|
||||
ha_free(&small_trash_buf1);
|
||||
}
|
||||
|
||||
/* Initialize the trash buffers. It returns 0 if an error occurred. */
|
||||
@ -207,6 +276,14 @@ int init_trash_buffers(int first)
|
||||
if (!pool_head_large_trash)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!first && global.tune.bufsize_small) {
|
||||
pool_head_small_trash = create_pool("small_trash",
|
||||
sizeof(struct buffer) + global.tune.bufsize_small,
|
||||
MEM_F_EXACT);
|
||||
if (!pool_head_small_trash)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
52
src/cli.c
52
src/cli.c
@ -400,6 +400,21 @@ struct cli_kw* cli_find_kw_exact(char **args)
|
||||
|
||||
void cli_register_kw(struct cli_kw_list *kw_list)
|
||||
{
|
||||
struct cli_kw *kw;
|
||||
|
||||
for (kw = &kw_list->kw[0]; kw->str_kw[0]; kw++) {
|
||||
/* store declaration file/line if known */
|
||||
if (kw->exec_ctx.type)
|
||||
continue;
|
||||
|
||||
if (caller_initcall) {
|
||||
kw->exec_ctx.type = TH_EX_CTX_INITCALL;
|
||||
kw->exec_ctx.initcall = caller_initcall;
|
||||
} else {
|
||||
kw->exec_ctx.type = TH_EX_CTX_CLI_KWL;
|
||||
kw->exec_ctx.cli_kwl = kw_list;
|
||||
}
|
||||
}
|
||||
LIST_APPEND(&cli_keywords.list, &kw_list->list);
|
||||
}
|
||||
|
||||
@ -849,6 +864,7 @@ static int cli_process_cmdline(struct appctx *appctx)
|
||||
else if (kw->level == ACCESS_EXPERIMENTAL)
|
||||
mark_tainted(TAINTED_CLI_EXPERIMENTAL_MODE);
|
||||
|
||||
appctx->cli_ctx.kw = kw;
|
||||
appctx->cli_ctx.io_handler = kw->io_handler;
|
||||
appctx->cli_ctx.io_release = kw->io_release;
|
||||
|
||||
@ -868,6 +884,7 @@ static int cli_process_cmdline(struct appctx *appctx)
|
||||
goto end;
|
||||
|
||||
fail:
|
||||
appctx->cli_ctx.kw = NULL;
|
||||
appctx->cli_ctx.io_handler = NULL;
|
||||
appctx->cli_ctx.io_release = NULL;
|
||||
|
||||
@ -1209,17 +1226,19 @@ void cli_io_handler(struct appctx *appctx)
|
||||
|
||||
case CLI_ST_CALLBACK: /* use custom pointer */
|
||||
if (appctx->cli_ctx.io_handler)
|
||||
if (appctx->cli_ctx.io_handler(appctx)) {
|
||||
if (EXEC_CTX_WITH_RET(appctx->cli_ctx.kw->exec_ctx, appctx->cli_ctx.io_handler(appctx))) {
|
||||
appctx->t->expire = TICK_ETERNITY;
|
||||
appctx->st0 = CLI_ST_PROMPT;
|
||||
if (appctx->cli_ctx.io_release) {
|
||||
appctx->cli_ctx.io_release(appctx);
|
||||
EXEC_CTX_NO_RET(appctx->cli_ctx.kw->exec_ctx, appctx->cli_ctx.io_release(appctx));
|
||||
appctx->cli_ctx.io_release = NULL;
|
||||
appctx->cli_ctx.kw = NULL;
|
||||
/* some release handlers might have
|
||||
* pending output to print.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
appctx->cli_ctx.kw = NULL;
|
||||
}
|
||||
break;
|
||||
default: /* abnormal state */
|
||||
@ -1325,8 +1344,9 @@ static void cli_release_handler(struct appctx *appctx)
|
||||
free_trash_chunk(appctx->cli_ctx.cmdline);
|
||||
|
||||
if (appctx->cli_ctx.io_release) {
|
||||
appctx->cli_ctx.io_release(appctx);
|
||||
EXEC_CTX_NO_RET(appctx->cli_ctx.kw->exec_ctx, appctx->cli_ctx.io_release(appctx));
|
||||
appctx->cli_ctx.io_release = NULL;
|
||||
appctx->cli_ctx.kw = NULL;
|
||||
}
|
||||
else if (appctx->st0 == CLI_ST_PRINT_DYN || appctx->st0 == CLI_ST_PRINT_DYNERR) {
|
||||
struct cli_print_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||
@ -2614,8 +2634,9 @@ static int cli_parse_echo(char **args, char *payload, struct appctx *appctx, voi
|
||||
|
||||
static int _send_status(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
{
|
||||
struct listener *mproxy_li;
|
||||
struct mworker_proc *proc;
|
||||
struct stconn *sc = appctx_sc(appctx);
|
||||
struct listener *mproxy_li = strm_li(__sc_strm(sc));
|
||||
char *msg = "READY\n";
|
||||
int pid;
|
||||
|
||||
@ -2625,12 +2646,18 @@ static int _send_status(char **args, char *payload, struct appctx *appctx, void
|
||||
pid = atoi(args[2]);
|
||||
|
||||
list_for_each_entry(proc, &proc_list, list) {
|
||||
|
||||
/* update status of the new worker */
|
||||
if (proc->pid == pid) {
|
||||
proc->options &= ~PROC_O_INIT;
|
||||
mproxy_li = fdtab[proc->ipc_fd[0]].owner;
|
||||
stop_listener(mproxy_li, 0, 0, 0);
|
||||
|
||||
/* the proxy used to receive the _send_status must be
|
||||
* the one corresponding to the PID we received in
|
||||
* argument */
|
||||
BUG_ON(proc->ipc_fd[0] < 0);
|
||||
BUG_ON(mproxy_li != fdtab[proc->ipc_fd[0]].owner);
|
||||
}
|
||||
|
||||
/* send TERM to workers, which have exceeded max_reloads counter */
|
||||
if (max_reloads != -1) {
|
||||
if ((proc->options & PROC_O_TYPE_WORKER) &&
|
||||
@ -2642,6 +2669,15 @@ static int _send_status(char **args, char *payload, struct appctx *appctx, void
|
||||
}
|
||||
}
|
||||
|
||||
/* the sockpair between the master and the worker is
|
||||
* used temporarly as a listener to receive
|
||||
* _send_status. Once it is received we don't want to
|
||||
* use this FD as a listener anymore, but only as a
|
||||
* server, to allow only connections from the master to
|
||||
* the worker for the master CLI */
|
||||
BUG_ON(mproxy_li == NULL);
|
||||
stop_listener(mproxy_li, 0, 0, 0);
|
||||
|
||||
/* At this point we are sure, that newly forked worker is started,
|
||||
* so we can write our PID in a pidfile, if provided. Master doesn't
|
||||
* perform chroot.
|
||||
@ -2840,7 +2876,7 @@ static int pcli_prefix_to_pid(const char *prefix)
|
||||
if (*errtol != '\0')
|
||||
return -1;
|
||||
list_for_each_entry(child, &proc_list, list) {
|
||||
if (!(child->options & PROC_O_TYPE_WORKER))
|
||||
if (!(child->options & PROC_O_TYPE_WORKER) || (child->options & PROC_O_INIT))
|
||||
continue;
|
||||
if (child->pid == proc_pid){
|
||||
return child->pid;
|
||||
@ -2863,7 +2899,7 @@ static int pcli_prefix_to_pid(const char *prefix)
|
||||
/* chose the right process, the current one is the one with the
|
||||
least number of reloads */
|
||||
list_for_each_entry(child, &proc_list, list) {
|
||||
if (!(child->options & PROC_O_TYPE_WORKER))
|
||||
if (!(child->options & PROC_O_TYPE_WORKER) || (child->options & PROC_O_INIT))
|
||||
continue;
|
||||
if (child->reloads == 0)
|
||||
return child->pid;
|
||||
|
||||
21
src/clock.c
21
src/clock.c
@ -266,6 +266,7 @@ void clock_update_global_date()
|
||||
{
|
||||
ullong old_now_ns;
|
||||
uint old_now_ms;
|
||||
int now_ns_changed = 0;
|
||||
|
||||
/* now that we have bounded the local time, let's check if it's
|
||||
* realistic regarding the global date, which only moves forward,
|
||||
@ -275,8 +276,10 @@ void clock_update_global_date()
|
||||
old_now_ms = _HA_ATOMIC_LOAD(global_now_ms);
|
||||
|
||||
do {
|
||||
if (now_ns < old_now_ns)
|
||||
if (now_ns < old_now_ns) {
|
||||
now_ns_changed = 1;
|
||||
now_ns = old_now_ns;
|
||||
}
|
||||
|
||||
/* now <now_ns> is expected to be the most accurate date,
|
||||
* equal to <global_now_ns> or newer. Updating the global
|
||||
@ -295,8 +298,11 @@ void clock_update_global_date()
|
||||
if (unlikely(now_ms == TICK_ETERNITY))
|
||||
now_ms++;
|
||||
|
||||
if (!((now_ns ^ old_now_ns) & ~0x7FFFULL))
|
||||
if (!((now_ns ^ old_now_ns) & ~0x7FFFULL)) {
|
||||
if (now_ns_changed)
|
||||
goto end;
|
||||
return;
|
||||
}
|
||||
|
||||
/* let's try to update the global_now_ns (both in nanoseconds
|
||||
* and ms forms) or loop again.
|
||||
@ -305,6 +311,7 @@ void clock_update_global_date()
|
||||
(now_ms != old_now_ms && !_HA_ATOMIC_CAS(global_now_ms, &old_now_ms, now_ms))) &&
|
||||
__ha_cpu_relax());
|
||||
|
||||
end:
|
||||
if (!th_ctx->curr_mono_time) {
|
||||
/* Only update the offset when monotonic time is not available.
|
||||
* <now_ns> and <now_ms> are now updated to the last value of
|
||||
@ -314,6 +321,16 @@ void clock_update_global_date()
|
||||
*/
|
||||
HA_ATOMIC_STORE(&now_offset, now_ns - tv_to_ns(&date));
|
||||
}
|
||||
else if (global_now_ns != &_global_now_ns) {
|
||||
/*
|
||||
* or global_now_ns is shared with other processes: this results
|
||||
* in the now_offset requiring to self-adjust so that it is consistent
|
||||
* with now_offset used by other processes, as we may have learned from
|
||||
* a new global_now_ns that was used in pair with a different offset from
|
||||
* ours
|
||||
*/
|
||||
HA_ATOMIC_STORE(&now_offset, now_ns - th_ctx->curr_mono_time);
|
||||
}
|
||||
}
|
||||
|
||||
/* must be called once at boot to initialize some global variables */
|
||||
|
||||
@ -141,7 +141,7 @@ int conn_create_mux(struct connection *conn, int *closed_connection)
|
||||
fail:
|
||||
/* let the upper layer know the connection failed */
|
||||
if (sc) {
|
||||
sc->app_ops->wake(sc);
|
||||
sc_conn_process(sc);
|
||||
}
|
||||
else if (conn_reverse_in_preconnect(conn)) {
|
||||
struct listener *l = conn_active_reverse_listener(conn);
|
||||
@ -232,14 +232,14 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
|
||||
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
||||
}
|
||||
|
||||
ret = conn->mux->wake(conn);
|
||||
ret = CALL_MUX_WITH_RET(conn->mux, wake(conn));
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
if (conn_in_list) {
|
||||
if (srv && (srv->cur_admin & SRV_ADMF_MAINT)) {
|
||||
/* Do not store an idle conn if server in maintenance. */
|
||||
conn->mux->destroy(conn->ctx);
|
||||
CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx));
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
@ -247,7 +247,7 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
|
||||
if (conn->flags & CO_FL_SESS_IDLE) {
|
||||
if (!session_reinsert_idle_conn(conn->owner, conn)) {
|
||||
/* session add conn failure */
|
||||
conn->mux->destroy(conn->ctx);
|
||||
CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx));
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
@ -291,7 +291,7 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
||||
old_mux_ctx = conn->ctx;
|
||||
conn->mux = new_mux;
|
||||
conn->ctx = ctx;
|
||||
if (new_mux->init(conn, bind_conf->frontend, conn->owner, buf) == -1) {
|
||||
if (CALL_MUX_WITH_RET(new_mux, init(conn, bind_conf->frontend, conn->owner, buf)) == -1) {
|
||||
/* The mux upgrade failed, so restore the old mux */
|
||||
conn->ctx = old_mux_ctx;
|
||||
conn->mux = old_mux;
|
||||
@ -300,7 +300,7 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
||||
|
||||
/* The mux was upgraded, destroy the old one */
|
||||
*buf = BUF_NULL;
|
||||
old_mux->destroy(old_mux_ctx);
|
||||
CALL_MUX_NO_RET(old_mux, destroy(old_mux_ctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -658,7 +658,7 @@ void conn_free(struct connection *conn)
|
||||
void conn_release(struct connection *conn)
|
||||
{
|
||||
if (conn->mux) {
|
||||
conn->mux->destroy(conn->ctx);
|
||||
CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx));
|
||||
}
|
||||
else {
|
||||
conn_stop_tracking(conn);
|
||||
@ -3034,7 +3034,7 @@ static struct task *mux_stopping_process(struct task *t, void *ctx, unsigned int
|
||||
|
||||
list_for_each_entry_safe(conn, back, &mux_stopping_data[tid].list, stopping_list) {
|
||||
if (conn->mux && conn->mux->wake)
|
||||
conn->mux->wake(conn);
|
||||
CALL_MUX_NO_RET(conn->mux, wake(conn));
|
||||
}
|
||||
|
||||
return t;
|
||||
|
||||
@ -367,6 +367,9 @@ void ha_thread_dump_one(struct buffer *buf, int is_caller)
|
||||
(now - th_ctx->sched_call_date));
|
||||
}
|
||||
|
||||
/* report the execution context when known */
|
||||
chunk_append_thread_ctx(buf, &th_ctx->exec_ctx, " exec_ctx: ", "\n");
|
||||
|
||||
/* this is the end of what we can dump from outside the current thread */
|
||||
|
||||
chunk_appendf(buf, " curr_task=");
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
struct pool_head *pool_head_buffer __read_mostly;
|
||||
struct pool_head *pool_head_large_buffer __read_mostly = NULL;
|
||||
struct pool_head *pool_head_small_buffer __read_mostly;
|
||||
|
||||
/* perform minimal initializations, report 0 in case of error, 1 if OK. */
|
||||
int init_buffer()
|
||||
@ -43,6 +44,12 @@ int init_buffer()
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (global.tune.bufsize_small) {
|
||||
pool_head_small_buffer = create_aligned_pool("small_buffer", global.tune.bufsize_small, 64, MEM_F_SHARED|MEM_F_EXACT);
|
||||
if (!pool_head_small_buffer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* make sure any change to the queues assignment isn't overlooked */
|
||||
BUG_ON(DB_PERMANENT - DB_UNLIKELY - 1 != DYNBUF_NBQ);
|
||||
BUG_ON(DB_MUX_RX_Q < DB_SE_RX_Q || DB_MUX_RX_Q >= DYNBUF_NBQ);
|
||||
|
||||
13
src/ech.c
13
src/ech.c
@ -136,6 +136,10 @@ static int cli_parse_show_ech(char **args, char *payload,
|
||||
{
|
||||
struct show_ech_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
|
||||
/* no parameter, shows only file list */
|
||||
if (*args[3]) {
|
||||
SSL_CTX *sctx = NULL;
|
||||
@ -297,6 +301,9 @@ static int cli_parse_add_ech(char **args, char *payload, struct appctx *appctx,
|
||||
OSSL_ECHSTORE *es = NULL;
|
||||
BIO *es_in = NULL;
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
if (!*args[3] || !payload)
|
||||
return cli_err(appctx, "syntax: add ssl ech <name> <PEM file content>");
|
||||
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
||||
@ -324,6 +331,9 @@ static int cli_parse_set_ech(char **args, char *payload, struct appctx *appctx,
|
||||
OSSL_ECHSTORE *es = NULL;
|
||||
BIO *es_in = NULL;
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
if (!*args[3] || !payload)
|
||||
return cli_err(appctx, "syntax: set ssl ech <name> <PEM file content>");
|
||||
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
||||
@ -351,6 +361,9 @@ static int cli_parse_del_ech(char **args, char *payload, struct appctx *appctx,
|
||||
char success_message[ECH_SUCCESS_MSG_MAX];
|
||||
OSSL_ECHSTORE *es = NULL;
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
if (!*args[3])
|
||||
return cli_err(appctx, "syntax: del ssl ech <name>");
|
||||
if (*args[4])
|
||||
|
||||
@ -221,7 +221,8 @@ static int fcgi_flt_check(struct proxy *px, struct flt_conf *fconf)
|
||||
}
|
||||
|
||||
list_for_each_entry(f, &px->filter_configs, list) {
|
||||
if (f->id == http_comp_flt_id || f->id == cache_store_flt_id)
|
||||
if (f->id == http_comp_req_flt_id || f->id == http_comp_res_flt_id ||
|
||||
f->id == cache_store_flt_id)
|
||||
continue;
|
||||
else if ((f->id == fconf->id) && f->conf != fcgi_conf) {
|
||||
ha_alert("proxy '%s' : only one fcgi-app supported per backend.\n",
|
||||
|
||||
@ -450,7 +450,8 @@ flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int fla
|
||||
f->flags |= flags;
|
||||
|
||||
if (FLT_OPS(f)->attach) {
|
||||
int ret = FLT_OPS(f)->attach(s, f);
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, f->config);
|
||||
int ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(f)->attach(s, f));
|
||||
if (ret <= 0) {
|
||||
pool_free(pool_head_filter, f);
|
||||
return ret;
|
||||
@ -506,8 +507,10 @@ flt_stream_release(struct stream *s, int only_backend)
|
||||
list_for_each_entry_safe(filter, back, &strm_flt(s)->filters, list) {
|
||||
if (!only_backend || (filter->flags & FLT_FL_IS_BACKEND_FILTER)) {
|
||||
filter->calls++;
|
||||
if (FLT_OPS(filter)->detach)
|
||||
FLT_OPS(filter)->detach(s, filter);
|
||||
if (FLT_OPS(filter)->detach) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
EXEC_CTX_NO_RET(exec_ctx, FLT_OPS(filter)->detach(s, filter));
|
||||
}
|
||||
LIST_DELETE(&filter->list);
|
||||
LIST_DELETE(&filter->req_list);
|
||||
LIST_DELETE(&filter->res_list);
|
||||
@ -530,8 +533,10 @@ flt_stream_start(struct stream *s)
|
||||
|
||||
list_for_each_entry(filter, &strm_flt(s)->filters, list) {
|
||||
if (FLT_OPS(filter)->stream_start) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
filter->calls++;
|
||||
if (FLT_OPS(filter)->stream_start(s, filter) < 0) {
|
||||
if (EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->stream_start(s, filter) < 0)) {
|
||||
s->last_entity.type = STRM_ENTITY_FILTER;
|
||||
s->last_entity.ptr = filter;
|
||||
return -1;
|
||||
@ -556,8 +561,10 @@ flt_stream_stop(struct stream *s)
|
||||
|
||||
list_for_each_entry(filter, &strm_flt(s)->filters, list) {
|
||||
if (FLT_OPS(filter)->stream_stop) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
filter->calls++;
|
||||
FLT_OPS(filter)->stream_stop(s, filter);
|
||||
EXEC_CTX_NO_RET(exec_ctx, FLT_OPS(filter)->stream_stop(s, filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -573,8 +580,10 @@ flt_stream_check_timeouts(struct stream *s)
|
||||
|
||||
list_for_each_entry(filter, &strm_flt(s)->filters, list) {
|
||||
if (FLT_OPS(filter)->check_timeouts) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
filter->calls++;
|
||||
FLT_OPS(filter)->check_timeouts(s, filter);
|
||||
EXEC_CTX_NO_RET(exec_ctx, FLT_OPS(filter)->check_timeouts(s, filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -601,8 +610,10 @@ flt_set_stream_backend(struct stream *s, struct proxy *be)
|
||||
end:
|
||||
list_for_each_entry(filter, &strm_flt(s)->filters, list) {
|
||||
if (FLT_OPS(filter)->stream_set_backend) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
filter->calls++;
|
||||
if (FLT_OPS(filter)->stream_set_backend(s, filter, be) < 0) {
|
||||
if (EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->stream_set_backend(s, filter, be) < 0)) {
|
||||
s->last_entity.type = STRM_ENTITY_FILTER;
|
||||
s->last_entity.ptr = filter;
|
||||
return -1;
|
||||
@ -650,9 +661,11 @@ flt_http_end(struct stream *s, struct http_msg *msg)
|
||||
continue;
|
||||
|
||||
if (FLT_OPS(filter)->http_end) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->http_end(s, filter, msg);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->http_end(s, filter, msg));
|
||||
if (ret <= 0) {
|
||||
resume_filter_list_break(s, msg->chn, filter, ret);
|
||||
goto end;
|
||||
@ -681,9 +694,11 @@ flt_http_reset(struct stream *s, struct http_msg *msg)
|
||||
for (filter = flt_list_start(s, msg->chn); filter;
|
||||
filter = flt_list_next(s, msg->chn, filter)) {
|
||||
if (FLT_OPS(filter)->http_reset) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
FLT_OPS(filter)->http_reset(s, filter, msg);
|
||||
EXEC_CTX_NO_RET(exec_ctx, FLT_OPS(filter)->http_reset(s, filter, msg));
|
||||
}
|
||||
}
|
||||
DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s);
|
||||
@ -701,9 +716,11 @@ flt_http_reply(struct stream *s, short status, const struct buffer *msg)
|
||||
DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s, s->txn, msg);
|
||||
list_for_each_entry(filter, &strm_flt(s)->filters, list) {
|
||||
if (FLT_OPS(filter)->http_reply) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
FLT_OPS(filter)->http_reply(s, filter, status, msg);
|
||||
EXEC_CTX_NO_RET(exec_ctx, FLT_OPS(filter)->http_reply(s, filter, status, msg));
|
||||
}
|
||||
}
|
||||
DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s);
|
||||
@ -732,6 +749,7 @@ flt_http_payload(struct stream *s, struct http_msg *msg, unsigned int len)
|
||||
DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s, s->txn, msg);
|
||||
for (filter = flt_list_start(s, msg->chn); filter;
|
||||
filter = flt_list_next(s, msg->chn, filter)) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
unsigned long long *flt_off = &FLT_OFF(filter, msg->chn);
|
||||
unsigned int offset = *flt_off - *strm_off;
|
||||
|
||||
@ -745,7 +763,7 @@ flt_http_payload(struct stream *s, struct http_msg *msg, unsigned int len)
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->http_payload(s, filter, msg, out + offset, data - offset);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->http_payload(s, filter, msg, out + offset, data - offset));
|
||||
if (ret < 0) {
|
||||
resume_filter_list_break(s, msg->chn, filter, ret);
|
||||
goto end;
|
||||
@ -815,9 +833,11 @@ flt_start_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
|
||||
|
||||
FLT_OFF(filter, chn) = 0;
|
||||
if (FLT_OPS(filter)->channel_start_analyze) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->channel_start_analyze(s, filter, chn);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->channel_start_analyze(s, filter, chn));
|
||||
if (ret <= 0) {
|
||||
resume_filter_list_break(s, chn, filter, ret);
|
||||
goto end;
|
||||
@ -852,9 +872,11 @@ flt_pre_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
|
||||
for (filter = resume_filter_list_start(s, chn); filter;
|
||||
filter = resume_filter_list_next(s, chn, filter)) {
|
||||
if (FLT_OPS(filter)->channel_pre_analyze && (filter->pre_analyzers & an_bit)) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->channel_pre_analyze(s, filter, chn, an_bit);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->channel_pre_analyze(s, filter, chn, an_bit));
|
||||
if (ret <= 0) {
|
||||
resume_filter_list_break(s, chn, filter, ret);
|
||||
goto check_result;
|
||||
@ -889,9 +911,11 @@ flt_post_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
|
||||
for (filter = flt_list_start(s, chn); filter;
|
||||
filter = flt_list_next(s, chn, filter)) {
|
||||
if (FLT_OPS(filter)->channel_post_analyze && (filter->post_analyzers & an_bit)) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->channel_post_analyze(s, filter, chn, an_bit);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->channel_post_analyze(s, filter, chn, an_bit));
|
||||
if (ret < 0) {
|
||||
resume_filter_list_break(s, chn, filter, ret);
|
||||
break;
|
||||
@ -922,9 +946,11 @@ flt_analyze_http_headers(struct stream *s, struct channel *chn, unsigned int an_
|
||||
for (filter = resume_filter_list_start(s, chn); filter;
|
||||
filter = resume_filter_list_next(s, chn, filter)) {
|
||||
if (FLT_OPS(filter)->http_headers) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->http_headers(s, filter, msg);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->http_headers(s, filter, msg));
|
||||
if (ret <= 0) {
|
||||
resume_filter_list_break(s, chn, filter, ret);
|
||||
goto check_result;
|
||||
@ -973,9 +999,11 @@ flt_end_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
|
||||
unregister_data_filter(s, chn, filter);
|
||||
|
||||
if (FLT_OPS(filter)->channel_end_analyze) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->channel_end_analyze(s, filter, chn);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->channel_end_analyze(s, filter, chn));
|
||||
if (ret <= 0) {
|
||||
resume_filter_list_break(s, chn, filter, ret);
|
||||
goto end;
|
||||
@ -1042,6 +1070,7 @@ flt_tcp_payload(struct stream *s, struct channel *chn, unsigned int len)
|
||||
DBG_TRACE_ENTER(STRM_EV_TCP_ANA|STRM_EV_FLT_ANA, s);
|
||||
for (filter = flt_list_start(s, chn); filter;
|
||||
filter = flt_list_next(s, chn, filter)) {
|
||||
struct thread_exec_ctx exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FLT, filter->config);
|
||||
unsigned long long *flt_off = &FLT_OFF(filter, chn);
|
||||
unsigned int offset = *flt_off - *strm_off;
|
||||
|
||||
@ -1055,7 +1084,7 @@ flt_tcp_payload(struct stream *s, struct channel *chn, unsigned int len)
|
||||
|
||||
DBG_TRACE_DEVEL(FLT_ID(filter), STRM_EV_TCP_ANA|STRM_EV_FLT_ANA, s);
|
||||
filter->calls++;
|
||||
ret = FLT_OPS(filter)->tcp_payload(s, filter, chn, out + offset, data - offset);
|
||||
ret = EXEC_CTX_WITH_RET(exec_ctx, FLT_OPS(filter)->tcp_payload(s, filter, chn, out + offset, data - offset));
|
||||
if (ret < 0) {
|
||||
resume_filter_list_break(s, chn, filter, ret);
|
||||
goto end;
|
||||
|
||||
@ -27,17 +27,15 @@
|
||||
|
||||
#define COMP_STATE_PROCESSING 0x01
|
||||
|
||||
const char *http_comp_flt_id = "compression filter";
|
||||
const char *http_comp_req_flt_id = "comp-req filter";
|
||||
const char *http_comp_res_flt_id = "comp-res filter";
|
||||
|
||||
struct flt_ops comp_ops;
|
||||
struct flt_ops comp_req_ops;
|
||||
struct flt_ops comp_res_ops;
|
||||
|
||||
struct comp_state {
|
||||
/*
|
||||
* For both comp_ctx and comp_algo, COMP_DIR_REQ is the index
|
||||
* for requests, and COMP_DIR_RES for responses
|
||||
*/
|
||||
struct comp_ctx *comp_ctx[2]; /* compression context */
|
||||
struct comp_algo *comp_algo[2]; /* compression algorithm if not NULL */
|
||||
struct comp_ctx *comp_ctx; /* compression context */
|
||||
struct comp_algo *comp_algo; /* compression algorithm if not NULL */
|
||||
unsigned int flags; /* COMP_STATE_* */
|
||||
};
|
||||
|
||||
@ -76,10 +74,8 @@ comp_strm_init(struct stream *s, struct filter *filter)
|
||||
if (st == NULL)
|
||||
return -1;
|
||||
|
||||
st->comp_algo[COMP_DIR_REQ] = NULL;
|
||||
st->comp_algo[COMP_DIR_RES] = NULL;
|
||||
st->comp_ctx[COMP_DIR_REQ] = NULL;
|
||||
st->comp_ctx[COMP_DIR_RES] = NULL;
|
||||
st->comp_algo = NULL;
|
||||
st->comp_ctx = NULL;
|
||||
st->flags = 0;
|
||||
filter->ctx = st;
|
||||
|
||||
@ -100,10 +96,8 @@ comp_strm_deinit(struct stream *s, struct filter *filter)
|
||||
return;
|
||||
|
||||
/* release any possible compression context */
|
||||
if (st->comp_algo[COMP_DIR_REQ])
|
||||
st->comp_algo[COMP_DIR_REQ]->end(&st->comp_ctx[COMP_DIR_REQ]);
|
||||
if (st->comp_algo[COMP_DIR_RES])
|
||||
st->comp_algo[COMP_DIR_RES]->end(&st->comp_ctx[COMP_DIR_RES]);
|
||||
if (st->comp_algo)
|
||||
st->comp_algo->end(&st->comp_ctx);
|
||||
pool_free(pool_head_comp_state, st);
|
||||
filter->ctx = NULL;
|
||||
}
|
||||
@ -172,9 +166,9 @@ comp_prepare_compress_request(struct comp_state *st, struct stream *s, struct ht
|
||||
if (txn->meth == HTTP_METH_HEAD)
|
||||
return;
|
||||
if (s->be->comp && s->be->comp->algo_req != NULL)
|
||||
st->comp_algo[COMP_DIR_REQ] = s->be->comp->algo_req;
|
||||
st->comp_algo = s->be->comp->algo_req;
|
||||
else if (strm_fe(s)->comp && strm_fe(s)->comp->algo_req != NULL)
|
||||
st->comp_algo[COMP_DIR_REQ] = strm_fe(s)->comp->algo_req;
|
||||
st->comp_algo = strm_fe(s)->comp->algo_req;
|
||||
else
|
||||
goto fail; /* no algo selected: nothing to do */
|
||||
|
||||
@ -189,43 +183,34 @@ comp_prepare_compress_request(struct comp_state *st, struct stream *s, struct ht
|
||||
goto fail;
|
||||
|
||||
/* initialize compression */
|
||||
if (st->comp_algo[COMP_DIR_REQ]->init(&st->comp_ctx[COMP_DIR_REQ], global.tune.comp_maxlevel) < 0)
|
||||
if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
|
||||
goto fail;
|
||||
|
||||
return;
|
||||
fail:
|
||||
st->comp_algo[COMP_DIR_REQ] = NULL;
|
||||
st->comp_algo = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
comp_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
|
||||
comp_req_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
|
||||
{
|
||||
struct comp_state *st = filter->ctx;
|
||||
int comp_flags = 0;
|
||||
|
||||
if (!strm_fe(s)->comp && !s->be->comp)
|
||||
goto end;
|
||||
|
||||
if (strm_fe(s)->comp)
|
||||
comp_flags |= strm_fe(s)->comp->flags;
|
||||
if (s->be->comp)
|
||||
comp_flags |= s->be->comp->flags;
|
||||
|
||||
if (!(comp_flags & COMP_FL_DIR_REQ))
|
||||
goto end;
|
||||
|
||||
if (!(msg->chn->flags & CF_ISRESP)) {
|
||||
if (comp_flags & COMP_FL_DIR_REQ) {
|
||||
comp_prepare_compress_request(st, s, msg);
|
||||
if (st->comp_algo[COMP_DIR_REQ]) {
|
||||
if (!set_compression_header(st, s, msg))
|
||||
goto end;
|
||||
register_data_filter(s, msg->chn, filter);
|
||||
st->flags |= COMP_STATE_PROCESSING;
|
||||
}
|
||||
}
|
||||
if (comp_flags & COMP_FL_DIR_RES)
|
||||
select_compression_request_header(st, s, msg);
|
||||
} else if (comp_flags & COMP_FL_DIR_RES) {
|
||||
/* Response headers have already been checked in
|
||||
* comp_http_post_analyze callback. */
|
||||
if (st->comp_algo[COMP_DIR_RES]) {
|
||||
comp_prepare_compress_request(st, s, msg);
|
||||
if (st->comp_algo) {
|
||||
if (!set_compression_header(st, s, msg))
|
||||
goto end;
|
||||
register_data_filter(s, msg->chn, filter);
|
||||
@ -238,8 +223,43 @@ comp_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
|
||||
}
|
||||
|
||||
static int
|
||||
comp_http_post_analyze(struct stream *s, struct filter *filter,
|
||||
struct channel *chn, unsigned an_bit)
|
||||
comp_res_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
|
||||
{
|
||||
struct comp_state *st = filter->ctx;
|
||||
int comp_flags = 0;
|
||||
|
||||
if (!strm_fe(s)->comp && !s->be->comp)
|
||||
goto end;
|
||||
|
||||
if (strm_fe(s)->comp)
|
||||
comp_flags |= strm_fe(s)->comp->flags;
|
||||
if (s->be->comp)
|
||||
comp_flags |= s->be->comp->flags;
|
||||
|
||||
if (!(comp_flags & COMP_FL_DIR_RES))
|
||||
goto end;
|
||||
|
||||
|
||||
if (!(msg->chn->flags & CF_ISRESP))
|
||||
select_compression_request_header(st, s, msg);
|
||||
else {
|
||||
/* Response headers have already been checked in
|
||||
* comp_res_http_post_analyze callback. */
|
||||
if (st->comp_algo) {
|
||||
if (!set_compression_header(st, s, msg))
|
||||
goto end;
|
||||
register_data_filter(s, msg->chn, filter);
|
||||
st->flags |= COMP_STATE_PROCESSING;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
comp_res_http_post_analyze(struct stream *s, struct filter *filter,
|
||||
struct channel *chn, unsigned an_bit)
|
||||
{
|
||||
struct http_txn *txn = s->txn;
|
||||
struct http_msg *msg = &txn->rsp;
|
||||
@ -259,19 +279,13 @@ comp_http_post_analyze(struct stream *s, struct filter *filter,
|
||||
|
||||
static int
|
||||
comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
|
||||
unsigned int offset, unsigned int len)
|
||||
unsigned int offset, unsigned int len, int dir)
|
||||
{
|
||||
struct comp_state *st = filter->ctx;
|
||||
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||
struct htx_ret htxret = htx_find_offset(htx, offset);
|
||||
struct htx_blk *blk, *next;
|
||||
int ret, consumed = 0, to_forward = 0, last = 0;
|
||||
int dir;
|
||||
|
||||
if (msg->chn->flags & CF_ISRESP)
|
||||
dir = COMP_DIR_RES;
|
||||
else
|
||||
dir = COMP_DIR_REQ;
|
||||
|
||||
blk = htxret.blk;
|
||||
offset = htxret.ret;
|
||||
@ -361,7 +375,7 @@ comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
|
||||
if (to_forward != consumed)
|
||||
flt_update_offsets(filter, msg->chn, to_forward - consumed);
|
||||
|
||||
if (st->comp_ctx[dir] && st->comp_ctx[dir]->cur_lvl > 0) {
|
||||
if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
|
||||
update_freq_ctr(&global.comp_bps_in, consumed);
|
||||
if (s->sess->fe_tgcounters) {
|
||||
_HA_ATOMIC_ADD(&s->sess->fe_tgcounters->comp_in[dir], consumed);
|
||||
@ -384,14 +398,33 @@ comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
comp_req_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
|
||||
unsigned int offset, unsigned int len)
|
||||
{
|
||||
if (msg->chn->flags & CF_ISRESP)
|
||||
return 0;
|
||||
|
||||
return comp_http_payload(s, filter, msg, offset, len, COMP_DIR_REQ);
|
||||
}
|
||||
|
||||
static int
|
||||
comp_http_end(struct stream *s, struct filter *filter,
|
||||
struct http_msg *msg)
|
||||
comp_res_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
|
||||
unsigned int offset, unsigned int len)
|
||||
{
|
||||
if (!(msg->chn->flags & CF_ISRESP))
|
||||
return 0;
|
||||
|
||||
return comp_http_payload(s, filter, msg, offset, len, COMP_DIR_RES);
|
||||
}
|
||||
|
||||
static int
|
||||
comp_res_http_end(struct stream *s, struct filter *filter,
|
||||
struct http_msg *msg)
|
||||
{
|
||||
struct comp_state *st = filter->ctx;
|
||||
|
||||
if (!(msg->chn->flags & CF_ISRESP) || !st || !st->comp_algo[COMP_DIR_RES])
|
||||
if (!(msg->chn->flags & CF_ISRESP) || !st || !st->comp_algo)
|
||||
goto end;
|
||||
|
||||
if (strm_fe(s)->mode == PR_MODE_HTTP && s->sess->fe_tgcounters)
|
||||
@ -411,18 +444,12 @@ set_compression_header(struct comp_state *st, struct stream *s, struct http_msg
|
||||
struct htx_sl *sl;
|
||||
struct http_hdr_ctx ctx, last_vary;
|
||||
struct comp_algo *comp_algo;
|
||||
int comp_index;
|
||||
|
||||
if (msg->chn->flags & CF_ISRESP)
|
||||
comp_index = COMP_DIR_RES;
|
||||
else
|
||||
comp_index = COMP_DIR_REQ;
|
||||
|
||||
sl = http_get_stline(htx);
|
||||
if (!sl)
|
||||
goto error;
|
||||
|
||||
comp_algo = st->comp_algo[comp_index];
|
||||
comp_algo = st->comp_algo;
|
||||
|
||||
/* add "Transfer-Encoding: chunked" header */
|
||||
if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
|
||||
@ -496,8 +523,8 @@ set_compression_header(struct comp_state *st, struct stream *s, struct http_msg
|
||||
return 1;
|
||||
|
||||
error:
|
||||
st->comp_algo[comp_index]->end(&st->comp_ctx[comp_index]);
|
||||
st->comp_algo[comp_index] = NULL;
|
||||
st->comp_algo->end(&st->comp_ctx);
|
||||
st->comp_algo = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -525,7 +552,7 @@ select_compression_request_header(struct comp_state *st, struct stream *s, struc
|
||||
*(ctx.value.ptr + 30) < '6' ||
|
||||
(*(ctx.value.ptr + 30) == '6' &&
|
||||
(ctx.value.len < 54 || memcmp(ctx.value.ptr + 51, "SV1", 3) != 0)))) {
|
||||
st->comp_algo[COMP_DIR_RES] = NULL;
|
||||
st->comp_algo = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -579,7 +606,7 @@ select_compression_request_header(struct comp_state *st, struct stream *s, struc
|
||||
for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
|
||||
if (*(ctx.value.ptr) == '*' ||
|
||||
word_match(ctx.value.ptr, toklen, comp_algo->ua_name, comp_algo->ua_name_len)) {
|
||||
st->comp_algo[COMP_DIR_RES] = comp_algo;
|
||||
st->comp_algo = comp_algo;
|
||||
best_q = q;
|
||||
break;
|
||||
}
|
||||
@ -588,7 +615,7 @@ select_compression_request_header(struct comp_state *st, struct stream *s, struc
|
||||
}
|
||||
|
||||
/* remove all occurrences of the header when "compression offload" is set */
|
||||
if (st->comp_algo[COMP_DIR_RES]) {
|
||||
if (st->comp_algo) {
|
||||
if ((s->be->comp && (s->be->comp->flags & COMP_FL_OFFLOAD)) ||
|
||||
(strm_fe(s)->comp && (strm_fe(s)->comp->flags & COMP_FL_OFFLOAD))) {
|
||||
http_remove_header(htx, &ctx);
|
||||
@ -604,13 +631,13 @@ select_compression_request_header(struct comp_state *st, struct stream *s, struc
|
||||
(strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos_res))) {
|
||||
for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
|
||||
if (comp_algo->cfg_name_len == 8 && memcmp(comp_algo->cfg_name, "identity", 8) == 0) {
|
||||
st->comp_algo[COMP_DIR_RES] = comp_algo;
|
||||
st->comp_algo = comp_algo;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
st->comp_algo[COMP_DIR_RES] = NULL;
|
||||
st->comp_algo = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -627,7 +654,7 @@ select_compression_response_header(struct comp_state *st, struct stream *s, stru
|
||||
unsigned int comp_minsize = 0;
|
||||
|
||||
/* no common compression algorithm was found in request header */
|
||||
if (st->comp_algo[COMP_DIR_RES] == NULL)
|
||||
if (st->comp_algo == NULL)
|
||||
goto fail;
|
||||
|
||||
/* compression already in progress */
|
||||
@ -725,13 +752,13 @@ select_compression_response_header(struct comp_state *st, struct stream *s, stru
|
||||
goto fail;
|
||||
|
||||
/* initialize compression */
|
||||
if (st->comp_algo[COMP_DIR_RES]->init(&st->comp_ctx[COMP_DIR_RES], global.tune.comp_maxlevel) < 0)
|
||||
if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
|
||||
goto fail;
|
||||
msg->flags |= HTTP_MSGF_COMPRESSING;
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
st->comp_algo[COMP_DIR_RES] = NULL;
|
||||
st->comp_algo = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -754,7 +781,7 @@ htx_compression_buffer_add_data(struct comp_state *st, const char *data, size_t
|
||||
struct buffer *out, int dir)
|
||||
{
|
||||
|
||||
return st->comp_algo[dir]->add_data(st->comp_ctx[dir], data, len, out);
|
||||
return st->comp_algo->add_data(st->comp_ctx, data, len, out);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -762,26 +789,58 @@ htx_compression_buffer_end(struct comp_state *st, struct buffer *out, int end, i
|
||||
{
|
||||
|
||||
if (end)
|
||||
return st->comp_algo[dir]->finish(st->comp_ctx[dir], out);
|
||||
return st->comp_algo->finish(st->comp_ctx, out);
|
||||
else
|
||||
return st->comp_algo[dir]->flush(st->comp_ctx[dir], out);
|
||||
return st->comp_algo->flush(st->comp_ctx, out);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
struct flt_ops comp_ops = {
|
||||
|
||||
struct flt_ops comp_req_ops = {
|
||||
.init = comp_flt_init,
|
||||
|
||||
.attach = comp_strm_init,
|
||||
.detach = comp_strm_deinit,
|
||||
|
||||
.channel_post_analyze = comp_http_post_analyze,
|
||||
|
||||
.http_headers = comp_http_headers,
|
||||
.http_payload = comp_http_payload,
|
||||
.http_end = comp_http_end,
|
||||
.http_headers = comp_req_http_headers,
|
||||
.http_payload = comp_req_http_payload,
|
||||
};
|
||||
|
||||
struct flt_ops comp_res_ops = {
|
||||
.init = comp_flt_init,
|
||||
|
||||
.attach = comp_strm_init,
|
||||
.detach = comp_strm_deinit,
|
||||
|
||||
.channel_post_analyze = comp_res_http_post_analyze,
|
||||
|
||||
.http_headers = comp_res_http_headers,
|
||||
.http_payload = comp_res_http_payload,
|
||||
.http_end = comp_res_http_end,
|
||||
};
|
||||
|
||||
/* returns compression options from <proxy> proxy or allocates them if
|
||||
* needed
|
||||
*
|
||||
* When compression options are created, flags will be set to <defaults>
|
||||
*
|
||||
* Returns NULL in case of memory error
|
||||
*/
|
||||
static inline struct comp *proxy_get_comp(struct proxy *proxy, int defaults)
|
||||
{
|
||||
struct comp *comp;
|
||||
|
||||
if (proxy->comp == NULL) {
|
||||
comp = calloc(1, sizeof(*comp));
|
||||
if (unlikely(!comp))
|
||||
return NULL;
|
||||
comp->flags = defaults;
|
||||
proxy->comp = comp;
|
||||
}
|
||||
return proxy->comp;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_compression_options(char **args, int section, struct proxy *proxy,
|
||||
const struct proxy *defpx, const char *file, int line,
|
||||
@ -791,19 +850,13 @@ parse_compression_options(char **args, int section, struct proxy *proxy,
|
||||
int ret = 0;
|
||||
const char *res;
|
||||
|
||||
if (proxy->comp == NULL) {
|
||||
comp = calloc(1, sizeof(*comp));
|
||||
if (unlikely(!comp)) {
|
||||
memprintf(err, "'%s': out of memory.", args[0]);
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
/* Always default to compress responses */
|
||||
comp->flags = COMP_FL_DIR_RES;
|
||||
proxy->comp = comp;
|
||||
/* always default to compress responses */
|
||||
comp = proxy_get_comp(proxy, COMP_FL_DIR_RES);
|
||||
if (comp == NULL) {
|
||||
memprintf(err, "'%s': out of memory.", args[0]);
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
comp = proxy->comp;
|
||||
|
||||
if (strcmp(args[1], "algo") == 0 || strcmp(args[1], "algo-res") == 0) {
|
||||
struct comp_ctx *ctx;
|
||||
@ -970,27 +1023,109 @@ parse_http_comp_flt(char **args, int *cur_arg, struct proxy *px,
|
||||
struct flt_conf *fconf, char **err, void *private)
|
||||
{
|
||||
struct flt_conf *fc, *back;
|
||||
struct flt_conf *fconf_res;
|
||||
|
||||
list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
|
||||
if (fc->id == http_comp_flt_id) {
|
||||
if (fc->id == http_comp_req_flt_id || fc->id == http_comp_res_flt_id) {
|
||||
memprintf(err, "%s: Proxy supports only one compression filter\n", px->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fconf->id = http_comp_flt_id;
|
||||
fconf->id = http_comp_req_flt_id;
|
||||
fconf->conf = NULL;
|
||||
fconf->ops = &comp_ops;
|
||||
fconf->ops = &comp_req_ops;
|
||||
|
||||
/* FILTER API prepared a single filter_conf struct as it is meant to
|
||||
* initialize exactly one fconf per keyword, but with the "compression"
|
||||
* filter, for retro-compatibility we want to emulate the historical
|
||||
* behavior which is to compress both requests and responses, so to
|
||||
* emulate that we manually initialize the comp-res filter as well
|
||||
*/
|
||||
fconf_res = calloc(1, sizeof(*fconf_res));
|
||||
if (!fconf_res) {
|
||||
memprintf(err, "'%s' : out of memory", args[0]);
|
||||
return -1;
|
||||
}
|
||||
fconf_res->id = http_comp_res_flt_id;
|
||||
fconf_res->conf = NULL;
|
||||
fconf_res->ops = &comp_res_ops;
|
||||
|
||||
/* manually add the fconf_res to the list because filter API doesn't
|
||||
* know about it
|
||||
*/
|
||||
LIST_APPEND(&px->filter_configs, &fconf_res->list);
|
||||
|
||||
|
||||
(*cur_arg)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_http_comp_req_flt(char **args, int *cur_arg, struct proxy *px,
|
||||
struct flt_conf *fconf, char **err, void *private)
|
||||
{
|
||||
struct flt_conf *fc, *back;
|
||||
struct comp *comp;
|
||||
|
||||
list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
|
||||
if (fc->id == http_comp_req_flt_id) {
|
||||
memprintf(err, "%s: Proxy supports only one comp-req filter\n", px->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
comp = proxy_get_comp(px, 0);
|
||||
if (comp == NULL) {
|
||||
memprintf(err, "memory failure\n");
|
||||
return -1;
|
||||
}
|
||||
comp->flags |= COMP_FL_DIR_REQ;
|
||||
|
||||
fconf->id = http_comp_req_flt_id;
|
||||
fconf->conf = NULL;
|
||||
fconf->ops = &comp_req_ops;
|
||||
(*cur_arg)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_http_comp_res_flt(char **args, int *cur_arg, struct proxy *px,
|
||||
struct flt_conf *fconf, char **err, void *private)
|
||||
{
|
||||
struct flt_conf *fc, *back;
|
||||
struct comp *comp;
|
||||
|
||||
list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
|
||||
if (fc->id == http_comp_res_flt_id) {
|
||||
memprintf(err, "%s: Proxy supports only one comp-res filter\n", px->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
comp = proxy_get_comp(px, 0);
|
||||
if (comp == NULL) {
|
||||
memprintf(err, "memory failure\n");
|
||||
return -1;
|
||||
}
|
||||
comp->flags |= COMP_FL_DIR_RES;
|
||||
|
||||
fconf->id = http_comp_res_flt_id;
|
||||
fconf->conf = NULL;
|
||||
fconf->ops = &comp_res_ops;
|
||||
(*cur_arg)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
check_implicit_http_comp_flt(struct proxy *proxy)
|
||||
{
|
||||
struct flt_conf *fconf;
|
||||
struct flt_conf *fconf_req = NULL;
|
||||
struct flt_conf *fconf_res = NULL;
|
||||
int explicit = 0;
|
||||
int comp = 0;
|
||||
int err = 0;
|
||||
@ -999,7 +1134,7 @@ check_implicit_http_comp_flt(struct proxy *proxy)
|
||||
goto end;
|
||||
if (!LIST_ISEMPTY(&proxy->filter_configs)) {
|
||||
list_for_each_entry(fconf, &proxy->filter_configs, list) {
|
||||
if (fconf->id == http_comp_flt_id)
|
||||
if (fconf->id == http_comp_req_flt_id || fconf->id == http_comp_res_flt_id)
|
||||
comp = 1;
|
||||
else if (fconf->id == cache_store_flt_id) {
|
||||
if (comp) {
|
||||
@ -1027,17 +1162,25 @@ check_implicit_http_comp_flt(struct proxy *proxy)
|
||||
|
||||
/* Implicit declaration of the compression filter is always the last
|
||||
* one */
|
||||
fconf = calloc(1, sizeof(*fconf));
|
||||
if (!fconf) {
|
||||
fconf_req = calloc(1, sizeof(*fconf));
|
||||
fconf_res = calloc(1, sizeof(*fconf));
|
||||
if (!fconf_req || !fconf_res) {
|
||||
ha_alert("config: %s '%s': out of memory\n",
|
||||
proxy_type_str(proxy), proxy->id);
|
||||
ha_free(&fconf_req);
|
||||
ha_free(&fconf_res);
|
||||
err++;
|
||||
goto end;
|
||||
}
|
||||
fconf->id = http_comp_flt_id;
|
||||
fconf->conf = NULL;
|
||||
fconf->ops = &comp_ops;
|
||||
LIST_APPEND(&proxy->filter_configs, &fconf->list);
|
||||
fconf_req->id = http_comp_req_flt_id;
|
||||
fconf_req->conf = NULL;
|
||||
fconf_req->ops = &comp_req_ops;
|
||||
LIST_APPEND(&proxy->filter_configs, &fconf_req->list);
|
||||
|
||||
fconf_res->id = http_comp_res_flt_id;
|
||||
fconf_res->conf = NULL;
|
||||
fconf_res->ops = &comp_res_ops;
|
||||
LIST_APPEND(&proxy->filter_configs, &fconf_res->list);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@ -1072,7 +1215,7 @@ smp_fetch_res_comp_algo(const struct arg *args, struct sample *smp,
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(filter, &strm_flt(smp->strm)->filters, list) {
|
||||
if (FLT_ID(filter) != http_comp_flt_id)
|
||||
if (FLT_ID(filter) != http_comp_res_flt_id)
|
||||
continue;
|
||||
|
||||
if (!(st = filter->ctx))
|
||||
@ -1080,8 +1223,8 @@ smp_fetch_res_comp_algo(const struct arg *args, struct sample *smp,
|
||||
|
||||
smp->data.type = SMP_T_STR;
|
||||
smp->flags = SMP_F_CONST;
|
||||
smp->data.u.str.area = st->comp_algo[COMP_DIR_RES]->cfg_name;
|
||||
smp->data.u.str.data = st->comp_algo[COMP_DIR_RES]->cfg_name_len;
|
||||
smp->data.u.str.area = st->comp_algo->cfg_name;
|
||||
smp->data.u.str.data = st->comp_algo->cfg_name_len;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -1099,6 +1242,8 @@ INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||
/* Declare the filter parser for "compression" keyword */
|
||||
static struct flt_kw_list filter_kws = { "COMP", { }, {
|
||||
{ "compression", parse_http_comp_flt, NULL },
|
||||
{ "comp-req", parse_http_comp_req_flt, NULL },
|
||||
{ "comp-res", parse_http_comp_res_flt, NULL },
|
||||
{ NULL, NULL, NULL },
|
||||
}
|
||||
};
|
||||
|
||||
@ -516,8 +516,11 @@ static void spoe_handle_appctx(struct appctx *appctx)
|
||||
appctx->st0 = SPOE_APPCTX_ST_END;
|
||||
applet_set_error(appctx);
|
||||
}
|
||||
else if (!spoe_handle_receiving_frame_appctx(appctx))
|
||||
break;
|
||||
else {
|
||||
SPOE_APPCTX(appctx)->spoe_ctx->state = SPOE_CTX_ST_WAITING_ACK;
|
||||
if (!spoe_handle_receiving_frame_appctx(appctx))
|
||||
break;
|
||||
}
|
||||
goto switchstate;
|
||||
|
||||
case SPOE_APPCTX_ST_EXIT:
|
||||
@ -1109,6 +1112,16 @@ static int spoe_process_event(struct stream *s, struct spoe_context *ctx,
|
||||
agent->id, spoe_event_str[ev], s->uniq_id, ctx->status_code, ctx->stats.t_process,
|
||||
agent->counters.nb_errors, agent->counters.nb_processed);
|
||||
}
|
||||
else if (ret == 0) {
|
||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) && proxy_abrt_close_def(s->be, 1))) {
|
||||
ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
|
||||
spoe_stop_processing(agent, ctx);
|
||||
spoe_handle_processing_error(s, agent, ctx, dir);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
10
src/h2.c
10
src/h2.c
@ -319,6 +319,7 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
|
||||
*/
|
||||
int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len, int relaxed)
|
||||
{
|
||||
struct htx_blk *tailblk = htx_get_tail_blk(htx);
|
||||
struct ist phdr_val[H2_PHDR_NUM_ENTRIES];
|
||||
uint32_t fields; /* bit mask of H2_PHDR_FND_* */
|
||||
uint32_t idx;
|
||||
@ -533,6 +534,7 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
htx_truncate_blk(htx, tailblk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -637,6 +639,7 @@ static struct htx_sl *h2_prepare_htx_stsline(uint32_t fields, struct ist *phdr,
|
||||
*/
|
||||
int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len, char *upgrade_protocol)
|
||||
{
|
||||
struct htx_blk *tailblk = htx_get_tail_blk(htx);
|
||||
struct ist phdr_val[H2_PHDR_NUM_ENTRIES];
|
||||
uint32_t fields; /* bit mask of H2_PHDR_FND_* */
|
||||
uint32_t idx;
|
||||
@ -793,6 +796,7 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
htx_truncate_blk(htx, tailblk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -812,6 +816,7 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m
|
||||
*/
|
||||
int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx)
|
||||
{
|
||||
struct htx_blk *tailblk = htx_get_tail_blk(htx);
|
||||
const char *ctl;
|
||||
struct ist v;
|
||||
uint32_t idx;
|
||||
@ -861,8 +866,8 @@ int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check the number of blocks against "tune.http.maxhdr" value before adding EOT block */
|
||||
if (htx_nbblks(htx) > global.tune.max_http_hdr)
|
||||
/* Check the number of trailers against "tune.http.maxhdr" value before adding EOT block */
|
||||
if (idx > global.tune.max_http_hdr)
|
||||
goto fail;
|
||||
|
||||
if (!htx_add_endof(htx, HTX_BLK_EOT))
|
||||
@ -871,5 +876,6 @@ int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx)
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
htx_truncate_blk(htx, tailblk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
53
src/h3.c
53
src/h3.c
@ -641,7 +641,7 @@ static ssize_t h3_req_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
/* TODO support trailer parsing in this function */
|
||||
|
||||
/* TODO support buffer wrapping */
|
||||
BUG_ON(b_head(buf) + len >= b_wrap(buf));
|
||||
BUG_ON(b_head(buf) + len > b_wrap(buf));
|
||||
ret = qpack_decode_fs((const unsigned char *)b_head(buf), len, tmp,
|
||||
list, sizeof(list) / sizeof(list[0]));
|
||||
if (ret < 0) {
|
||||
@ -1111,6 +1111,7 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
struct buffer *tmp = get_trash_chunk();
|
||||
struct htx *htx = NULL;
|
||||
struct htx_sl *sl;
|
||||
struct htx_blk *tailblk = NULL;
|
||||
struct http_hdr list[global.tune.max_http_hdr * 2];
|
||||
unsigned int flags = HTX_SL_F_NONE;
|
||||
struct ist status = IST_NULL;
|
||||
@ -1141,7 +1142,7 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
TRACE_ENTER(H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
|
||||
/* TODO support buffer wrapping */
|
||||
BUG_ON(b_head(buf) + len >= b_wrap(buf));
|
||||
BUG_ON(b_head(buf) + len > b_wrap(buf));
|
||||
ret = qpack_decode_fs((const unsigned char *)b_head(buf), len, tmp,
|
||||
list, sizeof(list) / sizeof(list[0]));
|
||||
if (ret < 0) {
|
||||
@ -1161,7 +1162,7 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
}
|
||||
BUG_ON(!b_size(appbuf)); /* TODO */
|
||||
htx = htx_from_buf(appbuf);
|
||||
|
||||
tailblk = htx_get_tail_blk(htx);
|
||||
/* Only handle one HEADERS frame at a time. Thus if HTX buffer is too
|
||||
* small, it happens solely from a single frame and the only option is
|
||||
* to close the stream.
|
||||
@ -1351,8 +1352,11 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
}
|
||||
|
||||
out:
|
||||
if (appbuf)
|
||||
if (appbuf) {
|
||||
if ((ssize_t)len < 0)
|
||||
htx_truncate_blk(htx, tailblk);
|
||||
htx_to_buf(htx, appbuf);
|
||||
}
|
||||
|
||||
TRACE_LEAVE(H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
return len;
|
||||
@ -1376,6 +1380,7 @@ static ssize_t h3_trailers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
struct buffer *appbuf = NULL;
|
||||
struct htx *htx = NULL;
|
||||
struct htx_sl *sl;
|
||||
struct htx_blk *tailblk = NULL;
|
||||
struct http_hdr list[global.tune.max_http_hdr * 2];
|
||||
int hdr_idx, ret;
|
||||
const char *ctl;
|
||||
@ -1386,7 +1391,7 @@ static ssize_t h3_trailers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
TRACE_ENTER(H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
|
||||
/* TODO support buffer wrapping */
|
||||
BUG_ON(b_head(buf) + len >= b_wrap(buf));
|
||||
BUG_ON(b_head(buf) + len > b_wrap(buf));
|
||||
ret = qpack_decode_fs((const unsigned char *)b_head(buf), len, tmp,
|
||||
list, sizeof(list) / sizeof(list[0]));
|
||||
if (ret < 0) {
|
||||
@ -1406,6 +1411,7 @@ static ssize_t h3_trailers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
}
|
||||
BUG_ON(!b_size(appbuf)); /* TODO */
|
||||
htx = htx_from_buf(appbuf);
|
||||
tailblk = htx_get_tail_blk(htx);
|
||||
|
||||
if (!h3s->data_len) {
|
||||
/* Notify that no body is present. This can only happens if
|
||||
@ -1505,7 +1511,7 @@ static ssize_t h3_trailers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
}
|
||||
|
||||
/* Check the number of blocks against "tune.http.maxhdr" value before adding EOT block */
|
||||
if (htx_nbblks(htx) > global.tune.max_http_hdr) {
|
||||
if (hdr_idx > global.tune.max_http_hdr) {
|
||||
len = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -1521,8 +1527,11 @@ static ssize_t h3_trailers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||
|
||||
out:
|
||||
/* HTX may be non NULL if error before previous htx_to_buf(). */
|
||||
if (appbuf)
|
||||
if (appbuf) {
|
||||
if ((ssize_t)len < 0)
|
||||
htx_truncate_blk(htx, tailblk);
|
||||
htx_to_buf(htx, appbuf);
|
||||
}
|
||||
|
||||
TRACE_LEAVE(H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
return len;
|
||||
@ -1745,6 +1754,14 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||
|
||||
if (!b_data(b) && fin && quic_stream_is_bidi(qcs->id)) {
|
||||
TRACE_PROTO("received FIN without data", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||
|
||||
/* FIN received, ensure body length is conform to any content-length header. */
|
||||
if ((h3s->flags & H3_SF_HAVE_CLEN) && h3_check_body_size(qcs, 1)) {
|
||||
qcc_abort_stream_read(qcs);
|
||||
qcc_reset_stream(qcs, h3s->err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (qcs_http_handle_standalone_fin(qcs)) {
|
||||
TRACE_ERROR("cannot set EOM", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||
qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1);
|
||||
@ -1801,10 +1818,11 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||
flen = h3s->demux_frame_len;
|
||||
ftype = h3s->demux_frame_type;
|
||||
|
||||
/* Do not demux incomplete frames except H3 DATA which can be
|
||||
* fragmented in multiple HTX blocks.
|
||||
/* Current HTTP/3 parser can currently only parse fully
|
||||
* received and aligned frames. The only exception is for DATA
|
||||
* frames as they can frequently be larger than bufsize.
|
||||
*/
|
||||
if (flen > b_data(b) && ftype != H3_FT_DATA) {
|
||||
if (ftype != H3_FT_DATA) {
|
||||
/* Reject frames bigger than bufsize.
|
||||
*
|
||||
* TODO HEADERS should in complement be limited with H3
|
||||
@ -1817,7 +1835,20 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||
qcc_report_glitch(qcs->qcc, 1);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
|
||||
/* TODO extend parser to support the realignment of a frame. */
|
||||
if (b_head(b) + b_data(b) > b_wrap(b)) {
|
||||
TRACE_ERROR("cannot parse unaligned data frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||
qcc_set_error(qcs->qcc, H3_ERR_EXCESSIVE_LOAD, 1);
|
||||
qcc_report_glitch(qcs->qcc, 1);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Only parse full HTTP/3 frames. */
|
||||
if (flen > b_data(b)) {
|
||||
TRACE_PROTO("pause parsing on incomplete payload", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
last_stream_frame = (fin && flen == b_data(b));
|
||||
|
||||
@ -271,6 +271,10 @@ unsigned int tainted = 0;
|
||||
unsigned int experimental_directives_allowed = 0;
|
||||
unsigned int deprecated_directives_allowed = 0;
|
||||
|
||||
/* mapped storage for collected libs */
|
||||
void *lib_storage = NULL;
|
||||
size_t lib_size = 0;
|
||||
|
||||
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
|
||||
char **errmsg)
|
||||
{
|
||||
@ -1635,18 +1639,11 @@ void haproxy_init_args(int argc, char **argv)
|
||||
argc--; argv++;
|
||||
}
|
||||
|
||||
ret = trace_parse_cmd(arg, &err_msg);
|
||||
if (ret <= -1) {
|
||||
if (ret < -1) {
|
||||
ha_alert("-dt: %s.\n", err_msg);
|
||||
ha_free(&err_msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else {
|
||||
printf("%s\n", err_msg);
|
||||
ha_free(&err_msg);
|
||||
exit(0);
|
||||
}
|
||||
ret = trace_add_cmd(arg, &err_msg);
|
||||
if (ret) {
|
||||
ha_alert("-dt: %s.\n", err_msg);
|
||||
ha_free(&err_msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
#ifdef HA_USE_KTLS
|
||||
@ -2523,6 +2520,10 @@ static void step_init_2(int argc, char** argv)
|
||||
chunk_appendf(&trash, "TARGET='%s'", pm_target_opts);
|
||||
|
||||
post_mortem_add_component("haproxy", haproxy_version, cc, cflags, opts, argv[0]);
|
||||
|
||||
if ((global.tune.options & (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS)) ==
|
||||
(GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS))
|
||||
collect_libs();
|
||||
}
|
||||
|
||||
/* This is a third part of the late init sequence, where we register signals for
|
||||
@ -3478,6 +3479,7 @@ int main(int argc, char **argv)
|
||||
list_for_each_entry_safe(cfg, cfg_tmp, &cfg_cfgfiles, list)
|
||||
ha_free(&cfg->content);
|
||||
|
||||
trace_parse_cmds();
|
||||
usermsgs_clr(NULL);
|
||||
}
|
||||
|
||||
@ -3771,6 +3773,7 @@ int main(int argc, char **argv)
|
||||
char *msg = NULL;
|
||||
char c;
|
||||
int r __maybe_unused;
|
||||
struct timeval tv = { .tv_sec = 2, .tv_usec = 0 };
|
||||
|
||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sock_pair) == -1) {
|
||||
ha_alert("[%s.main()] Cannot create socketpair to update the new worker state\n",
|
||||
@ -3780,10 +3783,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
list_for_each_entry(proc, &proc_list, list) {
|
||||
if (proc->pid == -1)
|
||||
if (proc->pid == -1 && proc->options & PROC_O_TYPE_WORKER)
|
||||
break;
|
||||
}
|
||||
|
||||
BUG_ON(!(proc->options & PROC_O_TYPE_WORKER));
|
||||
|
||||
if (send_fd_uxst(proc->ipc_fd[1], sock_pair[0]) == -1) {
|
||||
ha_alert("[%s.main()] Cannot transfer connection fd %d over the sockpair@%d\n",
|
||||
argv[0], sock_pair[0], proc->ipc_fd[1]);
|
||||
@ -3807,6 +3812,7 @@ int main(int argc, char **argv)
|
||||
* we make sure that the fd is received correctly.
|
||||
*/
|
||||
shutdown(sock_pair[1], SHUT_WR);
|
||||
setsockopt(sock_pair[1], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
r = read(sock_pair[1], &c, 1);
|
||||
close(sock_pair[1]);
|
||||
close(sock_pair[0]);
|
||||
|
||||
66
src/haterm.c
66
src/haterm.c
@ -250,7 +250,7 @@ static int hstream_htx_buf_rcv(struct connection *conn, struct hstream *hs)
|
||||
htx_reset(htxbuf(&hs->req));
|
||||
max = (IS_HTX_SC(hs->sc) ? htx_free_space(htxbuf(&hs->req)) : b_room(&hs->req));
|
||||
sc_ep_clr(hs->sc, SE_FL_WANT_ROOM);
|
||||
read = conn->mux->rcv_buf(hs->sc, &hs->req, max, 0);
|
||||
read = CALL_MUX_WITH_RET(conn->mux, rcv_buf(hs->sc, &hs->req, max, 0));
|
||||
cur_read += read;
|
||||
if (!htx_expect_more(htxbuf(&hs->req))) {
|
||||
fin = 1;
|
||||
@ -313,7 +313,7 @@ static int hstream_htx_buf_snd(struct connection *conn, struct hstream *hs)
|
||||
goto out;
|
||||
}
|
||||
|
||||
nret = conn->mux->snd_buf(hs->sc, &hs->res, htxbuf(&hs->res)->data, 0);
|
||||
nret = CALL_MUX_WITH_RET(conn->mux, snd_buf(hs->sc, &hs->res, htxbuf(&hs->res)->data, 0));
|
||||
if (nret <= 0) {
|
||||
if (hs->flags & HS_ST_CONN_ERROR ||
|
||||
conn->flags & CO_FL_ERROR || sc_ep_test(sc, SE_FL_ERROR)) {
|
||||
@ -447,15 +447,6 @@ err:
|
||||
goto leave;
|
||||
}
|
||||
|
||||
int hstream_wake(struct stconn *sc)
|
||||
{
|
||||
struct hstream *hs = __sc_hstream(sc);
|
||||
|
||||
TRACE_STATE("waking up task", HS_EV_HSTRM_IO_CB, hs);
|
||||
task_wakeup(hs->task, TASK_WOKEN_IO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add data to HTX response buffer from pre-built responses */
|
||||
static void hstream_add_data(struct htx *htx, struct hstream *hs)
|
||||
{
|
||||
@ -510,11 +501,14 @@ static int hstream_build_http_resp(struct hstream *hs)
|
||||
struct htx *htx;
|
||||
unsigned int flags = HTX_SL_F_IS_RESP | HTX_SL_F_XFER_LEN | (!hs->req_chunked ? HTX_SL_F_CLEN : 0);
|
||||
struct htx_sl *sl;
|
||||
char hdrbuf[128];
|
||||
char *end;
|
||||
|
||||
TRACE_ENTER(HS_EV_HSTRM_RESP, hs);
|
||||
|
||||
snprintf(hdrbuf, sizeof(hdrbuf), "%d", hs->req_code);
|
||||
chunk_reset(&trash);
|
||||
end = ultoa_o(hs->req_code, trash.area, trash.size);
|
||||
if (!end)
|
||||
goto err;
|
||||
buf = hstream_get_buf(hs, &hs->res);
|
||||
if (!buf) {
|
||||
TRACE_ERROR("could not allocate response buffer", HS_EV_HSTRM_RESP, hs);
|
||||
@ -524,7 +518,7 @@ static int hstream_build_http_resp(struct hstream *hs)
|
||||
htx = htx_from_buf(buf);
|
||||
sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
|
||||
!(hs->ka & 4) ? ist("HTTP/1.0") : ist("HTTP/1.1"),
|
||||
ist(hdrbuf), IST_NULL);
|
||||
ist2(trash.area, end - trash.area), IST_NULL);
|
||||
if (!sl) {
|
||||
TRACE_ERROR("could not add HTX start line", HS_EV_HSTRM_RESP, hs);
|
||||
goto err;
|
||||
@ -559,18 +553,46 @@ static int hstream_build_http_resp(struct hstream *hs)
|
||||
}
|
||||
|
||||
/* XXX TODO time? XXX */
|
||||
snprintf(hdrbuf, sizeof(hdrbuf), "time=%ld ms", 0L);
|
||||
if (!htx_add_header(htx, ist("X-req"), ist(hdrbuf))) {
|
||||
chunk_reset(&trash);
|
||||
if (!chunk_strcat(&trash, "time=0 ms") ||
|
||||
!htx_add_header(htx, ist("X-req"), ist2(trash.area, trash.data))) {
|
||||
TRACE_ERROR("could not add x-req HTX header", HS_EV_HSTRM_RESP, hs);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* XXX TODO time? XXX */
|
||||
snprintf(hdrbuf, sizeof(hdrbuf), "id=%s, code=%d, cache=%d,%s size=%lld, time=%d ms (%ld real)",
|
||||
"dummy", hs->req_code, hs->req_cache,
|
||||
hs->req_chunked ? " chunked," : "",
|
||||
hs->req_size, 0, 0L);
|
||||
if (!htx_add_header(htx, ist("X-rsp"), ist(hdrbuf))) {
|
||||
chunk_reset(&trash);
|
||||
if (!chunk_strcat(&trash, "id=dummy, code=")) {
|
||||
TRACE_ERROR("could not build x-rsp HTX header", HS_EV_HSTRM_RESP, hs);
|
||||
goto err;
|
||||
}
|
||||
end = ultoa_o(hs->req_code, trash.area + trash.data, trash.size - trash.data);
|
||||
if (!end)
|
||||
goto err;
|
||||
trash.data = end - trash.area;
|
||||
if (!chunk_strcat(&trash, ", cache=")) {
|
||||
TRACE_ERROR("could not build x-rsp HTX header", HS_EV_HSTRM_RESP, hs);
|
||||
goto err;
|
||||
}
|
||||
end = ultoa_o(hs->req_cache, trash.area + trash.data, trash.size - trash.data);
|
||||
if (!end)
|
||||
goto err;
|
||||
trash.data = end - trash.area;
|
||||
if (hs->req_chunked && !chunk_strcat(&trash, ", chunked,")) {
|
||||
TRACE_ERROR("could not build x-rsp HTX header", HS_EV_HSTRM_RESP, hs);
|
||||
goto err;
|
||||
}
|
||||
if (!chunk_strcat(&trash, " size=")) {
|
||||
TRACE_ERROR("could not build x-rsp HTX header", HS_EV_HSTRM_RESP, hs);
|
||||
goto err;
|
||||
}
|
||||
end = ultoa_o(hs->req_size, trash.area + trash.data, trash.size - trash.data);
|
||||
if (!end)
|
||||
goto err;
|
||||
trash.data = end - trash.area;
|
||||
|
||||
if (!chunk_strcat(&trash, ", time=0 ms (0 real)") ||
|
||||
!htx_add_header(htx, ist("X-rsp"), ist2(trash.area, trash.data))) {
|
||||
TRACE_ERROR("could not add x-rsp HTX header", HS_EV_HSTRM_RESP, hs);
|
||||
goto err;
|
||||
}
|
||||
@ -882,7 +904,7 @@ static struct task *process_hstream(struct task *t, void *context, unsigned int
|
||||
out:
|
||||
if (!hs->to_write && !hs->req_body && htx_is_empty(htxbuf(&hs->res))) {
|
||||
TRACE_DEVEL("shutting down stream", HS_EV_HSTRM_SEND, hs);
|
||||
conn->mux->shut(hs->sc, SE_SHW_SILENT|SE_SHW_NORMAL, NULL);
|
||||
CALL_MUX_NO_RET(conn->mux, shut(hs->sc, SE_SHW_SILENT|SE_SHW_NORMAL, NULL));
|
||||
}
|
||||
|
||||
if (hs->flags & HS_ST_CONN_ERROR ||
|
||||
|
||||
@ -4926,7 +4926,7 @@ __LJMP static int hlua_run_sample_fetch(lua_State *L)
|
||||
|
||||
/* Run the sample fetch process. */
|
||||
smp_set_owner(&smp, hsmp->p, hsmp->s->sess, hsmp->s, hsmp->dir & SMP_OPT_DIR);
|
||||
if (!f->process(args, &smp, f->kw, f->private)) {
|
||||
if (!EXEC_CTX_WITH_RET(f->exec_ctx, f->process(args, &smp, f->kw, f->private))) {
|
||||
if (hsmp->flags & HLUA_F_AS_STRING)
|
||||
lua_pushstring(L, "");
|
||||
else
|
||||
@ -5059,7 +5059,7 @@ __LJMP static int hlua_run_sample_conv(lua_State *L)
|
||||
}
|
||||
|
||||
/* Run the sample conversion process. */
|
||||
if (!conv->process(args, &smp, conv->private)) {
|
||||
if (!EXEC_CTX_WITH_RET(conv->exec_ctx, conv->process(args, &smp, conv->private))) {
|
||||
if (hsmp->flags & HLUA_F_AS_STRING)
|
||||
lua_pushstring(L, "");
|
||||
else
|
||||
|
||||
@ -1224,11 +1224,14 @@ static __inline int do_l7_retry(struct stream *s, struct stconn *sc)
|
||||
}
|
||||
|
||||
b_free(&req->buf);
|
||||
|
||||
/* Swap the L7 buffer with the channel buffer */
|
||||
/* We know we stored the co_data as b_data, so get it there */
|
||||
co_data = b_data(&s->txn->l7_buffer);
|
||||
b_set_data(&s->txn->l7_buffer, b_size(&s->txn->l7_buffer));
|
||||
b_xfer(&req->buf, &s->txn->l7_buffer, b_data(&s->txn->l7_buffer));
|
||||
|
||||
req->buf = s->txn->l7_buffer;
|
||||
s->txn->l7_buffer = BUF_NULL;
|
||||
co_set_data(req, co_data);
|
||||
|
||||
DBG_TRACE_DEVEL("perform a L7 retry", STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, s->txn);
|
||||
@ -2881,7 +2884,8 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
|
||||
s->waiting_entity.ptr = NULL;
|
||||
}
|
||||
|
||||
switch (rule->action_ptr(rule, px, sess, s, act_opts)) {
|
||||
switch (EXEC_CTX_WITH_RET(rule->exec_ctx,
|
||||
rule->action_ptr(rule, px, sess, s, act_opts))) {
|
||||
case ACT_RET_CONT:
|
||||
break;
|
||||
case ACT_RET_STOP:
|
||||
@ -3073,7 +3077,8 @@ resume_execution:
|
||||
s->waiting_entity.ptr = NULL;
|
||||
}
|
||||
|
||||
switch (rule->action_ptr(rule, px, sess, s, act_opts)) {
|
||||
switch (EXEC_CTX_WITH_RET(rule->exec_ctx,
|
||||
rule->action_ptr(rule, px, sess, s, act_opts))) {
|
||||
case ACT_RET_CONT:
|
||||
break;
|
||||
case ACT_RET_STOP:
|
||||
@ -4332,20 +4337,10 @@ enum rule_result http_wait_for_msg_body(struct stream *s, struct channel *chn,
|
||||
}
|
||||
|
||||
if (channel_htx_full(chn, htx, global.tune.maxrewrite) || sc_waiting_room(chn_prod(chn))) {
|
||||
struct buffer lbuf;
|
||||
char *area;
|
||||
struct buffer lbuf = BUF_NULL;
|
||||
|
||||
if (large_buffer == 0 || b_is_large(&chn->buf))
|
||||
goto end; /* don't use large buffer or large buffer is full */
|
||||
|
||||
/* normal buffer is full, allocate a large one
|
||||
*/
|
||||
area = pool_alloc(pool_head_large_buffer);
|
||||
if (!area)
|
||||
goto end; /* Allocation failure: TODO must be improved to use buffer_wait */
|
||||
lbuf = b_make(area, global.tune.bufsize_large, 0, 0);
|
||||
htx_xfer_blks(htx_from_buf(&lbuf), htx, htx_used_space(htx), HTX_BLK_UNUSED);
|
||||
htx_to_buf(htx, &chn->buf);
|
||||
if (large_buffer == 0 || b_is_large(&chn->buf) || !htx_move_to_large_buffer(&lbuf, &chn->buf))
|
||||
goto end; /* don't use large buffer or already a large buffer */
|
||||
b_free(&chn->buf);
|
||||
offer_buffers(s, 1);
|
||||
chn->buf = lbuf;
|
||||
|
||||
@ -604,10 +604,7 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
||||
htx_to_buf(htx, outbuf);
|
||||
b_xfer(outbuf, &hc->req.buf, b_data(&hc->req.buf));
|
||||
} else {
|
||||
struct htx_ret ret;
|
||||
|
||||
ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
|
||||
if (!ret.ret) {
|
||||
if (!htx_xfer(htx, hc_htx, htx_used_space(hc_htx), HTX_XFER_DEFAULT)) {
|
||||
applet_have_more_data(appctx);
|
||||
goto out;
|
||||
}
|
||||
@ -711,7 +708,6 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
||||
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
||||
/* HTX mode transfers the header to the hc buffer */
|
||||
struct htx *hc_htx;
|
||||
struct htx_ret ret;
|
||||
|
||||
if (!b_alloc(&hc->res.buf, DB_MUX_TX)) {
|
||||
applet_wont_consume(appctx);
|
||||
@ -720,8 +716,7 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
||||
hc_htx = htxbuf(&hc->res.buf);
|
||||
|
||||
/* xfer the headers */
|
||||
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_EOH);
|
||||
if (!ret.ret) {
|
||||
if (!htx_xfer(hc_htx, htx, htx_used_space(htx), HTX_XFER_HDRS_ONLY)) {
|
||||
applet_need_more_data(appctx);
|
||||
goto out;
|
||||
}
|
||||
@ -811,12 +806,10 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
||||
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
||||
/* HTX mode transfers the header to the hc buffer */
|
||||
struct htx *hc_htx;
|
||||
struct htx_ret ret;
|
||||
|
||||
hc_htx = htxbuf(&hc->res.buf);
|
||||
|
||||
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_UNUSED);
|
||||
if (!ret.ret)
|
||||
if (!htx_xfer(hc_htx, htx, htx_used_space(htx), HTX_XFER_DEFAULT))
|
||||
applet_wont_consume(appctx);
|
||||
else
|
||||
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
|
||||
|
||||
175
src/http_htx.c
175
src/http_htx.c
@ -41,17 +41,18 @@ struct list http_replies_list = LIST_HEAD_INIT(http_replies_list);
|
||||
/* The declaration of an errorfiles/errorfile directives. Used during config
|
||||
* parsing only. */
|
||||
struct conf_errors {
|
||||
char type; /* directive type (0: errorfiles, 1: errorfile) */
|
||||
enum http_err_directive directive; /* directive type: inline (errorfile <code> <file>) / section (errorfiles <section>) */
|
||||
union {
|
||||
struct {
|
||||
int status; /* the status code associated to this error */
|
||||
struct http_reply *reply; /* the http reply for the errorfile */
|
||||
} errorfile; /* describe an "errorfile" directive */
|
||||
} inl; /* for HTTP_ERR_DIRECTIVE_INLINE only */
|
||||
struct {
|
||||
char *name; /* the http-errors section name */
|
||||
char status[HTTP_ERR_SIZE]; /* list of status to import (0: ignore, 1: implicit import, 2: explicit import) */
|
||||
} errorfiles; /* describe an "errorfiles" directive */
|
||||
} info;
|
||||
struct http_errors *resolved; /* resolved section pointer set via proxy_check_http_errors() */
|
||||
enum http_err_import status[HTTP_ERR_SIZE]; /* list of status to import */
|
||||
} section; /* for HTTP_ERR_DIRECTIVE_SECTION only */
|
||||
} type;
|
||||
|
||||
char *file; /* file where the directive appears */
|
||||
int line; /* line where the directive appears */
|
||||
@ -2034,9 +2035,9 @@ static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx,
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
conf_err->type = 1;
|
||||
conf_err->info.errorfile.status = status;
|
||||
conf_err->info.errorfile.reply = reply;
|
||||
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||
conf_err->type.inl.status = status;
|
||||
conf_err->type.inl.reply = reply;
|
||||
|
||||
conf_err->file = strdup(file);
|
||||
conf_err->line = line;
|
||||
@ -2105,9 +2106,9 @@ static int proxy_parse_errorfile(char **args, int section, struct proxy *curpx,
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
conf_err->type = 1;
|
||||
conf_err->info.errorfile.status = status;
|
||||
conf_err->info.errorfile.reply = reply;
|
||||
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||
conf_err->type.inl.status = status;
|
||||
conf_err->type.inl.reply = reply;
|
||||
conf_err->file = strdup(file);
|
||||
conf_err->line = line;
|
||||
LIST_APPEND(&curpx->conf.errors, &conf_err->list);
|
||||
@ -2146,12 +2147,12 @@ static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
|
||||
memprintf(err, "%s : out of memory.", args[0]);
|
||||
goto error;
|
||||
}
|
||||
conf_err->type = 0;
|
||||
|
||||
conf_err->info.errorfiles.name = name;
|
||||
conf_err->directive = HTTP_ERR_DIRECTIVE_SECTION;
|
||||
conf_err->type.section.name = name;
|
||||
if (!*(args[2])) {
|
||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++)
|
||||
conf_err->info.errorfiles.status[rc] = 1;
|
||||
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_IMPLICIT;
|
||||
}
|
||||
else {
|
||||
int cur_arg, status;
|
||||
@ -2160,7 +2161,7 @@ static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
|
||||
|
||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||
if (http_err_codes[rc] == status) {
|
||||
conf_err->info.errorfiles.status[rc] = 2;
|
||||
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_EXPLICIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2231,16 +2232,16 @@ static int proxy_parse_http_error(char **args, int section, struct proxy *curpx,
|
||||
if (reply->type == HTTP_REPLY_ERRFILES) {
|
||||
int rc = http_get_status_idx(reply->status);
|
||||
|
||||
conf_err->type = 2;
|
||||
conf_err->info.errorfiles.name = reply->body.http_errors;
|
||||
conf_err->info.errorfiles.status[rc] = 2;
|
||||
conf_err->directive = HTTP_ERR_DIRECTIVE_SECTION;
|
||||
conf_err->type.section.name = reply->body.http_errors;
|
||||
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_EXPLICIT;
|
||||
reply->body.http_errors = NULL;
|
||||
release_http_reply(reply);
|
||||
}
|
||||
else {
|
||||
conf_err->type = 1;
|
||||
conf_err->info.errorfile.status = reply->status;
|
||||
conf_err->info.errorfile.reply = reply;
|
||||
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||
conf_err->type.inl.status = reply->status;
|
||||
conf_err->type.inl.reply = reply;
|
||||
LIST_APPEND(&http_replies_list, &reply->list);
|
||||
}
|
||||
conf_err->file = strdup(file);
|
||||
@ -2260,60 +2261,46 @@ static int proxy_parse_http_error(char **args, int section, struct proxy *curpx,
|
||||
|
||||
}
|
||||
|
||||
/* Check "errorfiles" proxy keyword */
|
||||
static int proxy_check_errors(struct proxy *px)
|
||||
/* Converts <conf_errors> initialized during config parsing for <px> proxy.
|
||||
* Each one of them is transfromed in a http_reply type, stored in proxy
|
||||
* replies array member. The original <conf_errors> becomes unneeded and is
|
||||
* thus removed and freed.
|
||||
*/
|
||||
static int proxy_finalize_http_errors(struct proxy *px)
|
||||
{
|
||||
struct conf_errors *conf_err, *conf_err_back;
|
||||
struct http_errors *http_errs;
|
||||
int rc, err = ERR_NONE;
|
||||
int rc;
|
||||
|
||||
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
||||
if (conf_err->type == 1) {
|
||||
/* errorfile */
|
||||
rc = http_get_status_idx(conf_err->info.errorfile.status);
|
||||
px->replies[rc] = conf_err->info.errorfile.reply;
|
||||
switch (conf_err->directive) {
|
||||
case HTTP_ERR_DIRECTIVE_INLINE:
|
||||
rc = http_get_status_idx(conf_err->type.inl.status);
|
||||
px->replies[rc] = conf_err->type.inl.reply;
|
||||
|
||||
/* For proxy, to rely on default replies, just don't reference a reply */
|
||||
if (px->replies[rc]->type == HTTP_REPLY_ERRMSG && !px->replies[rc]->body.errmsg)
|
||||
px->replies[rc] = NULL;
|
||||
}
|
||||
else {
|
||||
/* errorfiles */
|
||||
list_for_each_entry(http_errs, &http_errors_list, list) {
|
||||
if (strcmp(http_errs->id, conf_err->info.errorfiles.name) == 0)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
/* unknown http-errors section */
|
||||
if (&http_errs->list == &http_errors_list) {
|
||||
ha_alert("proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
|
||||
px->id, conf_err->info.errorfiles.name, conf_err->file, conf_err->line);
|
||||
err |= ERR_ALERT | ERR_FATAL;
|
||||
free(conf_err->info.errorfiles.name);
|
||||
goto next;
|
||||
}
|
||||
|
||||
free(conf_err->info.errorfiles.name);
|
||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||
if (conf_err->info.errorfiles.status[rc] > 0) {
|
||||
case HTTP_ERR_DIRECTIVE_SECTION:
|
||||
http_errs = conf_err->type.section.resolved;
|
||||
if (http_errs) {
|
||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||
if (conf_err->type.section.status[rc] == HTTP_ERR_IMPORT_NO)
|
||||
continue;
|
||||
if (http_errs->replies[rc])
|
||||
px->replies[rc] = http_errs->replies[rc];
|
||||
else if (conf_err->info.errorfiles.status[rc] == 2)
|
||||
ha_warning("config: proxy '%s' : status '%d' not declared in"
|
||||
" http-errors section '%s' (at %s:%d).\n",
|
||||
px->id, http_err_codes[rc], http_errs->id,
|
||||
conf_err->file, conf_err->line);
|
||||
}
|
||||
}
|
||||
}
|
||||
next:
|
||||
|
||||
LIST_DELETE(&conf_err->list);
|
||||
free(conf_err->file);
|
||||
free(conf_err);
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
static int post_check_errors()
|
||||
@ -2343,6 +2330,55 @@ static int post_check_errors()
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* Checks the validity of conf_errors stored in <px> proxy after the
|
||||
* configuration is completely parsed.
|
||||
*
|
||||
* Returns ERR_NONE on success and a combination of ERR_CODE on failure.
|
||||
*/
|
||||
int proxy_check_http_errors(struct proxy *px)
|
||||
{
|
||||
struct http_errors *http_errs;
|
||||
struct conf_errors *conf_err;
|
||||
int section_found;
|
||||
int rc, err = ERR_NONE;
|
||||
|
||||
list_for_each_entry(conf_err, &px->conf.errors, list) {
|
||||
if (conf_err->directive == HTTP_ERR_DIRECTIVE_SECTION) {
|
||||
section_found = 0;
|
||||
list_for_each_entry(http_errs, &http_errors_list, list) {
|
||||
if (strcmp(http_errs->id, conf_err->type.section.name) == 0) {
|
||||
section_found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!section_found) {
|
||||
ha_alert("proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
|
||||
px->id, conf_err->type.section.name, conf_err->file, conf_err->line);
|
||||
ha_free(&conf_err->type.section.name);
|
||||
err |= ERR_ALERT | ERR_FATAL;
|
||||
continue;
|
||||
}
|
||||
|
||||
conf_err->type.section.resolved = http_errs;
|
||||
ha_free(&conf_err->type.section.name);
|
||||
|
||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||
if (conf_err->type.section.status[rc] == HTTP_ERR_IMPORT_EXPLICIT &&
|
||||
!http_errs->replies[rc]) {
|
||||
ha_warning("config: proxy '%s' : status '%d' not declared in"
|
||||
" http-errors section '%s' (at %s:%d).\n",
|
||||
px->id, http_err_codes[rc], http_errs->id,
|
||||
conf_err->file, conf_err->line);
|
||||
err |= ERR_WARN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg)
|
||||
{
|
||||
struct conf_errors *conf_err, *new_conf_err = NULL;
|
||||
@ -2354,19 +2390,22 @@ int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx
|
||||
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
||||
goto out;
|
||||
}
|
||||
new_conf_err->type = conf_err->type;
|
||||
if (conf_err->type == 1) {
|
||||
new_conf_err->info.errorfile.status = conf_err->info.errorfile.status;
|
||||
new_conf_err->info.errorfile.reply = conf_err->info.errorfile.reply;
|
||||
}
|
||||
else {
|
||||
new_conf_err->info.errorfiles.name = strdup(conf_err->info.errorfiles.name);
|
||||
if (!new_conf_err->info.errorfiles.name) {
|
||||
new_conf_err->directive = conf_err->directive;
|
||||
switch (conf_err->directive) {
|
||||
case HTTP_ERR_DIRECTIVE_INLINE:
|
||||
new_conf_err->type.inl.status = conf_err->type.inl.status;
|
||||
new_conf_err->type.inl.reply = conf_err->type.inl.reply;
|
||||
break;
|
||||
|
||||
case HTTP_ERR_DIRECTIVE_SECTION:
|
||||
new_conf_err->type.section.name = strdup(conf_err->type.section.name);
|
||||
if (!new_conf_err->type.section.name) {
|
||||
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
||||
goto out;
|
||||
}
|
||||
memcpy(&new_conf_err->info.errorfiles.status, &conf_err->info.errorfiles.status,
|
||||
sizeof(conf_err->info.errorfiles.status));
|
||||
memcpy(&new_conf_err->type.section.status, &conf_err->type.section.status,
|
||||
sizeof(conf_err->type.section.status));
|
||||
break;
|
||||
}
|
||||
new_conf_err->file = strdup(conf_err->file);
|
||||
new_conf_err->line = conf_err->line;
|
||||
@ -2385,8 +2424,8 @@ void proxy_release_conf_errors(struct proxy *px)
|
||||
struct conf_errors *conf_err, *conf_err_back;
|
||||
|
||||
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
||||
if (conf_err->type == 0)
|
||||
free(conf_err->info.errorfiles.name);
|
||||
if (conf_err->directive == HTTP_ERR_DIRECTIVE_SECTION)
|
||||
free(conf_err->type.section.name);
|
||||
LIST_DELETE(&conf_err->list);
|
||||
free(conf_err->file);
|
||||
free(conf_err);
|
||||
@ -2505,7 +2544,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
||||
}};
|
||||
|
||||
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||
REGISTER_POST_PROXY_CHECK(proxy_check_errors);
|
||||
REGISTER_POST_PROXY_CHECK(proxy_finalize_http_errors);
|
||||
REGISTER_POST_CHECK(post_check_errors);
|
||||
|
||||
REGISTER_CONFIG_SECTION("http-errors", cfg_parse_http_errors, NULL);
|
||||
|
||||
@ -52,17 +52,17 @@ struct action_kw_list http_after_res_keywords = {
|
||||
|
||||
void http_req_keywords_register(struct action_kw_list *kw_list)
|
||||
{
|
||||
LIST_APPEND(&http_req_keywords.list, &kw_list->list);
|
||||
act_add_list(&http_req_keywords.list, kw_list);
|
||||
}
|
||||
|
||||
void http_res_keywords_register(struct action_kw_list *kw_list)
|
||||
{
|
||||
LIST_APPEND(&http_res_keywords.list, &kw_list->list);
|
||||
act_add_list(&http_res_keywords.list, kw_list);
|
||||
}
|
||||
|
||||
void http_after_res_keywords_register(struct action_kw_list *kw_list)
|
||||
{
|
||||
LIST_APPEND(&http_after_res_keywords.list, &kw_list->list);
|
||||
act_add_list(&http_after_res_keywords.list, kw_list);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
222
src/htx.c
222
src/htx.c
@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <haproxy/chunk.h>
|
||||
#include <haproxy/dynbuf.h>
|
||||
#include <haproxy/global.h>
|
||||
#include <haproxy/htx.h>
|
||||
#include <haproxy/net_helper.h>
|
||||
@ -469,6 +470,18 @@ void htx_truncate(struct htx *htx, uint32_t offset)
|
||||
blk = htx_remove_blk(htx, blk);
|
||||
}
|
||||
|
||||
/* Removes all blocks after <blk>, excluding it. if <blk> is NULL, all blocks
|
||||
* are removed.
|
||||
*/
|
||||
void htx_truncate_blk(struct htx *htx, struct htx_blk *blk)
|
||||
{
|
||||
if (!blk) {
|
||||
htx_drain(htx, htx->data);
|
||||
return;
|
||||
}
|
||||
for (blk = htx_get_next_blk(htx, blk); blk; blk = htx_remove_blk(htx, blk));
|
||||
}
|
||||
|
||||
/* Drains <count> bytes from the HTX message <htx>. If the last block is a DATA
|
||||
* block, it will be cut if necessary. Others blocks will be removed at once if
|
||||
* <count> is large enough. The function returns an htx_ret with the first block
|
||||
@ -707,10 +720,154 @@ struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
|
||||
return blk;
|
||||
}
|
||||
|
||||
/* Transfer HTX blocks from <src> to <dst>, stopping if <count> bytes were
|
||||
* transferred (including payload and meta-data). It returns the number of bytes
|
||||
* copied. By default, copied blocks are removed from <src> and only full
|
||||
* headers and trailers part can be moved. <flags> can be set to change the
|
||||
* default behavior:
|
||||
* - HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||
* - HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers part can be xferred
|
||||
* - HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||
*/
|
||||
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags)
|
||||
{
|
||||
struct htx_blk *blk, *last_dstblk;
|
||||
size_t ret = 0;
|
||||
int dst_full = 0;
|
||||
|
||||
last_dstblk = NULL;
|
||||
for (blk = htx_get_head_blk(src); blk && count; blk = htx_get_next_blk(src, blk)) {
|
||||
struct ist v;
|
||||
enum htx_blk_type type;
|
||||
uint32_t sz;
|
||||
|
||||
/* Ignore unused block */
|
||||
type = htx_get_blk_type(blk);
|
||||
if (type == HTX_BLK_UNUSED)
|
||||
continue;
|
||||
|
||||
if ((flags & HTX_XFER_HDRS_ONLY) &&
|
||||
type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL &&
|
||||
type != HTX_BLK_HDR && type != HTX_BLK_EOH)
|
||||
break;
|
||||
|
||||
sz = htx_get_blksz(blk);
|
||||
switch (type) {
|
||||
case HTX_BLK_DATA:
|
||||
v = htx_get_blk_value(src, blk);
|
||||
if (v.len > count)
|
||||
v.len = count;
|
||||
v.len = htx_add_data(dst, v);
|
||||
if (!v.len) {
|
||||
dst_full = 1;
|
||||
goto stop;
|
||||
}
|
||||
last_dstblk = htx_get_tail_blk(dst);
|
||||
count -= sizeof(*blk) + v.len;
|
||||
ret += sizeof(*blk) + v.len;
|
||||
if (v.len != sz) {
|
||||
dst_full = 1;
|
||||
goto stop;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (sz > count) {
|
||||
dst_full = 1;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
last_dstblk = htx_add_blk(dst, type, sz);
|
||||
if (!last_dstblk) {
|
||||
dst_full = 1;
|
||||
goto stop;
|
||||
}
|
||||
last_dstblk->info = blk->info;
|
||||
htx_memcpy(htx_get_blk_ptr(dst, last_dstblk), htx_get_blk_ptr(src, blk), sz);
|
||||
count -= sizeof(*blk) + sz;
|
||||
ret += sizeof(*blk) + sz;
|
||||
break;
|
||||
}
|
||||
|
||||
last_dstblk = NULL; /* Reset last_dstblk because it was fully copied */
|
||||
}
|
||||
stop:
|
||||
/* Here, if not NULL, <blk> point on the first not fully copied block in
|
||||
* <src>. And <last_dstblk>, if defined, is the last not fully copied
|
||||
* block in <dst>. So have:
|
||||
* - <blk> == NULL: everything was copied. <last_dstblk> must be NULL
|
||||
* - <blk> != NULL && <last_dstblk> == NULL: partial copy but the last block was fully copied
|
||||
* - <blk> != NULL && <last_dstblk> != NULL: partial copy and the last block was patially copied (DATA block only)
|
||||
*/
|
||||
if (!(flags & HTX_XFER_PARTIAL_HDRS_COPY)) {
|
||||
/* Partial headers/trailers copy is not supported */
|
||||
struct htx_blk *dstblk;
|
||||
enum htx_blk_type type = HTX_BLK_UNUSED;
|
||||
|
||||
dstblk = htx_get_tail_blk(dst);
|
||||
if (dstblk)
|
||||
type = htx_get_blk_type(dstblk);
|
||||
|
||||
/* the last copied block is a start-line, a header or a trailer */
|
||||
if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
||||
/* <src > cannot have partial headers or trailers part */
|
||||
BUG_ON(blk == NULL);
|
||||
|
||||
/* Remove partial headers/trailers from <dst> and rollback on <str> to not remove them later */
|
||||
while (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
||||
BUG_ON(type != htx_get_blk_type(blk));
|
||||
ret -= sizeof(*blk) + htx_get_blksz(blk);
|
||||
htx_remove_blk(dst, dstblk);
|
||||
dstblk = htx_get_tail_blk(dst);
|
||||
blk = htx_get_prev_blk(src, blk);
|
||||
if (!dstblk)
|
||||
break;
|
||||
type = htx_get_blk_type(dstblk);
|
||||
}
|
||||
|
||||
/* Report if the xfer was interrupted because <dst> was
|
||||
* full but is was originally empty
|
||||
*/
|
||||
if (dst_full && htx_is_empty(dst))
|
||||
src->flags |= HTX_FL_PARSING_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & HTX_XFER_KEEP_SRC_BLKS)) {
|
||||
/* True xfer performed, remove copied block from <src> */
|
||||
struct htx_blk *blk2;
|
||||
|
||||
/* Remove all fully copied blocks */
|
||||
if (!blk)
|
||||
htx_drain(src, src->data);
|
||||
else {
|
||||
for (blk2 = htx_get_head_blk(src); blk2 && blk2 != blk; blk2 = htx_remove_blk(src, blk2));
|
||||
|
||||
/* If copy was stopped on a DATA block and the last destination
|
||||
* block is not NULL, it means a partial copy was performed. So
|
||||
* cut the source block accordingly
|
||||
*/
|
||||
if (last_dstblk && blk2 && htx_get_blk_type(blk2) == HTX_BLK_DATA) {
|
||||
htx_cut_data_blk(src, blk2, htx_get_blksz(last_dstblk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Everything was copied, transfert terminal HTX flags too */
|
||||
if (!blk) {
|
||||
dst->flags |= (src->flags & (HTX_FL_EOM|HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
||||
src->flags = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Transfer HTX blocks from <src> to <dst>, stopping once the first block of the
|
||||
* type <mark> is transferred (typically EOH or EOT) or when <count> bytes were
|
||||
* moved (including payload and meta-data). It returns the number of bytes moved
|
||||
* and the last HTX block inserted in <dst>.
|
||||
*
|
||||
* DEPRECATED
|
||||
*/
|
||||
struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
|
||||
enum htx_blk_type mark)
|
||||
@ -1169,3 +1326,68 @@ int htx_append_msg(struct htx *dst, const struct htx *src)
|
||||
htx_truncate(dst, offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* If possible, trasnfer HTX blocks from <src> to a small buffer. This function
|
||||
* allocate the small buffer and makes <dst> point on it. If <dst> is not empty
|
||||
* or if <src> contains to many data, NULL is returned. If the allocation
|
||||
* failed, NULL is returned. Otherwise <dst> is returned. <flags> instructs how
|
||||
* the transfer must be performed.
|
||||
*/
|
||||
struct buffer *__htx_xfer_to_small_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
|
||||
{
|
||||
struct htx *dst_htx;
|
||||
struct htx *src_htx = htxbuf(src);
|
||||
size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
|
||||
|
||||
if (dst->size || sz > global.tune.bufsize_small || !b_alloc_small(dst))
|
||||
return NULL;
|
||||
dst_htx = htx_from_buf(dst);
|
||||
htx_xfer(dst_htx, src_htx, src_htx->size, flags);
|
||||
htx_to_buf(dst_htx, dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* If possible, trasnfer HTX blocks from <src> to a large buffer. This function
|
||||
* allocate the small buffer and makes <dst> point on it. If <dst> is not empty
|
||||
* or if <src> contains to many data, NULL is returned. If the allocation
|
||||
* failed, NULL is returned. Otherwise <dst> is returned. <flags> instructs how
|
||||
* the transfer must be performed.
|
||||
*/
|
||||
struct buffer *__htx_xfer_to_large_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
|
||||
{
|
||||
struct htx *dst_htx;
|
||||
struct htx *src_htx = htxbuf(src);
|
||||
size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
|
||||
|
||||
if (dst->size || sz > global.tune.bufsize_large || !b_alloc_large(dst))
|
||||
return NULL;
|
||||
dst_htx = htx_from_buf(dst);
|
||||
htx_xfer(dst_htx, src_htx, src_htx->size, flags);
|
||||
htx_to_buf(dst_htx, dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
|
||||
struct buffer *htx_move_to_small_buffer(struct buffer *dst, struct buffer *src)
|
||||
{
|
||||
return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_DEFAULT);
|
||||
}
|
||||
|
||||
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
|
||||
struct buffer *htx_move_to_large_buffer(struct buffer *dst, struct buffer *src)
|
||||
{
|
||||
return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_DEFAULT);
|
||||
}
|
||||
|
||||
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
|
||||
struct buffer *htx_copy_to_small_buffer(struct buffer *dst, struct buffer *src)
|
||||
{
|
||||
return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
|
||||
}
|
||||
|
||||
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
|
||||
struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src)
|
||||
{
|
||||
return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
|
||||
}
|
||||
|
||||
@ -89,6 +89,11 @@ struct list per_thread_free_list = LIST_HEAD_INIT(per_thread_free_list);
|
||||
* valgrind mostly happy. */
|
||||
struct list per_thread_deinit_list = LIST_HEAD_INIT(per_thread_deinit_list);
|
||||
|
||||
/* location of current INITCALL declaration being processed, or NULL */
|
||||
const struct initcall *caller_initcall = NULL;
|
||||
const char *caller_file = NULL;
|
||||
int caller_line = 0;
|
||||
|
||||
/* used to register some initialization functions to call before the checks. */
|
||||
void hap_register_pre_check(int (*fct)())
|
||||
{
|
||||
|
||||
@ -356,7 +356,7 @@ out:
|
||||
*/
|
||||
size_t jws_b64_signature(EVP_PKEY *pkey, enum jwt_alg alg, char *b64protected, char *b64payload, char *dst, size_t dsize)
|
||||
{
|
||||
EVP_MD_CTX *ctx;
|
||||
EVP_MD_CTX *ctx = NULL;
|
||||
const EVP_MD *evp_md = NULL;
|
||||
int ret = 0;
|
||||
struct buffer *sign = NULL;
|
||||
@ -450,6 +450,7 @@ size_t jws_b64_signature(EVP_PKEY *pkey, enum jwt_alg alg, char *b64protected, c
|
||||
ret = a2base64url(sign->area, sign->data, dst, dsize);
|
||||
|
||||
out:
|
||||
EVP_MD_CTX_free(ctx);
|
||||
free_trash_chunk(sign);
|
||||
|
||||
if (ret > 0)
|
||||
|
||||
35
src/jwt.c
35
src/jwt.c
@ -94,26 +94,30 @@ enum jwt_alg jwt_parse_alg(const char *alg_str, unsigned int alg_len)
|
||||
* now, we don't need to manage more than three subparts in the tokens.
|
||||
* See section 3.1 of RFC7515 for more information about JWS Compact
|
||||
* Serialization.
|
||||
* Returns 0 in case of success.
|
||||
* Returns -1 in case of error, 0 if the token has exactly <item_num> parts, a
|
||||
* positive value otherwise.
|
||||
*/
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int *item_num)
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int item_num)
|
||||
{
|
||||
char *ptr = jwt->area;
|
||||
char *jwt_end = jwt->area + jwt->data;
|
||||
unsigned int index = 0;
|
||||
unsigned int length = 0;
|
||||
|
||||
if (index < *item_num) {
|
||||
items[index].start = ptr;
|
||||
items[index].length = 0;
|
||||
}
|
||||
if (item_num == 0)
|
||||
return -1;
|
||||
|
||||
while (index < *item_num && ptr < jwt_end) {
|
||||
items[index].start = ptr;
|
||||
items[index].length = 0;
|
||||
|
||||
while (ptr < jwt_end) {
|
||||
if (*ptr++ == '.') {
|
||||
items[index++].length = length;
|
||||
/* We found enough items, no need to keep looking for
|
||||
* separators. */
|
||||
if (index == item_num)
|
||||
return 1;
|
||||
|
||||
if (index == *item_num)
|
||||
return -1;
|
||||
items[index].start = ptr;
|
||||
items[index].length = 0;
|
||||
length = 0;
|
||||
@ -121,10 +125,11 @@ int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int
|
||||
++length;
|
||||
}
|
||||
|
||||
if (index < *item_num)
|
||||
items[index].length = length;
|
||||
/* We might not have found enough items */
|
||||
if (index < item_num - 1)
|
||||
return -1;
|
||||
|
||||
*item_num = (index+1);
|
||||
items[index].length = length;
|
||||
|
||||
return (ptr != jwt_end);
|
||||
}
|
||||
@ -493,13 +498,9 @@ enum jwt_vrfy_status jwt_verify(const struct buffer *token, const struct buffer
|
||||
if (ctx.alg == JWT_ALG_DEFAULT)
|
||||
return JWT_VRFY_UNKNOWN_ALG;
|
||||
|
||||
if (jwt_tokenize(token, items, &item_num))
|
||||
if (jwt_tokenize(token, items, item_num))
|
||||
return JWT_VRFY_INVALID_TOKEN;
|
||||
|
||||
if (item_num != JWT_ELT_MAX)
|
||||
if (ctx.alg != JWS_ALG_NONE || item_num != JWT_ELT_SIG)
|
||||
return JWT_VRFY_INVALID_TOKEN;
|
||||
|
||||
ctx.jose = items[JWT_ELT_JOSE];
|
||||
ctx.claims = items[JWT_ELT_CLAIMS];
|
||||
ctx.signature = items[JWT_ELT_SIG];
|
||||
|
||||
116
src/log.c
116
src/log.c
@ -2913,6 +2913,7 @@ static inline void __send_log_set_metadata_sd(struct ist *metadata, char *sd, si
|
||||
struct process_send_log_ctx {
|
||||
struct session *sess;
|
||||
struct stream *stream;
|
||||
struct log_profile *profile;
|
||||
struct log_orig origin;
|
||||
};
|
||||
|
||||
@ -2942,6 +2943,10 @@ static inline void _process_send_log_override(struct process_send_log_ctx *ctx,
|
||||
enum log_orig_id orig = (ctx) ? ctx->origin.id : LOG_ORIG_UNSPEC;
|
||||
uint16_t orig_fl = (ctx) ? ctx->origin.flags : LOG_ORIG_FL_NONE;
|
||||
|
||||
/* ctx->profile gets priority over logger profile */
|
||||
if (ctx && ctx->profile)
|
||||
prof = ctx->profile;
|
||||
|
||||
BUG_ON(!prof);
|
||||
|
||||
if (!b_is_null(&prof->log_tag))
|
||||
@ -3095,8 +3100,8 @@ static void process_send_log(struct process_send_log_ctx *ctx,
|
||||
|
||||
nblogger += 1;
|
||||
|
||||
/* logger may use a profile to override a few things */
|
||||
if (unlikely(logger->prof))
|
||||
/* caller or default logger may use a profile to override a few things */
|
||||
if (unlikely(logger->prof || (ctx && ctx->profile)))
|
||||
_process_send_log_override(ctx, logger, hdr, message, size, nblogger);
|
||||
else
|
||||
_process_send_log_final(logger, hdr, message, size, nblogger);
|
||||
@ -5200,17 +5205,11 @@ out:
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* opportunistic log when at least the session is known to exist
|
||||
* <s> may be NULL
|
||||
*
|
||||
* Will not log if the frontend has no log defined. By default it will
|
||||
* try to emit the log as INFO, unless the stream already exists and
|
||||
* set-log-level was used.
|
||||
*/
|
||||
void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
||||
static void do_log_ctx(struct process_send_log_ctx *ctx)
|
||||
{
|
||||
struct process_send_log_ctx ctx;
|
||||
struct stream *s = ctx->stream;
|
||||
struct session *sess = ctx->sess;
|
||||
struct log_orig origin = ctx->origin;
|
||||
int size;
|
||||
int sd_size = 0;
|
||||
int level = -1;
|
||||
@ -5242,11 +5241,27 @@ void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
||||
|
||||
size = sess_build_logline_orig(sess, s, logline, global.max_syslog_len, &sess->fe->logformat, origin);
|
||||
|
||||
__send_log(ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
||||
logline, size, logline_rfc5424, sd_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* opportunistic log when at least the session is known to exist
|
||||
* <s> may be NULL
|
||||
*
|
||||
* Will not log if the frontend has no log defined. By default it will
|
||||
* try to emit the log as INFO, unless the stream already exists and
|
||||
* set-log-level was used.
|
||||
*/
|
||||
void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
||||
{
|
||||
struct process_send_log_ctx ctx;
|
||||
|
||||
ctx.origin = origin;
|
||||
ctx.sess = sess;
|
||||
ctx.stream = s;
|
||||
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
||||
logline, size, logline_rfc5424, sd_size);
|
||||
ctx.profile = NULL;
|
||||
do_log_ctx(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5297,6 +5312,7 @@ void strm_log(struct stream *s, struct log_orig origin)
|
||||
ctx.origin = origin;
|
||||
ctx.sess = sess;
|
||||
ctx.stream = s;
|
||||
ctx.profile = NULL;
|
||||
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
||||
logline, size, logline_rfc5424, sd_size);
|
||||
s->logs.logwait = 0;
|
||||
@ -5364,6 +5380,7 @@ void _sess_log(struct session *sess, int embryonic)
|
||||
ctx.origin = orig;
|
||||
ctx.sess = sess;
|
||||
ctx.stream = NULL;
|
||||
ctx.profile = NULL;
|
||||
__send_log(&ctx, &sess->fe->loggers,
|
||||
&sess->fe->log_tag, level,
|
||||
logline, size, logline_rfc5424, sd_size);
|
||||
@ -6910,24 +6927,87 @@ static int px_parse_log_steps(char **args, int section_type, struct proxy *curpx
|
||||
static enum act_return do_log_action(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags)
|
||||
{
|
||||
struct process_send_log_ctx ctx;
|
||||
|
||||
/* do_log() expects valid session pointer */
|
||||
BUG_ON(sess == NULL);
|
||||
|
||||
do_log(sess, s, log_orig(rule->arg.expr_int.value, LOG_ORIG_FL_NONE));
|
||||
ctx.origin = log_orig(rule->arg.do_log.orig, LOG_ORIG_FL_NONE);
|
||||
ctx.sess = sess;
|
||||
ctx.stream = s;
|
||||
ctx.profile = rule->arg.do_log.profile;
|
||||
|
||||
do_log_ctx(&ctx);
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* Parse a "do_log" action. It doesn't take any argument
|
||||
static int do_log_action_check(struct act_rule *rule, struct proxy *px, char **err)
|
||||
{
|
||||
if (rule->arg.do_log.profile_name) {
|
||||
struct log_profile *prof;
|
||||
|
||||
prof = log_profile_find_by_name(rule->arg.do_log.profile_name);
|
||||
if (!prof) {
|
||||
memprintf(err, "do-log action: profile '%s' is invalid", rule->arg.do_log.profile_name);
|
||||
ha_free(&rule->arg.do_log.profile_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ha_free(&rule->arg.do_log.profile_name);
|
||||
|
||||
if (!log_profile_postcheck(px, prof, err)) {
|
||||
memprintf(err, "do-log action on %s %s uses incompatible log-profile '%s': %s", proxy_type_str(px), px->id, prof->id, *err);
|
||||
return 0;
|
||||
}
|
||||
rule->arg.do_log.profile = prof;
|
||||
}
|
||||
return 1; // success
|
||||
}
|
||||
|
||||
static void do_log_action_release(struct act_rule *rule)
|
||||
{
|
||||
ha_free(&rule->arg.do_log.profile_name);
|
||||
}
|
||||
|
||||
|
||||
/* Parse a "do_log" action. It takes optional "log-profile" argument to
|
||||
* specifically use a given log-profile when generating the log message
|
||||
*
|
||||
* May be used from places where per-context actions are usually registered
|
||||
*/
|
||||
enum act_parse_ret do_log_parse_act(enum log_orig_id id,
|
||||
const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
int cur_arg = *orig_arg;
|
||||
|
||||
rule->action_ptr = do_log_action;
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->release_ptr = NULL;
|
||||
rule->arg.expr_int.value = id;
|
||||
rule->check_ptr = do_log_action_check;
|
||||
rule->release_ptr = do_log_action_release;
|
||||
rule->arg.do_log.orig = id;
|
||||
|
||||
while (*args[*orig_arg]) {
|
||||
if (!strcmp(args[*orig_arg], "profile")) {
|
||||
if (!*args[*orig_arg + 1]) {
|
||||
memprintf(err,
|
||||
"action '%s': 'profile' expects argument.",
|
||||
args[cur_arg-1]);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
rule->arg.do_log.profile_name = strdup(args[*orig_arg + 1]);
|
||||
if (!rule->arg.do_log.profile_name) {
|
||||
memprintf(err,
|
||||
"action '%s': memory error when setting 'profile'",
|
||||
args[cur_arg-1]);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
*orig_arg += 2;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
|
||||
43
src/mjson.c
43
src/mjson.c
@ -24,7 +24,7 @@
|
||||
|
||||
#include <import/mjson.h>
|
||||
|
||||
static double mystrtod(const char *str, char **end);
|
||||
static double mystrtod(const char *str, int len, char **end);
|
||||
|
||||
static int mjson_esc(int c, int esc) {
|
||||
const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
|
||||
@ -101,7 +101,7 @@ int mjson(const char *s, int len, mjson_cb_t cb, void *ud) {
|
||||
tok = MJSON_TOK_FALSE;
|
||||
} else if (c == '-' || ((c >= '0' && c <= '9'))) {
|
||||
char *end = NULL;
|
||||
mystrtod(&s[i], &end);
|
||||
mystrtod(&s[i], len - i, &end);
|
||||
if (end != NULL) i += (int) (end - &s[i] - 1);
|
||||
tok = MJSON_TOK_NUMBER;
|
||||
} else if (c == '"') {
|
||||
@ -212,7 +212,7 @@ static int mjson_get_cb(int tok, const char *s, int off, int len, void *ud) {
|
||||
} else if (tok == '[') {
|
||||
if (data->d1 == data->d2 && data->path[data->pos] == '[') {
|
||||
data->i1 = 0;
|
||||
data->i2 = (int) mystrtod(&data->path[data->pos + 1], NULL);
|
||||
data->i2 = (int) mystrtod(&data->path[data->pos + 1], strlen(&data->path[data->pos + 1]), NULL);
|
||||
if (data->i1 == data->i2) {
|
||||
data->d2++;
|
||||
data->pos += 3;
|
||||
@ -272,7 +272,7 @@ int mjson_get_number(const char *s, int len, const char *path, double *v) {
|
||||
const char *p;
|
||||
int tok, n;
|
||||
if ((tok = mjson_find(s, len, path, &p, &n)) == MJSON_TOK_NUMBER) {
|
||||
if (v != NULL) *v = mystrtod(p, NULL);
|
||||
if (v != NULL) *v = mystrtod(p, n, NULL);
|
||||
}
|
||||
return tok == MJSON_TOK_NUMBER ? 1 : 0;
|
||||
}
|
||||
@ -343,57 +343,56 @@ static int is_digit(int c) {
|
||||
}
|
||||
|
||||
/* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
|
||||
static double mystrtod(const char *str, char **end) {
|
||||
static double mystrtod(const char *str, int len, char **end) {
|
||||
double d = 0.0;
|
||||
int sign = 1, __attribute__((unused)) n = 0;
|
||||
const char *p = str, *a = str;
|
||||
const char *end_p = str + len;
|
||||
|
||||
/* decimal part */
|
||||
if (*p == '-') {
|
||||
if (p < end_p && *p == '-') {
|
||||
sign = -1;
|
||||
++p;
|
||||
} else if (*p == '+') {
|
||||
} else if (p < end_p && *p == '+') {
|
||||
++p;
|
||||
}
|
||||
if (is_digit(*p)) {
|
||||
if (p < end_p && is_digit(*p)) {
|
||||
d = (double) (*p++ - '0');
|
||||
while (*p && is_digit(*p)) {
|
||||
while (p < end_p && is_digit(*p)) {
|
||||
d = d * 10.0 + (double) (*p - '0');
|
||||
++p;
|
||||
++n;
|
||||
}
|
||||
a = p;
|
||||
} else if (*p != '.') {
|
||||
} else if (p >= end_p || *p != '.') {
|
||||
goto done;
|
||||
}
|
||||
d *= sign;
|
||||
|
||||
/* fraction part */
|
||||
if (*p == '.') {
|
||||
if (p < end_p && *p == '.') {
|
||||
double f = 0.0;
|
||||
double base = 0.1;
|
||||
++p;
|
||||
|
||||
if (is_digit(*p)) {
|
||||
while (*p && is_digit(*p)) {
|
||||
f += base * (*p - '0');
|
||||
base /= 10.0;
|
||||
++p;
|
||||
++n;
|
||||
}
|
||||
while (p < end_p && is_digit(*p)) {
|
||||
f += base * (*p - '0');
|
||||
base /= 10.0;
|
||||
++p;
|
||||
++n;
|
||||
}
|
||||
d += f * sign;
|
||||
a = p;
|
||||
}
|
||||
|
||||
/* exponential part */
|
||||
if ((*p == 'E') || (*p == 'e')) {
|
||||
if (p < end_p && ((*p == 'E') || (*p == 'e'))) {
|
||||
double exp, f;
|
||||
int i, e = 0, neg = 0;
|
||||
p++;
|
||||
if (*p == '-') p++, neg++;
|
||||
if (*p == '+') p++;
|
||||
while (is_digit(*p)) e = e * 10 + *p++ - '0';
|
||||
if (p < end_p && *p == '-') p++, neg++;
|
||||
if (p < end_p && *p == '+') p++;
|
||||
while (p < end_p && is_digit(*p)) e = e * 10 + *p++ - '0';
|
||||
i = e;
|
||||
if (neg) e = -e;
|
||||
#if 0
|
||||
|
||||
@ -862,7 +862,7 @@ static void fcgi_strm_notify_recv(struct fcgi_strm *fstrm)
|
||||
{
|
||||
if (fstrm->subs && (fstrm->subs->events & SUB_RETRY_RECV)) {
|
||||
TRACE_POINT(FCGI_EV_STRM_WAKE, fstrm->fconn->conn, fstrm);
|
||||
tasklet_wakeup(fstrm->subs->tasklet);
|
||||
tasklet_wakeup(fstrm->subs->tasklet, TASK_WOKEN_IO);
|
||||
fstrm->subs->events &= ~SUB_RETRY_RECV;
|
||||
if (!fstrm->subs->events)
|
||||
fstrm->subs = NULL;
|
||||
@ -875,7 +875,7 @@ static void fcgi_strm_notify_send(struct fcgi_strm *fstrm)
|
||||
if (fstrm->subs && (fstrm->subs->events & SUB_RETRY_SEND)) {
|
||||
TRACE_POINT(FCGI_EV_STRM_WAKE, fstrm->fconn->conn, fstrm);
|
||||
fstrm->flags |= FCGI_SF_NOTIFIED;
|
||||
tasklet_wakeup(fstrm->subs->tasklet);
|
||||
tasklet_wakeup(fstrm->subs->tasklet, TASK_WOKEN_IO);
|
||||
fstrm->subs->events &= ~SUB_RETRY_SEND;
|
||||
if (!fstrm->subs->events)
|
||||
fstrm->subs = NULL;
|
||||
@ -886,26 +886,28 @@ static void fcgi_strm_notify_send(struct fcgi_strm *fstrm)
|
||||
}
|
||||
}
|
||||
|
||||
/* Alerts the data layer, trying to wake it up by all means, following
|
||||
* this sequence :
|
||||
* - if the fcgi stream' data layer is subscribed to recv, then it's woken up
|
||||
* for recv
|
||||
* - if its subscribed to send, then it's woken up for send
|
||||
* - if it was subscribed to neither, its ->wake() callback is called
|
||||
* It is safe to call this function with a closed stream which doesn't have a
|
||||
* stream connector anymore.
|
||||
|
||||
/* Alerts the data layer by waking it up. TASK_WOKEN_MSG state is used by
|
||||
* default and if the data layer is also subscribed to recv or send,
|
||||
* TASK_WOKEN_IO is added. But first of all, we check if the shut tasklet must
|
||||
* be woken up or not instead.
|
||||
*/
|
||||
static void fcgi_strm_alert(struct fcgi_strm *fstrm)
|
||||
{
|
||||
TRACE_POINT(FCGI_EV_STRM_WAKE, fstrm->fconn->conn, fstrm);
|
||||
if (fstrm->subs ||
|
||||
(fstrm->flags & (FCGI_SF_WANT_SHUTR|FCGI_SF_WANT_SHUTW))) {
|
||||
fcgi_strm_notify_recv(fstrm);
|
||||
fcgi_strm_notify_send(fstrm);
|
||||
}
|
||||
else if (fcgi_strm_sc(fstrm) && fcgi_strm_sc(fstrm)->app_ops->wake != NULL) {
|
||||
TRACE_POINT(FCGI_EV_STRM_WAKE, fstrm->fconn->conn, fstrm);
|
||||
fcgi_strm_sc(fstrm)->app_ops->wake(fcgi_strm_sc(fstrm));
|
||||
if (!fstrm->subs && (fstrm->flags & (FCGI_SF_WANT_SHUTR|FCGI_SF_WANT_SHUTW)))
|
||||
tasklet_wakeup(fstrm->shut_tl);
|
||||
else if (fcgi_strm_sc(fstrm)) {
|
||||
unsigned int state = TASK_WOKEN_MSG;
|
||||
|
||||
if (fstrm->subs) {
|
||||
if (fstrm->subs->events & SUB_RETRY_SEND)
|
||||
fstrm->flags |= FCGI_SF_NOTIFIED;
|
||||
fstrm->subs->events = 0;
|
||||
fstrm->subs = NULL;
|
||||
state |= TASK_WOKEN_IO;
|
||||
}
|
||||
tasklet_wakeup(fcgi_strm_sc(fstrm)->wait_event.tasklet, state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3734,7 +3736,7 @@ static void fcgi_detach(struct sedesc *sd)
|
||||
if (eb_is_empty(&fconn->streams_by_id)) {
|
||||
if (!fconn->conn->owner) {
|
||||
/* Session insertion above has failed and connection is idle, remove it. */
|
||||
fconn->conn->mux->destroy(fconn);
|
||||
CALL_MUX_NO_RET(fconn->conn->mux, destroy(fconn));
|
||||
TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR);
|
||||
return;
|
||||
}
|
||||
@ -3747,7 +3749,7 @@ static void fcgi_detach(struct sedesc *sd)
|
||||
|
||||
/* Ensure session can keep a new idle connection. */
|
||||
if (session_check_idle_conn(sess, fconn->conn) != 0) {
|
||||
fconn->conn->mux->destroy(fconn);
|
||||
CALL_MUX_NO_RET(fconn->conn->mux, destroy(fconn));
|
||||
TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR);
|
||||
return;
|
||||
}
|
||||
@ -3778,7 +3780,7 @@ static void fcgi_detach(struct sedesc *sd)
|
||||
|
||||
if (!srv_add_to_idle_list(objt_server(fconn->conn->target), fconn->conn, 1)) {
|
||||
/* The server doesn't want it, let's kill the connection right away */
|
||||
fconn->conn->mux->destroy(fconn);
|
||||
CALL_MUX_NO_RET(fconn->conn->mux, destroy(fconn));
|
||||
TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
33
src/mux_h1.c
33
src/mux_h1.c
@ -1197,7 +1197,7 @@ static int h1s_finish_detach(struct h1s *h1s)
|
||||
if (!session_add_conn(sess, h1c->conn)) {
|
||||
/* HTTP/1.1 conn is always idle after detach, can be removed if session insert failed. */
|
||||
h1c->conn->owner = NULL;
|
||||
h1c->conn->mux->destroy(h1c);
|
||||
CALL_MUX_NO_RET(h1c->conn->mux, destroy(h1c));
|
||||
goto released;
|
||||
}
|
||||
|
||||
@ -1213,7 +1213,7 @@ static int h1s_finish_detach(struct h1s *h1s)
|
||||
/* Ensure session can keep a new idle connection. */
|
||||
if (session_check_idle_conn(sess, h1c->conn)) {
|
||||
TRACE_DEVEL("outgoing connection rejected", H1_EV_STRM_END|H1_EV_H1C_END, h1c->conn);
|
||||
h1c->conn->mux->destroy(h1c);
|
||||
CALL_MUX_NO_RET(h1c->conn->mux, destroy(h1c));
|
||||
goto released;
|
||||
}
|
||||
|
||||
@ -1236,7 +1236,7 @@ static int h1s_finish_detach(struct h1s *h1s)
|
||||
|
||||
if (!srv_add_to_idle_list(objt_server(h1c->conn->target), h1c->conn, is_not_first)) {
|
||||
/* The server doesn't want it, let's kill the connection right away */
|
||||
h1c->conn->mux->destroy(h1c);
|
||||
CALL_MUX_NO_RET(h1c->conn->mux, destroy(h1c));
|
||||
TRACE_DEVEL("outgoing connection killed", H1_EV_STRM_END|H1_EV_H1C_END);
|
||||
goto released;
|
||||
}
|
||||
@ -3716,7 +3716,7 @@ static void h1_wake_stream_for_recv(struct h1s *h1s)
|
||||
{
|
||||
if (h1s && h1s->subs && h1s->subs->events & SUB_RETRY_RECV) {
|
||||
TRACE_POINT(H1_EV_STRM_WAKE, h1s->h1c->conn, h1s);
|
||||
tasklet_wakeup(h1s->subs->tasklet);
|
||||
tasklet_wakeup(h1s->subs->tasklet, TASK_WOKEN_IO);
|
||||
h1s->subs->events &= ~SUB_RETRY_RECV;
|
||||
if (!h1s->subs->events)
|
||||
h1s->subs = NULL;
|
||||
@ -3726,28 +3726,31 @@ static void h1_wake_stream_for_send(struct h1s *h1s)
|
||||
{
|
||||
if (h1s && h1s->subs && h1s->subs->events & SUB_RETRY_SEND) {
|
||||
TRACE_POINT(H1_EV_STRM_WAKE, h1s->h1c->conn, h1s);
|
||||
tasklet_wakeup(h1s->subs->tasklet);
|
||||
tasklet_wakeup(h1s->subs->tasklet, TASK_WOKEN_IO);
|
||||
h1s->subs->events &= ~SUB_RETRY_SEND;
|
||||
if (!h1s->subs->events)
|
||||
h1s->subs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* alerts the data layer following this sequence :
|
||||
* - if the h1s' data layer is subscribed to recv, then it's woken up for recv
|
||||
* - if its subscribed to send, then it's woken up for send
|
||||
* - if it was subscribed to neither, its ->wake() callback is called
|
||||
/* Alerts the data layer by waking it up. TASK_WOKEN_MSG state is used by
|
||||
* default and if the data layer is also subscribed to recv or send,
|
||||
* TASK_WOKEN_IO is added.
|
||||
*/
|
||||
static void h1_alert(struct h1s *h1s)
|
||||
{
|
||||
unsigned int state = TASK_WOKEN_MSG;
|
||||
|
||||
TRACE_POINT(H1_EV_STRM_WAKE, h1s->h1c->conn, h1s);
|
||||
if (!h1s_sc(h1s))
|
||||
return;
|
||||
|
||||
if (h1s->subs) {
|
||||
h1_wake_stream_for_recv(h1s);
|
||||
h1_wake_stream_for_send(h1s);
|
||||
}
|
||||
else if (h1s_sc(h1s) && h1s_sc(h1s)->app_ops->wake != NULL) {
|
||||
TRACE_POINT(H1_EV_STRM_WAKE, h1s->h1c->conn, h1s);
|
||||
h1s_sc(h1s)->app_ops->wake(h1s_sc(h1s));
|
||||
h1s->subs->events = 0;
|
||||
h1s->subs = NULL;
|
||||
state |= TASK_WOKEN_IO;
|
||||
}
|
||||
tasklet_wakeup(h1s_sc(h1s)->wait_event.tasklet, state);
|
||||
}
|
||||
|
||||
/* Try to send an HTTP error with h1c->errcode status code. It returns 1 on success
|
||||
|
||||
227
src/mux_h2.c
227
src/mux_h2.c
@ -101,6 +101,7 @@ struct h2c {
|
||||
struct wait_event wait_event; /* To be used if we're waiting for I/Os */
|
||||
|
||||
struct list *next_tasklet; /* which applet to wake up next (NULL by default) */
|
||||
uint32_t streams_hard_limit; /* maximum number of concurrent streams supported locally */
|
||||
};
|
||||
|
||||
|
||||
@ -488,9 +489,14 @@ static int h2_be_glitches_threshold = 0; /* backend's max glitches
|
||||
static int h2_fe_glitches_threshold = 0; /* frontend's max glitches: unlimited */
|
||||
static uint h2_be_rxbuf = 0; /* backend's default total rxbuf (bytes) */
|
||||
static uint h2_fe_rxbuf = 0; /* frontend's default total rxbuf (bytes) */
|
||||
static unsigned int h2_be_max_frames_at_once = 0; /* backend value: 0=no limit */
|
||||
static unsigned int h2_fe_max_frames_at_once = 0; /* frontend value: 0=no limit */
|
||||
static unsigned int h2_fe_max_rst_at_once = 0; /* frontend value: 0=no limit */
|
||||
static unsigned int h2_settings_max_concurrent_streams = 100; /* default value */
|
||||
static unsigned int h2_be_settings_max_concurrent_streams = 0; /* backend value */
|
||||
static unsigned int h2_fe_settings_max_concurrent_streams = 0; /* frontend value */
|
||||
static unsigned int h2_fe_min_concurrent_streams = 1; /* minimum concurrent streams when using rq-load */
|
||||
static unsigned int h2_fe_max_rq_load = ~0; /* max rq for FE dynamic MCS sizing. 0=def rq */
|
||||
static int h2_settings_max_frame_size = 0; /* unset */
|
||||
static int h2_settings_log_errors = H2_ERR_LOG_ERR_STRM;
|
||||
|
||||
@ -744,7 +750,8 @@ static inline int h2c_may_expire(const struct h2c *h2c)
|
||||
|
||||
/* returns the number of max concurrent streams permitted on a connection,
|
||||
* depending on its side (frontend or backend), falling back to the default
|
||||
* h2_settings_max_concurrent_streams. It may even be zero.
|
||||
* h2_settings_max_concurrent_streams. It may even be zero. It only relies
|
||||
* on configuration settings.
|
||||
*/
|
||||
static inline int h2c_max_concurrent_streams(const struct h2c *h2c)
|
||||
{
|
||||
@ -755,6 +762,22 @@ static inline int h2c_max_concurrent_streams(const struct h2c *h2c)
|
||||
h2_fe_settings_max_concurrent_streams;
|
||||
|
||||
ret = ret ? ret : h2_settings_max_concurrent_streams;
|
||||
|
||||
/* if h2_fe_max_rq_load is set, adjust the max concurrent streams
|
||||
* according to it and the current load.
|
||||
*/
|
||||
if (!(h2c->flags & H2_CF_IS_BACK) && h2_fe_max_rq_load != ~0) {
|
||||
uint limit = h2_fe_max_rq_load ? h2_fe_max_rq_load : global.tune.runqueue_depth;
|
||||
uint load = MAX(swrate_avg(th_ctx->rq_tot_peak, RQ_LOAD_SAMPLES), th_ctx->rq_total);
|
||||
|
||||
/* divide limits by the square of the ratio of current load to
|
||||
* the limit so as to react fast.
|
||||
*/
|
||||
if (load > limit)
|
||||
ret = (uint64_t)ret * limit / load * limit / load;
|
||||
if (ret < h2_fe_min_concurrent_streams)
|
||||
ret = h2_fe_min_concurrent_streams;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1114,7 +1137,7 @@ static inline void h2c_restart_reading(const struct h2c *h2c, int consider_buffe
|
||||
/* returns true if the front connection has too many stream connectors attached */
|
||||
static inline int h2_frt_has_too_many_sc(const struct h2c *h2c)
|
||||
{
|
||||
return h2c->nb_sc > h2c_max_concurrent_streams(h2c) ||
|
||||
return h2c->nb_sc > h2c->streams_hard_limit ||
|
||||
unlikely(conn_reverse_in_preconnect(h2c->conn));
|
||||
}
|
||||
|
||||
@ -1425,7 +1448,7 @@ static int h2_init(struct connection *conn, struct proxy *prx, struct session *s
|
||||
/* Initialise the context. */
|
||||
h2c->st0 = H2_CS_PREFACE;
|
||||
h2c->conn = conn;
|
||||
h2c->streams_limit = h2c_max_concurrent_streams(h2c);
|
||||
h2c->streams_limit = h2c->streams_hard_limit = h2c_max_concurrent_streams(h2c);
|
||||
nb_rxbufs = (h2c->flags & H2_CF_IS_BACK) ? h2_be_rxbuf : h2_fe_rxbuf;
|
||||
nb_rxbufs = (nb_rxbufs + global.tune.bufsize - 9 - 1) / (global.tune.bufsize - 9);
|
||||
nb_rxbufs = MAX(nb_rxbufs, h2c->streams_limit);
|
||||
@ -1668,9 +1691,10 @@ static void __maybe_unused h2s_notify_recv(struct h2s *h2s)
|
||||
TRACE_POINT(H2_EV_STRM_WAKE, h2s->h2c->conn, h2s);
|
||||
if (h2s->h2c->next_tasklet ||
|
||||
(th_ctx->current && th_ctx->current->process == h2_io_cb))
|
||||
h2s->h2c->next_tasklet = tasklet_wakeup_after(h2s->h2c->next_tasklet, h2s->subs->tasklet);
|
||||
h2s->h2c->next_tasklet = tasklet_wakeup_after(h2s->h2c->next_tasklet, h2s->subs->tasklet,
|
||||
TASK_WOKEN_IO);
|
||||
else
|
||||
tasklet_wakeup(h2s->subs->tasklet);
|
||||
tasklet_wakeup(h2s->subs->tasklet, TASK_WOKEN_IO);
|
||||
h2s->subs->events &= ~SUB_RETRY_RECV;
|
||||
if (!h2s->subs->events)
|
||||
h2s->subs = NULL;
|
||||
@ -1683,7 +1707,7 @@ static void __maybe_unused h2s_notify_send(struct h2s *h2s)
|
||||
if (h2s->subs && h2s->subs->events & SUB_RETRY_SEND) {
|
||||
TRACE_POINT(H2_EV_STRM_WAKE, h2s->h2c->conn, h2s);
|
||||
h2s->flags |= H2_SF_NOTIFIED;
|
||||
tasklet_wakeup(h2s->subs->tasklet);
|
||||
tasklet_wakeup(h2s->subs->tasklet, TASK_WOKEN_IO);
|
||||
h2s->subs->events &= ~SUB_RETRY_SEND;
|
||||
if (!h2s->subs->events)
|
||||
h2s->subs = NULL;
|
||||
@ -1694,26 +1718,27 @@ static void __maybe_unused h2s_notify_send(struct h2s *h2s)
|
||||
}
|
||||
}
|
||||
|
||||
/* alerts the data layer, trying to wake it up by all means, following
|
||||
* this sequence :
|
||||
* - if the h2s' data layer is subscribed to recv, then it's woken up for recv
|
||||
* - if its subscribed to send, then it's woken up for send
|
||||
* - if it was subscribed to neither, its ->wake() callback is called
|
||||
* It is safe to call this function with a closed stream which doesn't have a
|
||||
* stream connector anymore.
|
||||
/* alerts the data layer by waking it up. TASK_WOKEN_MSG state is used by
|
||||
* default and if the data layer is also subscribed to recv or send,
|
||||
* TASK_WOKEN_IO is added. But first of all, we check if the shut tasklet must
|
||||
* be woken up or not instead.
|
||||
*/
|
||||
static void __maybe_unused h2s_alert(struct h2s *h2s)
|
||||
{
|
||||
TRACE_ENTER(H2_EV_H2S_WAKE, h2s->h2c->conn, h2s);
|
||||
if (!h2s->subs && (h2s->flags & (H2_SF_WANT_SHUTR | H2_SF_WANT_SHUTW)))
|
||||
tasklet_wakeup(h2s->shut_tl);
|
||||
else if (h2s_sc(h2s)) {
|
||||
unsigned int state = TASK_WOKEN_MSG;
|
||||
|
||||
if (h2s->subs ||
|
||||
(h2s->flags & (H2_SF_WANT_SHUTR | H2_SF_WANT_SHUTW))) {
|
||||
h2s_notify_recv(h2s);
|
||||
h2s_notify_send(h2s);
|
||||
}
|
||||
else if (h2s_sc(h2s) && h2s_sc(h2s)->app_ops->wake != NULL) {
|
||||
TRACE_POINT(H2_EV_STRM_WAKE, h2s->h2c->conn, h2s);
|
||||
h2s_sc(h2s)->app_ops->wake(h2s_sc(h2s));
|
||||
if (h2s->subs) {
|
||||
if (h2s->subs->events & SUB_RETRY_SEND)
|
||||
h2s->flags |= H2_SF_NOTIFIED;
|
||||
h2s->subs->events = 0;
|
||||
h2s->subs = NULL;
|
||||
state |= TASK_WOKEN_IO;
|
||||
}
|
||||
tasklet_wakeup(h2s_sc(h2s)->wait_event.tasklet, state);
|
||||
}
|
||||
|
||||
TRACE_LEAVE(H2_EV_H2S_WAKE, h2s->h2c->conn, h2s);
|
||||
@ -1741,9 +1766,9 @@ static inline int _h2c_report_glitch(struct h2c *h2c, int increment)
|
||||
* to force clients to periodically reconnect.
|
||||
*/
|
||||
if (h2c->last_sid <= 0 ||
|
||||
h2c->last_sid > h2c->max_id + 2 * h2c_max_concurrent_streams(h2c)) {
|
||||
h2c->last_sid > h2c->max_id + 2 * h2c->streams_hard_limit) {
|
||||
/* not set yet or was too high */
|
||||
h2c->last_sid = h2c->max_id + 2 * h2c_max_concurrent_streams(h2c);
|
||||
h2c->last_sid = h2c->max_id + 2 * h2c->streams_hard_limit;
|
||||
h2c_send_goaway_error(h2c, NULL);
|
||||
}
|
||||
|
||||
@ -2105,7 +2130,7 @@ static struct h2s *h2c_frt_stream_new(struct h2c *h2c, int id, struct buffer *in
|
||||
/* Cannot handle stream if active reversed connection is not yet accepted. */
|
||||
BUG_ON(conn_reverse_in_preconnect(h2c->conn));
|
||||
|
||||
if (h2c->nb_streams >= h2c_max_concurrent_streams(h2c)) {
|
||||
if (h2c->nb_streams >= h2c->streams_hard_limit) {
|
||||
h2c_report_glitch(h2c, 1, "HEADERS frame causing MAX_CONCURRENT_STREAMS to be exceeded");
|
||||
TRACE_ERROR("HEADERS frame causing MAX_CONCURRENT_STREAMS to be exceeded", H2_EV_H2S_NEW|H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn);
|
||||
session_inc_http_req_ctr(sess);
|
||||
@ -2285,7 +2310,7 @@ static int h2c_send_settings(struct h2c *h2c)
|
||||
chunk_memcat(&buf, str, 6);
|
||||
}
|
||||
|
||||
mcs = h2c_max_concurrent_streams(h2c);
|
||||
mcs = h2c->streams_hard_limit;
|
||||
if (mcs != 0) {
|
||||
char str[6] = "\x00\x03"; /* max_concurrent_streams */
|
||||
|
||||
@ -2906,8 +2931,8 @@ static int h2c_handle_settings(struct h2c *h2c)
|
||||
case H2_SETTINGS_MAX_CONCURRENT_STREAMS:
|
||||
if (h2c->flags & H2_CF_IS_BACK) {
|
||||
/* the limit is only for the backend; for the frontend it is our limit */
|
||||
if ((unsigned int)arg > h2c_max_concurrent_streams(h2c))
|
||||
arg = h2c_max_concurrent_streams(h2c);
|
||||
if ((unsigned int)arg > h2c->streams_hard_limit)
|
||||
arg = h2c->streams_hard_limit;
|
||||
h2c->streams_limit = arg;
|
||||
}
|
||||
break;
|
||||
@ -3287,7 +3312,7 @@ static int h2c_handle_window_update(struct h2c *h2c, struct h2s *h2s)
|
||||
goto out0;
|
||||
}
|
||||
|
||||
inc = h2_get_n32(&h2c->dbuf, 0);
|
||||
inc = h2_get_n32(&h2c->dbuf, 0) & 0x7FFFFFFF;
|
||||
|
||||
if (h2c->dsi != 0) {
|
||||
/* stream window update */
|
||||
@ -3382,7 +3407,7 @@ static int h2c_handle_goaway(struct h2c *h2c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
last = h2_get_n32(&h2c->dbuf, 0);
|
||||
last = h2_get_n32(&h2c->dbuf, 0) & 0x7FFFFFFF; // mask R bit
|
||||
h2c->errcode = h2_get_n32(&h2c->dbuf, 4);
|
||||
if (h2c->last_sid < 0)
|
||||
h2c->last_sid = last;
|
||||
@ -3559,7 +3584,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
|
||||
goto out; // IDLE but too many sc still present
|
||||
}
|
||||
else if (h2_fe_max_total_streams &&
|
||||
h2c->stream_cnt >= h2_fe_max_total_streams + h2c_max_concurrent_streams(h2c)) {
|
||||
h2c->stream_cnt >= h2_fe_max_total_streams + h2c->streams_hard_limit) {
|
||||
/* We've already told this client we were going to close a
|
||||
* while ago and apparently it didn't care, so it's time to
|
||||
* stop processing its requests for real.
|
||||
@ -3686,9 +3711,9 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
|
||||
* ID.
|
||||
*/
|
||||
if (h2c->last_sid <= 0 ||
|
||||
h2c->last_sid > h2c->max_id + 2 * h2c_max_concurrent_streams(h2c)) {
|
||||
h2c->last_sid > h2c->max_id + 2 * h2c->streams_hard_limit) {
|
||||
/* not set yet or was too high */
|
||||
h2c->last_sid = h2c->max_id + 2 * h2c_max_concurrent_streams(h2c);
|
||||
h2c->last_sid = h2c->max_id + 2 * h2c->streams_hard_limit;
|
||||
h2c_send_goaway_error(h2c, NULL);
|
||||
}
|
||||
}
|
||||
@ -4217,6 +4242,8 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
struct h2_fh hdr;
|
||||
unsigned int padlen = 0;
|
||||
int32_t old_iw = h2c->miw;
|
||||
uint frames_budget = 0;
|
||||
uint rst_budget = 0;
|
||||
|
||||
TRACE_ENTER(H2_EV_H2C_WAKE, h2c->conn);
|
||||
|
||||
@ -4305,6 +4332,14 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
}
|
||||
}
|
||||
|
||||
if (h2c->flags & H2_CF_IS_BACK) {
|
||||
frames_budget = h2_be_max_frames_at_once;
|
||||
}
|
||||
else {
|
||||
frames_budget = h2_fe_max_frames_at_once;
|
||||
rst_budget = h2_fe_max_rst_at_once;
|
||||
}
|
||||
|
||||
/* process as many incoming frames as possible below */
|
||||
while (1) {
|
||||
int ret = 0;
|
||||
@ -4607,6 +4642,29 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
h2c->st0 = H2_CS_FRAME_H;
|
||||
}
|
||||
}
|
||||
|
||||
/* If more frames remain in the buffer, let's first check if we've
|
||||
* depleted the frames processing budget. Consuming the RST budget
|
||||
* makes the tasklet go to TL_BULK to make it less prioritary than
|
||||
* other processing since it's often used by attacks, while other
|
||||
* frame types just yield normally.
|
||||
*/
|
||||
if (b_data(&h2c->dbuf)) {
|
||||
if (h2c->dft == H2_FT_RST_STREAM && (rst_budget && !--rst_budget)) {
|
||||
/* we've consumed all RST frames permitted by
|
||||
* the budget, we have to yield now.
|
||||
*/
|
||||
tasklet_wakeup(h2c->wait_event.tasklet, 0);
|
||||
break;
|
||||
}
|
||||
else if ((frames_budget && !--frames_budget)) {
|
||||
/* we've consumed all frames permitted by the
|
||||
* budget, we have to yield now.
|
||||
*/
|
||||
tasklet_wakeup(h2c->wait_event.tasklet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (h2c_update_strm_rx_win(h2c) &&
|
||||
@ -4689,16 +4747,7 @@ static void h2_resume_each_sending_h2s(struct h2c *h2c, struct list *head)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (h2s->subs && h2s->subs->events & SUB_RETRY_SEND) {
|
||||
h2s->flags |= H2_SF_NOTIFIED;
|
||||
tasklet_wakeup(h2s->subs->tasklet);
|
||||
h2s->subs->events &= ~SUB_RETRY_SEND;
|
||||
if (!h2s->subs->events)
|
||||
h2s->subs = NULL;
|
||||
}
|
||||
else if (h2s->flags & (H2_SF_WANT_SHUTR|H2_SF_WANT_SHUTW)) {
|
||||
tasklet_wakeup(h2s->shut_tl);
|
||||
}
|
||||
h2s_notify_send(h2s);
|
||||
}
|
||||
|
||||
TRACE_LEAVE(H2_EV_H2C_SEND|H2_EV_H2S_WAKE, h2c->conn);
|
||||
@ -5679,7 +5728,7 @@ static void h2_detach(struct sedesc *sd)
|
||||
if (eb_is_empty(&h2c->streams_by_id)) {
|
||||
if (!h2c->conn->owner) {
|
||||
/* Session insertion above has failed and connection is idle, remove it. */
|
||||
h2c->conn->mux->destroy(h2c);
|
||||
CALL_MUX_NO_RET(h2c->conn->mux, destroy(h2c));
|
||||
TRACE_DEVEL("leaving on error after killing outgoing connection", H2_EV_STRM_END|H2_EV_H2C_ERR);
|
||||
return;
|
||||
}
|
||||
@ -5692,7 +5741,7 @@ static void h2_detach(struct sedesc *sd)
|
||||
|
||||
/* Ensure session can keep a new idle connection. */
|
||||
if (session_check_idle_conn(sess, h2c->conn) != 0) {
|
||||
h2c->conn->mux->destroy(h2c);
|
||||
CALL_MUX_NO_RET(h2c->conn->mux, destroy(h2c));
|
||||
TRACE_DEVEL("leaving without reusable idle connection", H2_EV_STRM_END);
|
||||
return;
|
||||
}
|
||||
@ -5723,7 +5772,7 @@ static void h2_detach(struct sedesc *sd)
|
||||
|
||||
if (!srv_add_to_idle_list(objt_server(h2c->conn->target), h2c->conn, 1)) {
|
||||
/* The server doesn't want it, let's kill the connection right away */
|
||||
h2c->conn->mux->destroy(h2c);
|
||||
CALL_MUX_NO_RET(h2c->conn->mux, destroy(h2c));
|
||||
TRACE_DEVEL("leaving on error after killing outgoing connection", H2_EV_STRM_END|H2_EV_H2C_ERR);
|
||||
return;
|
||||
}
|
||||
@ -7817,7 +7866,6 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
||||
struct htx *h2s_htx = NULL;
|
||||
struct htx *buf_htx = NULL;
|
||||
struct buffer *rxbuf = NULL;
|
||||
struct htx_ret htxret;
|
||||
size_t ret = 0;
|
||||
uint prev_h2c_flags = h2c->flags;
|
||||
unsigned long long prev_body_len = h2s->body_len;
|
||||
@ -7852,17 +7900,7 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
||||
goto end;
|
||||
}
|
||||
|
||||
htxret = htx_xfer_blks(buf_htx, h2s_htx, count, HTX_BLK_UNUSED);
|
||||
count -= htxret.ret;
|
||||
|
||||
if (h2s_htx->flags & HTX_FL_PARSING_ERROR) {
|
||||
buf_htx->flags |= HTX_FL_PARSING_ERROR;
|
||||
if (htx_is_empty(buf_htx))
|
||||
se_fl_set(h2s->sd, SE_FL_EOI);
|
||||
}
|
||||
else if (htx_is_empty(h2s_htx)) {
|
||||
buf_htx->flags |= (h2s_htx->flags & HTX_FL_EOM);
|
||||
}
|
||||
count -= htx_xfer(buf_htx, h2s_htx, count, HTX_XFER_DEFAULT);
|
||||
|
||||
htx_to_buf(buf_htx, buf);
|
||||
htx_to_buf(h2s_htx, rxbuf);
|
||||
@ -7891,13 +7929,7 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
||||
|
||||
/* tell the stream layer whether there are data left or not */
|
||||
if (h2s_rxbuf_cnt(h2s)) {
|
||||
/* Note that parsing errors can also arrive here, we may need
|
||||
* to propagate errors upstream otherwise no new activity will
|
||||
* unblock them.
|
||||
*/
|
||||
se_fl_set(h2s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
|
||||
if (h2s_htx && h2s_htx->flags & HTX_FL_PARSING_ERROR)
|
||||
h2s_propagate_term_flags(h2c, h2s);
|
||||
BUG_ON_HOT(!buf->data);
|
||||
}
|
||||
else {
|
||||
@ -8713,20 +8745,56 @@ static int h2_parse_max_concurrent_streams(char **args, int section_type, struct
|
||||
char **err)
|
||||
{
|
||||
uint *vptr;
|
||||
|
||||
if (too_many_args(1, args, err, NULL))
|
||||
return -1;
|
||||
int arg;
|
||||
|
||||
/* backend/frontend/default */
|
||||
vptr = (args[0][8] == 'b') ? &h2_be_settings_max_concurrent_streams :
|
||||
(args[0][8] == 'f') ? &h2_fe_settings_max_concurrent_streams :
|
||||
&h2_settings_max_concurrent_streams;
|
||||
|
||||
|
||||
if (args[0][8] != 'f' && too_many_args(1, args, err, NULL))
|
||||
return -1;
|
||||
|
||||
*vptr = atoi(args[1]);
|
||||
if ((int)*vptr < 0) {
|
||||
memprintf(err, "'%s' expects a positive numeric value.", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (args[0][8] != 'f')
|
||||
goto leave;
|
||||
|
||||
/* tune.h2.fe. here */
|
||||
for (arg = 2; *args[arg]; arg += 2) {
|
||||
if (strcmp(args[arg], "rq-load") == 0) {
|
||||
if (strcmp(args[arg + 1], "ignore") == 0)
|
||||
h2_fe_max_rq_load = ~0;
|
||||
else if (strcmp(args[arg + 1], "auto") == 0)
|
||||
h2_fe_max_rq_load = 0;
|
||||
else if (!*args[arg + 1] || (h2_fe_max_rq_load = atoi(args[arg + 1])) <= 0) {
|
||||
memprintf(err, "'%s' expects a strictly positive run-queue length, or 'auto' or 'ignore'.", args[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[arg], "min") == 0) {
|
||||
if (!*args[arg + 1] || (h2_fe_min_concurrent_streams = atoi(args[arg + 1])) <= 0) {
|
||||
memprintf(err, "'%s' expects a strictly positive minimum number of streams'.", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (h2_fe_min_concurrent_streams > h2_fe_settings_max_concurrent_streams) {
|
||||
memprintf(err, "'%s' minimum number of streams (%u) cannot be higher than the maximum (%u)'.",
|
||||
args[0], h2_fe_min_concurrent_streams, h2_fe_settings_max_concurrent_streams);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (*args[arg]) {
|
||||
memprintf(err, "'%s' only supports 'rq-load' or 'min' after the numeric value, but found '%s'.", args[0], args[arg]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
leave:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -8751,6 +8819,30 @@ static int h2_parse_max_total_streams(char **args, int section_type, struct prox
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* config parser for global "tune.h2.{be.,fe.,}max-{frames,rst}-at-once" */
|
||||
static int h2_parse_max_frames_at_once(char **args, int section_type, struct proxy *curpx,
|
||||
const struct proxy *defpx, const char *file, int line,
|
||||
char **err)
|
||||
{
|
||||
uint *vptr;
|
||||
|
||||
/* backend/frontend/default */
|
||||
if (strcmp(args[0], "tune.h2.be.max-frames-at-once") == 0)
|
||||
vptr = &h2_be_max_frames_at_once;
|
||||
else if (strcmp(args[0], "tune.h2.fe.max-frames-at-once") == 0)
|
||||
vptr = &h2_fe_max_frames_at_once;
|
||||
else if (strcmp(args[0], "tune.h2.fe.max-rst-at-once") == 0)
|
||||
vptr = &h2_fe_max_rst_at_once;
|
||||
else
|
||||
BUG_ON(1, "unhandled keyword");
|
||||
|
||||
if (too_many_args(1, args, err, NULL))
|
||||
return -1;
|
||||
|
||||
*vptr = atoi(args[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* config parser for global "tune.h2.max-frame-size" */
|
||||
static int h2_parse_max_frame_size(char **args, int section_type, struct proxy *curpx,
|
||||
const struct proxy *defpx, const char *file, int line,
|
||||
@ -8849,10 +8941,13 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
||||
{ CFG_GLOBAL, "tune.h2.be.glitches-threshold", h2_parse_glitches_threshold },
|
||||
{ CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size },
|
||||
{ CFG_GLOBAL, "tune.h2.be.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
||||
{ CFG_GLOBAL, "tune.h2.be.max-frames-at-once", h2_parse_max_frames_at_once },
|
||||
{ CFG_GLOBAL, "tune.h2.be.rxbuf", h2_parse_rxbuf },
|
||||
{ CFG_GLOBAL, "tune.h2.fe.glitches-threshold", h2_parse_glitches_threshold },
|
||||
{ CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size },
|
||||
{ CFG_GLOBAL, "tune.h2.fe.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
||||
{ CFG_GLOBAL, "tune.h2.fe.max-frames-at-once", h2_parse_max_frames_at_once },
|
||||
{ CFG_GLOBAL, "tune.h2.fe.max-rst-at-once", h2_parse_max_frames_at_once },
|
||||
{ CFG_GLOBAL, "tune.h2.fe.max-total-streams", h2_parse_max_total_streams },
|
||||
{ CFG_GLOBAL, "tune.h2.fe.rxbuf", h2_parse_rxbuf },
|
||||
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },
|
||||
|
||||
24
src/mux_pt.c
24
src/mux_pt.c
@ -252,20 +252,21 @@ struct task *mux_pt_io_cb(struct task *t, void *tctx, unsigned int status)
|
||||
|
||||
TRACE_ENTER(PT_EV_CONN_WAKE, ctx->conn);
|
||||
if (!se_fl_test(ctx->sd, SE_FL_ORPHAN)) {
|
||||
unsigned int state = TASK_WOKEN_MSG;
|
||||
|
||||
/* There's a small race condition.
|
||||
* mux_pt_io_cb() is only supposed to be called if we have no
|
||||
* stream attached. However, maybe the tasklet got woken up,
|
||||
* and this connection was then attached to a new stream.
|
||||
* If this happened, just wake the tasklet up if anybody
|
||||
* subscribed to receive events, and otherwise call the wake
|
||||
* method, to make sure the event is noticed.
|
||||
* If this happened, just wake the tasklet up.
|
||||
*/
|
||||
if (ctx->conn->subs) {
|
||||
ctx->conn->subs->events = 0;
|
||||
tasklet_wakeup(ctx->conn->subs->tasklet);
|
||||
ctx->conn->subs = NULL;
|
||||
} else if (pt_sc(ctx)->app_ops->wake)
|
||||
pt_sc(ctx)->app_ops->wake(pt_sc(ctx));
|
||||
state |= TASK_WOKEN_IO;
|
||||
}
|
||||
tasklet_wakeup(pt_sc(ctx)->wait_event.tasklet, state);
|
||||
|
||||
TRACE_DEVEL("leaving waking up SC", PT_EV_CONN_WAKE, ctx->conn);
|
||||
return t;
|
||||
}
|
||||
@ -360,14 +361,9 @@ static int mux_pt_wake(struct connection *conn)
|
||||
int ret = 0;
|
||||
|
||||
TRACE_ENTER(PT_EV_CONN_WAKE, ctx->conn);
|
||||
if (!se_fl_test(ctx->sd, SE_FL_ORPHAN)) {
|
||||
ret = pt_sc(ctx)->app_ops->wake ? pt_sc(ctx)->app_ops->wake(pt_sc(ctx)) : 0;
|
||||
|
||||
if (ret < 0) {
|
||||
TRACE_DEVEL("leaving waking up SC", PT_EV_CONN_WAKE, ctx->conn);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (!se_fl_test(ctx->sd, SE_FL_ORPHAN))
|
||||
tasklet_wakeup(pt_sc(ctx)->wait_event.tasklet, TASK_WOKEN_MSG);
|
||||
else {
|
||||
conn_ctrl_drain(conn);
|
||||
if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) {
|
||||
TRACE_DEVEL("leaving destroying PT context", PT_EV_CONN_WAKE, ctx->conn);
|
||||
|
||||
@ -506,19 +506,23 @@ static struct ncbuf *qcs_get_ncbuf(struct qcs *qcs, struct ncbuf *ncbuf)
|
||||
return ncbuf;
|
||||
}
|
||||
|
||||
/* Notify an eventual subscriber on <qcs> or else wakeup up the stconn layer if
|
||||
* initialized.
|
||||
/* Notify the stconn layer if initialized with TASK_WOKEN_MSG state and
|
||||
* eventually TASK_WOKEN_IO.
|
||||
*/
|
||||
static void qcs_alert(struct qcs *qcs)
|
||||
{
|
||||
unsigned int state = TASK_WOKEN_MSG;
|
||||
|
||||
TRACE_POINT(QMUX_EV_STRM_WAKE, qcs->qcc->conn, qcs);
|
||||
if (!qcs_sc(qcs))
|
||||
return;
|
||||
|
||||
if (qcs->subs) {
|
||||
qcs_notify_recv(qcs);
|
||||
qcs_notify_send(qcs);
|
||||
}
|
||||
else if (qcs_sc(qcs) && qcs->sd->sc->app_ops->wake) {
|
||||
TRACE_POINT(QMUX_EV_STRM_WAKE, qcs->qcc->conn, qcs);
|
||||
qcs->sd->sc->app_ops->wake(qcs->sd->sc);
|
||||
qcs->subs->events = 0;
|
||||
qcs->subs = NULL;
|
||||
state |= TASK_WOKEN_IO;
|
||||
}
|
||||
tasklet_wakeup(qcs_sc(qcs)->wait_event.tasklet, state);
|
||||
}
|
||||
|
||||
int qcs_subscribe(struct qcs *qcs, int event_type, struct wait_event *es)
|
||||
@ -548,7 +552,7 @@ void qcs_notify_recv(struct qcs *qcs)
|
||||
{
|
||||
if (qcs->subs && qcs->subs->events & SUB_RETRY_RECV) {
|
||||
TRACE_POINT(QMUX_EV_STRM_WAKE, qcs->qcc->conn, qcs);
|
||||
tasklet_wakeup(qcs->subs->tasklet);
|
||||
tasklet_wakeup(qcs->subs->tasklet, TASK_WOKEN_IO);
|
||||
qcs->subs->events &= ~SUB_RETRY_RECV;
|
||||
if (!qcs->subs->events)
|
||||
qcs->subs = NULL;
|
||||
@ -559,7 +563,7 @@ void qcs_notify_send(struct qcs *qcs)
|
||||
{
|
||||
if (qcs->subs && qcs->subs->events & SUB_RETRY_SEND) {
|
||||
TRACE_POINT(QMUX_EV_STRM_WAKE, qcs->qcc->conn, qcs);
|
||||
tasklet_wakeup(qcs->subs->tasklet);
|
||||
tasklet_wakeup(qcs->subs->tasklet, TASK_WOKEN_IO);
|
||||
qcs->subs->events &= ~SUB_RETRY_SEND;
|
||||
if (!qcs->subs->events)
|
||||
qcs->subs = NULL;
|
||||
|
||||
@ -952,7 +952,7 @@ static void spop_strm_notify_recv(struct spop_strm *spop_strm)
|
||||
{
|
||||
if (spop_strm->subs && (spop_strm->subs->events & SUB_RETRY_RECV)) {
|
||||
TRACE_POINT(SPOP_EV_STRM_WAKE, spop_strm->spop_conn->conn, spop_strm);
|
||||
tasklet_wakeup(spop_strm->subs->tasklet);
|
||||
tasklet_wakeup(spop_strm->subs->tasklet, TASK_WOKEN_IO);
|
||||
spop_strm->subs->events &= ~SUB_RETRY_RECV;
|
||||
if (!spop_strm->subs->events)
|
||||
spop_strm->subs = NULL;
|
||||
@ -965,33 +965,33 @@ static void spop_strm_notify_send(struct spop_strm *spop_strm)
|
||||
if (spop_strm->subs && (spop_strm->subs->events & SUB_RETRY_SEND)) {
|
||||
TRACE_POINT(SPOP_EV_STRM_WAKE, spop_strm->spop_conn->conn, spop_strm);
|
||||
spop_strm->flags |= SPOP_SF_NOTIFIED;
|
||||
tasklet_wakeup(spop_strm->subs->tasklet);
|
||||
tasklet_wakeup(spop_strm->subs->tasklet, TASK_WOKEN_IO);
|
||||
spop_strm->subs->events &= ~SUB_RETRY_SEND;
|
||||
if (!spop_strm->subs->events)
|
||||
spop_strm->subs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Alerts the data layer, trying to wake it up by all means, following
|
||||
* this sequence :
|
||||
* - if the spop stream' data layer is subscribed to recv, then it's woken up
|
||||
* for recv
|
||||
* - if its subscribed to send, then it's woken up for send
|
||||
* - if it was subscribed to neither, its ->wake() callback is called
|
||||
* It is safe to call this function with a closed stream which doesn't have a
|
||||
* stream connector anymore.
|
||||
/* Alerts the data layer by waking it up. TASK_WOKEN_MSG state is used by
|
||||
* default and if the data layer is also subscribed to recv or send,
|
||||
* TASK_WOKEN_IO is added.
|
||||
*/
|
||||
static void spop_strm_alert(struct spop_strm *spop_strm)
|
||||
{
|
||||
unsigned int state = TASK_WOKEN_MSG;
|
||||
|
||||
TRACE_POINT(SPOP_EV_STRM_WAKE, spop_strm->spop_conn->conn, spop_strm);
|
||||
if (!spop_strm_sc(spop_strm))
|
||||
return;
|
||||
|
||||
if (spop_strm->subs) {
|
||||
spop_strm_notify_recv(spop_strm);
|
||||
spop_strm_notify_send(spop_strm);
|
||||
}
|
||||
else if (spop_strm_sc(spop_strm) && spop_strm_sc(spop_strm)->app_ops->wake != NULL) {
|
||||
TRACE_POINT(SPOP_EV_STRM_WAKE, spop_strm->spop_conn->conn, spop_strm);
|
||||
spop_strm_sc(spop_strm)->app_ops->wake(spop_strm_sc(spop_strm));
|
||||
if (spop_strm->subs->events & SUB_RETRY_SEND)
|
||||
spop_strm->flags |= SPOP_SF_NOTIFIED;
|
||||
spop_strm->subs->events = 0;
|
||||
spop_strm->subs = NULL;
|
||||
state |= TASK_WOKEN_IO;
|
||||
}
|
||||
tasklet_wakeup(spop_strm_sc(spop_strm)->wait_event.tasklet, state);
|
||||
}
|
||||
|
||||
/* Writes the 32-bit frame size <len> at address <frame> */
|
||||
@ -2023,13 +2023,7 @@ static void spop_resume_each_sending_spop_strm(struct spop_conn *spop_conn, stru
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spop_strm->subs && spop_strm->subs->events & SUB_RETRY_SEND) {
|
||||
spop_strm->flags |= SPOP_SF_NOTIFIED;
|
||||
tasklet_wakeup(spop_strm->subs->tasklet);
|
||||
spop_strm->subs->events &= ~SUB_RETRY_SEND;
|
||||
if (!spop_strm->subs->events)
|
||||
spop_strm->subs = NULL;
|
||||
}
|
||||
spop_strm_notify_send(spop_strm);
|
||||
}
|
||||
|
||||
TRACE_LEAVE(SPOP_EV_SPOP_CONN_SEND|SPOP_EV_STRM_WAKE, spop_conn->conn);
|
||||
@ -3019,7 +3013,7 @@ static void spop_detach(struct sedesc *sd)
|
||||
if (eb_is_empty(&spop_conn->streams_by_id)) {
|
||||
if (!spop_conn->conn->owner) {
|
||||
/* Session insertion above has failed and connection is idle, remove it. */
|
||||
spop_conn->conn->mux->destroy(spop_conn);
|
||||
CALL_MUX_NO_RET(spop_conn->conn->mux, destroy(spop_conn));
|
||||
TRACE_DEVEL("leaving on error after killing outgoing connection", SPOP_EV_STRM_END|SPOP_EV_SPOP_CONN_ERR);
|
||||
return;
|
||||
}
|
||||
@ -3032,7 +3026,7 @@ static void spop_detach(struct sedesc *sd)
|
||||
|
||||
/* Ensure session can keep a new idle connection. */
|
||||
if (session_check_idle_conn(sess, spop_conn->conn) != 0) {
|
||||
spop_conn->conn->mux->destroy(spop_conn);
|
||||
CALL_MUX_NO_RET(spop_conn->conn->mux, destroy(spop_conn));
|
||||
TRACE_DEVEL("leaving without reusable idle connection", SPOP_EV_STRM_END);
|
||||
return;
|
||||
}
|
||||
@ -3063,7 +3057,7 @@ static void spop_detach(struct sedesc *sd)
|
||||
|
||||
if (!srv_add_to_idle_list(objt_server(spop_conn->conn->target), spop_conn->conn, 1)) {
|
||||
/* The server doesn't want it, let's kill the connection right away */
|
||||
spop_conn->conn->mux->destroy(spop_conn);
|
||||
CALL_MUX_NO_RET(spop_conn->conn->mux, destroy(spop_conn));
|
||||
TRACE_DEVEL("leaving on error after killing outgoing connection", SPOP_EV_STRM_END|SPOP_EV_SPOP_CONN_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
112
src/mworker.c
112
src/mworker.c
@ -121,11 +121,11 @@ void mworker_proc_list_to_env()
|
||||
|
||||
if (child->options & PROC_O_TYPE_MASTER)
|
||||
type = 'm';
|
||||
else if (child->options &= PROC_O_TYPE_WORKER)
|
||||
else if (child->options & PROC_O_TYPE_WORKER)
|
||||
type = 'w';
|
||||
|
||||
if (child->pid > -1)
|
||||
memprintf(&msg, "%s|type=%c;fd=%d;cfd=%d;pid=%d;reloads=%d;failedreloads=%d;timestamp=%d;id=%s;version=%s", msg ? msg : "", type, child->ipc_fd[0], child->ipc_fd[1], child->pid, child->reloads, child->failedreloads, child->timestamp, child->id ? child->id : "", child->version);
|
||||
memprintf(&msg, "%s|type=%c;fd=%d;cfd=%d;pid=%d;reloads=%d;failedreloads=%d;timestamp=%d;id=%s;version=%s", msg ? msg : "", type, child->ipc_fd[0], child->ipc_fd[1], child->pid, child->reloads, child->failedreloads, child->timestamp, child->id ? child->id : "", child->version ? child->version : "");
|
||||
}
|
||||
if (msg)
|
||||
setenv("HAPROXY_PROCESSES", msg, 1);
|
||||
@ -223,7 +223,20 @@ int mworker_env_to_proc_list()
|
||||
}
|
||||
}
|
||||
if (child->pid > 0) {
|
||||
LIST_APPEND(&proc_list, &child->list);
|
||||
struct list *insert_pt = &proc_list;
|
||||
struct mworker_proc *pos;
|
||||
|
||||
/* insert at the right position in ASC reload order;
|
||||
* search from the tail since items are sorted most of
|
||||
* the time
|
||||
*/
|
||||
list_for_each_entry_rev(pos, &proc_list, list) {
|
||||
if (pos->reloads <= child->reloads) {
|
||||
insert_pt = &pos->list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LIST_INSERT(insert_pt, &child->list);
|
||||
} else {
|
||||
mworker_free_child(child);
|
||||
}
|
||||
@ -232,28 +245,18 @@ int mworker_env_to_proc_list()
|
||||
/* set the leaving processes once we know which number of reloads are the current processes */
|
||||
|
||||
list_for_each_entry(child, &proc_list, list) {
|
||||
if (child->reloads > 0)
|
||||
if (child->reloads > 0 && !(child->options & PROC_O_TYPE_MASTER))
|
||||
child->options |= PROC_O_LEAVING;
|
||||
}
|
||||
|
||||
unsetenv("HAPROXY_PROCESSES");
|
||||
|
||||
no_env:
|
||||
|
||||
/* couldn't find the master element, exiting */
|
||||
if (!proc_self) {
|
||||
|
||||
proc_self = mworker_proc_new();
|
||||
if (!proc_self) {
|
||||
ha_alert("Cannot allocate process structures.\n");
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
proc_self->options |= PROC_O_TYPE_MASTER;
|
||||
proc_self->pid = pid;
|
||||
proc_self->timestamp = 0; /* we don't know the startime anymore */
|
||||
|
||||
LIST_APPEND(&proc_list, &proc_self->list);
|
||||
ha_warning("The master internals are corrupted or it was started with a too old version (< 1.9). Please restart the master process.\n");
|
||||
err = -1;
|
||||
ha_alert("Failed to deserialize data for the master process. Unrecoverable error, exiting.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
@ -825,7 +828,7 @@ void mworker_cleanup_proc()
|
||||
|
||||
struct cli_showproc_ctx {
|
||||
int debug;
|
||||
int next_reload; /* reload number to resume from, 0 = from the beginning */
|
||||
int resume_reload; /* reload count of the last flushed old worker row, 0 = none yet */
|
||||
};
|
||||
|
||||
/* Append a single worker row to trash (shared between current/old sections) */
|
||||
@ -860,7 +863,7 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
|
||||
|
||||
chunk_reset(&trash);
|
||||
|
||||
if (ctx->next_reload == 0) {
|
||||
if (ctx->resume_reload == 0) {
|
||||
memprintf(&reloadtxt, "%d [failed: %d]", proc_self->reloads, proc_self->failedreloads);
|
||||
chunk_printf(&trash, "#%-14s %-15s %-15s %-15s %-15s", "<PID>", "<type>", "<reloads>", "<uptime>", "<version>");
|
||||
if (ctx->debug)
|
||||
@ -878,12 +881,12 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
|
||||
ha_free(&uptime);
|
||||
|
||||
/* displays current processes */
|
||||
if (ctx->next_reload == 0)
|
||||
if (ctx->resume_reload == 0)
|
||||
chunk_appendf(&trash, "# workers\n");
|
||||
list_for_each_entry(child, &proc_list, list) {
|
||||
|
||||
/* don't display current worker if we only need the next ones */
|
||||
if (ctx->next_reload != 0)
|
||||
if (ctx->resume_reload != 0)
|
||||
continue;
|
||||
|
||||
if (!(child->options & PROC_O_TYPE_WORKER))
|
||||
@ -900,34 +903,69 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
|
||||
return 0;
|
||||
|
||||
/* displays old processes */
|
||||
if (old || ctx->next_reload) { /* there's more */
|
||||
if (ctx->next_reload == 0)
|
||||
if (old || ctx->resume_reload) { /* there's more */
|
||||
int skip = ctx->resume_reload; /* if resuming, skip until we pass this reload count */
|
||||
int prev_reload = 0; /* previous LEAVING entry's reload count during skip phase */
|
||||
|
||||
if (!ctx->resume_reload)
|
||||
chunk_appendf(&trash, "# old workers\n");
|
||||
list_for_each_entry(child, &proc_list, list) {
|
||||
/* If we're resuming, skip entries that were already printed (reload >= ctx->next_reload) */
|
||||
if (ctx->next_reload && child->reloads >= ctx->next_reload)
|
||||
continue;
|
||||
|
||||
if (!(child->options & PROC_O_TYPE_WORKER))
|
||||
continue;
|
||||
|
||||
if (child->options & PROC_O_LEAVING) {
|
||||
cli_append_worker_row(ctx, child, date.tv_sec);
|
||||
if (!(child->options & PROC_O_LEAVING))
|
||||
continue;
|
||||
|
||||
/* Try to flush so we can resume after this reload on next page if the buffer is full. */
|
||||
if (applet_putchk(appctx, &trash) == -1) {
|
||||
/* resume at this reload (exclude it on next pass) */
|
||||
ctx->next_reload = child->reloads; /* resume after entries >= this reload */
|
||||
return 0;
|
||||
/* When resuming after a flush failure, skip entries
|
||||
* up to and including the last successfully flushed
|
||||
* row (identified by its reload count). This is
|
||||
* direction-agnostic: works whether the list is in
|
||||
* ascending or descending reload order.
|
||||
*
|
||||
* If the target entry was deleted from proc_list
|
||||
* (e.g. process exited between handler calls), we
|
||||
* detect that we've passed its former position when
|
||||
* two consecutive LEAVING entries straddle the skip
|
||||
* value — i.e. one has reloads > skip and the next
|
||||
* has reloads < skip (or vice versa). In that case
|
||||
* we stop skipping and emit the current entry.
|
||||
*/
|
||||
if (skip) {
|
||||
if (child->reloads == skip) {
|
||||
skip = 0; /* found it, resume from the next entry */
|
||||
prev_reload = 0;
|
||||
continue;
|
||||
}
|
||||
if (prev_reload &&
|
||||
((prev_reload > skip) != (child->reloads > skip))) {
|
||||
/* Crossed where skip would have been —
|
||||
* the entry was deleted. Stop skipping
|
||||
* and fall through to emit this entry.
|
||||
*/
|
||||
skip = 0;
|
||||
} else {
|
||||
prev_reload = child->reloads;
|
||||
continue;
|
||||
}
|
||||
chunk_reset(&trash);
|
||||
}
|
||||
|
||||
cli_append_worker_row(ctx, child, date.tv_sec);
|
||||
|
||||
if (applet_putchk(appctx, &trash) == -1) {
|
||||
/* ctx->resume_reload already holds the last
|
||||
* flushed row or 0; don't update it here so
|
||||
* the failed row will be replayed.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
/* This row was successfully flushed, remember it */
|
||||
ctx->resume_reload = child->reloads;
|
||||
chunk_reset(&trash);
|
||||
}
|
||||
}
|
||||
|
||||
/* dump complete: reset resume cursor so next 'show proc' starts from the top */
|
||||
ctx->next_reload = 0;
|
||||
ctx->resume_reload = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -2351,6 +2351,11 @@ static inline int peer_treat_definemsg(struct appctx *appctx, struct peer *p,
|
||||
goto malformed_exit;
|
||||
}
|
||||
|
||||
if (table_type < 0 || table_type >= PEER_KT_TYPES) {
|
||||
TRACE_PROTO("ignore table definition message: unknown table type", PEERS_EV_SESS_IO|PEERS_EV_RX_MSG|PEERS_EV_PROTO_DEF, appctx, p);
|
||||
goto ignore_msg;
|
||||
}
|
||||
|
||||
table_keylen = intdecode(msg_cur, msg_end);
|
||||
if (!*msg_cur) {
|
||||
TRACE_ERROR("malformed table definition message: no key length", PEERS_EV_SESS_IO|PEERS_EV_RX_MSG|PEERS_EV_PROTO_ERR, appctx, p);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user