]> git.kaiwu.me - haproxy.git/log
haproxy.git
2 hours agoMEDIUM: ssl: add FIPS signature algorithm check for AWS-LC master
William Lallemand [Tue, 30 Jun 2026 13:39:14 +0000 (13:39 +0000)]
MEDIUM: ssl: add FIPS signature algorithm check for AWS-LC

Add ssl_fips_check_sigalgs() which validates the configured signature
algorithm list against the FIPS-approved set: ECDSA on NIST P-curves
with SHA-256/384/512, RSA-PSS (rsae and pss variants) with SHA-256/
384/512, and RSA-PKCS1 with SHA-256/384/512.  SHA-1 based algorithms
and non-FIPS primitives (ed25519, ed448) are rejected.

The check uses the same strchr-based string parsing as
ssl_fips_check_ciphersuites().  A NULL list is silently accepted since
the global defaults were already overwritten with FIPS values at init
time.

The check is called right after SSL_CTX_set1_sigalgs_list() and
SSL_CTX_set1_client_sigalgs_list() in both the bind
(ssl_sock_prepare_ctx) and server (ssl_sock_prepare_srv_ssl_ctx)
configuration paths.

2 hours agoMEDIUM: ssl: set FIPS-approved sigalgs defaults for AWS-LC FIPS builds
William Lallemand [Tue, 30 Jun 2026 13:37:27 +0000 (13:37 +0000)]
MEDIUM: ssl: set FIPS-approved sigalgs defaults for AWS-LC FIPS builds

When AWS-LC is built in FIPS mode, unconditionally override the
compile-time signature algorithm defaults with the FIPS-approved set
before config parsing. Explicit ssl-default-{bind,server}-sigalgs
keywords in the global section still take precedence over these
defaults.

The approved set is defined as macros in include/haproxy/defaults.h
alongside the existing CONNECT/LISTEN_DEFAULT_FIPS_CIPHERS family:
  CONNECT/LISTEN_DEFAULT_FIPS_SIGALGS        - ECDSA (P-256/384/521),
                                               RSA-PSS and RSA-PKCS1
                                               with SHA-256/384/512
  CONNECT/LISTEN_DEFAULT_FIPS_CLIENT_SIGALGS - same set for client
                                               certificate sigalgs

SHA-1 based algorithms and non-FIPS primitives (ed25519, ed448) are
excluded from the defaults.

2 hours agoMEDIUM: ssl: add FIPS elliptic curve check for AWS-LC
William Lallemand [Tue, 30 Jun 2026 13:28:03 +0000 (13:28 +0000)]
MEDIUM: ssl: add FIPS elliptic curve check for AWS-LC

Add ssl_fips_check_curves() which validates the configured curve list
against the FIPS-approved NIST P-curves (P-256, P-384, P-521).  Each
colon-separated name is resolved to a NID via OBJ_txt2nid() so all
standard aliases (P-256, prime256v1, secp256r1) are handled uniformly.
A NULL list is silently accepted since the global defaults were already
overwritten with FIPS values at init time.

The check is called right after SSL_CTX_set1_curves_list() in both the
bind (ssl_sock_prepare_ctx) and server (ssl_sock_prepare_srv_ssl_ctx)
configuration paths.

2 hours agoMEDIUM: ssl: set FIPS-approved curve defaults for AWS-LC FIPS builds
William Lallemand [Tue, 30 Jun 2026 13:26:19 +0000 (13:26 +0000)]
MEDIUM: ssl: set FIPS-approved curve defaults for AWS-LC FIPS builds

When AWS-LC is built in FIPS mode, unconditionally override the
compile-time curve defaults with the FIPS-approved NIST P-curves
before config parsing. Explicit ssl-default-{bind,server}-curves
keywords in the global section still take precedence over these
defaults.

The approved set is defined as macros in include/haproxy/defaults.h
alongside the existing CONNECT/LISTEN_DEFAULT_FIPS_CIPHERS family:
  CONNECT/LISTEN_DEFAULT_FIPS_CURVES - P-256, P-384, P-521

This ensures that internal servers (httpclient, Lua SSL sockets) that
inherit global defaults also operate with FIPS-compliant curve lists
without requiring explicit configuration.

2 hours agoMEDIUM: ssl: add FIPS TLS 1.3 ciphersuite check for AWS-LC
William Lallemand [Tue, 30 Jun 2026 13:14:53 +0000 (13:14 +0000)]
MEDIUM: ssl: add FIPS TLS 1.3 ciphersuite check for AWS-LC

