]> git.ipfire.org Git - thirdparty/haproxy.git/log
thirdparty/haproxy.git
6 days agoMINOR: ssl: add TLS 1.2 values in HAPROXY_KEYLOG_XX_LOG_FMT
William Lallemand [Tue, 14 Apr 2026 13:45:25 +0000 (15:45 +0200)] 
MINOR: ssl: add TLS 1.2 values in HAPROXY_KEYLOG_XX_LOG_FMT

Add the CLIENT_RANDOM line for TLS1.2 in HAPROXY_KEYLOG_FC_LOG_FMT and
HAPROXY_KEY_LOG_BC_FMT. These are useful to produce a keylog file
compatible with both TLS1.3 and TLS1.2.

6 days agoBUG/MEDIUM: htx: Don't count delta twice when block value is replaced
Christopher Faulet [Mon, 13 Apr 2026 16:58:35 +0000 (18:58 +0200)] 
BUG/MEDIUM: htx: Don't count delta twice when block value is replaced

A regression was introduced by the commit a8887e55a ("BUG/MEDIUM: htx: Fix
function used to change part of a block value when defrag").

When a block value was replaced and a defragmentation was performed, the
delta between the old value and the new one was counted twice. htx_defrag()
already is responsible to set the new size for the HTX message. So it must
not be performed in htx_replace_blk_value().

This patch must be backported with the commit above. So theorically to all
stable versions.

6 days agoBUG/MEDIUM: htx: Properly handle block modification during defragmentation
Christopher Faulet [Mon, 13 Apr 2026 17:07:08 +0000 (19:07 +0200)] 
BUG/MEDIUM: htx: Properly handle block modification during defragmentation

A regression was introcuded by the commit 0c6f2207f ("MEDIUM: htx: Refactor
htx defragmentation to merge data blocks").

When a defragmentation is performed, it is possible to alter a block
size. The main usage is to prepare a block value replacement. However, since
the commit above, the change is no longer handled. The block info are
changed but the size of the message is not modified accordingly.

This patch depends on the commit "MINOR: htx: Add helper function to get
type and size from the block info field"

No backport needed.

6 days agoMINOR: htx: Add helper function to get type and size from the block info field
Christopher Faulet [Mon, 13 Apr 2026 17:04:20 +0000 (19:04 +0200)] 
MINOR: htx: Add helper function to get type and size from the block info field

__htx_blkinfo_type() and __htx_blkinfo_size() function was added to return,
respectively, the type and the size from the block info field. The main
usage for these functions is internal to the htx code.

6 days agoCI: Build `dev/haring/` as part of contrib.yml
Tim Duesterhus [Mon, 13 Apr 2026 19:23:46 +0000 (21:23 +0200)] 
CI: Build `dev/haring/` as part of contrib.yml

This functionally is quite similar to `dev/flags/` and thus it makes sense to
include it.

6 days agoCI: Use `matrix:` in contrib.yml
Tim Duesterhus [Mon, 13 Apr 2026 19:23:45 +0000 (21:23 +0200)] 
CI: Use `matrix:` in contrib.yml

This makes it much easier to add additional "smoke-tests" to contrib.yml. The
previous set-up also didn't allow to easily see all failures when a single
build fails, because it would abort after any failed step.

6 days agoCI: Run contrib.yml on `ubuntu-slim`
Tim Duesterhus [Mon, 13 Apr 2026 19:23:44 +0000 (21:23 +0200)] 
CI: Run contrib.yml on `ubuntu-slim`

This is sufficient for this simple "does it compile" smoke-test.

6 days agoCI: Generate vtest.yml matrix on `ubuntu-slim`
Tim Duesterhus [Mon, 13 Apr 2026 19:23:43 +0000 (21:23 +0200)] 
CI: Generate vtest.yml matrix on `ubuntu-slim`

This runner image is more lightweight by running inside of a container rather
than a full VM. This is sufficient to run some Python.

6 days agoCI: Use `case()` function
Tim Duesterhus [Mon, 13 Apr 2026 19:23:42 +0000 (21:23 +0200)] 
CI: Use `case()` function

GitHub Actions introduced a new `case()` function in January that works just
like HAProxy’s `iif()` converter (just with an arbitrary number of
expressions). It is more robust than chaining strings with `&&` and `||`,
because it includes proper type-checking.

see https://github.blog/changelog/2026-01-29-github-actions-smarter-editing-clearer-debugging-and-a-new-case-function/#write-more-expressive-expressions-with-a-case-function

6 days agoCI: Integrate Musl build into vtest.yml
Tim Duesterhus [Mon, 13 Apr 2026 19:23:41 +0000 (21:23 +0200)] 
CI: Integrate Musl build into vtest.yml

With the previous sync, these two workflows perform almost the same steps and
both logically belong to "Run VTest tests". Integrate musl.yml into vtest.yml,
which will hopefully encourage future changes to consistently apply to all jobs
in that workflow.

6 days agoCI: Sync musl.yml with vtest.yml
Tim Duesterhus [Mon, 13 Apr 2026 19:23:40 +0000 (21:23 +0200)] 
CI: Sync musl.yml with vtest.yml

This syncs up musl.yml with vtest.yml as much as possible by:

- Aligning indentation.
- Reordering steps.
- Aligning step names.
- Adding missing functionality to musl.yml.

6 days agoCI: Use `sh` in `actions/setup-vtest/action.yml`
Tim Duesterhus [Mon, 13 Apr 2026 19:23:39 +0000 (21:23 +0200)] 
CI: Use `sh` in `actions/setup-vtest/action.yml`

Bash might not always be preinstalled and we don't make use of any
bash-specific features either. Switch to POSIX sh for simplicity.

This partly reverts the fix in 073240044e476c365242a5541c01bb0503f1e7c2, which
installed `bash` for the musl job.

6 days agoCI: Remove obsolete steps from musl.yml
Tim Duesterhus [Mon, 13 Apr 2026 19:23:38 +0000 (21:23 +0200)] 
CI: Remove obsolete steps from musl.yml

These have become obsolete with the use of `./.github/actions/setup-vtest` in
6e67b59aca11e8262c424fce062a09784d3da2ba.

6 days agoMINOR: mjson: reintroduce mjson_next()
William Lallemand [Tue, 14 Apr 2026 08:25:30 +0000 (10:25 +0200)] 
MINOR: mjson: reintroduce mjson_next()

The lack of mjson_next() prevents to iterate easily and need to hack by
iterating on a loop of snprintf + $.field[XXX] combined with
mjson_find().

This reintroduce mjson_next() so we could iterate without having to
build the string.

The patch does not reintroduce MJSON_ENABLE_NEXT so it could be used
without having to define it.

6 days agoMINOR: acme: display the type of challenge in ACME_INITIAL_DELAY
William Lallemand [Tue, 14 Apr 2026 08:09:03 +0000 (10:09 +0200)] 
MINOR: acme: display the type of challenge in ACME_INITIAL_DELAY

The ACME_INITIAL_DELAY state displays a message about 'dns-01', but this
state is also used for 'dns-persist-01'.

This patch displays the challenge that was configured instead of dns-01

7 days agoMINOR: http_fetch: Add support for checks to `unique-id` fetch
Tim Duesterhus [Mon, 13 Apr 2026 17:37:31 +0000 (19:37 +0200)] 
MINOR: http_fetch: Add support for checks to `unique-id` fetch

This allows to use the `unique-id` fetch within `tcp-check` or `http-check`
ruleset. The format is taken from the checked server's backend (which is
naturally inherited from the corresponding `defaults` section).

This is particularly useful with

    http-check send ... hdr request-id %[unique-id]

to ensure all requests sent by HAProxy have a unique ID header attached.

This resolves GitHub Issue #3307.

Reviewed-by: Volker Dusch <github@wallbash.com>
7 days agoMINOR: check: Support generating a `unique_id` for checks
Tim Duesterhus [Mon, 13 Apr 2026 17:37:30 +0000 (19:37 +0200)] 
MINOR: check: Support generating a `unique_id` for checks

This implementation is directly modeled after `stream_generate_unique_id()` and
the corresponding `unique_id` field on `struct stream`.

It will be used in a future commit to enable the use of the `%[unique-id]`
fetch in check rules.

7 days agoCLEANUP: log: Stop touching `struct stream` internals for `%ID`
Tim Duesterhus [Mon, 13 Apr 2026 17:37:29 +0000 (19:37 +0200)] 
CLEANUP: log: Stop touching `struct stream` internals for `%ID`

Use the return value of `stream_generate_unique_id()` instead of relying on the
`unique_id` field of `struct stream` when handling the `%ID` log placeholder.
This also allowed to unify the "stream available" and "stream not available"
paths.

Reviewed-by: Volker Dusch <github@wallbash.com>
7 days agoMINOR: Allow inlining of `stream_generate_unique_id()`
Tim Duesterhus [Mon, 13 Apr 2026 17:37:28 +0000 (19:37 +0200)] 
MINOR: Allow inlining of `stream_generate_unique_id()`

With the introduction of the `generate_unique_id()` helper, the actual
complicated logic is sitting in a different file. Allow inlining of
`stream_generate_unique_id()`, so that callers can benefit from an abstraction
without hiding away the access of `strm->unique_id` behind a function call.

7 days agoMINOR: Add `generate_unique_id()` helper
Tim Duesterhus [Mon, 13 Apr 2026 17:37:27 +0000 (19:37 +0200)] 
MINOR: Add `generate_unique_id()` helper

This new function will handle the actual generation of the unique ID according
to a format. The caller is responsible to check that no unique ID is stored
yet.

7 days agoCLEANUP: Make `lf_expr` parameter of `sess_build_logline_orig()` const
Tim Duesterhus [Mon, 13 Apr 2026 17:37:26 +0000 (19:37 +0200)] 
CLEANUP: Make `lf_expr` parameter of `sess_build_logline_orig()` const

Since this is safely possible without making any changes, we can provide this
hint to the compiler.

7 days agoBUG/MINOR: haterm: don't apply the default pipe size margin twice
Willy Tarreau [Mon, 13 Apr 2026 17:38:48 +0000 (19:38 +0200)] 
BUG/MINOR: haterm: don't apply the default pipe size margin twice

Commit 6d16b11022 ("BUG/MINOR: haterm: preserve the pipe size margin
for splicing") solved the issue of pipe size being sufficient for the
vmsplice() call, but as Christopher pointed out, the ratio was applied
to the default size of 64k, so now it's applied twice, giving 100k
instead of 80k. Let's drop it from there.

No backport needed.

7 days agoBUG/MINOR: acme: don't pass NULL into format string
Egor Shestakov [Mon, 13 Apr 2026 16:29:01 +0000 (16:29 +0000)] 
BUG/MINOR: acme: don't pass NULL into format string

Printing a "(null)" when NULL passed with the %s format specifier is a
GNU extension, so it must be avoided for portability reasons.

Must be backported as far as 3.2

7 days agoBUG/MINOR: acme: read the wildcard flag from the authorization response
William Lallemand [Mon, 13 Apr 2026 16:49:53 +0000 (18:49 +0200)] 
BUG/MINOR: acme: read the wildcard flag from the authorization response

The wildcard field was declared and used when building the dns-persist-01
TXT record value (policy=wildcard suffix), but was never populated from
the server's authorization response. Add the missing mjson_get_bool() call
to read $.wildcard before saving auth->dns.

7 days agoDOC: configuration: document dns-persist-01 challenge type and options
William Lallemand [Mon, 13 Apr 2026 16:44:11 +0000 (18:44 +0200)] 
DOC: configuration: document dns-persist-01 challenge type and options

Document the dns-persist-01 challenge type under the challenge keyword,
the challenge-ready dns option (existence-only TXT check for dns-persist-01),
and the default challenge-ready value when challenge is dns-persist-01.

7 days agoMINOR: acme: extend resolver-based DNS pre-check to dns-persist-01
William Lallemand [Mon, 13 Apr 2026 16:44:11 +0000 (18:44 +0200)] 
MINOR: acme: extend resolver-based DNS pre-check to dns-persist-01

Add challenge_type parameter to acme_rslv_start() to select the correct
DNS lookup prefix: _validation-persist.<domain> for dns-persist-01 and
_acme-challenge.<domain> for dns-01.

Default cond_ready to ACME_RDY_DNS|ACME_RDY_DELAY for dns-persist-01.
Extend ACME_CLI_WAIT to cover dns-persist-01 alongside dns-01.

In ACME_RSLV_READY, check only TXT record existence for dns-persist-01
since the resolver cannot parse multiple strings within a single TXT entry.

7 days agoMEDIUM: acme: implement dns-persist-01 challenge
Mia Kanashi [Wed, 25 Feb 2026 22:07:55 +0000 (00:07 +0200)] 
MEDIUM: acme: implement dns-persist-01 challenge

Implements draft DNS-PERSIST-01 challenge based on
https://datatracker.ietf.org/doc/html/draft-ietf-acme-dns-persist

Blog post: https://letsencrypt.org/2026/02/18/dns-persist-01

This challenge is designed to use preprovisioned DNS records,
unlike DNS-01 challenge it doesn't need per provider API integration.

In short instead of validating order by crafting a custom response
based on input recieved from ACME server, like other challenges do
in particular DNS-01, HTTP-01, TLS-ALPN-01, in this challenge you
authorize domain statically, ACME account key functions similar to
a private key and accounturi in the record functions like a public key,
ACME server verifies that account uri matches account key and authorizes
based on that. You only need to write DNS record one time,
accounturi binds to an account key, and will only change if new account
key is created, although it is possible to rotate account key without
changing account uri.

Main benefits of this challenge in contrast to DNS-01:
1. Security, no need to give reverse proxy write access to the DNS.
2. Simplicity, no complex per provider integrations like Lego needed.
3. Robustness, no worrying about DNS record cache each renewal.

It would be used like this:
1. generate an account key ahead of time
2. add required DNS record manually or automatically using IaC tools
3. start HAProxy with the same account key used

Intended way to use this challenge is with a code that will print
and maybe sets DNS records ahead of time. For example that could
be integrated into the IaC provisioning step. This challenge type
is extremely recent though, so those integrations are yet to be written.

It is possible to do this challenge without extra tools too,
with pebble / challtestsrv steps would be as following:

After starting HAProxy it will print required records in the logs.

With challtestsrv you can then set those records like this:

curl -d '{
  "host":"_validation-persist.localhost.",
  "value": "pebble.letsencrypt.org; accounturi=...; policy=wildcard"}
' http://localhost:8055/set-txt

After setting the records run renew with the name of the certificate:

echo "acme renew @cert/localhost.pem" \
  | socat stdio tcp4-connect:127.0.0.1:9999

Or just restart HAProxy.

Unlike with DNS-01 you don't have to worry about DNS records changing,
if there is any problem with DNS records you can just retry.

7 days agoBUG/MINOR: haterm: preserve the pipe size margin for splicing
Willy Tarreau [Mon, 13 Apr 2026 16:32:25 +0000 (18:32 +0200)] 
BUG/MINOR: haterm: preserve the pipe size margin for splicing

Originally in httpterm we used to allocate 5/4 of the size of a pipe to
permit to use vmsplice because there's some fragmentation or overhead
internally that requires to use a bit of margin. While this was initially
applied to haterm as well, it was accidentally lost with commit fb82dece47
("BUG/MEDIUM: haterm: Properly initialize the splicing support for haterm"),
resulting in errors about vmsplice() whenever tune.pipesize is set. Let's
enforce the ratio again.

No backport is needed.

7 days agoMINOR: errors: remove excessive errmsg checks
Egor Shestakov [Sat, 11 Apr 2026 16:03:43 +0000 (16:03 +0000)] 
MINOR: errors: remove excessive errmsg checks

I noticed some strange checks for presence of errmsg. Called functions
generate non-empty error message in case of failure, so a non-NULL address
of the error message is enough.

No backport needed.

7 days agoREGTESTS: Never reuse server connection in reg-tests/jwt/jwt_decrypt.vtc
Christopher Faulet [Mon, 13 Apr 2026 12:00:22 +0000 (14:00 +0200)] 
REGTESTS: Never reuse server connection in reg-tests/jwt/jwt_decrypt.vtc

A "Connection: close" header is added to responses to avoid any connection
reuse. This should avoid errors on the client side.

7 days agoBUG/MEDIUM: cli: Properly handle too big payload on a command line
Christopher Faulet [Wed, 8 Apr 2026 08:28:25 +0000 (10:28 +0200)] 
BUG/MEDIUM: cli: Properly handle too big payload on a command line

When command line is parsed, when the payload was too big the error was not
properly handled. Instead of leaving the parsing function to print the
error, we looped infinitly trying to parse remaining data.

When the command line is too big, we must exit the parsing function in
CLI_ST_PRINT_ERR state. Instead of exiting the function, we only left the
while loop, setting this way the cli applet in CLI_ST_PROMPT state.

This patch must be backported as far as 3.2.

7 days agoMINOR: tools: memvprintf(): remove <out> check that always true
Egor Shestakov [Wed, 8 Apr 2026 15:24:56 +0000 (15:24 +0000)] 
MINOR: tools: memvprintf(): remove <out> check that always true

memvprintf() exits early if the <out> is NULL, so the further NULL check is
redundant.

No backport needed.

7 days agoCI: build WolfSSL job with asan enabled
Ilia Shipitsin [Fri, 3 Apr 2026 18:10:11 +0000 (20:10 +0200)] 
CI: build WolfSSL job with asan enabled

Reference: https://github.com/haproxy/haproxy/issues/3317

this allows to distribute memory checking to WolfSSL code as well

Only applies on the WolfSSL weekly job which build the wolfssl git
version.

7 days agoCLEANUP: http_fetch: Use local `unique_id` variable in `smp_fetch_uniqueid()`
Tim Duesterhus [Fri, 3 Apr 2026 21:29:01 +0000 (23:29 +0200)] 
CLEANUP: http_fetch: Use local `unique_id` variable in `smp_fetch_uniqueid()`

Instead of relying on the implementation detail that
`stream_generate_unique_id()` will store the unique ID in `strm->unique_id` we
should use the returned value, especially since that one is already checked in
the `isttest()`.

Reviewed-by: Volker Dusch <github@wallbash.com>
7 days agoCLEANUP: stream: Reduce duplication in `stream_generate_unique_id()`
Tim Duesterhus [Fri, 3 Apr 2026 21:28:59 +0000 (23:28 +0200)] 
CLEANUP: stream: Reduce duplication in `stream_generate_unique_id()`

The return value of the `if()` and `else` branch is identical. We can just move
it out of conditional paths.

Reviewed-by: Volker Dusch <github@wallbash.com>
7 days agoCLEANUP: stream: Explain the two-step initialization in `stream_generate_unique_id()`
Tim Duesterhus [Fri, 3 Apr 2026 21:28:58 +0000 (23:28 +0200)] 
CLEANUP: stream: Explain the two-step initialization in `stream_generate_unique_id()`

This two-step initialization of `strm->unique_id` looks like a refactoring
target. Add a comment to prevent regressions of the fix in
fb7b5c8a53cb4f19a223abd20660d47162aa8708.

7 days agoCLEANUP: log: Return `size_t` from `sess_build_logline_orig()`
Tim Duesterhus [Fri, 3 Apr 2026 21:28:57 +0000 (23:28 +0200)] 
CLEANUP: log: Return `size_t` from `sess_build_logline_orig()`

`sess_build_logline_orig()` takes a `size_t maxsize` as input and accordingly
should also return `size_t` instead of `int` as the resulting length. In
practice most of the callers already stored the result in a `size_t` anyways.
The few places that used an `int` were adjusted.

This Coccinelle patch was used to check for completeness:

    @@
    type T != size_t;
    T var;
    @@

    (
    * var = build_logline(...)
    |
    * var = build_logline_orig(...)
    |
    * var = sess_build_logline(...)
    |
    * var = sess_build_logline_orig(...)
    )

Reviewed-by: Volker Dusch <github@wallbash.com>
7 days agoBUG/MINOR: log: Fix error message when using unavailable fetch in logfmt
Tim Duesterhus [Fri, 3 Apr 2026 21:28:56 +0000 (23:28 +0200)] 
BUG/MINOR: log: Fix error message when using unavailable fetch in logfmt

The following configuration:

    defaults
     unique-id-format TEST-%[srv_name]

    frontend fe_http
     mode http

     bind :::8080 v4v6

Emitted the following error:

    [ALERT]    (219835) : Parsing [./patch.cfg:2]: failed to parse unique-id : sample fetch <srv_name]> may not be reliably used here because it needs 'server' which is not available here.

