]> git.kaiwu.me - haproxy.git/log
haproxy.git
4 weeks agoBUG/MINOR: cfgparse-listen: don't lose the errors reported while creating a proxy
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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.

4 weeks agoBUG/MINOR: cfgparse-listen: always reject extra arguments to "persist"
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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.

4 weeks agoBUG/MINOR: cfgparse-global: reject extra arguments to numa-cpu-mapping and anonkey
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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.

4 weeks agoBUG/MINOR: cfgparse-listen: fix quoting of the condition conflict warning
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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 '.'.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-listen: fix the argument checked by "email-alert"
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-listen: release the previous description of the proxy
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-global: add missing calloc() check for the global description
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-global: add missing gotos to error label in max-spread-checks
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
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.

This may be backported to all supported versions.

4 weeks agoBUG/MAJOR: cfgparse: properly check userlist section when scanning keywords
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.

5 weeks agoBUG/MEDIUM: h3: fix incorrect DATA size block on DEM FULL
Amaury Denoyelle [Thu, 8 Oct 2026 17:24:20 +0000 (19:24 +0200)]
BUG/MEDIUM: h3: fix incorrect DATA size block on DEM FULL

The following patch fixes h3_data_to_htx() to ensure the correct value
is returned in case of buffer wrapper.

  bcdbc399d34f82a21c49162f203f2ff16b0aed79
  BUG/MEDIUM: h3: fix end-of-input report on DATA wrapping

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).

5 weeks agoMINOR: doc: elaborate on diagnostic mode behavior
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.

Should be backported to stable trees.

Co-Authored-By: Willy Tarreau <w@1wt.eu>
5 weeks agoMEDIUM: h3: do not close conn on unaligned non DATA frame
Amaury Denoyelle [Thu, 8 Oct 2026 07:34:34 +0000 (09:34 +0200)]
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(-)

diff --git a/src/h3.c b/src/h3.c
index 4750ff69f..1ed4a4d8b 100644
--- a/src/h3.c
+++ b/src/h3.c
@@ -2068,13 +2068,9 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
  goto err;
  }

- /* 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,
-               muxc_tevt_type_other_err);
- qcc_report_glitch(qcs->qcc, 1);
- goto err;
+ TRACE_PROTO("pause parsing on unaligned payload", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
+ break;
  }

  /* Only parse full HTTP/3 frames. */
diff --git a/src/mux_quic.c b/src/mux_quic.c
index de443a568..70a6cad2d 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -1255,6 +1255,24 @@ static inline struct buffer qcs_b_dup(const struct qc_stream_rxbuf *b)
  }
 }