AWS-LC does not expose TLS 1.3 ciphersuites set via SSL_CTX_set_ciphersuites()
through SSL_CTX_get_ciphers(), so the existing NID-based cipher check in
ssl_fips_check_ciphers() cannot catch non-FIPS TLS 1.3 suites.  This is
further compounded by a defect in the AWS-LC-FIPS 3.x branch where TLS 1.3
ciphers are missing from SSL_get_ciphers() entirely (fixed in
https://github.com/aws/aws-lc/pull/2092), making any SSL_CTX-based
inspection unreliable across versions.

Add ssl_fips_check_ciphersuites() which validates the ciphersuite string
directly against a FIPS-approved allowlist (TLS_AES_128_GCM_SHA256 and
TLS_AES_256_GCM_SHA384).  A NULL string is silently accepted since the
global defaults were already overwritten with FIPS values at init time.

The new check is called right after SSL_CTX_set_ciphersuites() in both
the bind (ssl_sock_prepare_ctx) and server (ssl_sock_prepare_srv_ssl_ctx)
configuration paths.

2 hours agoMEDIUM: ssl: set FIPS-approved cipher defaults for AWS-LC FIPS builds
William Lallemand [Tue, 30 Jun 2026 12:44:45 +0000 (12:44 +0000)]
MEDIUM: ssl: set FIPS-approved cipher defaults for AWS-LC FIPS builds

When AWS-LC is built in FIPS mode, unconditionally override the
compile-time cipher defaults with FIPS-approved sets before config
parsing. Explicit ssl-default-{bind,server}-ciphers{suites} keywords
in the global section still take precedence over these defaults.

The approved sets are defined as macros in include/haproxy/defaults.h
alongside the existing CONNECT/LISTEN_DEFAULT_CIPHERS family:
  CONNECT/LISTEN_DEFAULT_FIPS_CIPHERS     - AES-128-GCM-SHA256 and
                                            AES-256-GCM-SHA384 (TLS 1.2)
  CONNECT/LISTEN_DEFAULT_FIPS_CIPHERSUITES - TLS_AES_128_GCM_SHA256 and
                                             TLS_AES_256_GCM_SHA384 (TLS 1.3)

This ensures internal servers (httpclient, Lua SSL sockets) that
inherit global defaults also operate with FIPS-compliant cipher lists
without requiring explicit configuration.

2 hours agoMEDIUM: ssl: add FIPS TLS 1.2 cipher check for AWS-LC
William Lallemand [Tue, 30 Jun 2026 12:39:50 +0000 (12:39 +0000)]
MEDIUM: ssl: add FIPS TLS 1.2 cipher check for AWS-LC

Add ssl_fips_check_ciphers() to check the cipher list already loaded
into an SSL_CTX against the FIPS-approved bulk cipher NID allowlist
(AES-128-GCM, AES-256-GCM).  The check is run unconditionally after
SSL_CTX_set_cipher_list() for both bind and server contexts.

The function reuses the fips_obj_info() helper introduced with the
version check to emit precise 'type proxy/name [file:line]' error
messages and returns ERR_ALERT|ERR_ABORT|ERR_FATAL on violation.

2 hours agoMEDIUM: ssl: introduce src/fips.c with TLS version check
William Lallemand [Tue, 30 Jun 2026 12:38:31 +0000 (12:38 +0000)]
MEDIUM: ssl: introduce src/fips.c with TLS version check

Add src/fips.c and include/haproxy/fips.h to centralise FIPS compliance
checks for AWS-LC builds.

ssl_fips_check_version() verifies that ssl-min-ver is not set below
TLS 1.2, which AWS-LC in FIPS mode would refuse to negotiate.

The check accepts an enum obj_type * so it can resolve the owning
bind/server and produce a precise error message including the proxy
and object name in the standard HAProxy 'type proxy/name' form.

5 hours agoMEDIUM: cache: add support for hints-only HTTP caches
Maxime Henrion [Tue, 30 Jun 2026 15:33:37 +0000 (11:33 -0400)]
MEDIUM: cache: add support for hints-only HTTP caches

The "early-hints" option can now be set to "only", in which case the
cache will only store data relevant to the emission of 103 Early Hints
responses.

Hint records are extracted on the fly from the live response HTX
rather than going through a full-storage-then-strip cycle: the entry
is created as CACHE_EF_STRIPPED directly, with hint records written
past the cache_entry header at storage time. Response bodies are
never stored.

Because entries are stripped from the start, eviction in this mode
never has to strip a full entry; the full LRU stays empty and
cache_make_room() falls through to evicting the oldest stripped entry
when the cache fills up.

The max-object-size check (which gates caching based on the upstream-
announced Content-Length) is bypassed in this mode: the actual stored
entry is just the hint records (typically under 1 KB), so applying it
to the body size would needlessly discard hints for arbitrarily large
responses.

As a hints-only entry is never served as a response, the backend
response is forwarded unchanged in this mode: in particular its Age
header is kept rather than stripped as it is for a stored response.

5 hours agoMINOR: cache: factor cache_extract_link_hints out of cache_extract_hints
Maxime Henrion [Tue, 30 Jun 2026 15:32:50 +0000 (11:32 -0400)]
MINOR: cache: factor cache_extract_link_hints out of cache_extract_hints

Move the per-Link-value processing into a new cache_extract_link_hints()
helper. cache_extract_hints() continues to walk the cached row's HDR
blocks and now invokes the helper once per Link header it finds.

No behaviour change. This prepares the helper for reuse by an upcoming
hints-only storage path that extracts Link headers directly from the
live response HTX rather than from a cached row.

5 hours agoREGTESTS: cache: validate the emission of 103s
Maxime Henrion [Tue, 2 Jun 2026 13:08:23 +0000 (09:08 -0400)]
REGTESTS: cache: validate the emission of 103s

Add four regtests to validate the behavior of the HTTP cache when early
hints are enabled.

- early_hints.vtc: covers 103 emission on stripped entries and that the
  "no-early-hints" rule keyword suppresses it.
- early_hints_ratio.vtc: covers cap-driven eviction of hints entries when
  the ratio is reached.
- early_hints_parsing.vtc: pins down the (deliberately strict) Link
  header parsing behaviour of link_is_hint() so future relaxations
  cannot silently change hint detection.
- early_hints_multiblock.vtc: checks that hints are still extracted when
  the relevant Link headers span more than one cache block.

5 hours agoMINOR: cache: allow customizing ratio for early hints
Maxime Henrion [Mon, 1 Jun 2026 21:12:21 +0000 (17:12 -0400)]
MINOR: cache: allow customizing ratio for early hints

It is now possible to give a "ratio" parameter to the "early-hints"
option to indicate what percentage of blocks to reserve for hints-only
entries. Defaults to 25%.

5 hours agoMINOR: cache: allow opting out of early hints at the rule level
Maxime Henrion [Fri, 26 Jun 2026 20:45:23 +0000 (16:45 -0400)]
MINOR: cache: allow opting out of early hints at the rule level

A "cache-use" directive can now specify the "no-early-hints" keyword
to suppress 103 Early Hints emission for matching requests, when used
with a cache that has the "early-hints" setting enabled.

5 hours agoMINOR: cache: add a counter for cache hits serving early hints
Maxime Henrion [Tue, 7 Jul 2026 13:10:59 +0000 (09:10 -0400)]
MINOR: cache: add a counter for cache hits serving early hints

Add a cache_hint_hits counter to the per-proxy HTTP stats, incremented
each time the cache emits a 103 Early Hints response. It is reported
in the CSV/typed outputs, on the stats page next to the cache hits,
and exported to prometheus as http_cache_hint_hits_total. Since the
new field changes the shm stats file mapping, its minor version is
bumped along with the object size assertion.

5 hours agoMEDIUM: cache: emit early hints if configured to do so
Maxime Henrion [Fri, 26 Jun 2026 20:44:50 +0000 (16:44 -0400)]
MEDIUM: cache: emit early hints if configured to do so

The HTTP cache now supports sending 103 Early Hints responses if
enabled, without the need for explicit per-rule configuration.

Two paths feed the 103 response depending on the matched entry. A
stripped entry (CACHE_EF_STRIPPED) carries its Link records
pre-extracted alongside the cache entry header and is copied out of
the row as-is. A complete entry only carries the cached HTX; the
Link headers are walked on the fly at request time, deliberately
avoiding the extra storage cost of duplicating them alongside the
body.

The on-the-fly path has a cost: a workload where many hits land on
expired complete entries re-parses the cached response on each hit.
To bound this, the first such hit also strips the entry in place
when conditions allow (the hints pool is below its cap, and the row
is not currently being read by another request); subsequent hits for
the same URL then take the cheap pre-extracted path.

To let the cache-use lookup reach that logic, get_entry() now returns
expired entries to callers passing delete_expired=0; such callers are
responsible for checking entry->expire themselves.

5 hours agoMINOR: http: factor 103 emission into start/end helpers
Maxime Henrion [Fri, 26 Jun 2026 20:43:12 +0000 (16:43 -0400)]
MINOR: http: factor 103 emission into start/end helpers

Pull the 103 emission code out of http_action_early_hint() and split
it into http_early_hint_start() (open the response and return the htx)
and http_early_hint_end() (close with EOH and forward). The split
shape lets callers add their own Link headers in between, which is
what the HTTP cache will need when it gets support for sending 103
Early Hints from cached responses.

5 hours agoMINOR: cache: indicate whether entries are stripped or not
Maxime Henrion [Tue, 26 May 2026 19:53:40 +0000 (15:53 -0400)]
MINOR: cache: indicate whether entries are stripped or not

A full cache entry now shows "type:full", whereas stripped entries used
to emit 103 responses show "type:hints".

5 hours agoMEDIUM: cache: early hints-aware eviction code
Maxime Henrion [Tue, 30 Jun 2026 15:06:12 +0000 (11:06 -0400)]
MEDIUM: cache: early hints-aware eviction code

We now use the make_room callback from shctx to customize the eviction
procedure. We attempt to balance eviction between hints and full-blown
entries by capping the number of blocks used by hints entries to 25% of
the size of the cache. Upon eviction of a full entry, we first try to
strip it to a hints-only entry if possible.

A Link header's value (which may carry multiple comma-separated links)
is limited to 1024 bytes for hint extraction. Longer values are skipped
entirely and contribute no hint records.

5 hours agoMINOR: cache: track full and hints entries in per-pool LRU lists
Maxime Henrion [Tue, 30 Jun 2026 15:05:19 +0000 (11:05 -0400)]
MINOR: cache: track full and hints entries in per-pool LRU lists

Introduce two LRU lists in struct cache (full_lru and hints_lru) and an
"lru" link in struct cache_entry, plus a CACHE_EF_STRIPPED flag to
mark hints-only entries. A new cache_row_reattach() helper places each
entry in the appropriate LRU when a row is returned to the cold pool.

No behaviour change yet: the lists are populated but no code consumes
them. The next commit uses them to drive early hints-aware eviction.

5 hours agoMINOR: shctx: allow consumers to customize eviction strategy
Maxime Henrion [Fri, 26 Jun 2026 20:40:26 +0000 (16:40 -0400)]
MINOR: shctx: allow consumers to customize eviction strategy

This introduces a make_room() callback that consumers can provide in
order to implement more specific eviction strategies. To be used in the
HTTP cache code. This goes in hand with a new shctx_row_truncate()
function that allows consumers to truncate a row, which can be used in
the make_room() callback.

5 hours agoMINOR: cache: add helper functions for early hints support
Maxime Henrion [Tue, 26 May 2026 16:57:36 +0000 (12:57 -0400)]
MINOR: cache: add helper functions for early hints support

Add the hint_rels array, rel_is_hint(), and link_is_hint() functions
for identifying Link header values eligible for early hints emission.

5 hours agoMINOR: cache: add config options for early hints support
Maxime Henrion [Thu, 28 May 2026 20:41:02 +0000 (16:41 -0400)]
MINOR: cache: add config options for early hints support

Support for 103 early hints responses can now be enabled at the HTTP
cache level with "early-hints on" - defaults to off.

5 hours agoMINOR: cache: minor changes ahead of support for sending early hints
Maxime Henrion [Tue, 26 May 2026 13:53:59 +0000 (09:53 -0400)]
MINOR: cache: minor changes ahead of support for sending early hints

Add a flags field to struct cache_entry and replace the complete
field with a CACHE_EF_COMPLETE flag.

Likewise, add a flags field to struct cache and replace the
vary_processing_enabled configuration boolean with a
CACHE_CF_VARY_PROCESSING flag.

5 hours agoMINOR: http: add two header parsing functions
Maxime Henrion [Sat, 23 May 2026 01:18:47 +0000 (21:18 -0400)]
MINOR: http: add two header parsing functions

Add http_next_hdr_value() to iterate over the comma-separated values
that some headers contain. Add http_get_hdr_param() to iterate over the
';'-separated parameters that may follow a value.

Currently unused but will be used for parsing Link headers in the cache
to support 103 Early Hints, and could also clean up Cache-Control
parsing among others.

5 hours agoBUG/MEDIUM: cache: reattach the row when a secondary entry is incomplete
Maxime Henrion [Fri, 10 Jul 2026 00:46:42 +0000 (20:46 -0400)]
BUG/MEDIUM: cache: reattach the row when a secondary entry is incomplete

In http_action_req_cache_use() with Vary, the row of the secondary entry
matching the request is detached from the avail list for reading. Since
get_secondary_entry() only rejects expired entries, that secondary may
be incomplete (still being written by another stream), and in that case
the request was forwarded without reattaching the row, leaking its
blocks out of the avail list (refcount and nbav never restored). Over a
Vary workload this eventually prevents the cache from reserving any row.

Reattach the row before returning, as the other paths that give up on
the cache entry do.

Present since the Vary support was added in 2.4 (1785f3dd9); to be
backported as far as 2.6.

5 hours agoDOC: stats: document the per-proxy byte count fields in the CSV list
Maxime Henrion [Tue, 7 Jul 2026 14:26:28 +0000 (10:26 -0400)]
DOC: stats: document the per-proxy byte count fields in the CSV list

Fields 114 to 117 (reqbin, reqbout, resbin, resbout) were added to the
CSV output but never listed in the stats description of management.txt.

This should be backported up to 3.3.

5 hours agoMINOR: shctx: clamp shctx_row_data_get() reads against the offset
Maxime Henrion [Thu, 9 Jul 2026 16:08:33 +0000 (12:08 -0400)]
MINOR: shctx: clamp shctx_row_data_get() reads against the offset

The length clamp capped the request to the whole row length but ignored
<offset>, so a read starting past the beginning of the row could run
beyond the stored data and copy stale bytes from the reserved tail of
its blocks. No current caller reads past first->len so this was never
triggered, but the helper is public and its contract does not forbid it.

Clamp against (first->len - offset) instead, returning early when the
offset is already at or past the stored data.

This may be backported along with the shctx_row_data_get() offset fix.

5 hours agoBUG/MINOR: shctx: fix shctx_row_data_get() when offset exceeds a block
Maxime Henrion [Wed, 8 Jul 2026 16:37:07 +0000 (12:37 -0400)]
BUG/MINOR: shctx: fix shctx_row_data_get() when offset exceeds a block

The loop advanced the block pointer only after the copy, but skipped
blocks preceding <offset> with a continue, which bypassed that advance.
As a result any read starting past the first block copied nothing.

This notably affects conditional requests: should_send_notmodified_response()
reads the stored ETag at entry->etag_offset, so a cached response whose
ETag header sits beyond the first block fails revalidation and is served
in full (200) instead of 304.

Rewrite the loop as a for() so the block pointer always advances,
regardless of whether the current block is copied or skipped.

This should be backported as far as 2.6.

5 hours agoBUG/MEDIUM: stats: subject "stats admin" accesses to "stats scope" filtering
Willy Tarreau [Fri, 10 Jul 2026 09:27:37 +0000 (11:27 +0200)]
BUG/MEDIUM: stats: subject "stats admin" accesses to "stats scope" filtering

It was reported by Red Hat in partnership with AISLE Research that
proxies updated via a stats page in "stats admin" mode were not subject
to the filtering of "stats scope".

It is totally true and was never meant to be when "stats scope" was
added in 1.2 in 2006 while "stats admin" arrived 5 years later in 1.4,
nor was it even implied by the doc, but it is easy to see how someone
who would combine the two could expect them to work together:

- "stats scope" only limits which proxies are shown, the purpose being
  to avoid polluting the page with a long list of auxiliary proxies that
  take ages to render (e.g. those dedictated to stick-tables etc). A
  common use case is to only show known frontends so that numerous
  backends are not listed, it's most often used on public access.

- "stats admin" is used to permit to change a proxy or a server's
  state and is protected using HTTP Basic auth, cleartext passwords
  by default, and is mostly meant to be used from trusted sources,
  hence private access.

The two are almost never seen together (no single occurrence found
in github issues for 92 occurrences of "stats admin" and 5 of
"stats scope"), but admittedly, if someone were to combine the two,
seeing buttons on the listed proxies only, they would surely expect
to only be able to act on those.

So in order to clear this void, this commit does the following upon
a POST:
  - when a scope is defined, the requested proxy must exist and match
    the list of permitted ones, otherwise a DENY is returned. This
    ensures that the POST cannot be abused either to infer the existence
    of a given proxy name.
  - when no scope is defined, a non-existing proxy continues to return
    an "unknown proxy" error.

The doc was updated to explain this new behavior. This commit should
be backported to stable versions, along with the previous patch it
depends on:

   MINOR: stats: factor the proxy vs scope check into its own function

The stats code was split between 2.9 and 3.0, so the changes must all
be performed in stats.c before 3.0. Also, ctx->http_px does not exist
earlier, but http_px is simply the calling stream's backend (s->be at
some places).