The `]` in the name of the sample fetch should not be there.

This bug exists since at least HAProxy 2.4, which is the oldest supported
version. The fix should be backported there.

Reviewed-by: Volker Dusch <github@wallbash.com>
7 days agoBUG/MINOR: quic: do not use hardcoded values in QMux TP frame builder
Amaury Denoyelle [Mon, 13 Apr 2026 11:34:10 +0000 (13:34 +0200)] 
BUG/MINOR: quic: do not use hardcoded values in QMux TP frame builder

Reuse QUIC transport parameters value set in xprt_qstrm layer in frame
builder function. Prior to this patch, mux_quic would use different
values from the advertised ones.

No need to backport.

7 days agoCI: github: add bash to the musl job 20260413-musl flx04/20260413-musl
William Lallemand [Mon, 13 Apr 2026 09:26:52 +0000 (11:26 +0200)] 
CI: github: add bash to the musl job

Previous commit 6e67b59 ("CI: Consistently set up VTest with
./.github/actions/setup-vtest") requires bash to use the github action.

This commit adds bash to the list of installed package in alpine.

7 days agoMINOR: mux_quic: remove duplicate QMux local transport params
Amaury Denoyelle [Mon, 13 Apr 2026 07:15:37 +0000 (09:15 +0200)] 
MINOR: mux_quic: remove duplicate QMux local transport params

When QMux was first implemented, values used for emitted transport
parameters in xprt_qstrm and local flow control in mux_quic were
initialized separately. This is error prone in particular if a value is
change in one layer but not the other.

This patch fixes this by using xprt_qstrm_lparams() in QMux init
function. Mux flow control is then loaded with these values. Thus all
values are now initialized in a single place which is xprt_qstrm_init().

7 days agoCI: Consistently set up VTest with `./.github/actions/setup-vtest`
Tim Duesterhus [Sun, 12 Apr 2026 19:27:10 +0000 (21:27 +0200)] 
CI: Consistently set up VTest with `./.github/actions/setup-vtest`

Two jobs still used `scripts/build-vtest.sh` directly, which seems like an
oversight.

7 days agoCI: Merge `aws-lc-template.yml` into `aws-lc.yml`
Tim Duesterhus [Sun, 12 Apr 2026 19:27:09 +0000 (21:27 +0200)] 
CI: Merge `aws-lc-template.yml` into `aws-lc.yml`

There is no need to have an entirely separate workflow, when we can just use a
build matrix for the variation.

7 days agoCI: Merge `aws-lc.yml` and `aws-lc-fips.yml` into `aws-lc.yml`
Tim Duesterhus [Sun, 12 Apr 2026 19:27:08 +0000 (21:27 +0200)] 
CI: Merge `aws-lc.yml` and `aws-lc-fips.yml` into `aws-lc.yml`

These two jobs run on exactly the same triggers and are effectively variations
of each other. There is no need to have two separate workflows for them.

7 days agoCI: Simplify version extraction with `haproxy -vq`
Tim Duesterhus [Sun, 12 Apr 2026 19:27:07 +0000 (21:27 +0200)] 
CI: Simplify version extraction with `haproxy -vq`

Instead of running `awk` on the output of `haproxy -v` to extract the bare
version number, we can use `haproxy -vq`.

7 days agoCI: Update to actions/checkout@v6
Tim Duesterhus [Sun, 12 Apr 2026 19:27:06 +0000 (21:27 +0200)] 
CI: Update to actions/checkout@v6

No functional change, but we should keep this current.

see 5f4ddb54b05ae0355b1f64c22263a6bc381410df
see 5c923f1869881156bf3a25c9659655ae10f7dbd0
see b81a7f428bc0ae32df485c4c15637230eb60f459

7 days agoCI: Fix regular expression escaping in matrix.py
Tim Duesterhus [Sun, 12 Apr 2026 19:27:05 +0000 (21:27 +0200)] 
CI: Fix regular expression escaping in matrix.py

This fixes:

    .github/matrix.py:72: SyntaxWarning: "\." is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\."? A raw string is also an option.
      return re.match('^v[0-9]+(\.[0-9]+)*$', version_string)
    .github/matrix.py:89: SyntaxWarning: "\." is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\."? A raw string is also an option.
      return re.match('^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
    .github/matrix.py:106: SyntaxWarning: "\." is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\."? A raw string is also an option.
      return re.match('^v[0-9]+(\.[0-9]+)*-stable$', version_string)

7 days agoCI: Wrap all `if:` conditions in `${{ }}`
Tim Duesterhus [Sun, 12 Apr 2026 19:27:04 +0000 (21:27 +0200)] 
CI: Wrap all `if:` conditions in `${{ }}`

While `if:` also works with a bare condition, it is a best practice to always
wrap "dynamic placeholders" in `${{ }}`.

See: https://github.blog/changelog/2026-01-29-github-actions-smarter-editing-clearer-debugging-and-a-new-case-function/#better-if-condition-handling

7 days agoCI: Consistently add a top-level `permissions` definition to GHA workflows
Tim Duesterhus [Sun, 12 Apr 2026 19:24:19 +0000 (21:24 +0200)] 
CI: Consistently add a top-level `permissions` definition to GHA workflows

This makes it easy to verify the permissions and to apply them to all jobs
within a given workflow.

7 days agoCI: Drop obsolete `packages: write` permission from `quic-interop-*.yml`
Tim Duesterhus [Sun, 12 Apr 2026 19:24:18 +0000 (21:24 +0200)] 
CI: Drop obsolete `packages: write` permission from `quic-interop-*.yml`

This is no longer necessary since dfe1de4335f12d8ce436b906b2b6cddfcb8274c9.

7 days agoMINOR: ot: renamed the variable dbg_indent_level to flt_ot_dbg_indent_level
Miroslav Zagorac [Fri, 20 Jan 2023 23:44:13 +0000 (00:44 +0100)] 
MINOR: ot: renamed the variable dbg_indent_level to flt_ot_dbg_indent_level

The thread-local variable dbg_indent_level used a generic name that could
collide with identifiers in other compilation units.  Renamed it to
flt_ot_dbg_indent_level so that it carried the flt_ot_ prefix consistent
with the rest of the OpenTracing filter namespace.  The rename covered the
declaration, definition, and all macro references in debug.h, parser.c and
util.c.

7 days agoBUILD: ot: removed explicit include path when building opentracing filter
Miroslav Zagorac [Thu, 19 Jan 2023 08:50:56 +0000 (09:50 +0100)] 
BUILD: ot: removed explicit include path when building opentracing filter

The -Iaddons/ot/include flag in OT_CFLAGS allowed source files to use a
bare #include "include.h", which was fragile because it depended on the
compiler search path.  Removed that flag from the Makefile and changed
every source file under addons/ot/src/ to use the relative include path
../include/include.h instead.  This made header resolution explicit and
consistent with standard addon conventions.

7 days agoBUG/MINOR: ot: fixed wrong NULL check in flt_ot_parse_cfg_group()
Miroslav Zagorac [Wed, 18 Jan 2023 18:33:21 +0000 (19:33 +0100)] 
BUG/MINOR: ot: fixed wrong NULL check in flt_ot_parse_cfg_group()

After calling flt_ot_conf_group_init() and storing the result in
flt_ot_current_group, the code incorrectly checked flt_ot_current_config
for NULL instead of the newly assigned flt_ot_current_group.  This meant
a failed group init was never detected and the error path was never taken.

7 days agoBUG/MINOR: ot: removed dead code in flt_ot_parse_cfg_str()
Miroslav Zagorac [Wed, 18 Jan 2023 16:14:16 +0000 (17:14 +0100)] 
BUG/MINOR: ot: removed dead code in flt_ot_parse_cfg_str()

The local variable str was declared but never assigned a value other than
NULL.  The error-handling block that called flt_ot_conf_str_free(&str) on
it was therefore a no-op.  Removed both the unused variable and the dead
cleanup path.

7 days agoCLEANUP: ot: use the item API for the variables trees
Miroslav Zagorac [Thu, 25 Sep 2025 22:13:12 +0000 (00:13 +0200)] 
CLEANUP: ot: use the item API for the variables trees

In flt_ot_vars_scope_dump(), switched from cebu64_first()/cebu64_next() to
cebu64_imm_first()/cebu64_imm_next() for iterating the variable name trees.
Since this function only reads variables under a read lock, the immutable
traversal API is the correct choice.  Also updated the container_of()
member from 'node' to 'name_node' to match the current struct var layout.

7 days agoDOC: otel: added README.md overview document
Miroslav Zagorac [Wed, 8 Apr 2026 21:28:00 +0000 (23:28 +0200)] 
DOC: otel: added README.md overview document

Added a Markdown-formatted overview covering features, build instructions,
configuration, scope keywords, CLI commands, performance benchmarks and
test configurations.

7 days agoMINOR: otel: changed instrument attr to use sample expressions
Miroslav Zagorac [Mon, 6 Apr 2026 04:33:40 +0000 (06:33 +0200)] 
MINOR: otel: changed instrument attr to use sample expressions

Replaced the static key-value attribute storage in update-form instruments
with sample-evaluated attributes, matching the log-record attr change.
The 'attr' keyword now accepts a key and a HAProxy sample expression
evaluated at runtime.

The struct (conf.h) changed from otelc_kv/attr_len to a list of
flt_otel_conf_sample entries.  The parser (parser.c) calls
flt_otel_parse_cfg_sample() with n=1 per attr keyword.  At runtime
(event.c) each attribute is evaluated via flt_otel_sample_eval() and
added via flt_otel_sample_add_kv() to a bare flt_otel_scope_data_kv,
which is passed to the meter.

Updated documentation, debug macro and test configurations.

7 days agoMINOR: otel: changed log-record attr to use sample expressions
Miroslav Zagorac [Fri, 3 Apr 2026 10:33:26 +0000 (12:33 +0200)] 
MINOR: otel: changed log-record attr to use sample expressions

Replaced the static key-value attribute storage in log-record with
sample-evaluated attributes.  The 'attr' keyword now accepts a key and a
HAProxy sample expression evaluated at runtime, instead of a static string
value.

The struct (conf.h) changed from otelc_kv/attr_len to a list of
flt_otel_conf_sample entries.  The parser (parser.c) calls
flt_otel_parse_cfg_sample() with n=1 per attr keyword.  At runtime
(event.c) each attribute is evaluated via flt_otel_sample_eval() and added
via flt_otel_sample_add_kv() to a bare flt_otel_scope_data_kv, which is
passed to logger->log_span().

Updated documentation, debug macro and test configurations.

7 days agoMINOR: otel: added flt_otel_sample_eval and exposed flt_otel_sample_add_kv
Miroslav Zagorac [Thu, 2 Apr 2026 14:03:12 +0000 (16:03 +0200)] 
MINOR: otel: added flt_otel_sample_eval and exposed flt_otel_sample_add_kv

Factored the sample evaluation logic out of flt_otel_sample_add() into a
new flt_otel_sample_eval() function that evaluates a sample definition
into an otelc_value.  Both the log-format path and the bare sample
expression path are handled, with a flag_native parameter controlling
native type preservation for single-expression samples.
flt_otel_sample_add() now calls flt_otel_sample_eval() and dispatches the
result.

Made flt_otel_sample_add_kv() non-static so callers outside util.c can
add key-value pairs directly to a bare flt_otel_scope_data_kv without
requiring the full flt_otel_scope_data structure.

7 days agoDOC: otel: added cross-cutting design patterns document
Miroslav Zagorac [Wed, 1 Apr 2026 02:35:51 +0000 (04:35 +0200)] 
DOC: otel: added cross-cutting design patterns document

Added README-design covering the internal design patterns that span
multiple source files: X-macro code generation, type-safe generic macros,
error handling architecture, thread safety model, sample evaluation
pipeline, rate limiting, memory management, context propagation, debug
infrastructure, idle timeout mechanism, group action integration and CLI
runtime control.

7 days agoDOC: otel: test: added speed test guide and benchmark results
Miroslav Zagorac [Wed, 25 Mar 2026 11:17:03 +0000 (12:17 +0100)] 
DOC: otel: test: added speed test guide and benchmark results

The test directory gained a speed test guide (README-test-speed)
explaining how to run performance benchmarks at various rate-limit levels,
together with benchmark result files for the standalone, composite,
context-propagation, and frontend-backend test configurations.

7 days agoDOC: otel: test: added test README-* files
Miroslav Zagorac [Sat, 21 Mar 2026 17:39:52 +0000 (18:39 +0100)] 
DOC: otel: test: added test README-* files

Added README documentation for each test configuration (sa, cmp, ctx,
fe-be, empty, full) describing event coverage, signal usage, instrument
tables, span hierarchies and run instructions.

7 days agoDOC: otel: added documentation
Miroslav Zagorac [Sun, 12 Apr 2026 10:07:17 +0000 (12:07 +0200)] 
DOC: otel: added documentation

Added the full documentation set for the OpenTelemetry filter.

The main README served as the user-facing guide covering build
instructions, core OpenTelemetry concepts, the complete filter
configuration reference, usage examples with worked scenarios,
CLI commands, and known limitations.

Supplementary documents provided a detailed configuration guide with
worked examples (README-configuration), an internal C structure reference
for developers (README-conf), a function reference organized by source
file (README-func), an architecture and implementation review
(README-implementation), and miscellaneous notes (README-misc).

7 days agoMINOR: otel: test: added full-event test config
Miroslav Zagorac [Fri, 13 Mar 2026 21:12:49 +0000 (22:12 +0100)] 
MINOR: otel: test: added full-event test config

Added the 'full' test configuration that exercises all 29 supported OTel
filter events with all three signal types (traces, metrics, logs).  Every
instrument definition has a corresponding update.

7 days agoMINOR: otel: added log-record signal support
Miroslav Zagorac [Sat, 7 Mar 2026 14:28:10 +0000 (15:28 +0100)] 
MINOR: otel: added log-record signal support

Added "log-record" as the third OpenTelemetry signal alongside traces
(span) and metrics (instrument).  This includes the
flt_otel_conf_log_record structure definition, parser keyword defines,
the otel-scope section parser with optional "id", "event", "span", and
"attr" keywords followed by sample fetch expressions or a log-format
string, init/free lifecycle, scope list wiring, log-format evaluation
in flt_otel_scope_run_instrument_record(), a test configuration example,
log-record span reference validation in flt_otel_check(), and logger
handle creation, startup, and teardown in the filter lifecycle.

7 days agoMINOR: otel: added metrics instrument support
Miroslav Zagorac [Mon, 2 Mar 2026 08:41:57 +0000 (09:41 +0100)] 
MINOR: otel: added metrics instrument support

Added the "instrument" keyword to otel-scope sections for recording metric
measurements alongside traces.

Introduced flt_otel_conf_instrument holding instrument type, description,
unit, sample expressions, and optional key-value attributes.  The
supported synchronous integer-precision instrument types were counters,
histograms, up-down counters, and gauges.

Instruments followed a two-form design: a "create" form defined a new
instrument with its type and value expression, while an "update" form
recorded measurements against an existing instrument with per-scope
attributes.

Instrument creation was performed lazily at first use with HA_ATOMIC_CAS
to guarantee thread-safe one-time initialization.  The configuration
check phase validated that every update-form had a matching create-form
definition and that create-form names were unique across all scopes.

The meter lifecycle was integrated into filter init and deinit, starting
the meter alongside the tracer and shutting it down during cleanup.

7 days agoMINOR: otel: added span link support
Miroslav Zagorac [Thu, 26 Feb 2026 10:09:33 +0000 (11:09 +0100)] 
MINOR: otel: added span link support

Added span link support, allowing a span to reference other spans or
extracted contexts without establishing a parent relationship.

Introduced the flt_otel_conf_link structure and added a links list to
flt_otel_conf_span.  The parser accepted both an inline syntax on the span
declaration line ("span <name> link <target>") and a standalone multi-
argument form ("link <span> ..."), each creating a conf_link entry
appended to the span's link list.

At runtime, each configured link name was resolved against the active
spans and extracted contexts in the runtime context.  Resolved references
were collected into flt_otel_scope_data_link entries and passed to the C
wrapper add_link API during span creation.

Initialization, cleanup, and debug dump routines were added for the link
data structures at both configuration and runtime levels.

7 days agoMINOR: otel: test: added test and benchmark suite for the OTel filter
Miroslav Zagorac [Tue, 27 Jan 2026 12:02:59 +0000 (13:02 +0100)] 
MINOR: otel: test: added test and benchmark suite for the OTel filter

Added a test suite under addons/otel/test/ for the OpenTelemetry filter.
Five scenarios exercise different filter capabilities: standalone (sa)
covers all hook points including idle-timeout heartbeats, metrics and log
records; compact (cmp) covers the full request/response lifecycle with
ACL-based error handling; context (ctx) tests explicit inject/extract
propagation through numbered context variables; frontend/backend (fe/be)
tests distributed tracing across two HAProxy instances; and empty tests
bare filter initialisation with no active scopes.

A performance benchmarking script (test-speed.sh) uses wrk to measure
throughput and latency at different rate-limit settings (100% through 0%,
disabled, and filter-off).  Each scenario includes comprehensive YAML
exporter definitions covering OTLP file/gRPC/HTTP, ostream, memory,
Zipkin, and Elasticsearch backends.

7 days agoMINOR: otel: added log-format support to the sample parser and runtime
Miroslav Zagorac [Tue, 3 Feb 2026 20:36:11 +0000 (21:36 +0100)] 
MINOR: otel: added log-format support to the sample parser and runtime

Extended flt_otel_parse_cfg_sample() to accept log-format strings in
addition to bare sample expressions.  Added lf_expr and lf_used fields
to flt_otel_conf_sample.

Extended flt_otel_sample_add() to evaluate log-format expressions when
lf_used was set.

7 days agoMEDIUM: otel: added group action for rule-based scope execution
Miroslav Zagorac [Sun, 12 Apr 2026 10:20:15 +0000 (12:20 +0200)] 
MEDIUM: otel: added group action for rule-based scope execution

Added the "otel-group" action keyword that allows executing a named group
of OTel scopes from HAProxy TCP and HTTP action rule contexts.

The new group.c module registers the "otel-group" keyword for all four
action contexts (tcp-request, tcp-response, http-request, http-response)
and implements the action lifecycle callbacks.

The parser flt_otel_group_parse() accepts a filter ID and group ID as
arguments, duplicates them into the action rule's argument slots, and
wires up the check, action, and release callbacks.

The post-parse validator flt_otel_group_check() resolves the filter ID and
group ID string references into direct configuration pointers by searching
the proxy's filter list for a matching OTel filter and then looking up the
named group within that filter's configuration.

The action handler flt_otel_group_action() retrieves the filter and group
configuration from the resolved rule arguments, verifies the filter is
attached to the stream and not disabled, then iterates through all scopes
in the group and executes each via flt_otel_scope_run() with a shared
timestamp pair.  This allows operators to trigger OTel instrumentation
conditionally from HAProxy rules, for example applying different tracing
scopes based on ACL conditions or request properties.

7 days agoMEDIUM: otel: added CLI commands for runtime filter management
Miroslav Zagorac [Sun, 12 Apr 2026 09:31:30 +0000 (11:31 +0200)] 
MEDIUM: otel: added CLI commands for runtime filter management

Added HAProxy CLI commands that allow runtime inspection and modification
of OTel filter settings without requiring a configuration reload.

The new cli.c module registers CLI keywords under the "otel" prefix and
implements the following commands: flt_otel_cli_parse_status() displays a
comprehensive status report of all OTel filter instances including filter
ID, proxy, disabled state, hard-error mode, logging state, rate limit,
analyzer bits, and SDK diagnostic message count;
flt_otel_cli_parse_disabled() enables or disables filtering across all
instances; flt_otel_cli_parse_option() toggles the hard-error mode that
controls whether errors disable the filter for a stream or are silently
ignored; flt_otel_cli_parse_logging() manages the logging state with
support for off, on, and dontlog-normal modes; flt_otel_cli_parse_rate()
adjusts the sampling rate limit as a floating-point percentage; and
flt_otel_cli_parse_debug() sets the debug verbosity level in debug builds.
All modifications are applied atomically across every OTel filter instance
in every proxy.

The CLI initialization is called from flt_otel_ops_init() during filter
startup via flt_otel_cli_init(), which registers the keyword table through
cli_register_kw().

Supporting changes include the FLT_OTEL_U32_FLOAT macro for converting the
internal uint32_t rate representation to a human-readable percentage, the
FLT_OTEL_PROXIES_LIST_START/END iteration macros for traversing all OTel
filter instances across the proxy list, and flt_otel_filters_dump() for
debug logging of filter instances.

7 days agoMINOR: otel: added prefix-based variable scanning
Miroslav Zagorac [Wed, 11 Feb 2026 05:55:44 +0000 (06:55 +0100)] 
MINOR: otel: added prefix-based variable scanning

Introduced an alternative variable scanning strategy that directly walked
the CEB tree of HAProxy's variable store instead of maintaining a separate
tracking buffer.

The Makefile auto-detected whether struct var carried a "name" member
by inspecting include/haproxy/vars-t.h and conditionally defined
USE_OTEL_VARS_NAME.  When enabled, the tracking buffer (flt_otel_ctx) and
its callback type were compiled out and replaced by direct tree walks.

flt_otel_vars_unset() walked the CEB tree for the resolved scope, removed
every variable whose normalized name matched the given prefix followed by
a dot, and adjusted the variable accounting.  flt_otel_vars_get()
performed the same prefix scan under a read lock, denormalized each
matching variable name back to its original OTel form, and assembled the
results into an otelc_text_map.

A helper flt_otel_vars_get_scope() was added to resolve scope name strings
("txn", "sess", "proc", "req", "res") to the corresponding HAProxy
variable store.  The set path skipped the tracking buffer update when
prefix scanning was available.

7 days agoMEDIUM: otel: added HAProxy variable storage for context propagation
Miroslav Zagorac [Sun, 12 Apr 2026 09:25:14 +0000 (11:25 +0200)] 
MEDIUM: otel: added HAProxy variable storage for context propagation

Added support for storing OTel span context in HAProxy transaction
variables as an alternative to HTTP headers, enabled by the OTEL_USE_VARS
compile flag.

The new vars.c module implements variable-based context propagation
through the HAProxy variable subsystem.  Variable names are constructed
from a configurable prefix and the OTel propagation key, with dots
normalized to underscores for HAProxy variable name compatibility
and denormalized back during retrieval.  The module provides
flt_otel_var_register() to pre-register variables at parse time,
flt_otel_var_set() and flt_otel_vars_unset() to store and clear context
key-value pairs in the txn scope, flt_otel_vars_get() to collect all
variables matching a prefix into an otelc_text_map for context extraction,
and flt_otel_vars_dump() for debug logging of all OTel variables.

The inject/extract keywords in the scope parser now accept an optional
"use-vars" argument alongside "use-headers", controlled by the new
FLT_OTEL_CTX_USE_VARS flag.  Both storage types can be used simultaneously
on the same span context, allowing context to be propagated through both
HTTP headers and variables.

The scope runner in event.c was extended to handle variable-based
context in parallel with headers: during extraction, it reads matching
variables via flt_otel_vars_get() when FLT_OTEL_CTX_USE_VARS is set;
during injection, it stores each propagation key as a variable via
flt_otel_var_set().  The unused resource cleanup now also unsets context
variables when removing failed extraction contexts.

The filter attach callback registers and sets the sess.otel.uuid variable
with the generated session UUID, making the trace identifier available to
HAProxy log formats and ACL expressions.

The feature is conditionally compiled: the OTEL_USE_VARS flag controls
whether vars.c is included in the build and whether the "use-vars" keyword
is available in the configuration parser.

7 days agoMEDIUM: otel: added HTTP header operations for context propagation
Miroslav Zagorac [Sun, 12 Apr 2026 09:20:59 +0000 (11:20 +0200)] 
MEDIUM: otel: added HTTP header operations for context propagation

Added the HTTP header manipulation layer that enables span context
injection into and extraction from HAProxy's HTX message buffers,
completing the end-to-end context propagation path.

The new http.c module implements three public functions:
flt_otel_http_headers_get() extracts HTTP headers matching a name prefix
from the channel's HTX buffer into an otelc_text_map structure, stripping
the prefix and separator dash from header names before storage;
flt_otel_http_header_set() constructs a full header name from a prefix and
suffix joined by a dash, removes all existing occurrences, and optionally
adds the header with a new value; and flt_otel_http_headers_remove()
removes all headers matching a given prefix.  A debug-only
flt_otel_http_headers_dump() logs all HTTP headers from a channel at
NOTICE level.

The scope runner in event.c now extracts propagation contexts from HTTP
headers before processing spans: for each configured extract context, it
calls flt_otel_http_headers_get() to read matching headers into a text
map, then passes the text map to flt_otel_scope_context_init() which
extracts the OTel span context from the carrier.  After span execution,
the span runner injects the span context back into HTTP headers via
flt_otel_inject_http_headers() followed by flt_otel_http_header_set()
for each propagation key.

The unused resource cleanup in flt_otel_scope_free_unused() now also
removes contexts that failed extraction by deleting their associated
HTTP headers via flt_otel_http_headers_remove() before freeing the scope
context structure.

7 days agoMEDIUM: otel: added context propagation via carrier interfaces
Miroslav Zagorac [Sun, 12 Apr 2026 09:17:11 +0000 (11:17 +0200)] 
MEDIUM: otel: added context propagation via carrier interfaces

Added the span context injection and extraction layer that bridges the
OTel C wrapper's propagation API with HAProxy's HTTP headers and text map
carriers.

The new otelc.c module implements four public functions that wrap the
OTel C wrapper's context propagation methods: flt_otel_inject_text_map()
and flt_otel_inject_http_headers() serialize a span's context into
a text map or HTTP headers carrier for outbound propagation, while
flt_otel_extract_text_map() and flt_otel_extract_http_headers()
deserialize an inbound carrier into an otelc_span_context for parent
linking.

Each direction uses a pair of callbacks registered on the carrier
structure.  The injection writers (flt_otel_text_map_writer_set_cb and
flt_otel_http_headers_writer_set_cb) store key-value pairs emitted by the
SDK into the carrier's text map via OTELC_TEXT_MAP_ADD().  The extraction
readers (flt_otel_text_map_reader_foreach_key_cb and
flt_otel_http_headers_reader_foreach_key_cb) iterate the carrier's text
map entries and pass each pair to the SDK's handler callback.

The scope context initialization in flt_otel_scope_context_init() now
calls flt_otel_extract_http_headers() to extract the span context from the
provided text map carrier and stores it in the scope context structure,
making extracted contexts available for parent linking in subsequent span
creation.

7 days agoMEDIUM: otel: implemented scope execution and span management
Miroslav Zagorac [Sun, 12 Apr 2026 08:58:38 +0000 (10:58 +0200)] 
MEDIUM: otel: implemented scope execution and span management

Implemented the scope execution engine that creates OTel spans, evaluates
sample expressions to collect telemetry data, and manages span lifecycle
during request and response processing.

The scope runner flt_otel_scope_run() was expanded from a stub into a
complete implementation that evaluates ACL conditions on the scope,
extracts span contexts from HTTP headers when configured, iterates over
the scope's span definitions calling flt_otel_scope_run_span() for each,
marks and finishes completed spans, and cleans up unused runtime
resources.

The span runner flt_otel_scope_run_span() creates OTel spans via the
tracer with optional parent references (from other spans or extracted
contexts), collects telemetry by calling flt_otel_sample_add() for each
configured attribute, event, baggage and status entry, then applies the
collected data to the span (attributes, events with their own key-value
arrays, baggage items, and status code with description) and injects the
span context into HTTP headers when configured.

The sample evaluation layer converts HAProxy sample expressions into OTel
telemetry data.  flt_otel_sample_add() evaluates each sample expression
against the stream, converts the result via flt_otel_sample_to_value()
which preserves native types (booleans as OTELC_VALUE_BOOL, integers as
OTELC_VALUE_INT64, all others as strings), and routes the key-value pair
to the appropriate collector based on the sample type (attribute, event,
baggage, or status).  The key-value arrays grow dynamically using the
FLT_OTEL_ATTR_INIT_SIZE and FLT_OTEL_ATTR_INC_SIZE constants.

Span finishing is handled in two phases: flt_otel_scope_finish_mark()
marks spans and contexts for completion using exact name matching or
wildcards ("*" for all, "*req*" for request-direction, "*res*" for
response-direction), and flt_otel_scope_finish_marked() ends all marked
spans with a common monotonic timestamp and destroys their contexts.

7 days agoMEDIUM: otel: wired OTel C wrapper library integration
Miroslav Zagorac [Sun, 12 Apr 2026 08:52:53 +0000 (10:52 +0200)] 
MEDIUM: otel: wired OTel C wrapper library integration

Connected the OpenTelemetry C wrapper library to the filter lifecycle by
implementing the library initialization, tracer creation, memory and
thread callbacks, shutdown sequence, and span completion.

The flt_otel_lib_init() function now verifies the C wrapper library
version against the compiled headers, calls otelc_init() with the absolute
configuration file path, and creates the tracer via otelc_tracer_create().
On success, it registers HAProxy pool-based memory callbacks
(flt_otel_mem_malloc, flt_otel_mem_free) and a thread ID callback
(flt_otel_thread_id) through otelc_ext_init(), so the C++ SDK allocates
span and context objects from pool_head_otel_span_context.  A custom log
handler (flt_otel_log_handler_cb) is registered via otelc_log_set_handler()
to count OTel SDK internal diagnostic messages in the flt_otel_drop_cnt
counter.

The per-thread init callback now starts the tracer thread via
OTELC_OPS(tracer, start) instead of unconditionally returning success.

The deinit callback saves the tracer handle before freeing the
configuration, then shuts down the library via otelc_deinit() after the
pool is destroyed, ensuring the ext callbacks remain valid while the
configuration structures are still being freed.  In debug builds, it logs
wrapper statistics, attach counters, and per-event HTX usage counters
before shutdown.

The runtime context cleanup in flt_otel_runtime_context_free() now ends
all active spans with a common monotonic timestamp via
OTELC_OPSR(span, end_with_options) before freeing them.  The scope context
cleanup in flt_otel_scope_context_free() now destroys the underlying OTel
span context via OTELC_OPSR(context, destroy).

The parser gained static storage for the debug memory tracker
(OTELC_DBG_MEM) and its initialization in the parse entry point, used when
compiled with the OTELC_DBG_MEM flag.

7 days agoMEDIUM: otel: implemented filter callbacks and event dispatcher
Miroslav Zagorac [Sun, 12 Apr 2026 08:30:03 +0000 (10:30 +0200)] 
MEDIUM: otel: implemented filter callbacks and event dispatcher

Replaced the stub filter callbacks with full implementations that dispatch
OTel events through the scope execution engine, and added the supporting
debug, error handling and utility infrastructure.

The filter lifecycle callbacks (init, deinit, init_per_thread) now
initialize the OpenTelemetry C wrapper library, create the tracer from the
instrumentation configuration file, enable HTX stream filtering, and clean
up the configuration and memory pools on shutdown.

The stream callbacks (attach, stream_start, stream_set_backend,
stream_stop, detach, check_timeouts) create the per-stream runtime context
on attach with rate-limit based sampling, fire the corresponding OTel
events (on-stream-start, on-backend-set, on-stream-stop), manage the
idle timeout timer with reschedule logic in detach, and free the runtime
context in check_timeouts.  The attach callback also registers the
required pre and post channel analyzers from the instrumentation
configuration.

The channel callbacks (start_analyze, pre_analyze, post_analyze,
end_analyze) register per-channel analyzers, map analyzer bits to event
indices via flt_otel_get_event(), and dispatch the matching events.
The end_analyze callback also fires the on-server-unavailable event
when response analyzers were configured but never executed.

The HTTP callbacks (http_headers, http_end, http_reply, and the debug-only
http_payload and http_reset) dispatch their respective request/response
events based on the channel direction.

The event dispatcher flt_otel_event_run() in event.c iterates over all
scopes matching a given event index and calls flt_otel_scope_run() for
each, sharing a common monotonic and wall-clock timestamp across all spans
within a single event.

Error handling is centralized in flt_otel_return_int() and
flt_otel_return_void(), which implement the hard-error/soft-error policy:
hard errors disable the filter for the stream, soft errors are silently
cleared.

The new debug.h header provides conditional debug macros
(FLT_OTEL_DBG_ARGS, FLT_OTEL_DBG_BUF) and the FLT_OTEL_LOG macro for
structured logging through the instrumentation's log server list.  The
utility layer gained debug-only label functions for channel direction,
proxy mode, stream position, filter type, and analyzer bit name lookups.

7 days agoMEDIUM: otel: added memory pool and runtime scope layer
Miroslav Zagorac [Sun, 12 Apr 2026 07:47:18 +0000 (09:47 +0200)] 
MEDIUM: otel: added memory pool and runtime scope layer

Added the memory pool management and the runtime scope layer that track
per-stream OTel spans and contexts during request processing.

The pool layer in pool.c manages HAProxy memory pools for the runtime
structures used by the filter: scope spans, scope contexts, runtime
contexts, and span contexts.  Each pool is conditionally compiled via
USE_POOL_OTEL_* macros defined in config.h and registered with
REGISTER_POOL().  The allocation functions (flt_otel_pool_alloc,
flt_otel_pool_strndup, flt_otel_pool_free) transparently fall back to
heap allocation when the corresponding pool is not enabled.  Trash buffer
helpers (flt_otel_trash_alloc, flt_otel_trash_free) provide scratch space
using either HAProxy's trash chunk pool or direct heap allocation.

The scope layer in scope.c implements the per-stream runtime state.  The
flt_otel_runtime_context structure is allocated when a stream starts and
holds the stream and filter references, hard-error/disabled/logging flags
copied from the instrumentation configuration, idle timeout state, a
generated UUID, and lists of active scope spans and extracted scope
contexts.  Scope spans (flt_otel_scope_span) carry the operation name,
fetch direction, the OTel span handle, and optional parent references
resolved from other spans or extracted contexts.  Scope contexts
(flt_otel_scope_context) hold an extracted span context obtained from
a carrier text map via the tracer.  The scope data structures
(flt_otel_scope_data) aggregate growable key-value arrays for attributes
and baggage, a linked list of named events with their own attribute
arrays, and a span status code with description, representing the
telemetry collected during a single event execution.

7 days agoMEDIUM: otel: added post-parse configuration check
Miroslav Zagorac [Sun, 12 Apr 2026 07:58:34 +0000 (09:58 +0200)] 
MEDIUM: otel: added post-parse configuration check

Implemented the flt_otel_ops_check() callback that validates the parsed
OTel filter configuration after all HAProxy configuration sections have
been processed.

The check callback performs the following validations: resolves deferred
sample fetch arguments under full frontend and backend capabilities,
verifies uniqueness of filter IDs across all proxies, ensures the
instrumentation section and its configuration file are present, checks
for duplicate group and scope section names, verifies that groups are not
empty, resolves group-to-scope and instrumentation-to-group/scope
cross-references by linking placeholder entries to their definitions,
detects unused scopes, counts root spans and warns when the count differs
from one, and accumulates the required channel analyzer bits from all used
scopes into the instrumentation configuration.

The commit also added the flt_otel_counters structure to track per-event
diagnostic counters in debug builds, the FLT_OTEL_ALERT macro for
filter-scoped error messages, and the FLT_OTEL_DBG_LIST macro for
iterating and dumping named configuration lists.

7 days agoMEDIUM: otel: added configuration parser and event model
Miroslav Zagorac [Sun, 12 Apr 2026 06:41:03 +0000 (08:41 +0200)] 
MEDIUM: otel: added configuration parser and event model

Added the full configuration parser that reads the OTel filter's external
configuration file and the event model that maps filter events to HAProxy
channel analyzers.

The event model in event.h defines an X-macro table
(FLT_OTEL_EVENT_DEFINES) that maps each filter event to its HAProxy
channel analyzer bit, sample fetch direction, and event name.  Events
cover stream lifecycle (start, stop, backend-set, idle-timeout), client
and server sessions, request analyzers (frontend and backend TCP and
HTTP inspection, switching rules, sticking rules, RDP cookie), response
analyzers (TCP inspection, HTTP response processing), and HTTP headers,
end, and reply callbacks.  The event names are partially compatible with
the SPOE filter.  The flt_otel_event_data[] table in event.c is generated
from the same X-macro and provides per-event metadata at runtime.

The parser in parser.c implements section parsers for the three OTel
configuration blocks: otel-instrumentation (tracer identity, log server,
config file path, groups, scopes, ACLs, rate-limit, options for
disabled/hard-errors/nolognorm, and debug-level), otel-group (group
identity and scope list), and otel-scope (scope identity, span definitions
with optional root/parent modifiers, attributes, events, baggages, status
codes, inject/extract context operations, finish lists, idle-timeout,
ACLs, and otel-event binding with optional if/unless ACL conditions).
Each section has a post-parse callback that validates the parsed state.

The top-level flt_otel_parse_cfg() temporarily registers these section
parsers, loads the external configuration file via parse_cfg(), and
handles deferred resolution of sample fetch arguments by saving them in
conf->smp_args for later resolution in flt_otel_check() when full frontend
and backend capabilities are available.  The main flt_otel_parse() entry
point was extended to parse the filter ID and config file keywords, verify
that insecure-fork-wanted is enabled, and wire the parsed configuration
into the flt_conf structure.

The utility layer gained flt_otel_strtod() and flt_otel_strtoll() for
validated string-to-number conversion used by rate-limit and debug-level
parsing.

7 days agoMEDIUM: otel: added configuration and utility layer
Miroslav Zagorac [Sun, 12 Apr 2026 05:30:57 +0000 (07:30 +0200)] 
MEDIUM: otel: added configuration and utility layer

Added the configuration structures that model the OTel filter's
instrumentation hierarchy and the utility functions that support the
configuration parser.

The configuration is organized as a tree rooted at flt_otel_conf, which
holds the proxy reference, filter identity, and lists of groups and
scopes.  Below it, flt_otel_conf_instr carries the instrumentation
settings: tracer handle, rate limiting, hard-error mode, logging state,
channel analyzers, and placeholder references to groups and scopes.
Groups (flt_otel_conf_group) aggregate scopes by name.  Scopes
(flt_otel_conf_scope) bind an event to its ACL condition, span context
declarations, span definitions and a list of spans scheduled for
finishing.  Spans (flt_otel_conf_span) carry attributes, events,
baggages and status entries, each represented as flt_otel_conf_sample
structures that pair a key with concatenated sample-expression arguments.

All configuration types share a common header macro (FLT_OTEL_CONF_HDR)
that embeds an identifier string, its length, a configuration line number,
and a list link.  Their init and free functions are generated by the
FLT_OTEL_CONF_FUNC_INIT and FLT_OTEL_CONF_FUNC_FREE macros in
conf_funcs.h, with per-type custom initialization and cleanup bodies.

The utility layer in util.c provides argument counting and concatenation
for the configuration parser, sample data to string conversion covering
boolean, integer, IPv4, IPv6, string and HTTP method types, and debug
helpers for dumping argument arrays and linked list state.

7 days agoMEDIUM: otel: added OpenTelemetry filter skeleton
Miroslav Zagorac [Mon, 13 Apr 2026 05:48:51 +0000 (07:48 +0200)] 
MEDIUM: otel: added OpenTelemetry filter skeleton

The OpenTelemetry (OTel) filter enables distributed tracing of requests
across service boundaries, export of metrics such as request rates,
latencies and error counts, and structured logging tied to trace context,
giving operators a unified view of HAProxy traffic through any
OpenTelemetry-compatible backend.

The OTel filter is implemented using the standard HAProxy stream filter
API.  Stream filters attach to proxies and intercept traffic at each stage
of processing: they receive callbacks on stream creation and destruction,
channel analyzer events, HTTP header and payload processing, and TCP data
forwarding.  This allows the filter to collect telemetry data at every
stage of the request/response lifecycle without modifying the core proxy
logic.

This commit added the minimum set of files required for the filter to
compile: the addon Makefile with pkg-config-based detection of the
opentelemetry-c-wrapper library, header files with configuration
constants, utility macros and type definitions, and the source files
containing stub filter operation callbacks registered through
flt_otel_ops and the "opentelemetry" keyword parser entry point.

The filter uses the opentelemetry-c-wrapper library from HAProxy
Technologies, which provides a C interface to the OpenTelemetry C++ SDK.
This wrapper allows HAProxy, a C codebase, to leverage the full
OpenTelemetry observability pipeline without direct C++ dependencies
in the HAProxy source tree.

  https://github.com/haproxytech/opentelemetry-c-wrapper
  https://github.com/open-telemetry/opentelemetry-cpp

Build options:

  USE_OTEL     - enable the OpenTelemetry filter
  OTEL_DEBUG   - compile the filter in debug mode
  OTEL_INC     - force the include path to the C wrapper
  OTEL_LIB     - force the library path to the C wrapper
  OTEL_RUNPATH - add the C wrapper RUNPATH to the executable

Example build with OTel and debug enabled:

  make -j8 USE_OTEL=1 OTEL_DEBUG=1 TARGET=linux-glibc

7 days agoBUG/MINOR: xprt_qstrm: do not parse record length on read again
Amaury Denoyelle [Mon, 13 Apr 2026 06:47:01 +0000 (08:47 +0200)] 
BUG/MINOR: xprt_qstrm: do not parse record length on read again

conn_recv_qstrm() may be called several times per connection if the read
data is too short and a truncated record is received.

Previously, record length was parsed every time the function is invoked.
However, this must only be performed if record length varint is
incomplete. Once read and parsed, data are removed from the buffer via
b_quic_dec_int(). Thus, next conn_recv_qstrm() run will reread an
invalid record length this time.

This patch fixes this by only parsing record length if <rxrlen> member
is null. Prior to it, parsing of QMux transport parameters would fail in
case of a first truncated read, which would prevent the connection
initialization.

No need to backport.

7 days agoBUG/MINOR: mux_quic: prevent QMux crash on qcc_io_send() error path
Amaury Denoyelle [Fri, 10 Apr 2026 08:37:09 +0000 (10:37 +0200)] 
BUG/MINOR: mux_quic: prevent QMux crash on qcc_io_send() error path

A QCC connection may be flagged with QC_CF_ERRL to trigger a
CONNECTION_CLOSE emission. However, for now error reporting is not
functional with QMux, as it relies on quic_conn layer access.

To prevent a crash in qcc_io_send() when using QMux, add a
conn_is_quic() check when QC_CF_ERRL is set to ensure no access will be
performed on quic_conn layer. In the future, this should be extended so
that QMux is also able to emit CONNECTION_CLOSE for connection closure.

No need to backport.

10 days agoBUG/MEDIUM: haterm: Properly initialize the splicing support for haterm
Christopher Faulet [Fri, 10 Apr 2026 14:16:38 +0000 (16:16 +0200)] 
BUG/MEDIUM: haterm: Properly initialize the splicing support for haterm

First, we must not emit any warning if splicing is not configured and the
global maxpipes value is 0. Then we must not remove GTUNE_USE_SPLICE flag
when we fail to allocate the haterm master pipe. Instead, we test it when we
negociate with the opposite side, to properly exclude the splicing if it is
not usable.

No backport needed.

10 days agoRevert "BUG/MEDIUM: haterm: Move all init functions of haterm in haterm_init.c"
Christopher Faulet [Fri, 10 Apr 2026 13:50:41 +0000 (15:50 +0200)] 
Revert "BUG/MEDIUM: haterm: Move all init functions of haterm in haterm_init.c"

This reverts commit 8056117e988a3fde05d46ecc71b2d1a3d802977d.

Moving haterm init from haproxy is not the right way to fix the issue
because it should be possible to use a haterm configuration in haproxy.

So let's revert the commit above.

10 days agoBUG/MINOR: do not crash on QMux reception of BLOCKED frames
Amaury Denoyelle [Fri, 10 Apr 2026 08:28:23 +0000 (10:28 +0200)] 
BUG/MINOR: do not crash on QMux reception of BLOCKED frames

Add QUIC BLOCKED frames in the list of supported types in
qstrm_parse_frm(). Nothing is really implemented for them as for QUIC,
but this prevents a crash when receiving one of them via QMux.

No need to backport.

10 days agoDOC: update draft link for QMux protocol
Amaury Denoyelle [Wed, 8 Apr 2026 12:47:26 +0000 (14:47 +0200)] 
DOC: update draft link for QMux protocol

QMux draft 01 support is mostly achieved thanks to the recent
implementation of the Record layer. This patch thus updates the link in
the documentation to the validated draft version.

10 days agoMEDIUM: mux-quic/xprt_qstrm: implement QMux record emission
Amaury Denoyelle [Wed, 8 Apr 2026 08:33:10 +0000 (10:33 +0200)] 
MEDIUM: mux-quic/xprt_qstrm: implement QMux record emission

This patch implements emission of the new Record layer for QMux frames.
This handles mux-quic and xprt_qstrm layers as this is performed
similarly in both cases.

Currently, the simplest approach has been prefered : each frame is
encoded in its own record. This is not the most efficient in size but it
is extremely simple to implement for a first interop testing.

10 days agoMEDIUM: xprt_qstrm: implement QMux record parsing
Amaury Denoyelle [Wed, 8 Apr 2026 12:13:05 +0000 (14:13 +0200)] 
MEDIUM: xprt_qstrm: implement QMux record parsing

This patch implements the new QMux record layer parsing for xprt_qstrm.
This is mostly similar to the MUX code from the previous patch.

Along with this change, a new xprt_qstrm layer accessor exposes the
possible remaining record length after Transport parameters parsing.
This can only occur when xprt_qstrm Rx buffer is not completely emptied
due to other following frames. If stored in the same record, MUX layer
has to know the remaining record length.

Thus, xprt_qstrm_rxrlen() is now used in qmux_init() to preinitialize
<rx.rlen> QCC field.

10 days agoMEDIUM: mux-quic: implement QMux record parsing
Amaury Denoyelle [Wed, 8 Apr 2026 08:31:57 +0000 (10:31 +0200)] 
MEDIUM: mux-quic: implement QMux record parsing

This is the first patch of a serie which aims to support the new Record
layer defined by the draft 01 of QMux protocol.

  https://www.ietf.org/archive/id/draft-ietf-quic-qmux-01.html#name-qmux-records

This patch deals with QMux reception at the MUX layer. The function
qcc_qstrm_recv() is adapted to read record headers before frame parsing.
This requires to keep the last record length read in a new QCC field
named <rx.rlen>.

Frames are only parsed once a full record is received. One of the
advantage of the record layer is that it can only contains whole frame
without truncation.

10 days agoMINOR: xprt_qstrm: handle connection errors
Amaury Denoyelle [Wed, 8 Apr 2026 11:54:47 +0000 (13:54 +0200)] 
MINOR: xprt_qstrm: handle connection errors

This patch implements proper connection error handling for xprt_qstrm
layer. Basically, processing is interrupted if CO_FL_ERROR is
encountered after either rcv_buf or snd_buf operations. Connectionn
error is set to the newly defined value CO_ER_QSTRM.

10 days agoMINOR: xprt_qstrm: implement Tx buffering
Amaury Denoyelle [Wed, 8 Apr 2026 08:42:44 +0000 (10:42 +0200)] 
MINOR: xprt_qstrm: implement Tx buffering

This commit adds buffering on transmission for xprt_qstrm layer. This is
necessary in the rare case where send syscall only emits partial data.

A new <txbuf> member is defined in xprt_qstrm context. On first send
invokation, buffer is allocated and then the QMux transport parameters
frame is encoded. Then emission is performed via snd_buf and each time
the send function is invoked.

10 days agoMINOR: xprt_qstrm/mux-quic: handle extra QMux frames after params
Amaury Denoyelle [Fri, 10 Apr 2026 07:40:10 +0000 (09:40 +0200)] 
MINOR: xprt_qstrm/mux-quic: handle extra QMux frames after params

Layer xprt_qstrm is responsible to read the initial QMux transport
parameters frame. However, it could receive more data if some other
frames follow it. This extra content can only be handled by the MUX
layer once initialized.

Theorically, it could have been implemented via MSG_PEEK. However, this
flag is currently ignored by SSL layer. Besides, it is tedious to
implement safely. A new approach has been prefered where the MUX layer
is responsible to retrieve remaining data via xprt_qstrm_rxbuf()
accessor function during its initialization.

Thus, qmux_init() now may retrieve the buffer from xprt_qstrm layer.
This is performed via b_xfer() which will result in a zero copy
transfer. If this happens, tasklet is immediately scheduled to start
demuxing.

10 days agoMINOR: xprt_qstrm: implement Rx buffering
Amaury Denoyelle [Tue, 7 Apr 2026 13:19:47 +0000 (15:19 +0200)] 
MINOR: xprt_qstrm: implement Rx buffering

Implement buffering for reception on xprt_qstrm layer. This is necessary
to handle reception of a truncated QMux transport parameters frame.

This is performed via a new dedicated <rxbuf> member in xprt_qstrm
context. Read is performed by reusing the buffer until a whole frame can
be read.

10 days agoBUG/MINOR: quic: increment pos pointer on QMux transport params parsing
Amaury Denoyelle [Wed, 8 Apr 2026 08:32:17 +0000 (10:32 +0200)] 
BUG/MINOR: quic: increment pos pointer on QMux transport params parsing

QUIC frame parsers functions take a <pos> pointer as input argument for
the data to be parsed. If parsing is successful, <pos> must be
incremented to point to the next data.

Increment was not performed when parsing QMux transport parameters
frame. This commit fixes this. Note that for now there is no real issue
as xprt_qstrm does not check the QMux frame length.

No need to backport.

10 days agoBUG/MINOR: mux-quic: fix potential NULL deref on qcc_release()
Amaury Denoyelle [Wed, 8 Apr 2026 12:58:08 +0000 (14:58 +0200)] 
BUG/MINOR: mux-quic: fix potential NULL deref on qcc_release()

In qcc_release(), <conn> may be NULL. Thus every access on it must be
tested.

With recent QMux introduction, a call to conn_is_quic() has been added
prior to registration of the stream rejection callback. It could lead to
NULL deref as <conn> is not tested there. Fix this by adding an extra
check on the pointer validity.

No need to backport.

10 days agoBUG/MINOR: hlua: fix use-after-free of HTTP reason string
Greg Kroah-Hartman [Thu, 9 Apr 2026 11:06:44 +0000 (13:06 +0200)] 
BUG/MINOR: hlua: fix use-after-free of HTTP reason string

hlua_applet_http_status() stored the result of luaL_optlstring()
directly in http_ctx->reason. The pointer references Lua-managed
string storage which is only guaranteed valid until the C function
returns to Lua. If the GC runs between applet:set_status(200, str)
and applet:start_response(), the pointer dangles.

hlua_applet_http_send_response() then calls ist(http_ctx->reason)
which does strlen() on freed memory, followed by memcpy into the
HTX status line. The freed-and-reallocated chunk contents are sent
verbatim to the HTTP client.

Trigger:

    applet:set_status(200, table.concat({"Reason ", str:rep(50)}))
    collectgarbage("collect"); collectgarbage("collect")
    applet:start_response()

With heap grooming, adjacent allocation contents (session data, TLS
material from the same thread) leak into the response status line.

Anchor the Lua string in the registry keyed by the http_ctx field
address so it survives until the applet is done with it. The
registry entry is overwritten on each call (handles repeated
set_status) and naturally cleaned up when the lua_State is closed.

This patch should be backported to all stable versions.

10 days agoBUG/MEDIUM: mux-fcgi: prevent record-length truncation with large bufsize
Greg Kroah-Hartman [Thu, 9 Apr 2026 11:06:46 +0000 (13:06 +0200)] 
BUG/MEDIUM: mux-fcgi: prevent record-length truncation with large bufsize

FCGI content_length is a 16-bit field but fcgi_set_record_size()
is called with size_t/uint32_t arguments. With tune.bufsize >= 65544
(legal; cfgparse-global.c only enforces <= INT_MAX-16), a single
HTX DATA block or accumulated outbuf can exceed 65535 bytes. The
implicit conversion to uint16_t silently truncates the length field
while b_add(mbuf, outbuf.data) writes the full body.

A client posting ~99000 bytes can craft the body so that bytes
after the truncated length are parsed by PHP-FPM as fresh FCGI
records on the connection: a smuggled BEGIN_REQUEST + PARAMS with
arbitrary SCRIPT_FILENAME / PHP_VALUE bypasses all haproxy ACLs.

Fix the zero-copy path by refusing it when the block exceeds 65535
bytes (falls through to copy). Fix the copy path by capping
outbuf.size to 65535 + header so the data-fill loop naturally
stops at the FCGI maximum and emits the rest in a subsequent record.

The PARAMS path at line 2084 is similarly affected but harder to
trigger (requires combined header+param size > 65535) and is
covered by the same outbuf.size cap pattern if applied there.

This patch must be backported to all stable versions.