+/* 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

5 weeks agoBUG/MEDIUM: h3: fix end-of-input report on DATA wrapping
Amaury Denoyelle [Wed, 7 Oct 2026 13:31:40 +0000 (15:31 +0200)]
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.

5 weeks agoBUG/MINOR: ssl: Fix potential NULL-deref in "dump ssl cert" with AWS-LC
Remi Tricot-Le Breton [Mon, 5 Oct 2026 13:37:36 +0000 (15:37 +0200)]
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.

This patch can be backported up to 3.2.

5 weeks agoBUG/MEDIUM: mux_quic: notify stream on RESET_STREAM reception
Amaury Denoyelle [Tue, 29 Sep 2026 09:27:41 +0000 (11:27 +0200)]
BUG/MEDIUM: mux_quic: notify stream on RESET_STREAM reception

Always notify upper stream layer when a RESET_STREAM is received. This
is necessary as reset will be reported during rcv_buf callback.

Without this patch, reset won't be reported if stream is already
subscribed. This may result in a stream freeze, until its close on
timeout.

This should be backported up to 2.8, after a period of observation.

5 weeks agoBUG/MINOR: h3: reject RS/SS for QPACK uni streams
Amaury Denoyelle [Tue, 29 Sep 2026 10:05:41 +0000 (12:05 +0200)]
BUG/MINOR: h3: reject RS/SS for QPACK uni streams

A H3 peer must not close its qpack encoder/decoder stream. This is
already detected when a stream frame with FIN is received.

This patch extends the detection to RESET_STREAM frames. A similar error
H3_CLOSED_CRITICAL_STREAM is emitted.

This must be backported up to 2.8.

5 weeks agoBUG/MINOR: h3: close connection on GOAWAY frame too long
Amaury Denoyelle [Tue, 29 Sep 2026 09:48:54 +0000 (11:48 +0200)]
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.

This must be backported up to 3.4.

5 weeks agoBUG/MINOR: h3: reject frames with truncated header
Amaury Denoyelle [Thu, 24 Sep 2026 16:06:02 +0000 (18:06 +0200)]
BUG/MINOR: h3: reject frames with truncated header

The following patch ensures that H3 frames with a truncated payload are
now properly rejected with H3_FRAME_ERROR.

  86a4ebc761a278838e8cb06f3a292282ba704c65
  BUG/MAJOR: h3: reject H3 truncated frames

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.

This must be backported to all stable releases.

5 weeks agoBUG/MINOR: mux_quic: account data in conn fctl on early detach
Amaury Denoyelle [Tue, 29 Sep 2026 14:39:17 +0000 (16:39 +0200)]
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

5 weeks agoBUG/MEDIUM: mux_quic: account discarded data in connection flow control
Amaury Denoyelle [Wed, 23 Sep 2026 13:34:28 +0000 (15:34 +0200)]
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.

5 weeks agoBUG/MINOR: mux_quic: report conn error on RESET_STREAM fctl violation
Amaury Denoyelle [Wed, 23 Sep 2026 13:28:29 +0000 (15:28 +0200)]
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.

5 weeks agoBUG/MINOR: mux_quic: extend FINAL_SIZE_ERROR detection on stream
Amaury Denoyelle [Tue, 29 Sep 2026 09:49:13 +0000 (11:49 +0200)]
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.

This must be backported up to 2.6.

5 weeks agoBUG/MINOR: mux_quic: prevent purgeable QCS lingering
Amaury Denoyelle [Wed, 23 Sep 2026 12:31:37 +0000 (14:31 +0200)]
BUG/MINOR: mux_quic: prevent purgeable QCS lingering

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.

5 weeks agoBUG/MAJOR: mux-quic: prevent crash on QCS already set for purging
Amaury Denoyelle [Mon, 5 Oct 2026 08:57:22 +0000 (10:57 +0200)]
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.

5 weeks agoMINOR: ssl: accept integer group IDs in curve2name
William Lallemand [Tue, 6 Oct 2026 13:26:21 +0000 (15:26 +0200)]
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.

5 weeks agoMINOR: ssl: add curve2name converter
William Lallemand [Tue, 6 Oct 2026 12:59:43 +0000 (14:59 +0200)]
MINOR: ssl: add curve2name converter

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.

5 weeks agoMINOR: ssl: complete registered group names and library NID mappings
William Lallemand [Tue, 6 Oct 2026 12:41:52 +0000 (14:41 +0200)]
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.

5 weeks agoMINOR: ssl: skip unavailable NIDs in curves2nid lookup
William Lallemand [Tue, 6 Oct 2026 12:22:22 +0000 (14:22 +0200)]
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.

5 weeks agoMINOR: ssl: split curve names into ANSI, IANA and NIST columns
William Lallemand [Tue, 6 Oct 2026 11:59:03 +0000 (13:59 +0200)]
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.

5 weeks agoBUG/MINOR: ssl: use standard names for X25519 and X448 groups
William Lallemand [Tue, 6 Oct 2026 11:51:45 +0000 (13:51 +0200)]
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).

5 weeks agoDOC: ssl: clarify the NID-based curve fetch branch
William Lallemand [Mon, 5 Oct 2026 16:27:55 +0000 (16:27 +0000)]
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.

5 weeks agoBUG/MINOR: ssl: don't uppercase the native group name
William Lallemand [Mon, 5 Oct 2026 16:21:29 +0000 (16:21 +0000)]
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.

5 weeks agoBUG/MEDIUM: listener: Consider maxconn before waking listeners
Olivier Houchard [Tue, 6 Oct 2026 12:42:47 +0000 (14:42 +0200)]
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.

5 weeks agoCI: github: move wolfSSL 5.7.0 testing to a nightly workflow
William Lallemand [Tue, 6 Oct 2026 10:28:00 +0000 (12:28 +0200)]
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.

5 weeks agoCI: github: cache QuicTLS builds
William Lallemand [Tue, 6 Oct 2026 09:22:26 +0000 (11:22 +0200)]
CI: github: cache QuicTLS builds

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.

Enable SSL caching for QuicTLS.

5 weeks agoCI: github: test the latest AWS-LC release on development branches
William Lallemand [Tue, 6 Oct 2026 08:41:30 +0000 (10:41 +0200)]
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.

5 weeks agoCI: github: test AWS-LC 3.x and 4.x LTS branches
William Lallemand [Tue, 6 Oct 2026 08:35:28 +0000 (10:35 +0200)]
CI: github: test AWS-LC 3.x and 4.x 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.

5 weeks agoBUG/MEDIUM: ssl: crt-list curves ignored with AWS-LC
Frederic Lecaille [Tue, 6 Oct 2026 07:59:44 +0000 (09:59 +0200)]
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>
5 weeks agoBUG/MEDIUM: ssl: crt-list ciphers ignored with AWS-LC
Frederic Lecaille [Tue, 6 Oct 2026 07:46:11 +0000 (09:46 +0200)]
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>
5 weeks agoBUG/MINOR: ssl: fix trace crash on BE conn release in io cb
Amaury Denoyelle [Tue, 6 Oct 2026 07:45:37 +0000 (09:45 +0200)]
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.

This must be backported up to 3.3.

5 weeks agoBUG/MINOR: ssl: fix trace crash on FE conn release in io cb
Amaury Denoyelle [Mon, 5 Oct 2026 15:42:18 +0000 (17:42 +0200)]
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.

This must be backported up to 3.2.

5 weeks agoBUG/MINOR: hq-interop: prevent double EOM on request with standalone FIN
Amaury Denoyelle [Tue, 6 Oct 2026 08:37:06 +0000 (10:37 +0200)]
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.

This should be backported up to 3.2.

5 weeks agoDEBUG: support reading set-dumpable from HAPROXY_FORCE_DUMPABLE
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.

5 weeks agoBUILD: ssl: avoid mixed declarations in the AWS-LC curve fetch
William Lallemand [Mon, 5 Oct 2026 13:04:29 +0000 (13:04 +0000)]
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.

5 weeks agoMINOR: ssl: support curve fetches with AWS-LC API 30
William Lallemand [Mon, 5 Oct 2026 12:45:35 +0000 (14:45 +0200)]
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.

5 weeks agoDOC: config: document new "loggers" section and relevant uses
Aurelien DARRAGON [Fri, 2 Oct 2026 13:56:03 +0000 (15:56 +0200)]
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.

5 weeks agoMINOR: log: support 'to' parameter fo do-log action
Aurelien DARRAGON [Fri, 2 Oct 2026 07:15:32 +0000 (09:15 +0200)]
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.

5 weeks agoMINOR: log: support explicit <loggers> list for do_log_ctx()
Aurelien DARRAGON [Fri, 2 Oct 2026 09:10:03 +0000 (11:10 +0200)]
MINOR: log: support explicit <loggers> list for do_log_ctx()

This way we can provide our own list of loggers to override the default
one (defaults to sess->fe->loggers when <logger> is NULL).

5 weeks agoMINOR: log: support 'to' parameter for "log" directive
Aurelien DARRAGON [Thu, 1 Oct 2026 09:03:03 +0000 (11:03 +0200)]
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).

5 weeks agoMINOR: log: add "loggers" section
Aurelien DARRAGON [Wed, 30 Sep 2026 12:32:04 +0000 (14:32 +0200)]
MINOR: log: add "loggers" section

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.

5 weeks agoBUG/MINOR: log: fix erroneous error message in log-steps parser
Aurelien DARRAGON [Thu, 1 Oct 2026 09:03:18 +0000 (11:03 +0200)]
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.

It should be backported to 3.2.

5 weeks agoBUILD: sym: Define HA_HAVE_DL_ITERATE_PHDR on FreeBSD
Olivier Houchard [Fri, 2 Oct 2026 18:22:39 +0000 (20:22 +0200)]
BUILD: sym: Define HA_HAVE_DL_ITERATE_PHDR on FreeBSD

FreeBSD has been providing dl_iterate_phdr() since FreeBSD 7.0, so
define HA_HAVE_DL_ITERATE_PHDR and HA_HAVE_DUMP_LIBS there.

5 weeks agoBUILD: Hopefully fix build on FreeBSD
Olivier Houchard [Fri, 2 Oct 2026 15:24:20 +0000 (17:24 +0200)]
BUILD: Hopefully fix build on FreeBSD

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.

5 weeks agoREGTESTS: cli: don't check the HAProxy string
William Lallemand [Fri, 2 Oct 2026 13:26:49 +0000 (15:26 +0200)]
REGTESTS: cli: don't check the HAProxy string

Don't check the HAProxy string on "show info" since it can be changed
with -DPRODUCT_NAME, but check the "Name:" string instead.

5 weeks agoMINOR: http-ana: automatically enable http-no-delay for streaming events (SSE)
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:

Before:
  === gateway overhead (gateway minus loopback; negative = loopback noise) ===
  emit->recv (1st token)   mean +206.851ms  p50 +206.851ms  p95 +206.851ms  p99 +206.851ms
  emit->recv (tokens)      mean +104.002ms  p50 +103.851ms  p95 +197.599ms  p99 +205.709ms
  TTFT (client)            mean +207.122ms  p50 +207.122ms  p95 +207.122ms  p99 +207.122ms
  inter-token (client)     mean -0.205ms  p50 -5.128ms  p95 -5.331ms  p99 +202.624ms

After:
  === gateway overhead (gateway minus loopback; negative = loopback noise) ===
  emit->recv (1st token)   mean +0.026ms  p50 +0.026ms  p95 +0.026ms  p99 +0.026ms
  emit->recv (tokens)      mean +0.034ms  p50 +0.036ms  p95 +0.059ms  p99 +0.029ms
  TTFT (client)            mean +0.135ms  p50 +0.135ms  p95 +0.135ms  p99 +0.135ms
  inter-token (client)     mean -0.002ms  p50 +0.042ms  p95 +0.041ms  p99 +0.035ms

It would make sense to backport this to 3.4 to help users who forget to
adjust their config to handle the streamed events.

5 weeks agoDEV: sse-latency: add a latency measuring tool for SSE response streams
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):

  === loopback (direct to listen address): 1 requests, 1 ok, 0 failed, 200 tokens ===
  TTFT (client)            n=1  mean=0.04ms  min=0.04ms  p50=0.04ms  p95=0.04ms  p99=0.04ms  max=0.04ms
  TTFT emit->recv(1st)     n=1  mean=0.00ms  min=0.00ms  p50=0.00ms  p95=0.00ms  p99=0.00ms  max=0.00ms
  inter-token (client)     n=199  mean=5.00ms  min=4.07ms  p50=5.13ms  p95=5.33ms  p99=5.36ms  max=5.38ms
  emit->recv (tokens)      n=199  mean=0.04ms  min=0.01ms  p50=0.03ms  p95=0.07ms  p99=0.18ms  max=0.19ms

  === gateway: 1 requests, 1 ok, 0 failed, 1000 tokens ===
  TTFT (client)            n=1  mean=207.17ms  min=207.17ms  p50=207.17ms  p95=207.17ms  p99=207.17ms  max=207.17ms
  TTFT emit->recv(1st)     n=1  mean=206.86ms  min=206.86ms  p50=206.86ms  p95=206.86ms  p99=206.86ms  max=206.86ms
  inter-token (client)     n=999  mean=4.80ms  min=0.00ms  p50=0.00ms  p95=0.00ms  p99=207.98ms  max=208.97ms
  emit->recv (tokens)      n=999  mean=104.04ms  min=1.02ms  p50=103.88ms  p95=197.67ms  p99=205.89ms  max=208.07ms

  === gateway overhead (gateway minus loopback; negative = loopback noise) ===
  emit->recv (1st token)   mean +206.851ms  p50 +206.851ms  p95 +206.851ms  p99 +206.851ms
  emit->recv (tokens)      mean +104.002ms  p50 +103.851ms  p95 +197.599ms  p99 +205.709ms
  TTFT (client)            mean +207.122ms  p50 +207.122ms  p95 +207.122ms  p99 +207.122ms
  inter-token (client)     mean -0.205ms  p50 -5.128ms  p95 -5.331ms  p99 +202.624ms

With http-no-delay (+ ~40 microseconds max):

  === loopback (direct to listen address): 1 requests, 1 ok, 0 failed, 200 tokens ===
  TTFT (client)            n=1  mean=0.05ms  min=0.05ms  p50=0.05ms  p95=0.05ms  p99=0.05ms  max=0.05ms
  TTFT emit->recv(1st)     n=1  mean=0.01ms  min=0.01ms  p50=0.01ms  p95=0.01ms  p99=0.01ms  max=0.01ms
  inter-token (client)     n=199  mean=5.00ms  min=4.03ms  p50=5.06ms  p95=5.27ms  p99=5.34ms  max=5.59ms
  emit->recv (tokens)      n=199  mean=0.04ms  min=0.01ms  p50=0.03ms  p95=0.07ms  p99=0.18ms  max=0.49ms

  === gateway: 1 requests, 1 ok, 0 failed, 1000 tokens ===
  TTFT (client)            n=1  mean=0.18ms  min=0.18ms  p50=0.18ms  p95=0.18ms  p99=0.18ms  max=0.18ms
  TTFT emit->recv(1st)     n=1  mean=0.03ms  min=0.03ms  p50=0.03ms  p95=0.03ms  p99=0.03ms  max=0.03ms
  inter-token (client)     n=999  mean=5.00ms  min=4.04ms  p50=5.10ms  p95=5.31ms  p99=5.37ms  max=5.45ms
  emit->recv (tokens)      n=999  mean=0.07ms  min=0.03ms  p50=0.07ms  p95=0.13ms  p99=0.20ms  max=0.28ms

  === gateway overhead (gateway minus loopback; negative = loopback noise) ===
  emit->recv (1st token)   mean +0.026ms  p50 +0.026ms  p95 +0.026ms  p99 +0.026ms
  emit->recv (tokens)      mean +0.034ms  p50 +0.036ms  p95 +0.059ms  p99 +0.029ms
  TTFT (client)            mean +0.135ms  p50 +0.135ms  p95 +0.135ms  p99 +0.135ms
  inter-token (client)     mean -0.002ms  p50 +0.042ms  p95 +0.041ms  p99 +0.035ms

5 weeks agoBUILD: ssl: fix build without USE_OPENSSL + clear_ocsp_clt_check_ctx
William Lallemand [Fri, 2 Oct 2026 11:15:46 +0000 (13:15 +0200)]
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);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~

Add HAVE_SSL_OCSP to ensure we have the feature.

5 weeks agoREGTESTS: ocsp: Add 'do-ocsp-request' tests
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:30 +0000 (16:59 +0200)]
REGTESTS: ocsp: Add 'do-ocsp-request' tests

Add a regtest that uses the 'do-ocsp-request' action with a client that
uses a good certificate, a revoked certificate or no certificate.

5 weeks agoDOC: ocsp: Add doc for the new 'do-ocsp-request' action
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:29 +0000 (16:59 +0200)]
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.

5 weeks agoMINOR: ocsl: Add 'inapplicable' return code to client certificate ocsp check
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:28 +0000 (16:59 +0200)]
MINOR: ocsl: Add 'inapplicable' return code to client certificate ocsp check

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).

5 weeks agoMEDIUM: ocsp: Create new do-ocsp-request action
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:27 +0000 (16:59 +0200)]
MEDIUM: ocsp: Create new do-ocsp-request action

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.

5 weeks agoMINOR: ocsp: Dup OCSP_CERTID in ssl_ocsp_create_request_details
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:26 +0000 (16:59 +0200)]
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.

5 weeks agoMINOR: ocsp: Raise specific ocsp error code in ocsp parsing/verifying functions
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:25 +0000 (16:59 +0200)]
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.

5 weeks agoMINOR: ocsp: Send OCSP response status back to caller
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:24 +0000 (16:59 +0200)]
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.

5 weeks agoMINOR: ocsp: Remove free of main structure from ssl_sock_free_ocsp_data
Remi Tricot-Le Breton [Thu, 1 Oct 2026 14:59:23 +0000 (16:59 +0200)]
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.

5 weeks agoDOC: server: explicit refcount interaction in "del server"
Amaury Denoyelle [Mon, 21 Sep 2026 07:45:55 +0000 (09:45 +0200)]
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.

5 weeks agoBUG/MEDIUM: server: remove immediately from GUID tree on delete server
Amaury Denoyelle [Mon, 21 Sep 2026 07:05:09 +0000 (09:05 +0200)]
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.

5 weeks agoBUG/MINOR: cli: update wait help usage
Amaury Denoyelle [Tue, 15 Sep 2026 08:48:13 +0000 (10:48 +0200)]
BUG/MINOR: cli: update wait help usage

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.

This must be backported up to 3.4.

5 weeks agoBUG/MINOR: server: fix "del server" description
Amaury Denoyelle [Thu, 17 Sep 2026 12:33:43 +0000 (14:33 +0200)]
BUG/MINOR: server: fix "del server" description

Update help for "del server" by stating that any servers can be removed,
not just dynamically added ones. This is supported since version 2.5.

This must be backported up to 2.6.

5 weeks agoBUG/MINOR: proxy: add PR_CAP_LB for dynamically added backend
Amaury Denoyelle [Fri, 2 Oct 2026 07:25:13 +0000 (09:25 +0200)]
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.

This must be backported up to 3.4.

6 weeks agoMINOR: makefile: add CROSS_TOOLCHAIN to prefix the target's tools 20261001-oho-debug-syms
Olivier Houchard [Thu, 17 Sep 2026 09:18:48 +0000 (11:18 +0200)]
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.

6 weeks agoOPTIM: sym: skip the embedded table when the file provided the symbols
Olivier Houchard [Tue, 15 Sep 2026 15:51:03 +0000 (17:51 +0200)]
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.

6 weeks agoMINOR: sym: also read the compressed symbol tables from the ELF files
Olivier Houchard [Tue, 15 Sep 2026 15:51:02 +0000 (17:51 +0200)]
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".

6 weeks agoMINOR: sym: read local symbols and debug info from ELF files
Olivier Houchard [Tue, 15 Sep 2026 15:50:37 +0000 (17:50 +0200)]
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.

6 weeks agoMEDIUM: sym: store a compressed symbol table in a loadable section
Olivier Houchard [Wed, 16 Sep 2026 14:10:08 +0000 (16:10 +0200)]
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.

6 weeks agoIMPORT: xz: import the xz-embedded decoder
Olivier Houchard [Tue, 15 Sep 2026 14:04:29 +0000 (16:04 +0200)]
IMPORT: xz: import the xz-embedded decoder

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.

6 weeks agoMINOR: sym: add helpers to collect the symbols of an ELF image
Olivier Houchard [Tue, 15 Sep 2026 15:48:50 +0000 (17:48 +0200)]
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.

6 weeks agoMINOR: sym: collect the symbols known to the dynamic linker
Olivier Houchard [Tue, 15 Sep 2026 13:07:53 +0000 (15:07 +0200)]
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.

6 weeks agoMINOR: sym: add an in-process symbol table for backtraces
Olivier Houchard [Tue, 15 Sep 2026 13:03:31 +0000 (15:03 +0200)]
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.

6 weeks agoCLEANUP: sym: move symbol resolution out of tools.c
Olivier Houchard [Thu, 24 Sep 2026 15:26:03 +0000 (17:26 +0200)]
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.

6 weeks ago[RELEASE] Released version 3.5-dev8 v3.5-dev8
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

6 weeks agoREGTESTS: vars: Add a test for the set-vars-from-map action
Remi Tricot-Le Breton [Thu, 1 Oct 2026 12:33:28 +0000 (14:33 +0200)]
REGTESTS: vars: Add a test for the set-vars-from-map action

Test the new "set-vars-from-map" action with different input types and
with valid and invalid values.

6 weeks agoMEDIUM: vars: Add the set-vars-from-map action
Remi Tricot-Le Breton [Thu, 1 Oct 2026 12:33:27 +0000 (14:33 +0200)]
MEDIUM: vars: Add 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.

This was GH issue #1619.

6 weeks agoMINOR: vars: Add 'var_name_is_valid' helper function
Remi Tricot-Le Breton [Thu, 1 Oct 2026 12:33:26 +0000 (14:33 +0200)]
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 '.'.

6 weeks agoMINOR: vars: Add a helper to parse a variable scope name
Remi Tricot-Le Breton [Thu, 1 Oct 2026 12:33:25 +0000 (14:33 +0200)]
MINOR: vars: Add a helper to parse a variable scope name

Add var_parse_scope_str() which resolves a scope name (req, res, txn,
proc, sess or check) to its SCOPE_* value.

6 weeks agoREGTESTS: http-rules: one connection per request in the replace-uri test
Willy Tarreau [Tue, 22 Sep 2026 22:53:53 +0000 (22:53 +0000)]
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.

6 weeks agoREGTESTS: http-messaging: make the /c5 abort land during the retries
Willy Tarreau [Tue, 22 Sep 2026 22:50:12 +0000 (22:50 +0000)]
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.

6 weeks agoREGTESTS: converter: serve the two sha2 requests on one connection
Willy Tarreau [Tue, 22 Sep 2026 22:50:12 +0000 (22:50 +0000)]
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.

6 weeks agoREGTESTS: http-messaging: give each frontend its own syslog server
Willy Tarreau [Tue, 22 Sep 2026 22:16:40 +0000 (22:16 +0000)]
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.

6 weeks agoREGTESTS: log: serve the transaction state test on a single connection
Willy Tarreau [Tue, 22 Sep 2026 22:16:40 +0000 (22:16 +0000)]
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:

    00000000:be.srvrep[003b:003a]: HTTP/1.1 200 OK
    00000000:be.srvcls[003b:003a]
    00000001:fe.clireq[003b:ffffffff]: GET /err HTTP/1.1
    00000001:be.srvcls[003b:003a]
    00000001:be.clicls[003b:003a]

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.

6 weeks agoREGTESTS: http-messaging: widen the abort window of the abortonclose test
Willy Tarreau [Tue, 22 Sep 2026 21:53:49 +0000 (21:53 +0000)]
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.

6 weeks agoREGTESTS: sample_fetches: make the raw hello test parallel-safe
Willy Tarreau [Tue, 22 Sep 2026 21:47:33 +0000 (21:47 +0000)]
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.

6 weeks agoREGTESTS: sample_fetches: do not race with the server close in proxy_state
Willy Tarreau [Tue, 22 Sep 2026 21:15:20 +0000 (21:15 +0000)]
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.

6 weeks agoREGTESTS: http-messaging: check that a truncated response loses no byte
Willy Tarreau [Tue, 22 Sep 2026 21:09:47 +0000 (21:09 +0000)]
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.

6 weeks agoREGTESTS: http-messaging: test the option http-no-delay
Willy Tarreau [Tue, 22 Sep 2026 20:39:21 +0000 (20:39 +0000)]
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.

6 weeks agoREGTESTS: http-messaging: test the option httpclose
Willy Tarreau [Tue, 22 Sep 2026 20:39:11 +0000 (20:39 +0000)]
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.

6 weeks agoREGTESTS: ssl: test the wait-for-handshake action and fc.timer.handshake
Willy Tarreau [Tue, 22 Sep 2026 20:13:27 +0000 (20:13 +0000)]
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.

6 weeks agoREGTESTS: sample_fetches: test the stream state fetches over HTTP/2
Willy Tarreau [Tue, 22 Sep 2026 20:09:16 +0000 (20:09 +0000)]
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.

6 weeks agoREGTESTS: sample_fetches: test the request_date fetch
Willy Tarreau [Tue, 22 Sep 2026 20:09:16 +0000 (20:09 +0000)]
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.

6 weeks agoREGTESTS: sample_fetches: test res.body, res.body_len and server_status
Willy Tarreau [Tue, 22 Sep 2026 20:09:16 +0000 (20:09 +0000)]
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.