Thanks to Red Hat and AISLE Research for reporting this.

5 hours agoMINOR: stats: factor the proxy vs scope check into its own function
Willy Tarreau [Fri, 10 Jul 2026 08:45:07 +0000 (10:45 +0200)]
MINOR: stats: factor the proxy vs scope check into its own function

We'll need to check for matching between a proxy and a stats scope at
a few more places, so let's first move the code out of its current
function (stats_dump_proxy_to_buffer) into its own function
(stats_proxy_in_scope). At this point it doesn't change anything.
Note that this will have to be backported as this will be used with
a subsequent fix.

9 hours agoBUG/MEDIUM: applet: Reenable reads in applet context if requesting a connection
Christopher Faulet [Fri, 10 Jul 2026 09:53:33 +0000 (11:53 +0200)]
BUG/MEDIUM: applet: Reenable reads in applet context if requesting a connection

When a applet is waiting for a connection, like Socket applet in lua, reads
are blocked. When the connection is finally available , a read activity is
reported and reads are reenabled (flag SE_FL_APPLET_NEED_CONN is removed).

However, most of time (always ?), this operation is performed in the context
of the connection and the applet is woken up. It means the task expiration
date is not updated where the read activity is reported. It is an issue for
small timeouts because the task may be queued in past, triggering a BUG_ON
in sc_notify(). In fact, a read activity must never be reported in another
context of the endpoint itself or the upper stream, specifically to properly
set the task expiration date.

To fix the issue, in sc_chk_rcv(), the applet is only woken up in that case
and the reads are re-enabled when the applet is executed. For applets using
the new API, it is performed in sc_appet_sync_recv(). For legacy applets, it
is directly performed in task_run_applet().

This patch is related to #3442. It must be backported as far as 2.8. On the
2.8, only task_run_applet must be fixed because sc_applet_sync_recv() does
not exist.

9 hours agoBUG/MINOR: hlua: Apply socket timeout on server side only
Christopher Faulet [Fri, 10 Jul 2026 09:49:32 +0000 (11:49 +0200)]
BUG/MINOR: hlua: Apply socket timeout on server side only

In lua, the timeout that can be set on an Socket applet was applied on
client side and server side. In fact, it must only be applied on server
side, to apply a timeout on the connection. The client side is the applet.

This patch should fix the issue #3442. It must be backported to all
supported versions.

29 hours agoBUILD: task: Fix build when no 8B CAS is available at all
Olivier Houchard [Thu, 9 Jul 2026 14:10:17 +0000 (16:10 +0200)]
BUILD: task: Fix build when no 8B CAS is available at all

clang (rightfully) complains that when neither HA_CAS_IS_8B nor
HA_HAVE_CAS_DW is defined, there is a mismatch when we're doing a CAS,
as we're mixing unsigned int and int.
To fix that, just turn old_state to an unsigned int, as it should be.

This should fix github issue #3441

2 days agoBUILD: haload: Increase a buffer size so that gcc will stop complaining
Olivier Houchard [Wed, 8 Jul 2026 17:13:10 +0000 (19:13 +0200)]
BUILD: haload: Increase a buffer size so that gcc will stop complaining

gcc 16 whines that in human_number(), the calls to snprintf() could lead
to a truncated output because the buffer is not big enough. This is not
really true, because we always have a limited number of digits, but gcc
can't figure that out, so just bump the buffer size from 5 bytes to 8
bytes to make gcc happy.

2 days ago[RELEASE] Released version 3.5-dev2 v3.5-dev2
Willy Tarreau [Wed, 8 Jul 2026 16:26:54 +0000 (18:26 +0200)]
[RELEASE] Released version 3.5-dev2

Released version 3.5-dev2 with the following main changes :
    - MINOR: proxy: permit to report version info for option deprecation
    - MAJOR: proxy: remove support for "dispatch" and "transparent" proxy keywords
    - MEDIUM: cli/show-fd: no longer accept filtering for dispatch mode
    - CLEANUP: connection: remove some checks for objt_proxy(conn->target)
    - CLEANUP: backend: drop checks for OBJ_TYPE_PROXY in connect() code
    - CLEANUP: trace: remove backend retrieval attempt from conn->target
    - MAJOR: ot: remove deprecated OpenTracing support
    - BUG/MEDIUM: h3: fix trace crash on frontend response headers
    - CI: github: remove OpenTracing leftovers
    - BUG/MEDIUM: mux_quic: fix memory leak of rx app_buf on stream free
    - MINOR: ssl: export ssl_sock_init_srv()
    - MEDIUM: httpclient: initialize the httpclient with default SSL values
    - BUG/MINOR: hq-interop: fix transcoding of wrapping response buffer
    - BUG/MINOR: hq-interop: support transcoding of absolute URI
    - BUG/MINOR: h3: adjust HTTP headers traces
    - MINOR: mux_quic: add minimal traces for QUIC MUX init/release
    - MINOR: hq-interop: add request start-line traces
    - MINOR: hq-interop: trace transcoding of response status line
    - MINOR: hq-interop: trace HTX headers
    - BUG/MEDIUM: server: initialise agent.health in srv_settings_init()
    - BUG/MINOR: sample: set SMP_F_CONST on srv_name fetch
    - MINOR: server: distinguish name references with new SRV_F_NAME_REFD flag
    - MEDIUM: server: add 'set server name' CLI command for runtime server renaming
    - REGTESTS: server: add test for 'set server name' CLI command
    - DOC: server: document 'set server name' CLI command
    - BUG/MEDIUM: servers: Use a refcount for port_range and free it properly
    - MINOR: hbuf: new lightweight hbuf API
    - MINOR: init: add no listener mode
    - MINOR: trace: add definitions for haload streams
    - MINOR: hldstream: add definition of hldstream struct objects
    - MINOR: obj_type: add OBJ_TYPE_HALOAD for haload stream objects
    - MINOR: stconn: add sc_hastream() and __sc_hastream() helpers
    - MINOR: stconn: export sc_new()
    - MINOR: server: export functions used during server initialization
    - MINOR: haload: import source code and documentation
    - MINOR: log: add app_log_raw() and send_log_raw() for binary-safe logging
    - BUG/MINOR: init: fix default global settings being overwritten by -G
    - BUG/MINOR: tools: fix invalid character detection in strl2ic()
    - BUG/MAJOR: htx: Don't swap buffers for empty HTX message with an error
    - CLEANUP: applet/http-client: Don't needlessly copy HTX flags after htx_xfer()
    - BUG/MINOR: mux-quic: Fix handling EOM after in qcs_http_rcv_buf()
    - BUG/MINOR: http-htx: Don't by-pass HTX API when merging cookie values
    - BUG/MEDIUM: h3: fix parser desync on error with multiple frames
    - BUG/MINOR: mux_quic: prevent multiple STOP_SENDING emission per stream
    - BUILD: quic: workaround a gcc bug saying "maybe used uninitialized" when USE_TRACE=0
    - CLEANUP: traces: get rid of a few rare empty args in TRACE calls
    - MINOR: compiler: add a macro to ignore all arguments
    - MINOR: trace: always pretend to use args when disabled
    - BUILD: ssl: avoid a wrong null deref warning in ssl_sock_handshake
    - CLEANUP: haproxy: remove -dt parsing and help when !USE_TRACE
    - CLEANUP: mux-h2/traces: remove unused trace code when building without USE_TRACE
    - CLEANUP: debug/trace: remote "debug dev trace" when USE_TRACE is not set
    - BUG/MINOR: trace/quic_frame: use buf, not trace_buf in chunk_frm_appendf()
    - CLEANUP: trace/h3: allow to disable traces in H3
    - CLEANUP: trace/config: do not register section "traces" with USE_TRACE=0
    - CLEANUP: trace/tree-wide: drop trace decoding/definition when USE_TRACE=0
    - BUILD: makefile: only build trace.c and ssl_trace.c when USE_TRACE is set
    - BUILD: makefile: add macros enable_opts and disable_opts
    - BUILD: makefile: add an option to enable or disable HTTP/2 (USE_H2)
    - BUILD: makefile: add an option to enable or disable FCGI (USE_FCGI)
    - BUILD: makefile: add an option to enable or disable SPOE (USE_SPOE)
    - BUILD: makefile: add a new generic target "tiny"
    - BUG/MEDIUM: mux_quic: do not free QCS if STOP_SENDING to sent
    - MINOR: mux_quic: use separate error code for STOP_SENDING
    - MINOR: mux_quic: adjust shut stream callback
    - BUG/MEDIUM: mux_quic: complete stream shutdown for read channel
    - BUG/MINOR: quic: ignore STREAM after MUX closure on BE side
    - BUG/MEDIUM: fd: Fix a deadlock when closing other tgroups fds
    - MEDIUM: quic: remove deprecated keywords
    - DEV: patchbot: keep the review start in sync with the radios on reload
    - DEV: patchbot: only display the first 8 chars of the commit id
    - DEV: patchbot: pass the branch version to the generated page
    - DEV: patchbot: let the page fetch the shared review state
    - DEV: patchbot: let the page save the review edits to the server
    - DEV: patchbot: let the page edit and delete whole notes
    - DEV: patchbot: gray the save button when there is nothing to save
    - DEV: patchbot: don't pretend a save succeeded when the server ignored it
    - DEV: patchbot: tolerate polluted save responses and show server warnings
    - DEV: patchbot: repeat the syncing buttons at the bottom of the page
    - DEV: patchbot: update: add an awk backend to persist review edits
    - DEV: patchbot: update: return the stored overlay as JSON on GET
    - DEV: patchbot: update: support replacing a whole note blob (setnotes)
    - DEV: patchbot: update: report git commit failures in the save response
    - DEV: patchbot: update: report the exact git error to the user
    - DEV: patchbot: update: never write to stderr, thttpd sends it first
    - DEV: patchbot: update: name the touched commits in the storage messages
    - DEV: patchbot: document the shared review persistence
    - BUG/MINOR: haload: fix spurious task wakeup in hld_strm_task()
    - BUG/MINOR: hbuf: treat unexpected escape sequences as literals
    - BUG/MEDIUM: tcpcheck: Add proxy used for healthcheck sections in proxies list
    - BUG/MINOR: sample: Fix a possible underflow on be2hex for large chunk size
    - MINOR: chunks: Add function to get a large/regular chunk depending on a buffer
    - BUG/MEDIUM: chunk: Review chunks usage to not retrieve a large buffer by error
    - MINOR: htx: Add a field to save the headers data size
    - MEDIUM: htx: Be sure size of headers never exceed regular buffer on update
    - BUG/MINOR: stream: Fix custom timeouts initialization when setting backend
    - REGTESTS: Improve script testing the set-timeout action
    - BUG/MINOR: stream: Fix custom max-retries initialization when setting backend
    - MINOR: stream: Add be_max_retries/cur_max_retries sample fetch functions
    - BUG/MINOR: http-conv: Make url-dec failed if no space for trailing null byte
    - MAJOR: mworker: remove deprecated "master-worker" global keyword
    - DOC: haterm: add a missing 'haterm' build target on an example
    - DOC: readme: add a pointer to haterm/haload docs
    - MINOR: ocsp: Do not see ocsp loading failures as fatal anymore
    - REGTESTS: Remove unused `add_range_to_test_list` function from `scripts/run-regtests.sh`
    - REGTESTS: Remove unused `_version` function from `scripts/run-regtests.sh`
    - REGTESTS: Migrate REQUIRE_OPTION to `haproxy -cc`
    - REGTESTS: Remove support for `REQUIRE_OPTION` from scripts/run-regtests.sh
    - REGTESTS: Migrate `REQUIRE_SERVICE=prometheus-exporter` to a `feature(PROMEX)` check
    - REGTESTS: Remove support for `REQUIRE_SERVICE` from scripts/run-regtests.sh
    - CI: github: update vmactions/freebsd-vm to 14.4
    - MINOR: haload: move statistics header printing to mtask_cb
    - MINOR: haterm: add note about QUIC usage on SSL port
    - MINOR: haload: allow "0" shortcut for IPv4 bind address

