BUG/MINOR: cfgparse-listen: don't lose the errors reported while creating a proxy
When a proxy inherits from its defaults section, the return value of
proxy_ref_defaults() was assigned to err_code instead of being ORed
into it, wiping all the errors collected earlier for that line. As a
result an invalid character in a proxy's name was reported but the
process started anyway:
$ cat inv.cfg
defaults
mode http
frontend inv@lid
bind :8080
$ ./haproxy -c -f inv.cfg
[ALERT] : parsing [inv.cfg:3] : character '@' is not permitted in
'frontend' name 'inv@lid'.
$ echo 0
0
The same applies to the duplicate name and ambiguous defaults reference
diagnostics emitted before the proxy is created.
Configurations which were mistakenly accepted until will now be rejected,
though they were already emitting an alert. The issue was only introduced
in 3.4 with commit 87ea407cce ("MINOR: proxy: refactor proxy inheritance
of a defaults section"), so it's fairly unlikely that a bogus configuration
was introduced only there, thus a backport would be preferable, or maybe
only the err_code should be turned to ERR_WARN to remain tolerant for
existing versions.
BUG/MINOR: cfgparse-listen: always reject extra arguments to "persist"
The check for extra arguments was placed inside the "if" block which
parses "persist rdp-cookie(<name>)", so "persist rdp-cookie foo" was
silently accepted. Let's move it to the beginning of the keyword's
processing so that it covers all the cases.
This must NOT be backported since it makes the config parser stricter.
BUG/MINOR: cfgparse-global: reject extra arguments to numa-cpu-mapping and anonkey
Both keywords silently ignored anything passed after their expected
argument, contrary to all the other keywords of the section which
properly check for extra args. Let's just fix this.
This must NOT be backported since it makes the config parser stricter.
BUG/MINOR: cfgparse-listen: fix quoting of the condition conflict warning
The 10 places which report the message returned by warnif_cond_conflicts()
use "'%s.\n'" as a format, so the closing quote is emitted after the \n and
ends up alone at the beginning of the next line:
[WARNING] : parsing [x.cfg:4] : 'acl 'a' will never match ...
'
Let's just move the quote before the LF and even the trailing '.'.
BUG/MINOR: cfgparse-listen: fix the argument checked by "email-alert"
The "from", "mailers", "myhostname" and "to" options of "email-alert"
each check that a value was passed, but they test args[1], which is the
option's own name instead fo args[2]. As a consequence an empty args[2]
was accepted. The same copy-paste mistake made the "level" option error
handling report the option name instead of the wrong value.
BUG/MINOR: cfgparse-listen: release the previous description of the proxy
The proxy's "description" keyword did not release the previous value
before assigning the new one, so the first one was leaked when the
keyword was used twice, e.g. once in a defaults section and once in the
proxy. Note that the global section's "description" does it properly.
BUG/MINOR: cfgparse-global: add missing calloc() check for the global description
The global "description" keyword passes the result of calloc() directly to
snprintf() without checking it, resulting in a crash instead of an error
message if the allocation fails.
BUG/MINOR: cfgparse-global: add missing gotos to error label in max-spread-checks
The three error paths of the parse_time_err() call in the
"max-spread-checks" keyword only set the error code without leaving the
block, so that global.max_spread_checks was assigned an uninitialized
value right after the error was reported. No impact but clearly what
could report a build warning or a report from a static analyser. Let's
just add the missing gotos like everywhere else.
Willy Tarreau [Fri, 9 Oct 2026 06:52:44 +0000 (08:52 +0200)]
BUG/MAJOR: cfgparse: properly check userlist section when scanning keywords
In cfg_parse_users(), we scan all config keywords and only want to
evaluate those whose section is CFG_USERLIST. However, the section
is checked as a bit field instead of an equality, and CFG_USERLIST
is 3, which is CFG_GLOBAL|CFG_LISTEN, meaning that we can call a
wrong parser for keywords "user" or "group" when they are registered
in one of these sections. This is what randomly happens when the
global "group" keyword is refactored to have its own parser, depending
on build options the linker, it may randomly be found before or after
the userlist's and cause startup failures if the global section's
keyword parser is called from the userlist parser, such as in this
regtest that only fails with ASAN on the CI when the config only has
"group" set in a userlist section:
config : parsing [cfg:18] : cannot find group id for 'admins' (0:Success)
This was introduced in 3.4 with commit c17ed69bf3 ("MINOR: cfgparse:
Refactor "userlist" parser to print it in -dKall operation") so it must
be backported there.
If a config works it's OK, but this was still marked major because
there's simply no workaround for a failing binary where it only
depends on build options. It could happen for example if other
keywords are added via Lua or patches.
However, this introduces a regression when HTX buffer is smaller than
the copied block. In this case, QCS is flagged with QC_SF_DEM_FULL, but
early goto skip the increment on <htx_sent> total value.
This causes corrupted transfers with H3 POST larger than a bufsize
resulting in a larger payload. This is caused by duplicated data blocks
when copy resumes after hitting QC_SF_DEM_FULL condition.
Fix this by ensuring <htx_sent> is always updated before the early goto
path. Intent of the above fix is kept as well as <ret> still serves as a
comparison for QC_SF_DEM_FULL / EOM cases.
This must be backported along the above patch, planned up to 2.6 (after
a period of observation).
Lukas Tribus [Thu, 8 Oct 2026 17:16:21 +0000 (17:16 +0000)]
MINOR: doc: elaborate on diagnostic mode behavior
Commit 7b01a8dbdd("MINOR: global: define diagnostic mode of execution")
in 2.4-dev15 introduced diagnostic mode for config warnings.
It was initially designed to be ignored by "zero-warning" mode
(-dW) and documented accordingly.
Since commit ced4148401 ("BUG/MINOR: diag: always show the version
before dumping a diag warning") in 3.0-dev3 this changed, and
zero-warning mode combined with diagnostic messages stop haproxy from
starting. This appears to be a unintended consequence in ced4148401.
Since commit 7d63efa5f5 ("MINOR: errors: add ha_diag_notice() to
report diag-level notifications") there is a distinction between
what should not interact with zero-warning mode (ha_diag_notice()),
and the previous behavior with ha_diag_warning().
Explain the that some diagnostic message interact with zero-warning
and that diagnostic message will be backported, as such diagnostic
mode must only be used interactively.
MEDIUM: h3: do not close conn on unaligned non DATA frame
Until now, H3 parser rejected a non aligned frame, except for DATA. The
whole connection was closed with error H3_EXCESSIVE_LOAD, which can be
seen as a medium defect given that the client did not produce anything
invalid or even suspect.
In fact, most frame parsers are already able to support wrapping
content, as they deal with a struct buffer representation. This is also
the case for QPACK uni streams. The real limitation is for HEADERS frame
which rely on qpack_decode_fs() which can only work on aligned content.
This patch removes the connection closure on unaligned content. For H3
HEADERS frames, the buffer content is realigned by performing two
memmove() calls into a temporarily allocated alternative area. The
current parsing iteration will continue in this new aligned storage.
A future optimisation would be to support wrapping content in
qpack_decode_fs() to remove realign operation.
This can be considered as a bug, as previous state prevents to handle
valid H3 content. As no user complains about it, it may be better to
refrain from backporting the patch, unless there is some explicit demand
about it.
Note that original Rx buffer is not realigned, as this would mangle the
underlying MUX ncbuf storage. An alternative approach was implemented
first, performing the realign at the MUX level, however it was probably
less efficient as it required to copy the whole ncbuf. For the record,
this patch is attached here with its original message.
---
Subject: [PATCH] MEDIUM: quic/h3: support parsing of unaligned non DATA H3
frame
Until now, H3 parser was not able to parse a non DATA frame which wraps
in QCS Rx buffer. When this happened, the whole connection was closed
with error H3_EXCESSIVE_LOAD, which can be seen as a medium defect given
that the client did not produce anything invalid or even suspect.
This patch finally implements a method to deal with unaligned non DATA
H3 frames. When such frame is detected by H3 parser, rcv_buf callback is
interrupted. MUX will react to a null returned value and detect that the
buffer data is wrapping. In this case, the new function
qcs_realign_rx_data() is used to perform the realign operation using
b_slow_realign() helper. Callback rcv_buf is then reinvoked immediately.
This can be considered as a bug, as previous state prevents to handle
valid H3 content. As no user complains about it, it may be better to
refrain from backporting the patch, unless there is some explicit demand
about it.
---
src/h3.c | 8 ++------
src/mux_quic.c | 35 ++++++++++++++++++++++++++++++++---
2 files changed, 34 insertions(+), 9 deletions(-)
+/* Realign buffer content in <rxbuf>. This should be called when parsing is
+ * blocked due to wrapping content.
+ */
+static void qcs_realign_rx_data(struct qc_stream_rxbuf *rxbuf)
+{
+ struct ncbuf *ncb = &rxbuf->ncb;
+ struct buffer old;
+
+ /* Convert ncbuf to a full buffer type. This forces b_slow_realign()
+ * will copy every non contiguous blocks plus first ncb block header.
+ */
+ old = b_make(ncb_orig(ncb), ncb->size, ncb->head, ncb->size);
+ b_slow_realign(&old, trash.area, 0);
+
+ /* ncb area now realigned - ensure head is resetted as well. */
+ ncb->head = 0;
+}
+
/* Transfer data into <qcs> stream <rxbuf> current Rx buffer from its directly
* following buffer. This is useful when parsing was interrupted due to partial
* data. If following buffer does not exists, nothing is done.
@@ -1508,9 +1526,20 @@ static int qcc_decode_qcs(struct qcc *qcc, struct qcs *qcs)
* at a Rx buffer boundary. Try to realign data if possible and
* restart decoding.
*/
- if (!ret && rxbuf && !(qcs->flags & QC_SF_DEM_FULL) &&
- qcs->rx.offset + ncb_data(&rxbuf->ncb, 0) == rxbuf->off_end) {
- if (!qcs_transfer_rx_data(qcs, rxbuf)) {
+ if (!ret && rxbuf && !(qcs->flags & QC_SF_DEM_FULL)) {
+ int need_restart = 0;
+
+ if (ncb_head(&rxbuf->ncb) + ncb_data(&rxbuf->ncb, 0) > ncb_wrap(&rxbuf->ncb)) {
+ qcs_realign_rx_data(rxbuf);
+ need_restart = 1;
+ }
+
+ if (qcs->rx.offset + ncb_data(&rxbuf->ncb, 0) == rxbuf->off_end) {
+ if (!qcs_transfer_rx_data(qcs, rxbuf))
+ need_restart = 1;
+ }
+
+ if (need_restart) {
TRACE_DEVEL("restart parsing after data realignment", QMUX_EV_QCS_RECV, qcc->conn, qcs);
goto restart;
}
--
2.56.0
BUG/MEDIUM: h3: fix end-of-input report on DATA wrapping
A received H3 DATA frame may wrap in the buffer. Function
h3_data_to_htx() will parse it in two pass in this case, calling
htx_add_data() twice.
A double bug was present in case of such parsing as second call of
htx_add_data() return value was immediately added in <htx_sent>. This
could cause first check to activate QC_SF_DEM_FULL to be ignored. This
is a minor issue, as MUX would immediately reinvoke h3_rcv_buf(), with
the flag set this time.
The second bug is more important : comparison for HTX EOM reporting was
invalid in case of a two pass parsing. Most of the time, this result in
EOM not reported, causing the stream layer to report a stream
interruption with state CL--.
Fix this by using an intermediary return value for the second
htx_add_data() invokation. This value is only added to <htx_sent> once
necessary checks are performed. It is important to keep h3_data_to_htx()
to still return the total value of HTX added data to avoid parser desync
issue.
This must be backported up to 2.6, after a period of observation.
BUG/MINOR: ssl: Fix potential NULL-deref in "dump ssl cert" with AWS-LC
In some corner cases we could have a public certificate with no private
key stored in a crt-store. In such a case, when trying to dump the
contents of the store entry via the "dump ssl cert" CLI command, the
'PEM_write_bio_PrivateKey' was always called without checking for a NULL
pointer.
This poses no problems with OpenSSL libraries which already check the
pointer but on some other libs, AWS-LC for instance, the NULL pointer is
dereferenced without check.
The only other use of this function is in acme code where the function
can never be called with a NULL 'key' pointer.
BUG/MINOR: h3: close connection on GOAWAY frame too long
GOAWAY frame contains a single stream ID varint. When decoding it,
ensure its length match the announced value in frame header. If not the
case, the connection must be closed with code FRAME_ERROR.
However, this fix is not complete as it did not detect H3 frame
truncated at the header boundary. This happens when one of the two
fields varint cannot be decoded and FIN is reported as well.
This patch corrects this by reporting a connection error H3_FRAME_ERROR
in this condition. This allows to quickly teardown the connection
instead of waiting for the stream timeout without any error.
BUG/MINOR: mux_quic: account data in conn fctl on early detach
The previous patch accounts unconsumed data when QCS is freed before
being remotely closed. The objective is to ensure to limit the
inconsistency in conn flow control between client and server.
In fact, this patch is incomplete as peer consumed level cannot be known
before receiving its final size. Thus, discrepancies in connection flow
control may still appear over time. The proper method is to delay QCS
release until remote close is performed. However, this is a significant
change which will be implemented in a future patch.
This must be backported up to 2.8, after a period of observation
BUG/MEDIUM: mux_quic: account discarded data in connection flow control
Read data not yet consumed by the upper stream layer are discarded when
a RESET_STREAM is received. This patch implements their accounting in
the connection flow control.
This feature is mandatory as it could result in discrepancies between
each peers for the measured connection flow control. If this grows too
big, the sender may be indefinitely blocked, forcing the opening of a
new connection.
This patch introduces a new function qcc_account_conn_fctl(), which is
used to factorize the code already present during STREAM frames
handling. This is now also called for RESET_STREAM frames.
This must be backported up to 2.8, after a period of observation.
BUG/MINOR: mux_quic: report conn error on RESET_STREAM fctl violation
When a RESET_STREAM is received, its announced final size must be taken
into account in the connection flow control.
This patch is a prerequisite prior to implement this accounting. It adds
the mandatory check of the final size field to ensure it does not
violate either the stream or the connection flow control.
In case of invalid value, the connection is closed with
FLOW_CONTROL_ERROR.
This must be backported up to 2.8, after a period of observation.
BUG/MINOR: mux_quic: extend FINAL_SIZE_ERROR detection on stream
On STREAM reception, a frame is rejected if final size is known but a
frame which exceeds it is received. Connection is closed with
FINAL_SIZE_ERROR code. This is also the case for a frame with FIN set
but shorter than the already received maximum offset.
This patch rewrites the condition so that the second check on frame with
FIN is always performed. Indeed, it does not depend on the known final
size status. A similar test is already performed as expected in the
RESET_STREAM frame handler.
Ensure QCC tasklet is woken up when a QCS is inserted in the purge list.
The objective is to prevent keeping QCS for too long in it.
Changes are only necessary when running outside of QCC tasklet. Thus new
wakeup are only inserted in receive handlers to avoid unnecessary
tasklet execution.
Prior to this patch, a purgeable QCS could have been kept virtually
indefinitely. This probably did not really happen as QCC tasklet is
frequently woken after frames handling.
This should be backported up to 3.2, after a period of observation.
BUG/MAJOR: mux-quic: prevent crash on QCS already set for purging
QCC connection has a purge list member. It contains QCS instances
considered as completed, whiches will be freed during the next tasklet
run. Such QCS are reported via qcs_is_completed() helper function.
QCS member <el_send> is used for purge list storage. It is also used for
send list, however an inactive QCS should not be present in the latter.
To enforce this rule, a BUG_ON() is present before purge list insertion
in qcs_close_remote(). This would also be triggered if QCS was already
inserted in the purge list before remote closing.
This BUG_ON() may be triggered when a QCS is scheduled for purge while
it still contains stom data to read. When the final read is performed,
crash will occur in qcs_close_remote(). This happens with detached QCS
only, as they are considered as completed even if only closed locally.
To prevent this situation, add an early return in qcs_close_remote() :
if QCS is already considered completed, do not change its state to fully
closed. As stated above, this only occur for detached QCS instances. The
main objective is to prevent the BUG_ON() crash while still preventing
double insert. As an added value, it guarantees that QCS state remains
constant once scheduled for purge.
Thanks to Brendon C. <bren@bren.win> for his detailled report on the
issue.
This must be backported up to 3.2, after a period of observation.
MINOR: ssl: accept integer group IDs in curve2name
Allow curve2name to convert integer TLS Supported Group identifiers
in addition to curve names. Register the converter with SMP_T_ANY to
preserve the input type, and accept only integer and string samples.
Return unknown IDs, or IDs without a name in the requested convention,
as decimal strings. Preserve the existing fallback for string inputs.
Document binary group extraction and extend regression coverage for
integer IDs, missing names, unknown IDs and values outside the TLS
identifier range. The build and converter regression test pass.
Add a string converter to normalize curve names to IANA, ANSI X9.62
or NIST notation. Match both the requested format and input names
case-insensitively, and resolve old ecdh_* aliases to canonical names.
Keep the original string unchanged when no curve matches or the
requested naming convention has no corresponding name. The converter
uses the curve table independently of the SSL library's available NIDs.
Document the supported formats and add regression coverage for curve
aliases, mixed-case formats and unchanged-string fallbacks. The build
and all three SSL curve regression tests pass.
MINOR: ssl: complete registered group names and library NID mappings
Add the missing IANA groups for SM2, ML-KEM and hybrid key exchanges.
Provide both AWS-LC and OpenSSL NID spellings for standalone ML-KEM
and use AWS-LC NIDs for the supported hybrid and Kyber draft groups.
Undefined NID macros remain unavailable through the existing V macro.
The mappings were checked with OpenSSL 3.5, OpenSSL 4 development
headers and AWS-LC 4.2.
MINOR: ssl: skip unavailable NIDs in curves2nid lookup
Curve table entries with NIDs undefined by the SSL library remain at -1
after initialization. Skip these entries during name lookup so an
unavailable mapping does not hide a later valid mapping for the same
name. This allows the table to contain both OpenSSL and AWS-LC NID
spellings without conditional compilation.
MINOR: ssl: split curve names into ANSI, IANA and NIST columns
Store ANSI X9.62 aliases separately from IANA and NIST names,
consolidating the duplicate P-192 and P-256 entries. Make curves2nid()
search all three columns and keep curveid2str() returning the existing
TLS group name.
BUG/MINOR: ssl: use standard names for X25519 and X448 groups
The curve table uses ecdh_x25519 and ecdh_x448, which are OpenSSL TLS1.3
draft11 name labels rather than the released group names registered at
IANA. As a result, SSL traces print those labels and curves2nid() cannot
resolve x25519 or x448 even when the corresponding NID is available.
Use x25519 and x448 as the primary names. Keep the old ecdh_* strings
as aliases at the end of the table so existing name-to-NID lookups
continue to work. The aliases must follow the standard names because
curveid2str() returns the first entry matching a group identifier.
curveid2str() is only used in the traces so there's no fonctionnal
changes.
This should be backported to 3.3 and 3.4, which contain the curve table
introduced by d799a1b3b (MINOR: ssl: Add curve id to curve name table
and mapping functions).
DOC: ssl: clarify the NID-based curve fetch branch
Document that the SSL_get_negotiated_group() path is used for OpenSSL
before 3.2 and AWS-LC API 35 or later. This only adds a comment and
does not change the fetch behavior.
BUG/MINOR: ssl: don't uppercase the native group name
The change to SSL_get0_group_name() assumed that its output differed
from SSL_get_negotiated_group() followed by OBJ_nid2sn() only in
case. This is true for x25519/X25519 and x448/X448, but cannot be
generalized to other groups.
For P-256, the APIs return secp256r1 and prime256v1 respectively:
these are aliases, not a case difference. For P-384 and P-521, both
APIs already return secp384r1 and secp521r1, so uppercasing actually
introduces a mismatch. It also changes native mixed-case hybrid names
such as SecP256r1MLKEM768.
Moreover, SSL_get0_group_name() returns a library-owned string shared
through the SSL_CTX. Uppercasing it in place modifies OpenSSL state
and risks concurrent access to that string.
Remove the uppercase conversion and return the native name unchanged.
This should not be backported as it would could break matching of the
curves.
BUG/MEDIUM: listener: Consider maxconn before waking listeners
When dequeueing listeners in dequeue_all_listeners() and
dequeue_proxy_listeners(), consider if we reached maxconn before doing
so.
If we do not, with enough thread groups, we may enter a state in which we
dequeue listeners, try again to accept more connections, but that would
fail, as maxconn would have been reached again by a different thread
group, and we'd end up being stuck for long enough for the watchdog to
trigger.
This should be backported up to 2.8.
This was submitted by Bruno Henc in github issue #3525.
CI: github: move wolfSSL 5.7.0 testing to a nightly workflow
Remove wolfSSL 5.7.0 from the push-triggered VTest matrix. Update the
standalone wolfSSL workflow to test that version nightly instead of
building git-master weekly.
The weekly git-master worflow was not working anymore for months.
Resolve the QuicTLS ref to a commit SHA when generating the VTest
matrix and include it in the SSL build inputs and cache key. Keep job
labels readable by showing the ref and an eight-character SHA.
Fetch and check out the resolved commit in build-ssl.sh while retaining
QUICTLS_VERSION for the OpenSSL 1.1.1 build target selection. Preserve
the existing download behavior when QUICTLS_COMMIT is unset.
CI: github: test the latest AWS-LC release on development branches
Replace the AWS-LC 1.39.0 pin with latest-release discovery using the
existing determine_latest_aws_lc() helper. Add it alongside the other
latest SSL libraries in the development-only matrix list.
Remove the separate AWS-LC workflow now that the VTest matrix covers
the latest release and both LTS branches.
Add the fips-2024-09-27 (3.x) and fips-2025-09-12-lts (4.x) AWS-LC
branches to the VTest matrix for GCC and Clang, including QUIC support.
Resolve each branch head to a commit SHA when generating the matrix
and download the corresponding immutable archive in build-ssl.sh.
Read the AWS-LC version from that commit's header to display readable
job names such as ssl=aws_lc=4.2.0+37341efe.
Generate the SSL cache key from both the job name and SSL source,
retaining the full commit SHA even though the display uses only eight
characters. Builds are reused until the branch advances, and distinct
commits cannot share a cache entry because of a short SHA collision.
Validated branch resolution and version extraction against GitHub,
readable GCC and Clang jobs in development and stable matrices, commit
and tag archive URLs, cache reuse, shell syntax, and git diff --check.
BUG/MEDIUM: ssl: crt-list curves ignored with AWS-LC
SSL_new() copies the curves of the SSL_CTX into the SSL object, and
SSL_set_SSL_CTX() does not update this copy while the handshake uses it.
The "curves" and "ecdhe" options of a crt-list line were thus silently
ignored: a client could negotiate a curve excluded by the certificate as
long as the bind line allowed it.
The ClientHello callback used by AWS-LC now sets them again on the SSL
object after the context switch. The value is picked in the same order
as when the SSL_CTX is prepared, so the curves of the bind line still
win over the <ecdhe> of the crt-list line.
The curves setter is inefficient here: it parses the configuration
string for each connection.
This adds the ssl_crt-list_curves reg-tests for TCP and QUIC.
Tested with AWS-LC 3.6.0 (LTS) and 4.2.0. The curves regression tests
pass over TCP and QUIC with both versions.
The TLSv1.2, TLSv1.3 and QUIC cipher regression tests also pass.
Thank you to @ygkat for having reported this issue in GH #3500
This should be backported to all stable versions. Note the code is in
ssl_sock.c before 3.1.
Co-authored-by: William Lallemand <wlallemand@haproxy.com>
BUG/MEDIUM: ssl: crt-list ciphers ignored with AWS-LC
AWS-LC's SSL_new() copies the cipher lists of the SSL_CTX into the SSL
object, and SSL_set_SSL_CTX() does not update this copy while the server
handshake uses it. The "ciphers" and "ciphersuites" options of a
crt-list line were thus silently ignored: a client could negotiate a cipher
excluded by the certificate as long as the bind line allowed it. The same
configuration built against OpenSSL rejects such a connection.
The SNI callback now sets both lists again on the SSL object after the
context switch when built with AWS-LC API versions greater than 30.
API version 30 (AWS-LC 3.x) uses the selected SSL_CTX cipher lists
directly and does not need this workaround.
These setters are inefficient here: they parse the cipher configuration
strings for each connection.
This adds the ssl_crt-list_ciphers reg-test for TLSv1.2 and the
ssl_crt-list_ciphersuites ones for TLSv1.3, over TCP and QUIC.
Tested with AWS-LC LTS versions 3.6.0 and 4.2.0.
Thank you to @ygkat for having reported this issue in GH #3500
This should be backported as far as 3.0. Note the code is in ssl_sock.c
before 3.1.
Co-authored-by: William Lallemand <wlallemand@haproxy.com>
BUG/MINOR: ssl: fix trace crash on BE conn release in io cb
Since 3.3, handling of idle connections in io handler for muxes/SSL has
been extended. On io completion, connection is not reinserted anymore in
the server list if it is in maintenance mode. Also, private connections
are need race protection as they may be purged : on completion, a
connection is reinserted in its parent session. This operation is
failible as it may require an allocation.
In SSL io tasklet, if a BE connection is not reinserted in its idle pool
for one of the two reasons described above, it is freed instead via MUX
destroy callback. This causes a crash if traces are activated as conn
pointer is not updated.
Fix this by ensuring conn pointer is set to NULL after destroy
invokation.
BUG/MINOR: ssl: fix trace crash on FE conn release in io cb
The following patch has added an output parameter in conn_create_mux()
to report if the connection has been destroyed. This can happen for
frontend connection only.
In case the connection is destroyed, tasklet is set to NULL as expected
to avoid returning an invalid pointer. However, conn pointer is left
unchanged. This leads to a crash if traces are activated. The proper fix
is to also set conn to NULL.
BUG/MINOR: hq-interop: prevent double EOM on request with standalone FIN
Previously, HTX EOM was unconditionnaly set during HTTP/0.9 to HTX
request conversion. This was valid as content was only parsed when all
STREAM were received.
However, this has changed in 3.2 with support for incomplete request and
standalone FIN in HTTP/0.9 parser. The objective is to better test a
mechanism close to HTTP/3 parser. But unconditionnal EOM was not
adjusted. Thus, EOM would be set two times per message if FIN is
transmitted via a dedicated empty STREAM frame.
Previously, this was silently ignored by stream layer. However, this is
not case anymore since conversion of HTX EOM from a message level to a
block level flag. A new BUG_ON() added in htx_add_blk() would report
incorrect blocks found after EOM : this may be triggered in case the
second EOM is reported through an EOT block.
Fix this only setting EOM if fin is set during HTTP/0.9 request parsing.
Handling of standalone FIN is already supported here so there is no need
for more changes. Now, HTTP/0.9 parser can now considered even more
closer to HTTP/3.
Willy Tarreau [Tue, 6 Oct 2026 08:10:02 +0000 (10:10 +0200)]
DEBUG: support reading set-dumpable from HAPROXY_FORCE_DUMPABLE
When debugging regtests, it is super complicated to get a core to be
dumped. It often requires to place a set-dumpable directive in the
config, but when it's generated it's really not convenient. A better
approach in this case is to read the value from an environment variable.
That's what this patch does, by accepting to preset the set-dumpable
options from the contents of HAPROXY_FORCE_DUMPABLE. It's a bit more
relaxed, though, as it accepts "off" to stay disabled, "libs" to just
load the libs, and anything else to enable.
The config and management docs were updated. This could be backported
to older versions if found helpful.
BUILD: ssl: avoid mixed declarations in the AWS-LC curve fetch
The AWS-LC API 30-34 curve fetch declares the group variable after
statements, which fails builds using -Werror=declaration-after-statement.
Move the declaration to the beginning of smp_fetch_ssl_fc_ec() and keep
the assignment in the AWS-LC branch. Mark the variable __maybe_unused
for builds using other SSL libraries.
This fix must be backported wherever commit e671cb696d ("MINOR: ssl:
support curve fetches with AWS-LC API 30") was backported.
MINOR: ssl: support curve fetches with AWS-LC API 30
Enable ssl_fc_curve and ssl_bc_curve with AWS-LC API 30-34, including
AWS-LC 3.0.0, using SSL_get_group_id() and SSL_get_group_name() instead
of SSL_get_negotiated_group(), which requires API 35.
Map NIST curve group IDs to OpenSSL short names to keep the output
consistent with newer AWS-LC versions. Preserve native names for other
groups, including post-quantum and hybrid groups.
Update the compatibility documentation and enable the curve regression
tests for AWS-LC API 30.
DOC: config: document new "loggers" section and relevant uses
loggers (plural) section may be used to defined a group of log
directives within a single section that can be used later in the
config as a replacement of "log" directive to specify log target(s),
for instance in a proxy section to inherit from existing loggers or
directly on a do-log action to send generated log to specific endpoints
only.
MINOR: log: support 'to' parameter fo do-log action
Similar to the "to" argument for "log" directive, the optional "to"
parameter allows to specify a list of comma separater loggers section
names to be used as log destination for the logs produce by the do-log
action.
However, while the "to" argument from "log" directive adds up loggers
to existing loggers of the proxy, the do-log "to" argument actually
replaces the loggers configured on the proxy! Indeed, the ultimate
goal is to be able to filter which loggers sections should receive
do-log logs, which by default are sent to all loggers from the proxy.
MINOR: log: support 'to' parameter for "log" directive
"to" is like "global", this is a special (optional) argument to be used
alone without ordinary log's arguments. It expects a comma separated list
of "loggers" section names which indicates which loggers section to inherit
loggers from. (each loggers section defines a number of "log" directive,
called logger).
loggers section allows to define multiple loggers (ie: "log" directive)
within a single config block, and group them under a common "loggers"
group identified by a unique name.
The end goal is to be able to reference a loggers section from a proxy
and other places such as do-log action in order to inherit from the
loggers defined in the section.
BUG/MINOR: log: fix erroneous error message in log-steps parser
When invalid arguments are provided to "log-steps" directive, the
following error is returned: "invalid arguments, expects 'all' or a
composition of logging steps separated by spaces."
While in fact the parser expects comma separated values, and even the
documentation states it.
Let's fix the error message to match with the actual behavior and doc.
Commit 005038b80d468cdca0a9fdcb4b3c993ec02f8367 introduced a new script,
to inject the gnu_debugdata section into the binary. However, the script
used %b to print binary values, and %b behaves differently on Linux and
FreeBSD shells, and on FreeBSD, it would end up corrupting the binary,
making it crash on start.
Fix that by avoid using %b at all.
Willy Tarreau [Fri, 2 Oct 2026 12:19:09 +0000 (14:19 +0200)]
MINOR: http-ana: automatically enable http-no-delay for streaming events (SSE)
Some people get trapped using plain HTTP in AI gateway environments which
rely on server-sent events (SSE, documented at
https://html.spec.whatwg.org/multipage/server-sent-events.html).
This mechanism consists in pushing body updates and expecting intermediaries
not to buffer them. The way to detect this is to check the advertised
content-type in the response, which is "text/event-stream" in this case.
What happens by default is that the Nagle algorithm delays short messages
by up to ~200ms hoping to aggregate them. But when all messages are only
a few tens of bytes large, sent every 5-10ms, this shows a jerky traffic
that's not pleasant to use.
The current patch simply adds a check for the aforementioned content-type
on the response path, and automatically enables http-no-delay for sending
to the client when found. It's sufficient to address the case without
requiring the user to adjust the config, as measured with sse-latency:
Willy Tarreau [Fri, 2 Oct 2026 12:08:59 +0000 (14:08 +0200)]
DEV: sse-latency: add a latency measuring tool for SSE response streams
Common HTTP load generation tools are not convenient to measure latency
through haproxy when it comes to server-sent events, because the
measurements do not apply to the partial contents that SSE are. These
are particularly important for AI environments where agents need to
minimize latency.
This commit introduces a new tool that is both a client and a server,
and that is meant to measure an intermediary's added latency. For this,
it connects to itself to measure its own inflicted latency (loopback
test) then does it again through the configured proxy, and reports both
as well as the traversal time. The principle is to note the timestamp
at which each SSE is sent, and measure the difference when received by
the client, since both sides are the same process and use the same clock
reference. The output shows the reference time (loopback), the gateway
time, and the gateway overhead as the difference between the two.
The tool supports adding more clients, doing more requests, delaying the
first token response, and setting the inter-token latency.
An example haproxy config is provided (gateway.cfg) that measures both
with and without option http-no-delay.
Without http-no-delay (+ ~100ms, max 200 due to Nagle):
BUILD: ssl: fix build without USE_OPENSSL + clear_ocsp_clt_check_ctx
src/stream.c: In function ‘stream_free’:
src/stream.c:722:9: error: implicit declaration of function ‘clear_ocsp_clt_check_ctx’ [-Werror=implicit-function-declaration]
722 | clear_ocsp_clt_check_ctx(&s->ocsp_clt_check_ctx);
| ^~~~~~~~~~~~~~~~~~~~~~~~
DOC: ocsp: Add doc for the new 'do-ocsp-request' action
The new 'do-ocsp-request' action checks for a client certificate after
an SSL handshake and checks its validity by sending an OCSP request to
the right OCSP responder. The resulting status is stored in a variable
and the request is denied by default is the resulting status is not
good.
When no client certificate is provided or no ocsp uri is present in the
certificate, the "status-var" content of the "wait-for-ocsp" action will
be set to "inapplicable" instead of the "unknown" value (which is an
actual OCSP status that could be sent by the OCSP responder).
Create a new 'do-ocsp-request' http-request and "tcp-request content"
action. When an SSL handshake occurred and was successful, the action
checks for a client certificate. If any is provided and it contains an
OCSP URI, we build an OCSP request and send it to the responder. No
other action is performed until a response is processed or a timeout
occurs.
When the response is received, we check its signature thanks to an
issuer certificate that should be in the issuer tree (see
'issers-chain-path' option), check its expiration dates and the client
certificate's status, and put the status and error reason (if any) in
the user provided "status-var" and "reason-var".
By default the action denies the request is the received status is not
'good' but this can be tuned thanks to the 'no-deny' option.
MINOR: ocsp: Dup OCSP_CERTID in ssl_ocsp_create_request_details
Instead of giving our only reference to the OCSP_CERTID to the
OCSP_REQUEST in ssl_ocsp_create_request_details we will duplicate it and
let the caller free it.
MINOR: ocsp: Raise specific ocsp error code in ocsp parsing/verifying functions
Instead of relying purely on error messages, the ssl_ocsp_check_response
and ssl_sock_update_ocsp_response functions can now return custom error
codes.
They were loosely based on the new verify error codes introduced in
newer version of OpenSSL (3.6.2 for instance) but needed to be
duplicated rather than used directly because of compatibility issues
with various SSL libraries.
MINOR: ocsp: Send OCSP response status back to caller
The 'ssl_sock_load_ocsp_response' function parses an OCSP response,
checks its status and it expiration times but it did not provide the
status anywhere, which will come to use in upcoming OCSP-related devs.
Now it returns the OCSP status to the caller.
MINOR: ocsp: Remove free of main structure from ssl_sock_free_ocsp_data
The 'ssl_sock_free_ocsp_data' now only frees the contents of the
'certificate_ocsp' while leaving the actual structure unfreed.
This will allow to keep certificate_ocsp structures on the stack later.
DOC: server: explicit refcount interaction in "del server"
Add a paragraph in "del server" to highlight that a server instance may
still be kept for some time after command completion for internal
management. This does not prevent though to reuse its name or GUID for a
newly created instance.
BUG/MEDIUM: server: remove immediately from GUID tree on delete server
On "delete server", the target instance is removed from its various
attach points in the parent proxy and global collections. The server
will then be freed immediately or kept temporarily in the process until
its refcount is null.
In effect, this means that a server name can be reused immediately after
"delete server" success, even if the instance is not immediately freed.
This was not the case though with its GUID, as guid_remove() was
performed in srv_drop() on null refcount.
Changes this behavior so that guid_remove() is now called directly
during "delete server" handler. This is better as there is no difference
now for server names and GUIDs : both can be reused immediately after
the command completion.
This could be considered as a bug with a possible backport up to 3.0.
Currently it may be better to not pick it unless it becomes mandatory.
CLI command wait has a built-in help message which notably list all
supported condition. This patch complete it to add "be-removable" as a
valid entry, available since the implementation of the removal of
backends at runtime.
BUG/MINOR: proxy: add PR_CAP_LB for dynamically added backend
When a new backend is added at runtime via the CLI, ensures its cap
contains PR_CAP_LB along with already set PR_CAP_BE.
The purpose of this cap is to flag a proxy with "load-balancing
capabilities". In fact this is a loose meaning which could be understand
in several manners. Currently, this cap is reserved for frontend,
backend and listen instances from the configuration file explicitely
created by users, by contrast to internal proxies. Thus, dynamically
added backends also enter in this definition.
The impact of this bug is limited as PR_CAP_LB usage is only used for
checking the compatibility of a server with the proxy mode. However,
this may change in the future so this change is mandatory.
MINOR: makefile: add CROSS_TOOLCHAIN to prefix the target's tools
Cross-building also needs the target's nm, objcopy and readelf, since the
host ones do not know a foreign object. Instead of setting each one,
CROSS_TOOLCHAIN=arm-linux-gnueabi- prefixes them all, giving arm-linux-
gnueabi-gcc, -nm and so on. Empty by default.
OPTIM: sym: skip the embedded table when the file provided the symbols
The embedded compressed table and the file's .symtab describe the same
local functions, the latter being a superset, so decompressing it when the
file is readable is pure loss, which is the common case.
Only decompress it when the file gave us nothing, i.e. once stripped or
unreachable. This is what the table is for and saves inflating about 1.5 MB
per object at every start.
MINOR: sym: also read the compressed symbol tables from the ELF files
Distributions ship libraries stripped, keeping local symbols in a
compressed .gnu_debugdata section. It is not loaded in memory, so it was
only reachable for objects mapping it in a segment of their own, i.e. the
ones we build, leaving system libraries unnamed.
Look for that section in the ELF files already mapped to read their
.symtab, and hand it to the same decompressor. A separate debug file still
wins as it is more complete, and the file mappings are released right away
since names point into the decompressed image. On Fedora this names
libcrypto, libssl, liblua, libm, libcrypt and libz, turning
"liblua-5:luaopen_package+0x41a0/0x205" into "liblua-5:luaV_execute+0x90f".
MINOR: sym: read local symbols and debug info from ELF files
The dynamic linker and the embedded table only cover exported symbols and
our own objects, so static functions of system libraries still show as the
nearest export plus a large offset.
When files are readable, parse their .symtab, which lists local functions
too. On Linux the executable is read from /proc/self/exe, so a moved or
replaced file, or a relative exec path after a chdir (-C), still designates
the running image; a file whose program headers, or build-id when present,
do not match the image is ignored. Stripped objects fall back to the
separate debug file, looked up by GNU build-id and .gnu_debuglink as gdb
does, in /usr/lib/debug and the "tune.debug-file-directory" directories,
skipping a stale one whose build-id or CRC32 does not match. Files are
mapped only for the build.
MEDIUM: sym: store a compressed symbol table in a loadable section
Implement the MiniDebugInfo, and store the symtab, compressed with xz,
into the .gnu_debugdata section, provided the needed utilities are
available, and we're building an ELF binary.
Makes it a loadable section, so that it is always available, and teach
haproxy to parse it, so that we will always get all available symbols
for binaries built that way, even if we can't even read the binary
itself.
Import the .xz decoder from xz-embedded v2024-12-30 (0BSD). Only what is
needed for a plain LZMA2 stream is taken: the stream and LZMA2 decoders and
the two check modules; BCJ filters, SHA-256 and the kernel glue are left
out. The Makefile enables CRC64, the integrity check "xz" uses by default,
and links the decoder only when the compiler produces ELF objects, since
its only user in sym.c is not compiled for another object format.
Files are imported verbatim for later resyncing: C files in src/, headers
in include/import/. As the C files include their private headers quoted,
src/ carries small wrappers including the real ones, as src/cebtree-prv.h
does. include/import/xz_config.h maps the few kernel calls onto malloc()
and string functions.
MINOR: sym: add helpers to collect the symbols of an ELF image
The next commits read symbols from ELF images (an embedded compressed table
and the object's file), and both need the same primitives, so introduce
them here: validate an ELF header and its section header table, keep every
read inside the image, find .symtab and its string table, and collect its
function symbols into the builder, applying the object's load address.
Parsing an ELF image needs no help from the dynamic linker, so they are
gated on __ELF__ alone; only enumerating the loaded objects needs
dl_iterate_phdr().
They are tagged __maybe_unused because their users come in later commits,
which may be picked independently.
MINOR: sym: collect the symbols known to the dynamic linker
The dynamic linker keeps .dynsym/.dynstr of every loaded object mapped for
relocation. Where dl_iterate_phdr() exists, walk each object's PT_DYNAMIC
to collect its exported function symbols from the memory image, plus its
address range from PT_LOAD. This works even when files are unreadable (e.g.
after chroot) and gives what dladdr() sees, but lock-free.
When an address falls past the nearest lower symbol, resolve_sym_name() now
prints "sym+off/size" instead of a bare offset, easier to feed to gdb. Add
"tune.disable-elf-symbols" to restore the previous output.
MINOR: sym: add an in-process symbol table for backtraces
Build an address-sorted table at boot and publish immutable snapshots for
lock-free, signal-safe lookups. Seed it with the existing known functions.
The table is built while the process is still starting and no thread is
running yet, so the build needs no locking.
The table is sorted using a radix sort.
CLEANUP: sym: move symbol resolution out of tools.c
Group symbol resolution and the shared dynamic-loader helpers in sym.c
before extending them with the in-process table: resolve_sym_name(),
the dladdr() and dlsym() wrappers, static-build detection, the executable
path, and the dlopen() interposer which verifies the loaded symbols
through the same helpers. Their declarations, and the backtrace helpers
that report the frames they resolve, move into a new sym.h which their
callers now include.
No functional change.
Willy Tarreau [Thu, 1 Oct 2026 12:49:21 +0000 (14:49 +0200)]
[RELEASE] Released version 3.5-dev8
Released version 3.5-dev8 with the following main changes :
- BUG/MEDIUM: task: Do not destroy a task that is not ours in task_destroy
- BUG/MEDIUM: http-client: Don't use the httpclient context if it was released
- MINOR: http-client: Add an option to not stop the reception of the response
- BUG/MEDIUM: ssl/ocsp: Set HTTPCLIENT_O_RES_ACCUM option on the http-client
- BUG/MEDIUM: acme: Set HTTPCLIENT_O_RES_ACCUM option on the http-client
- MINOR: http-client: Add a function to notify some data were consumed
- BUG/MEDIUM: http-client/cli: Notify the http-client when the response is consumed
- MINOR: ssl/ocsp: Remove the useless res_payload callback of the http-client
- BUG/MINOR: ssl/ocsp: Report an error when an empty OCSP response is received
- BUG/MEDIUM: htx: Fix the position returned by htx_defrag()
- BUG/MINOR: tools: Don't try to anonymize a NULL string
- BUG/MAJOR: htx: Check the header/trailer length limits when one is updated
- MINOR: htx: Add macros for the header/trailer name and value max lengths
- BUG/MINOR: mux-h2: Reject trailers received on a tunneled stream
- MEDIUM: htx: Skip UNUSED blocs when getting previous,next and first blocks
- MEDIUM: htx/tree-wide: Remove tests on HTX_BLK_UNUSED when possible
- MEDIUM: htx: Insert headers and trailers before corresponding end-of block
- MINOR: htx: Add support for flags on HTX blocks
- MAJOR: htx: Use htx_set_eom() instead of setting HTX_FL_EOM by hand
- MAJOR: htx: Rename HTX_FL_EOM into HTX_FL_HAS_EOM
- MEDIUM: htx: Add HTX_BLK_FL_EOM flag and set it on last block of the message
- MINOR: compression: Rely on HTX block flags to detect the end of message
- MINOR: fcgi-app: Stop on last HTX block when getting the payload size
- MINOR: mux-h2: Use flags of HTX blocks to detect end of message during sending
- MINOR: mux-h1: Use flags of HTX blocks to detect end of message during sending
- MINOR: mux-fcgi: Use flags of HTX blocks to detect end of message during sending
- MEDIUM: h3: Use flags of HTX blocks to detect end of message during sending
- MINOR: stats-html: Detect a complete request by testing flags on the tail bloc
- MINOR: hlua: Use flags of HTX blocks to detect EOM from an HTTP applet
- MINOR: htx: Add function to know if the tail block carry EOM flag
- MAJOR: tree-wide: No longer use HTX_FL_HAS_EOM to detect end of message
- MAJOR: htx: Remove HTX_FL_HAS_EOM flag
- MEDIUM: mux-h2: Don't mix HTTP and tunneled data in the same buffer
- MEDIUM: htx: Harden HTX API to avoid invalid uses when EOM was reached
- MEDIUM: htx: Remove usless htx_is_unique_blk() function
- DOC: internals: Update the HTX API documentation
- MINOR: htx: Rename htx_has_eom() into htx_msg_ended()
- MINOR: htx: Add HTX_BLK_RAW_DATA block type
- MINOR: htx: Add the block type as argument of the data insertion functions
- MINOR: htx: Handle RAW_DATA blocks like DATA blocks in the HTX API
- MEDIUM: htx: Allow tunneled data to be added in a message already ended
- MINOR: mux-h1: Handle RAW_DATA blocks when sending data
- MINOR: mux-h2: Handle RAW_DATA blocks when sending data
- MEDIUM: mux-h1: Use RAW_DATA blocks to store tunneled data
- MEDIUM: mux-h2: Use RAW_DATA blocks to store tunneled data
- Revert "MEDIUM: mux-h2: Don't mix HTTP and tunneled data in the same buffer"
- MINOR: http-htx: Really test the EOM flag presence in internal.htx.has_eom
- MINOR: http-htx: Report the payload of RAW_DATA blocks in internal.htx_blk.data
- MINOR: mux-h1: Only handle RAW_DATA blocks when emitting tunneled data
- DOC: internals: Update the HTX API documentation for tunneled data
- REGTESTS: http-messaging: Add a test for the CONNECT tunnels
- BUG/MINOR: hlua: release private Lua rule data on teardown
- BUG/MINOR: ssl: Fix possible null deref on the SSL context in ssl_sock_to_buf()
- MEDIUM: ssl: add ocsp-update.timeout global option
- BUG/MINOR: http-client: Convert server timeout ticks when setting it
- BUG/MEDIUM: h1-htx: Don't report EOM for H1 interim responses during parsing
- BUG/MINOR: mux-h2: Don't expect more HTX data on 1xx interim responses
- REG-TESTS: http-messaging: Add a script to test interim responses for H1/H2
- BUG/MEDIUM: log: reserve the trailing 0 in CBOR int and bool encoders
- BUG/MEDIUM: http-htx: don't build the new authority in a rotating trash chunk
- BUG/MEDIUM: http-act: ignore "capture len" rules evaluated from a backend
- BUG/MEDIUM: tcp-rules: ignore "capture len" rules evaluated from a backend
- BUG/MINOR: http-ana: count response body failures only once in http_fail_cnt
- BUG/MINOR: http-fetch: http_auth_bearer() must not match a missing header
- BUG/MINOR: http-fetch: fix the space check of http_auth_bearer(<hdr>)
- BUG/MINOR: mux-h1: only mark C-L and T-E as sent once the header is emitted
- BUG/MINOR: tcpcheck: refresh the start-line after updating the authority
- CLEANUP: mux-h2: replace a non-UTF-8 character in a comment
- BUG/MINOR: http-ana: restore the transaction status after early hints
- REG-TESTS: http-messaging: Send one request per h2 client in h2_trailers.vtc
- BUG/MEDIUM: h3: Set FIN when last DATA block is sent via zero-copy
- BUG/MAJOR: mux_quic: fix leak on RESET_STREAM reception
- BUG/MEDIUM: mux_quic: activate timeout on FE init
- BUG/MINOR: h2: reject :protocol pseudo-header in H2 responses
- MINOR: ssl: cleanse TLS ticket keys before freeing
- MINOR: init: also set the worker-id in file-less mode
- BUG/MINOR: acme: exact domain match in acme_challenge_ready()
- BUG/MINOR: ssl: copy curves, sigalgs and client_sigalgs in srv_ssl_settings_cpy()
- BUG/MINOR: ssl: free curves, sigalgs and client_sigalgs in ssl_sock_free_srv_ctx()
- BUG/MINOR: ssl: free curves, sigalgs and client_sigalgs in srv_parse_*()
- MINOR: log: pass the input end to the _lf_encode_bytes() byte encoders
- MINOR: log: add the +utf8 encoding option to let valid UTF-8 pass
- BUG/MAJOR: mux_quic: fix potential crash on RESET_STREAM receive
- BUG/MEDIUM: sink: reconnect attempt not working after session lasted more than ~25 days
- BUG/MEDIUM: server: skip log backend addr checks for internal proxies
- REGTESTS: log: check that a ring's internal server may target a UNIX socket
- Revert "CLEANUP: proxy: mention that px->conn_retries isn't relevant in some cases"
- BUILD: haring: fix compile issue due to nop warning.
- BUG/MINOR: chunk: Allow large chunks uses from large const buffers
- MINOR: htx: Remove htx_move_blk_before() function
- BUG/MAJOR: mux-h2: Preserve raw data on aborted frontend tunnels
- BUG/MEDIUM: mux-h1: Don't subscribe for sends while output is blocked
- BUG/MINOR: mux-h1: Discard pending raw data after a tunnel rejection
- MINOR: stick-table: provide a function to estimate on-wire data size
- MINOR: stick-table: calculate the on-wire size of each key
- BUG/MEDIUM: stick-table: restrict key sizes to what fits in a buffer
- REGTESTS: http-messaging: consume window update before txdata
- BUG/MEDIUM: pattern: don't dereference static_pattern's data when not filling it
- BUG/MINOR: jwe: Buffer overflow when filling fake cek in case of RSA1.5
- BUG/MINOR: jwe: Avoid buffer overflow in fake cek (RSA1.5)
- BUG/MINOR: startup: don't chroot by default when set-dumpable is set
- BUG/MINOR: http: do not consume the delimiter of a truncated q-factor
- BUG/MINOR: sample: apply the ms_/us_ time offsets in the input unit
- BUG/MINOR: sample: zero-pad the fractional part emitted by http_date()
- BUG/MINOR: sample: make quic_enabled() always succeed
- BUG/MINOR: http-ana: return a 502 when checkcache blocks a response
- BUG/MINOR: stick-table: do not count the lookup itself in table_trackers()
- BUG/MINOR: payload: always report a boolean from req.ssl_ec_ext
- BUG/MINOR: http-fetch: do not count Set-Cookie attributes as cookies
- BUG/MEDIUM: tcpcheck: do not run a regex on an unallocated buffer
- BUG/MINOR: sample: return the decoded JWT part when no path is given
- DOC: config: mention other responses not blocked by option checkcache
- DOC: config: fix doc for hex2i() converter on unparsable input
- DOC: config: mention that url_ip and url_port fail when passed a name
- REGTESTS: converter: add a test for the arithmetic and bitwise converters
- REGTESTS: converter: add a test for the string trimming and parsing converters
- REGTESTS: converter: add a test for the date formatting converters
- REGTESTS: converter: cover all the map match methods and output types
- REGTESTS: http-rules: test the run-time ACL and map update actions
- REGTESTS: http-rules: test the ACL and map management commands of the CLI
- REGTESTS: sample_fetches: add a test for the HTTP response fetches
- REGTESTS: sample_fetches: add a test for the HTTP request fetches
- REGTESTS: sample_fetches: add a test for the connection address fetches
- REGTESTS: sample_fetches: add a test for the proxy and server state fetches
- REGTESTS: stick-table: test the general purpose counters and tags
- REGTESTS: stick-table: test the traffic counters of the stick counters
- REGTESTS: http-rules: test the HTTP authentication action, fetches and ACL
- REGTESTS: log: test the transaction state fetches and the logging actions
- REGTESTS: http-rules: test the binary header manipulation actions
- REGTESTS: http-rules: test replace-uri and the header replacement actions
- REGTESTS: tcp-rules: test the set-src, set-dst and port rewriting actions
- REGTESTS: sample_fetches: add a test for the constant and process fetches
- REGTESTS: converter: add a test for the when() and debug() converters
- REGTESTS: http-errorfiles: test the errorloc directives
- REGTESTS: proxy: test the "option checkcache" response filter
- REGTESTS: proxy: test the connection retries, retry-on and redispatch
- REGTESTS: http-rules: test the tarpit, deny and silent-drop actions
- REGTESTS: log: test the log filtering options of a frontend
- REGTESTS: proxy: test monitor-uri and the monitor fail condition
- REGTESTS: cli: add a smoke test for the read-only CLI commands
- REGTESTS: cli: test the commands which change the run-time settings
- REGTESTS: http-rules: test the declared capture slots
- REGTESTS: tcp-rules: test the raw payload fetches
- REGTESTS: sample_fetches: pin one thread in the connection address test
- REGTESTS: sample_fetches: pin one thread in the proxy state test
- REGTESTS: server: test the server settings reported by the runtime
- REGTESTS: checks: test the health adjusting from observed traffic
- REGTESTS: cli: test the privilege levels of the CLI
- REGTESTS: connection: test the expect-proxy action
- REGTESTS: sample_fetches: test the raw TLS hello fetches
- REGTESTS: connection: do not race with the reset in the expect-proxy test
- REGTESTS: ssl: test the ssl_fc_* and ssl_bc_* connection fetches
- REGTESTS: protobuf: add a test for the ungrpc converter
- REGTESTS: http-messaging: test the options dropping the HTTP trailers
- REGTESTS: protobuf: cover all the field types of the ungrpc converter
- REGTESTS: sample_fetches: test req.ssl_ver on an SSLv2 CLIENT-HELLO
- REGTESTS: sample_fetches: test the req.ssl_alpn fetch
- REGTESTS: ssl: test ssl_c_used, ssl_c_san and ssl_c_ca_err_depth
- REGTESTS: stick-table: read the counters through sc1_ and sc2_ as well
- REGTESTS: stick-table: cover the remaining gpc and gpt spellings
- REGTESTS: http-rules: cover the rfc7239_nn and rfc7239_np converters
- REGTESTS: proxy: test the disable-l7-retry action
- REGTESTS: connection: test the NetScaler CIP protocol
- REGTESTS: tcp-rules: test the RDP cookie fetches
- REGTESTS: tcp-rules: read the payload through the deprecated spellings
- REGTESTS: sample_fetches: test the TCP_INFO counters
- REGTESTS: sample_fetches: check the rtt headers this test was filling
- REGTESTS: sample_fetches: test the req.cook() fetch itself
- REGTESTS: sample_fetches: test fe_tarpit_timeout
- REGTESTS: sample_fetches: test res.body, res.body_len and server_status
- REGTESTS: sample_fetches: test the request_date fetch
- REGTESTS: sample_fetches: test the stream state fetches over HTTP/2
- REGTESTS: ssl: test the wait-for-handshake action and fc.timer.handshake
- REGTESTS: http-messaging: test the option httpclose
- REGTESTS: http-messaging: test the option http-no-delay
- REGTESTS: http-messaging: check that a truncated response loses no byte
- REGTESTS: sample_fetches: do not race with the server close in proxy_state
- REGTESTS: sample_fetches: make the raw hello test parallel-safe
- REGTESTS: http-messaging: widen the abort window of the abortonclose test
- REGTESTS: log: serve the transaction state test on a single connection
- REGTESTS: http-messaging: give each frontend its own syslog server
- REGTESTS: converter: serve the two sha2 requests on one connection
- REGTESTS: http-messaging: make the /c5 abort land during the retries
- REGTESTS: http-rules: one connection per request in the replace-uri test
- MINOR: vars: Add a helper to parse a variable scope name
- MINOR: vars: Add 'var_name_is_valid' helper function
- MEDIUM: vars: Add the set-vars-from-map action
- REGTESTS: vars: Add a test for the set-vars-from-map action
Add a new action, set-vars-from-map(<scope>,<map-file>[,<type>]), that
loads a two-column (key/value) map file and sets one variable per entry,
named "<scope>.<key>", to the entry's value. The scope is one of sess,
txn, req, res or check and the variable name is relative to it.
The new "tune.vars.from-map.max" global option defines the maximum
number of entries taken into account in each map. It default to 100.
Values are read as strings and converted according to the optional
<type>, which may be "str" (default), "int" or "bool". Integer
conversion is strict, so an entry with trailing garbage is rejected
rather than silently truncated, and booleans accept 0/1 as well as
true/false in any case. An entry that cannot be parsed is skipped
without aborting the remaining entries.
The file is loaded as a pattern reference flagged PAT_REF_SMP, or reused
if it was already loaded, in which case it must also have been loaded as
a two-column file. The action is registered for all rule sets that
already support set-var.
MINOR: vars: Add 'var_name_is_valid' helper function
This function checks a variable name (scope excluded) and fails if any
invalid character is found. Variable names must contain alphanumeric
characters, '-', '_' or '.'.
REGTESTS: http-rules: one connection per request in the replace-uri test
The verification campaign caught this test with "HTTP header is
incomplete" when the whole suite runs with "--j 32" on a loaded
machine. Its first client was sending its six requests over a single
connection while the server answers each of them in its own iteration,
so the server closed right when the next request was sent over the
connection haproxy reuses, and the client got that close instead of a
response.
The test is about rewriting the request target, not about keep-alive,
so the six requests are now sent by six clients, each on its own
connection, which is also what the three other clients of the test
already do. Twenty-four runs in parallel with the machine loaded pass.
REGTESTS: http-messaging: make the /c5 abort land during the retries
The previous attempt at making this case robust, which raised the
connect timeout so that the client abort is noticed before the retries
are over, was not enough: the client closes immediately after its
request, so under load the close is sometimes seen while the request is
still being parsed, and the transaction is then logged as "CH--" with a
400 instead of the expected "CC--".
The client now waits fifty milliseconds before closing, which leaves
haproxy the time to read the request and to start connecting, and the
raised connect timeout gives the abort a full second to be noticed.
Twenty runs in parallel with the machine loaded pass.
REGTESTS: converter: serve the two sha2 requests on one connection
The final validation campaign caught this test with "HTTP header is
incomplete" when the whole suite runs with "--j 32" on a loaded
machine. Its client sends its two requests over a single connection
while the server answers each of them in its own iteration, so it
closes after the first response, exactly when the second request is
sent over the connection haproxy reuses, and the client gets that close
instead of a response.
This is the same shape which was fixed in the transaction state test:
the server now keeps its connection for both requests. Twenty runs in
parallel with the machine loaded pass.
REGTESTS: http-messaging: give each frontend its own syslog server
The two frontends of this test were logging to the same syslog server
and their two messages were expected in order. A stream is logged when
it ends, which may be after the next client has been served by the
other frontend, so that order is not guaranteed. This is the same race
which was caught in the raw hello test when the suite runs in parallel,
found here by inspection rather than by a failure.
REGTESTS: log: serve the transaction state test on a single connection
Running the whole suite with "--j 32" caught the client of this test
receiving a close instead of the response to its second request. The
server was answering each of the four requests in its own iteration,
so it closed its connection after every response, and the debug trace
of the failing run shows the close reaching haproxy just as the next
request was sent over the connection it had reused:
The four requests are now served on a single connection, which is what
a keep-alive server does, so nothing closes in the middle of the
client's series. Twenty-four runs in parallel with the machine loaded
pass where the previous version failed twice out of sixteen.
REGTESTS: http-messaging: widen the abort window of the abortonclose test
Running the whole suite with "--j 32" caught the /c5 request being
answered with the 503 of the exhausted connection retries and logged
"SC--" where the test expects the "CC--" of a client abort.
That request is sent by a client which closes immediately, and the
abort has to be noticed before the retries are over, which the connect
timeout of a hundred milliseconds does not guarantee on a process
starved by thirty-two parallel tests: the log of the failing run shows
the whole retry sequence completed in exactly those 100 ms.
The connect timeout of the section that request uses is raised to one
second, which leaves ten times more room and does not make the test
any slower, since the expected outcome is precisely that the abort
cuts the retries short. The other section, whose request is expected
to wait for all the retries, is left untouched.
REGTESTS: sample_fetches: make the raw hello test parallel-safe
Running the suite with "--j 4" caught the client of the third
ClientHello getting an empty response. The single server was reading
only the 47 bytes the three hellos have in common, so the rest of each
of them was still unread when it closed its socket, which makes the
system emit a reset instead of a FIN, and that reset discards the
ServerHello which haproxy had not read yet.
Each hello now has its own server, reading exactly the number of bytes
its client sends, and the backend distributes the three connections in
order with round robin. Nothing is left unread, so the close is a plain
FIN again.
That uncovered a second race: the log of a stream is emitted when the
stream ends, which may be after the next client has been served by the
other frontend, and the third message of the first frontend was
sometimes overtaken by the first message of the second one. The two
frontends now log to their own syslog server, so only their own series
has to be ordered, and each client of the first frontend waits for the
close before ending, which finishes its stream.
Thirty-two runs in parallel pass where the previous version failed
about once in twenty.
REGTESTS: sample_fetches: do not race with the server close in proxy_state
A full run of the suite caught srv_conn() reporting zero where the test
expects one. The server closed its connection as soon as it had
answered, and when that close reached haproxy before the response rules
were evaluated, the server connection was already released and the
counter had gone back to zero.
The server now holds its connection until the client has read the
response, which is necessarily after the rules have run.
REGTESTS: http-messaging: check that a truncated response loses no byte
When a server announces a content-length and closes before having sent
that many bytes, haproxy can only forward what it received and abort
the client side. Whether everything which was sent really reaches the
client is a recurring question when such reports are analysed, since a
byte lost there is indistinguishable from a server truncating its
response, and nothing covered it.
The first four cases announce 100000 bytes and stop after 40010, which
spans more than two 16 kB buffers so that the data cannot sit in a
single one and the forwarding has to run several times. They differ by
the way the server ends: an H1 server writing everything at once then
shutting down, the same one writing the last bytes only once the client
has drained the bulk, so that they reach haproxy in the same event as
the close, an H2 server closing without any END_STREAM, and an H2
server setting END_STREAM 60000 bytes too early. The fifth one repeats
the first with a body of 200000 bytes and a client which does not read
for half a second, so that the close lands while the data is still
queued; a larger body would stall it further but vtest asserts in its
own logging code somewhere below the megabyte.
The client counts the bytes it receives, so a missing one leaves it
waiting until the close ends the test with a failure. Asking for one
byte more than the server sent was verified to fail in each of the five
cases.
The two HTTP/2 servers never read the WINDOW_UPDATE frames haproxy
sends them, so closing their socket right away makes the system emit a
reset which discards what is still in flight. This was caught by
running the test 16 times in parallel, and is avoided by shutting the
socket down, which is what a truncating server does anyway, and by
letting vtest close only once the client has seen the whole exchange.
The client is an HTTP/1 one in every case: haproxy always ends such a
stream with an RST_STREAM, which stops "rxdata -all", and the number of
DATA frames it emits before that depends on how its buffers were
filled, so an HTTP/2 client can neither read to the end of the stream
nor use a fixed number of reads.
REGTESTS: http-messaging: test the option http-no-delay
The option was listed as out of reach because it only changes when the
data are pushed out and because no vtest assertion expresses a duration.
It turns out haproxy measures that duration itself: %Tt reports the
whole transaction, so it is enough to build an exchange whose length is
exactly the delay being looked for and to let the syslog expect count
the digits.
The server sends a chunked response, then its first chunk alone, and
only terminates the message once a barrier tells it the client has
received that chunk. Without the option, the incomplete message is held
by the system for its cork timeout, which is 200 ms on Linux, and the
transaction lasts that long; with the option it lasts zero millisecond.
Five runs here gave 200 to 222 ms against 0, so a three digit value
against a one or two digit one leaves a comfortable margin.
The delay comes from MSG_MORE, which is compiled out as a no-op where
it does not exist, leaving nothing to observe, hence the excluded
targets.
REGTESTS: http-messaging: test the option httpclose
The option only appeared in other tests as a convenient way to get rid
of the idle connections, its own behavior was never checked, and
"option http-pretend-keepalive", which modifies it, was not covered
either.
The five proxies here cover the four ways it can be declared. The side
whose connection is closed is the one the option is declared on: the
client one for a frontend, the server one for a backend, both for a
listener, and the "no option" form undoes what a defaults section had
set. The header it adds in that direction is checked by the server for
the request side and by the client for the response side.
Whether the client connection was closed is directly observable, and
whether the server one was is reported by bc_reused, since a connection
which was closed cannot be reused by the next request: the frontend
case reuses its server connection while the listener case cannot. This
is also how "http-pretend-keepalive" is checked, the server not seeing
the header while its connection is closed all the same.
REGTESTS: ssl: test the wait-for-handshake action and fc.timer.handshake
Both were listed as reachable from one of the SSL tests, the action
because it only makes sense on a connection which performs a handshake,
and the timer because it stays at zero elsewhere. They join the test
which already reads the ssl_fc_* family on a TLSv1.2 connection. The
action is a no-op there since the handshake is over before the rules
run, no early data being involved, so it only checks that it is
accepted and does not disturb the exchange.
REGTESTS: sample_fetches: test the stream state fetches over HTTP/2
fs.id was only covered by a QUIC test, and bs.id, fs.aborted,
bs.aborted, fs.rst_code, bs.rst_code, fc_settings_streams_limit,
bc_settings_streams_limit and bc_glitches had no coverage at all. They
describe the multiplexed stream and its connection, so they need HTTP/2
on both sides, which vtest provides.
The first listener runs two streams over the same pair of connections,
which shows the stream ids of both sides and the two stream limits, the
frontend one being what haproxy announces and the backend one what the
server announced. The second listener has its stream reset by the
client and the third one by the server, and since no response reaches
the client in those two cases, the abort flag and the reset code are
read from the log.
REGTESTS: sample_fetches: test the request_date fetch
request_date is the accept date plus the handshake and idle times, so
it is read in seconds, in milliseconds, and as a difference with the
accept date it derives from, which is zero here since the request
follows the accept immediately.
REGTESTS: sample_fetches: test res.body, res.body_len and server_status
The response test covered res.body_size but neither the body itself nor
its length, and not the status as it was received from the server. The
two body fetches only see what has already been received, so an
"http-response wait-for-body" is added in front of them to make the
result deterministic.