2 days agoMINOR: haload: allow "0" shortcut for IPv4 bind address
Frederic Lecaille [Wed, 8 Jul 2026 15:34:30 +0000 (17:34 +0200)]
MINOR: haload: allow "0" shortcut for IPv4 bind address

The address parser was failing when using "0" as a shortcut for INADDR_ANY.
This patch intercepts the "0" prefix and normalizes it to "0.0.0.0"
before the address is parsed.

2 days agoMINOR: haterm: add note about QUIC usage on SSL port
Frederic Lecaille [Wed, 8 Jul 2026 15:19:31 +0000 (17:19 +0200)]
MINOR: haterm: add note about QUIC usage on SSL port

Update the usage message to clarify that QUIC is automatically enabled
on the SSL port when supported by the build.

Should be backported to 3.4.

2 days agoMINOR: haload: move statistics header printing to mtask_cb
Frederic Lecaille [Wed, 8 Jul 2026 15:14:54 +0000 (17:14 +0200)]
MINOR: haload: move statistics header printing to mtask_cb

Move the header printing logic from hld_init to mtask_cb using a static flag.
This ensures the header is displayed only when the main task starts
processing, avoiding issues where the header might be printed before other
haproxy initialization messages or unexpected early exits.

2 days agoCI: github: update vmactions/freebsd-vm to 14.4
William Lallemand [Wed, 8 Jul 2026 15:06:00 +0000 (17:06 +0200)]
CI: github: update vmactions/freebsd-vm to 14.4

Update the freebsd image to 14.4

2 days agoREGTESTS: Remove support for `REQUIRE_SERVICE` from scripts/run-regtests.sh
Tim Duesterhus [Fri, 3 Jul 2026 21:02:37 +0000 (23:02 +0200)]
REGTESTS: Remove support for `REQUIRE_SERVICE` from scripts/run-regtests.sh

This is no longer used in tests and thus can be removed from the test runner.

2 days agoREGTESTS: Migrate `REQUIRE_SERVICE=prometheus-exporter` to a `feature(PROMEX)` check
Tim Duesterhus [Fri, 3 Jul 2026 21:02:36 +0000 (23:02 +0200)]
REGTESTS: Migrate `REQUIRE_SERVICE=prometheus-exporter` to a `feature(PROMEX)` check

The prometheus exporter is a first class citizen now. This keeps consistency
with the other tests.

2 days agoREGTESTS: Remove support for `REQUIRE_OPTION` from scripts/run-regtests.sh
Tim Duesterhus [Fri, 3 Jul 2026 21:02:35 +0000 (23:02 +0200)]
REGTESTS: Remove support for `REQUIRE_OPTION` from scripts/run-regtests.sh

This is no longer used in tests and thus can be removed from the test runner.

2 days agoREGTESTS: Migrate REQUIRE_OPTION to `haproxy -cc`
Tim Duesterhus [Fri, 3 Jul 2026 21:02:34 +0000 (23:02 +0200)]
REGTESTS: Migrate REQUIRE_OPTION to `haproxy -cc`

With the EOL of HAProxy 2.4 all supported versions of HAProxy support `haproxy
-cc`, allowing us to unify the tests in this regard.

2 days agoREGTESTS: Remove unused `_version` function from `scripts/run-regtests.sh`
Tim Duesterhus [Fri, 3 Jul 2026 21:02:33 +0000 (23:02 +0200)]
REGTESTS: Remove unused `_version` function from `scripts/run-regtests.sh`

This function is no longer used since the support for `REQUIRE_VERSION` was
removed in 8ee8b8a04deaa2689262715957dc72ab7fe50ccb.

2 days agoREGTESTS: Remove unused `add_range_to_test_list` function from `scripts/run-regtests.sh`
Tim Duesterhus [Fri, 3 Jul 2026 21:02:32 +0000 (23:02 +0200)]
REGTESTS: Remove unused `add_range_to_test_list` function from `scripts/run-regtests.sh`

This function is unused since dc1a3bd99972c7f6ab7462ce05fb95ad1f651862 which
removed the `LEVEL` option.

2 days agoMINOR: ocsp: Do not see ocsp loading failures as fatal anymore
Remi Tricot-Le Breton [Thu, 25 Jun 2026 13:39:18 +0000 (15:39 +0200)]
MINOR: ocsp: Do not see ocsp loading failures as fatal anymore

Until now, if the call to 'ssl_sock_load_ocsp' raised an error, because
of a missing issuer certificate for instance, we would send a fatal
error code and the init would fail.
With this patch we only consider this as a non-critical error and we
will send a warning instead. In such a case the OCSP response will not
be loaded and stapling or auto update features will not work for the
certificate.

2 days agoDOC: readme: add a pointer to haterm/haload docs
Willy Tarreau [Wed, 8 Jul 2026 12:46:06 +0000 (14:46 +0200)]
DOC: readme: add a pointer to haterm/haload docs

People landing on the page seeking for help on how to build haterm/haload
need to have a direct entry on this page, so let's add a line pointing to
both docs.

2 days agoDOC: haterm: add a missing 'haterm' build target on an example
Willy Tarreau [Wed, 8 Jul 2026 12:43:07 +0000 (14:43 +0200)]
DOC: haterm: add a missing 'haterm' build target on an example

An example build command line was missing "haterm", resulting in building
haproxy instead of haterm. May be backported to 3.4 though not important.

2 days agoMAJOR: mworker: remove deprecated "master-worker" global keyword
Willy Tarreau [Tue, 30 Jun 2026 08:49:54 +0000 (10:49 +0200)]
MAJOR: mworker: remove deprecated "master-worker" global keyword

This keyword, when alone, was already deprecated in 3.3 and marked
for removal in 3.5. Now using it results in an error message inviting
to start haproxy with -W or -Ws instead.

In addition, "master-worker exit-on-failure", which used to automatically
enable the master-worker mode, now doesn't set it and complains if used
without. -W/-Ws. A special case was made for config checks though (-c).

Also worth noting that "daemon" applies to master as well as max-reloads,
and will soon need to be adjusted.

2 days agoBUG/MINOR: http-conv: Make url-dec failed if no space for trailing null byte
Christopher Faulet [Tue, 7 Jul 2026 09:54:05 +0000 (11:54 +0200)]
BUG/MINOR: http-conv: Make url-dec failed if no space for trailing null byte

for url-dec converter, a trailing null byte is added at the end of the input
sample because it is requested by url_decode() function. However, when the
buffer was full, the last byte was crushed by the trailing null byte. In
that case, the last character was lost and not decoded.

Now, the converter just fails by returning 0.

This patch could be backported to all supported versions but it is a very
minor issue.

2 days agoMINOR: stream: Add be_max_retries/cur_max_retries sample fetch functions
Christopher Faulet [Tue, 7 Jul 2026 08:36:45 +0000 (10:36 +0200)]
MINOR: stream: Add be_max_retries/cur_max_retries sample fetch functions

These new samples can be used to get, resepectively, the backend value for
the maximum connection retries and the current value applied to the stream.
They could be used to know if a "set-retries" action was performed.

A dedicated reg-test was also added.

2 days agoBUG/MINOR: stream: Fix custom max-retries initialization when setting backend
Christopher Faulet [Tue, 7 Jul 2026 08:07:31 +0000 (10:07 +0200)]
BUG/MINOR: stream: Fix custom max-retries initialization when setting backend

It is possible to overwrite the configured max-retries value with the
"set-retries" action. However there is an issue with the listeners. When the
backend is set, this value is reset with the backend value. However, when
the backend is unchanged, the custom value must be preserved.

To fix the issue, when the stream is created, we set the max-retries to the
listener value. It remains 0 for pure-frontend. And the backend value is
used only if it is not the same proxy.

The documentation of set-retries action was improved.

This patch should be backported as far as 3.2.

2 days agoREGTESTS: Improve script testing the set-timeout action
Christopher Faulet [Tue, 7 Jul 2026 08:04:33 +0000 (10:04 +0200)]
REGTESTS: Improve script testing the set-timeout action

First, instead of using syslog to test the result, http-after-response rules
are now used. It is a bit simple. In addition, all custom timeouts are
tested. Some testcase were useless and were thus removed.

In addition, the script was moved in "reg-tests/http-rules/" directory.

2 days agoBUG/MINOR: stream: Fix custom timeouts initialization when setting backend
Christopher Faulet [Tue, 7 Jul 2026 07:03:46 +0000 (09:03 +0200)]
BUG/MINOR: stream: Fix custom timeouts initialization when setting backend

Timeouts may be overwritten via the action "set-timeout". Client timeout is
supported on frontends, connect/queue/tunnel/server timeouts are supported
on backends and tarpit timeout is supported on both.

However, there is an issue with the listener. In that case, the action is
always executed in the frontend context and the backend-side timeouts are
then reset, when the backend is selected. If it is another backend, it is
logical. But most of time, the backend is unchanged. And in that case, these
timeouts must be preserved.

Documentation of set-timeout action was improved.

This patch should be backported to all supported versions. Be carefull, all
timeouts are not supported in 3.3 and lower (3.3->3.0: server/tunnel/client
- 2.8->2.6: server/tunnel)

2 days agoMEDIUM: htx: Be sure size of headers never exceed regular buffer on update
Christopher Faulet [Mon, 29 Jun 2026 16:33:02 +0000 (18:33 +0200)]
MEDIUM: htx: Be sure size of headers never exceed regular buffer on update

When an HTX header or an HTX start-line is added or updated, a test is now
performed to be sure the whole headers size, including the start-line never
exceeds the regular buffer size. It is mandatory because it is now possible
to use larger buffers. However, it remains impossible to send HEADERS frame
in H2 and QUIC larger than a regular buffer. So we must be sure the HTTP
analysis will never produce too big headers because they will be rejected
later, at the forwarding stage. The purpose of large buffers is to be able
to store large payload, larger than regular buffers. Headers must remain
quite small.

This commit depends on the following one:

  MINOR: htx: Add a field to save the headers data size

Both should be backported to 3.4 to avoid any trouble with large buffers. It
is not strictly speaking a bug, but it will avoid hazardous behavior
depending on the payload size.

2 days agoMINOR: htx: Add a field to save the headers data size
Christopher Faulet [Mon, 22 Jun 2026 16:36:45 +0000 (18:36 +0200)]
MINOR: htx: Add a field to save the headers data size

The size of headers present in an HTX message are now counted. A dedicated
field was added in the HTX structure to do so. This patch is quite simple
but it will be mandatory to be able to perform some tests on headers. One of
them is to be sure headers never exceed the regular buffer size, even when a
large buffer is used.

2 days agoBUG/MEDIUM: chunk: Review chunks usage to not retrieve a large buffer by error
Christopher Faulet [Mon, 6 Jul 2026 14:34:24 +0000 (16:34 +0200)]
BUG/MEDIUM: chunk: Review chunks usage to not retrieve a large buffer by error

All calls to get_trash_chunk_sz() and alloc_trash_chunk_sz() were reviewed
to be sure we never retrieve a large chunk when it is not expected. Some
calls were not upgrade, but several calls now rely on get_best_trash_chunk()
and alloc_best_trash_chunk() functions.

The idea of the fix is to get a large buffer only when the original buffer
is already a large buffer and the expected size of data exceeds the size of
a regular buffer. This should prevent unexpected memory usage.

This commit relies on the previous one:

  MINOR: chunk: Add function to get a large/regular chunk depending on a buffer

Both must be backported to 3.4.

2 days agoMINOR: chunks: Add function to get a large/regular chunk depending on a buffer
Christopher Faulet [Thu, 2 Jul 2026 14:51:21 +0000 (16:51 +0200)]
MINOR: chunks: Add function to get a large/regular chunk depending on a buffer

get_best_trash_chunk() function was added to be able to get a large or a
regular chunk depending on a given size but never larget than a given
buffer. It will be usefull in a futur fix, to prevent unexpected large chunk
usage.

alloc_best_trash_chunk() function is similar but instead of returning one of
the static chunks, it allocate it from the corresponding pool, large or
regular.

2 days agoBUG/MINOR: sample: Fix a possible underflow on be2hex for large chunk size
Christopher Faulet [Tue, 7 Jul 2026 12:13:49 +0000 (14:13 +0200)]
BUG/MINOR: sample: Fix a possible underflow on be2hex for large chunk size

For a chunk size exactly equal to bufsize/2, the "max_size" variable could
underflow if a separator is provided. Indeed, "max_size" is first set to
(trash->size - 2 * chunk_size), so to 0. And on the first iteration, the
separator length is removed, making it to underflow.

On 3.3 and lower it is especially an issue with samples larger than
bufsize/2 because data could be written ouside of the buffer. On 3.4 and
3.5, it is not an issue because we fail to retrieve a chunk.

This should be backported to all supported versions.

2 days agoBUG/MEDIUM: tcpcheck: Add proxy used for healthcheck sections in proxies list
Christopher Faulet [Tue, 7 Jul 2026 17:21:04 +0000 (19:21 +0200)]
BUG/MEDIUM: tcpcheck: Add proxy used for healthcheck sections in proxies list

The proxy used to parse healthcheck sections was not inserted in the proxies
list. So its initialization was not properly finalized. Among other things,
it was an issue for some arguments, like regular expressions.

This proxy is now inserted in the proxies list.

This patch should fix the issue #3440. It must be backported to 3.4.

2 days agoBUG/MINOR: hbuf: treat unexpected escape sequences as literals
Frederic Lecaille [Wed, 8 Jul 2026 06:24:19 +0000 (08:24 +0200)]
BUG/MINOR: hbuf: treat unexpected escape sequences as literals

When encountering an unexpected escape sequence, treat it as a literal
character instead of skipping it.

This is a deliberate choice for two reasons:

- to avoid a desynchronization between the h->data counter and the
  buffer content, which would otherwise leave uninitialized memory
  ("garbage") in the destination buffer;

- to ensure that an invalid configuration string triggers a parsing
  error immediately (fail-fast), rather than resulting in a silently
  malformed configuration.

Thank to @dirkmueller for having reported this issue.

No need to backport. huf arrived with this current version.

2 days agoBUG/MINOR: haload: fix spurious task wakeup in hld_strm_task()
Frederic Lecaille [Tue, 7 Jul 2026 14:00:47 +0000 (16:00 +0200)]
BUG/MINOR: haload: fix spurious task wakeup in hld_strm_task()

When a stream is freed, hld_strm_task() was unconditionally re-queuing
the user task, even when no streams were left or when no update was
needed. This caused spurious task wakeups, which could incorrectly inflate
connection and request rate counters to come for the -R option implementation.

Only queue the user task if there are remaining streams to process, and
properly update the expiration time.

No need to backport. haload arrived with this current version.

3 days agoDEV: patchbot: document the shared review persistence
Willy Tarreau [Mon, 6 Jul 2026 14:32:41 +0000 (16:32 +0200)]
DEV: patchbot: document the shared review persistence

Update the README to cover the new syncing feature: the deployment
section explains the three optional pieces and their relations. We
also explain the special cases of "Get updates" and "Save updates"
when combined with pending/conflicting changes (particularly in
edition mode).

An explanation of the difficulties to set git permission is also
provided.

3 days agoDEV: patchbot: update: name the touched commits in the storage messages
Willy Tarreau [Mon, 6 Jul 2026 21:13:29 +0000 (23:13 +0200)]
DEV: patchbot: update: name the touched commits in the storage messages

Every save used to be committed as a bare "update 3.5", which makes the
storage history useless to navigate. The subject now names the branch
and the first commit whose review was touched, followed by "+ N more"
when several, and the body lists all of them one per line:

    update 3.5: 6a7b27a0 + 2 more

    6a7b27a0
    d13aaf05
    b12dd0b5

This is what to grep for when hand-editing the storage repository, for
example to locate the change to revert or rebase. A commit touched by
several directives of the same save (state plus notes) is only listed
once.

3 days agoDEV: patchbot: update: never write to stderr, thttpd sends it first
Willy Tarreau [Mon, 6 Jul 2026 21:13:29 +0000 (23:13 +0200)]
DEV: patchbot: update: never write to stderr, thttpd sends it first

thttpd forwards the CGI's stderr to the client *ahead* of its stdout:
the socket receives the HTTP status line, then any stderr log line, and
only then the CGI headers, turning the log line into a bogus response
header; on an error path the same mechanism could push garbage in front
of the "Status:" header and corrupt the response entirely.

All diagnostics are already carried by the response itself (die()'s
body, the in-band "warning:" line with git's captured error), so the
duplicated stderr logging brings nothing and only risks breaking the
channel it leaks into: drop it, and state the constraint in a comment
above die() so it doesn't come back. The usage text for a bad command
line is folded into the 500 response body, which is also what a shell
user sees when testing by hand.

3 days agoDEV: patchbot: update: report the exact git error to the user
Willy Tarreau [Mon, 6 Jul 2026 21:13:29 +0000 (23:13 +0200)]
DEV: patchbot: update: report the exact git error to the user

The "git commit failed" warning said nothing about the cause, leaving
the admin to guess between a missing identity, an ownership refusal, a
git binary absent from the restricted PATH the web server gives to its
CGIs, etc. run_git() now captures the command's stdout and stderr
through a pipe and the warning line carries git's own message (first
255 bytes, control chars flattened), so the admin directly sees the
cause; as the command runs through /bin/sh, an unfindable git yields
status 127 and the shell's message, reworded as "cannot execute git:
..." to directly point at the typical PATH issue. Capturing also
guarantees that git output can never corrupt the CGI response nor leak
to the client on servers which wire the CGI's stderr to the socket.

3 days agoDEV: patchbot: update: report git commit failures in the save response
Willy Tarreau [Mon, 6 Jul 2026 21:13:10 +0000 (23:13 +0200)]
DEV: patchbot: update: report git commit failures in the save response

When the git commit fails after a save (typically a missing committer
identity in the storage repository, or an ownership/permission issue),
the failure was only logged to stderr, which lands in the web server's
error log at best: the file kept being updated but the history silently
stopped being recorded. Report it as a "warning: git commit failed ..."
line appended to the response, where the page can show it to the user,
on top of the stderr log. Also stop treating a no-op as a failure:
re-pushing identical content stages nothing, so the commit is now
simply skipped when "git diff --cached --quiet" reports no staged
change, instead of letting "git commit" fail on an empty commit.

3 days agoDEV: patchbot: update: support replacing a whole note blob (setnotes)
Willy Tarreau [Mon, 6 Jul 2026 21:13:10 +0000 (23:13 +0200)]
DEV: patchbot: update: support replacing a whole note blob (setnotes)

Notes are append-only on the wire, which makes concurrent edits
conflict-free but leaves no way to revise or clean up a note from the
page: fixing a note requires a hand-edit of the storage file. This adds
the replacement directive that the design had reserved:

    <cid> setnotes <hash> <replacement text>

Unlike the other directives, a replacement must carry a token of the
base it was computed from, or it could silently destroy a concurrent
update (one reviewer's append landing between another's read and
replace). The token is the SDBM hash (8 hex chars) of the note blob
the client based its edit on: the server only applies the replacement
if it still matches the stored blob (empty-string hash for a commit
without notes). On mismatch the directive is dropped, the line is left
exactly as found, and a "conflict <cid>" line is emitted in the
response before the resulting lines so that the client can point the
user at what needs manual reconciliation; other directives from the
same POST are still applied, and nothing is written nor committed when
everything conflicted. SDBM is trivially computed on both sides (a
concurrency token, not a security feature, and JS crypto is unavailable
on plain http anyway), and its small multiplier keeps the whole hash
computation exact in awk's double-precision arithmetic, which is
precisely why it was chosen over wider-multiplier hashes.

An empty replacement deletes the notes (and the line if no state is
left), finally allowing obsolete notes to be removed without editing
the file by hand. Replacements are capped to 4000 chars instead of the
500-char append cap, since a coalesced blob may legitimately have grown
beyond a single addition.

3 days agoDEV: patchbot: update: return the stored overlay as JSON on GET
Willy Tarreau [Mon, 6 Jul 2026 21:12:22 +0000 (23:12 +0200)]
DEV: patchbot: update: return the stored overlay as JSON on GET

This adds the read side of the review persistence CGI: GET
update.cgi?branch=X.Y now returns the current overlay for that branch
as a JSON array of {"cid", "state", "notes"} objects with absent fields
omitted; a missing or empty file yields "[]". The raw storage format
never travels: the notes are unescaped by the parser and JSON-escaped
on output, so the client can JSON.parse() the response and insert the
notes via textContent directly. Unparseable lines or fields are
silently skipped as everywhere else.

Reads are lockless: the atomic rename on the write side guarantees
that the file is always a complete valid version. The response carries
Cache-Control: no-store so that a browser never reuses a stale overlay
on refresh.

3 days agoDEV: patchbot: update: add an awk backend to persist review edits
Willy Tarreau [Mon, 6 Jul 2026 21:11:47 +0000 (23:11 +0200)]
DEV: patchbot: update: add an awk backend to persist review edits

The backport review page keeps the human edits (verdict overrides and
notes) only in the loaded DOM: they are lost on reload and never shared
between reviewers. This adds the server side of the shared persistence
design: update.awk, a GNU awk CGI script which stores these edits into
one file per major branch (e.g. "3.5") inside a dedicated git
repository, one line per touched commit:

    <commit_id> [state <n|u|w|y>] [notes "<quoted notes>"]

The overlay only ever stores human edits keyed on the commit id; the AI
verdict and explanation stay in the generated HTML. Commit ids are
length-agnostic and matched by symmetric prefix (first match wins), so
the current 8-char pipeline and a future 12-char one both work without
any migration.

POST applies line-oriented directives ("<cid> state <n|u|w|y|revert>",
"<cid> notes <text to append>"), none of which carries a base value:
states are last-write-wins and notes are append-only (capped to 500
chars per push and sanitised so that no newline may ever enter a stored
line), which keeps concurrent edits conflict-free. Broken directives,
fields or lines are silently ignored, never fatal, and lines not being
modified are preserved byte-for-byte so that admin hand-edits survive.
Writers are serialised by a mkdir lock at an obvious place (<repo>/lock)
with PID-gated crash takeover via atomic rename, the file itself is
replaced by an atomic rename from a temp file inside the lock dir, and
every resulting state is committed to git, which acts as the event log
(git blame/log -L provide the full history). A crash at any point leaves
at worst a stale lock (reclaimed on the next write) or a valid but
uncommitted tree (folded into the next commit), never a broken file.

The takeover races are covered: the staleness decision and the takeover
rename are not one atomic operation, so the thief verifies after the
rename, discarding the stolen dir only if it still carries the pid that
was judged dead and renaming it back in place untouched otherwise; the
victim redoes the whole locked cycle from a fresh read when its final
rename fails, since nothing was applied to the branch file yet; a writer
finding its own pid in the lock adopts it as stale; and the release only
removes the lock after checking that it still contains our own pid.

A few awk specifics are worth noting: external commands (git, mkdir,
mv) go through /bin/sh, so everything interpolated into a command line
is shell-quoted (single-quote is escaped and the argument placed inside
single quotes); the -b (bytes) flag keeps all string operations byte-
based regardless of the locale; and a manual argv parsing due to gawk
silently consumes a leading "-r" argument, ignoring ours.

Also note that gawk uses a file cache for getline() and co, which opens
lots of traps so we need to be extremely careful about properly closing
files if we want to check for changes (e.g. lock's pid file).

Finally, writing through a redirection whose target cannot be opened is
a fatal awk error terminating the script without even a response, so the
writes into the (stealable) lock dir are arranged to resist a takeover:
the pid is written through the shell, where a vanished dir is a plain
command failure, and the temp file is opened the very instant the lock
is acquired, its descriptor surviving a later theft. The update.cgi
wrapper considers any >0 return code as a failure and returns a generic
error as it will indicate that the awk script itself couldn't produce
a valid response.

For now, only the POST ("save changes") action is implemented.

3 days agoDEV: patchbot: repeat the syncing buttons at the bottom of the page
Willy Tarreau [Mon, 6 Jul 2026 18:45:22 +0000 (20:45 +0200)]
DEV: patchbot: repeat the syncing buttons at the bottom of the page

The "Get updates" and "Save changes" buttons only existed at the top
right of the page, while a review session ends at the bottom of the
table: with no button left in sight there, it was way too easy to
forget to save the work. Emit a copy of both buttons and of the status
line at the bottom right, sharing the same handlers; the status
message and the save-button graying are applied to both instances at
once so the two spots always tell the same story.

3 days agoDEV: patchbot: tolerate polluted save responses and show server warnings
Willy Tarreau [Mon, 6 Jul 2026 17:30:15 +0000 (19:30 +0200)]
DEV: patchbot: tolerate polluted save responses and show server warnings

Some server setups leak the CGI's stderr into the response body (e.g.
inetd-style servers where fd 2 is the client socket): since stderr is
unbuffered and stdout is buffered, a git error message then lands
*before* the "OK <n>" line, and the applied-count check failed with a
cryptic "server applied only ? of N changes" although everything had
been applied. Scan the whole response for the lines of interest (the
"OK <n>" count, the "conflict" and "warning" lines) instead of assuming
they come first, dump the raw response to the console when no count is
found at all to ease diagnosis, and display the server's warning lines
(such as the new "git commit failed" one) next to the save status so
that a recording problem is visible from the page instead of being
buried in a server log.

3 days agoDEV: patchbot: don't pretend a save succeeded when the server ignored it
Willy Tarreau [Mon, 6 Jul 2026 17:17:48 +0000 (19:17 +0200)]
DEV: patchbot: don't pretend a save succeeded when the server ignored it

The save handler treated any HTTP 200 as a full success and advanced
the local reference for everything it had sent, only special-casing the
reported conflicts. But the server legitimately drops directives it
cannot parse, and answers "OK <n> directives applied" with what it
really did. The typical case is an outdated update scripton the server
which ignores the whole "setnotes" directive, applies nothing, and
the client still displayed the edit as saved... until the next "Get
updates" reverted it.

Let's count the directives sent, and when the server's applied count
plus the reported conflicts don't add up, believe the server, not
ourselves: advance nothing, keep every edit local (boxes open, save
button lit) and tell the user how many changes were ignored,
suggesting a version mismatch.

3 days agoDEV: patchbot: gray the save button when there is nothing to save
Willy Tarreau [Mon, 6 Jul 2026 16:55:56 +0000 (18:55 +0200)]
DEV: patchbot: gray the save button when there is nothing to save

The "Save changes" button used to remain active all the time, giving no
hint about whether anything was pending. It is now disabled whenever
nothing differs from the reference: no verdict change, no non-empty
note addition, no note edition differing from its base. It gets
re-evaluated after every action which may change that (verdict clicks,
typing in a note input, opening/cancelling a box, updates and saves),
bailing out at the first pending change so the common case stays cheap.

As a side effect, the button lighting up right after a reload confirms
at a glance that the browser restored unsaved local edits.

3 days agoDEV: patchbot: let the page edit and delete whole notes
Willy Tarreau [Mon, 6 Jul 2026 16:53:41 +0000 (18:53 +0200)]
DEV: patchbot: let the page edit and delete whole notes

An "[edit note]" link now appears next to "[add note]" whenever a line
has shared notes: it presents the whole note blob in the input box for
edition, and the save sends it as a replacement (the "setnotes"
directive, carrying the hash of the blob the edit was based on).
Emptying the box deletes the note. Clicking "add note" first and then
"edit note" merges the reference notes with the pending addition so
nothing typed so far is lost, and a "[cancel]" link aborts an edition
opened by mistake without touching anything.

An open input only disappears once its content is synchronized with the
reference: a successful save, an update proving an exact match, or an
explicit cancel. To that end, "Get updates" first silently closes the
no-op boxes (opened but nothing changed), then after applying the
fetched state it closes the boxes it made redundant (an addition
someone already pushed, an edition matching the current notes), and
re-bases an edition whose base moved, turning it red: red uniformly
means "the reference changed under your edit, review the notes above
against your text before saving". A save refused by the server (the
"conflict <cid>" response lines) turns the input red the same way,
keeping it in edition; states and additions from the same save are
unaffected. After a reload, a browser-restored pending note reopens in
append mode, the only safe assumption since a lost replacement base
cannot be recovered.

3 days agoDEV: patchbot: let the page save the review edits to the server
Willy Tarreau [Mon, 6 Jul 2026 14:29:32 +0000 (16:29 +0200)]
DEV: patchbot: let the page save the review edits to the server

This is the write side of the review syncing: a "Save changes" button
next to "Get updates" collects the local edits and pushes them to
update.cgi. An edit is a radio button change differing from the
reference state (so clicking around and coming back to the reference
sends nothing), or a note typed in the per-line input revealed by the
new "[add note]" link under the AI explanation (500 chars max, matching
the server-side cap).

No directive carries a base value: states are last-write-wins and notes
are append-only server-side, so two reviewers saving concurrently
cannot conflict. On success the reference advances to the pushed values
and the note inputs are cleared, so the page is clean without needing a
refetch; on error (server busy or unreachable) everything stays local
and a later click simply retries.

3 days agoDEV: patchbot: let the page fetch the shared review state
Willy Tarreau [Mon, 6 Jul 2026 14:26:45 +0000 (16:26 +0200)]
DEV: patchbot: let the page fetch the shared review state

This adds the read side of the review syncing to the generated page: a
"Get updates" button at the top right retrieves the shared state from
update.cgi (reached by a bare relative URL, so it must be in a cgi-bin
directory next to the page) and applies it. Nothing is fetched
automatically, not even at load time: it's up to the user to explicitly
click to resynchronize, and without it (or with the server down) the
page keeps behaving fully standalone as today.

Three states exist per line to make this work. The original state is
the verdict the bot chose, captured at load time and constant. The
reference state is the last known shared state, on top of which the
user's edits sit; it starts equal to the original. The local state is
the DOM itself (the checked radios). Applying a fetched overlay
recomputes every line's reference as "the server's entry if any,
otherwise the bot's verdict", so a removed override properly falls back
to the original; the reference always advances but the displayed state
only moves where the user had no local edit: local edits win, and
re-applying the same overlay twice changes nothing.

The received commit ids are resolved exactly first, then by symmetric
prefix (one id being a prefix of the other, for mixed-length ids), first
line wins. The shared notes land in a dedicated container below the AI
explanation, rendered via innerText (no HTML injection) and replaced
wholesale so the operation stays idempotent; entries for commits absent
from the page are simply ignored.

The whole exchange was tested with the real generated page's scripts
running against a stubbed DOM covering load capture, prefix resolution,
adopt-vs-keep on both changed and disappeared entries, idempotent
re-application, and silent degradation on fetch failure.

3 days agoDEV: patchbot: pass the branch version to the generated page
Willy Tarreau [Mon, 6 Jul 2026 14:20:50 +0000 (16:20 +0200)]
DEV: patchbot: pass the branch version to the generated page

The review page will need to exchange its state with the update.cgi
service sitting next to it, and for this it must know which branch it
covers since all branches' pages may share a directory. post-ai.sh
takes a new "-v <version>" argument and emits it as a "branch" JS
variable; when absent the variable is empty and the page will simply
not offer syncing, keeping the output standalone as today.

update-3.0.sh deduces the version from its own name (update-3.0.sh ->
3.0), so that adding a symlink with another version for a new branch
continues to work with no other change, and passes it to post-ai.sh.

3 days agoDEV: patchbot: only display the first 8 chars of the commit id
Willy Tarreau [Mon, 6 Jul 2026 14:21:33 +0000 (16:21 +0200)]
DEV: patchbot: only display the first 8 chars of the commit id

The commit id column doesn't need to show more than 8 chars to stay
unambiguous within a single page, and longer ids needlessly widen the
table. Everything that is keyed on the id (the commit link, the row's
name= attribute and the cid[] JS array) keeps the full id produced by
the pipeline, whatever its length, so this is a display-only change
which also gets us ready for a possible future move of the pipeline to
longer ids.

3 days agoDEV: patchbot: keep the review start in sync with the radios on reload
Willy Tarreau [Mon, 6 Jul 2026 18:33:03 +0000 (20:33 +0200)]
DEV: patchbot: keep the review start in sync with the radios on reload

When the page is reloaded, the browser restores the "review" radio
column to the user's last selection (e.g. "All"), but the "review" JS
variable is regenerated to the default first line to review: the
listing then restarts from that line while "All" still appears
selected, and one has to click a random line then "All" again to
really see everything.

Give an id to each line's review radio and resynchronize the variable
from the actually checked radio when the page loads: a restored "All"
(or any restored line) now behaves as selected, and on a fresh load the
checked radio is the generated one so nothing changes.

4 days agoMEDIUM: quic: remove deprecated keywords quic-interop
Amaury Denoyelle [Mon, 6 Jul 2026 14:35:39 +0000 (16:35 +0200)]
MEDIUM: quic: remove deprecated keywords

Several QUIC related keywords were removed in 3.4. The legacy options
were marked as deprecated and scheduled for removal in 3.5. This patch
applies this removal for the upcoming 3.5.

4 days agoBUG/MEDIUM: fd: Fix a deadlock when closing other tgroups fds
Olivier Houchard [Mon, 6 Jul 2026 13:36:08 +0000 (15:36 +0200)]
BUG/MEDIUM: fd: Fix a deadlock when closing other tgroups fds

Commit 061754b249b9903913d6766c1ab31bb393ee5c0d attempted to make it
possible to close file descriptors belonging to other thread groups by
using thread isolation.
The problem is, closing other thread groups' fds usually happens when we
destroy a listener, in which case we hold the listener lock. If any
other thread tries to get that lock while we're waiting for the thread
isolation, then they will deadlock.
This can happen with any type of listener, but it is easier to reproduce
with a suspend/resume loop with ABNS sockets, as a suspend translates to
a close here.
To fix that, instead of using thread isolation, do something similar to
what's done when the fd belongs to our thread group. Increase the tgid
ref counter, so that we're sure nobody will close the fd while we're
dealing with it, then set the FD_MUST_CLOSE bit and set thread_mask to
0.
At this point, if no thread was running on that fd, no one will and we
can safely close it. So just call _fd_delete_orphan() if the
running_mask is 0 and if the FD_MUST_CLOSE bit is still there, otherwise
we can safely assume another thread will take care of it.

This should be backported up to 2.8.

4 days agoBUG/MINOR: quic: ignore STREAM after MUX closure on BE side
Amaury Denoyelle [Wed, 17 Jun 2026 16:05:52 +0000 (18:05 +0200)]
BUG/MINOR: quic: ignore STREAM after MUX closure on BE side

For frontend connections, quic_conn layer is able to reject any new
streams opened after MUX closure. This is necessary as the peer may not
have been notified yet of the closure.

This operation is unnecessary on backend side. This is due to the fact
that only HTTP protocols are currently supported on top of QUIC, with
requests initiated by the client. For requests started before the MUX
closure, either they are already completed or closed early with a
STOP_SENDING emitted during stream shut.

Prior to this patch, spurrious RESET_STREAM could have been emitted on
backend connections after MUX closure as quic_conn stream_max_bidi was
not correctly set. Now reject is only performed for frontend connections
so this should not occured anymore.

This should be backported up to 3.3.

4 days agoBUG/MEDIUM: mux_quic: complete stream shutdown for read channel
Amaury Denoyelle [Wed, 17 Jun 2026 16:04:56 +0000 (18:04 +0200)]
BUG/MEDIUM: mux_quic: complete stream shutdown for read channel

Prior to this patch, shut stream callback only handles write channel
closure. In case of an early closure, a RESET_STREAM would be emitted.

On the frontend side in most cases this is sufficient as read channel is
already closed, as HTTP/3 GET requests has been fully received. However,
this may not be the case for POST requests. Also, on the backend side,
haproxy acts a client. In this case, a stream early closure will
typically happen before receiving the full response. Nothing will be
emitted (RESET_STREAM is unnecessary as write channel is already
closed), thus the server peer will continue to emit.

To fix this situation, the current patch implement read channel closure
on shut if SE_SHR_RESET is set. Callback lclose from app_ops is called
with a new dedicated mode for read channel closure, which will result in
a STOP_SENDING frame generated by H3 and hq transcoders. This will
instruct the peer to stop emission.

This should be backported up to 3.3. Note that this depends on the
following patch :
  dde3ee06c30f20091443bdafdda0e0294f7ac26b
  MINOR: mux_quic: use separate error code for STOP_SENDING

4 days agoMINOR: mux_quic: adjust shut stream callback
Amaury Denoyelle [Tue, 30 Jun 2026 16:29:50 +0000 (18:29 +0200)]
MINOR: mux_quic: adjust shut stream callback

Clean up MUX QUIC stream shut callback with a central invokation for
lclose app_ops. Also, traces are added at H3 layer to log the method of
closure.

4 days agoMINOR: mux_quic: use separate error code for STOP_SENDING
Amaury Denoyelle [Tue, 30 Jun 2026 16:29:07 +0000 (18:29 +0200)]
MINOR: mux_quic: use separate error code for STOP_SENDING

Prior to this patch, a single error code was registrable at the QCS
level. This code was used both for RESET_STREAM and STOP_SENDING
emission. It was specified via qcc_reset_stream().

This patch extends the API so that now a dedicated error code is
implemented for STOP_SENDING as well. This may be necessary as both
frames can be sent in different context, with a diverging error code.

This patch is required to implement STOP_SENDING emission during shut
callback when read channel is closed.

4 days agoBUG/MEDIUM: mux_quic: do not free QCS if STOP_SENDING to sent
Amaury Denoyelle [Fri, 26 Jun 2026 15:15:30 +0000 (17:15 +0200)]
BUG/MEDIUM: mux_quic: do not free QCS if STOP_SENDING to sent

When stream detach callback is called, the default behavior is to free
the associated QCS instance. However, QCS may be preserved in so-called
detached state if there is remaining data to sent.

This condition is checked via qcs_is_close_local() which ensures that
either FIN or a RESET_STREAM was emitted. However, this does not take
into account a scheduled STOP_SENDING emission, which can happen in case
of request abort for example.

Adjusts qcm_strm_detach() to also take into account STOP_SENDING
emission before freeing or keeping a detached QCS instance. As a
complement, QCS have to be purged after STOP_SENDING emission when
reaching completion.

On frontend side, this bug is probably only visible in case of HTTP/3
POST. When dealing with GET, FIN is most of the time received earlier,
which render STOP_SENDING unnecessary. This issue however has a bigger
impact on the backend side. In case of stream abort, for example on
timeout, the server may be left unnotified and will continue to emit
STREAM data despite QCS closure on haproxy client side.

Note that this fix also has a side effect on backend connection reuse.
Indeed it may increase the rate of QCS in detached state. This may
prevent an idle connection to be reinserted in the server pool, without
any possibility to reinsert it later. In the end this causes a lower
reuse rate. This is an issue which must be addressed in a dedicated
patch. For now, add a COUNT_IF_HOT() to report when such situation
occurs.

This should be backported to all stable releases, after a period of
observation. COUNT_IF_HOT() is unnecessary on 3.2 and below.

7 days agoBUILD: makefile: add a new generic target "tiny"
Willy Tarreau [Thu, 21 May 2026 07:26:03 +0000 (09:26 +0200)]
BUILD: makefile: add a new generic target "tiny"

This target disables all possible features except poll(). It is meant to
serve as a base for small embedded setups, on top of which one may manually
enable select features. Even threads, traces/h2/fcgi/SPOE are disabled.
The default executable is roughly 80% smaller than with linux-glibc:

  $ size haproxy-linux-glibc haproxy-tiny
     text    data     bss      dec    hex filename
  3660924  176964 9868784 13706672 d125b0 haproxy-linux-glibc
  2537864  146512   84928  2769304 2a4198 haproxy-tiny

With SSL enabled, the difference shrinks a bit (-77%):

  $ size haproxy-linux-glibc-ssl haproxy-tiny-ssl
     text    data     bss      dec    hex filename
  4163373  208788 9873904 14246065 d960b1 haproxy-linux-glibc-ssl
  2950852  177732   90048  3218632 311cc8 haproxy-tiny-ssl

7 days agoBUILD: makefile: add an option to enable or disable SPOE (USE_SPOE)
Willy Tarreau [Thu, 21 May 2026 12:35:21 +0000 (14:35 +0200)]
BUILD: makefile: add an option to enable or disable SPOE (USE_SPOE)

USE_SPOE is enabled by default and allows to disable SPOE when forced to
zero. It saves roughly 92kB on the executable.

7 days agoBUILD: makefile: add an option to enable or disable FCGI (USE_FCGI)
Willy Tarreau [Thu, 21 May 2026 12:33:56 +0000 (14:33 +0200)]
BUILD: makefile: add an option to enable or disable FCGI (USE_FCGI)

USE_FCGI is enabled by default and allows to disable FCGI when forced
to zero. It saves roughly 75kB on the executable.

7 days agoBUILD: makefile: add an option to enable or disable HTTP/2 (USE_H2)
Willy Tarreau [Thu, 21 May 2026 12:30:26 +0000 (14:30 +0200)]
BUILD: makefile: add an option to enable or disable HTTP/2 (USE_H2)

USE_H2 is enabled by default and allows to disable HTTP/2 when forced to
zero. It saves roughly 127kB on the executable.

7 days agoBUILD: makefile: add macros enable_opts and disable_opts
Willy Tarreau [Thu, 21 May 2026 07:24:45 +0000 (09:24 +0200)]
BUILD: makefile: add macros enable_opts and disable_opts

These ones are used to only enable or disable selected options.

7 days agoBUILD: makefile: only build trace.c and ssl_trace.c when USE_TRACE is set
Willy Tarreau [Wed, 1 Jul 2026 13:46:09 +0000 (15:46 +0200)]
BUILD: makefile: only build trace.c and ssl_trace.c when USE_TRACE is set

There's no point in building these ones anymore when traces are disabled,
nothing relies on them. This brings extra 28kB savings, resulting in 709kB
total savings when disabling traces.

7 days agoCLEANUP: trace/tree-wide: drop trace decoding/definition when USE_TRACE=0
Willy Tarreau [Wed, 1 Jul 2026 13:40:44 +0000 (15:40 +0200)]
CLEANUP: trace/tree-wide: drop trace decoding/definition when USE_TRACE=0

The various trace sources always have the same pattern:
  - trace events
  - trace source
  - trace decoding function

Dropping these when USE_TRACE=0 definitely makes sense. There are two
modes of definition here:
  - those designed after mux_h2 which interleave #define and the entry
    definition in the event. These ones cannot be removed without a
    significant code move to split the #define and usage apart. Instead
    here we mark the struct __maybe_unused, so that the compiler will
    just not implement it.

  - those designed like stream.c where defines are separated. Here we
    can simply enclose the events definition inside the USE_TRACE guard

For most of these the static declaration of the trace function was moved
after the events so that the #if defined(USE_TRACE) could be placed between
the two. Nothing else was changed.

This saves another 51 kB of object code when USE_TRACE=0.

7 days agoCLEANUP: trace/config: do not register section "traces" with USE_TRACE=0
Willy Tarreau [Wed, 1 Jul 2026 13:38:34 +0000 (15:38 +0200)]
CLEANUP: trace/config: do not register section "traces" with USE_TRACE=0

We don't want to register the "traces" section when traces are disabled.
Also this forces us to continue to build trace.c.

7 days agoCLEANUP: trace/h3: allow to disable traces in H3
Willy Tarreau [Wed, 1 Jul 2026 09:43:05 +0000 (11:43 +0200)]
CLEANUP: trace/h3: allow to disable traces in H3

It requires essentially a few ifdefs and to add a dummy definition of
h3_trace_header() to completely disable traces in H3. This reduces the
object code by 35 kB.

7 days agoBUG/MINOR: trace/quic_frame: use buf, not trace_buf in chunk_frm_appendf()
Willy Tarreau [Wed, 1 Jul 2026 09:30:13 +0000 (11:30 +0200)]
BUG/MINOR: trace/quic_frame: use buf, not trace_buf in chunk_frm_appendf()

The function takes a buffer in argument which is the target buffer. The
first calls properly use it but the subsequent ones, probably due to
reused/moved code, directly write into &trace_buf, thus ignoring the
buf argument. Fortunately all call places pass &trace_buf for buf, so
it currently has no impact but could possibly change.

No backport is needed, but it doesn't hurt to backport it if it helps.

7 days agoCLEANUP: debug/trace: remote "debug dev trace" when USE_TRACE is not set
Willy Tarreau [Wed, 1 Jul 2026 09:26:17 +0000 (11:26 +0200)]
CLEANUP: debug/trace: remote "debug dev trace" when USE_TRACE is not set

The functions associated with this command were already subject to
DEBUG_DEV, let's also add USE_TRACE for them to be defined. This saves
8kB.

7 days agoCLEANUP: mux-h2/traces: remove unused trace code when building without USE_TRACE
Willy Tarreau [Wed, 1 Jul 2026 09:22:06 +0000 (11:22 +0200)]
CLEANUP: mux-h2/traces: remove unused trace code when building without USE_TRACE

By just moving a few definitions, creating two dummy inline functions and
a few ifdefs, we can get rid of the entire trace generation code in the
H2 mux and save ~96 kB. This is what this patch does. Even the trace_h2
struct is removed in this case.

7 days agoCLEANUP: haproxy: remove -dt parsing and help when !USE_TRACE
Willy Tarreau [Wed, 1 Jul 2026 09:09:18 +0000 (11:09 +0200)]
CLEANUP: haproxy: remove -dt parsing and help when !USE_TRACE

Better not show -dt in the help message and stop parsing it when
USE_TRACE is disabled since traces won't work anyway.