Skip to content

10.2.0: final batch of For v10.2.0 picks - #13460

Merged
cmcfarlen merged 11 commits into
apache:10.2.xfrom
cmcfarlen:10.2.x-picks-20260730
Jul 31, 2026
Merged

10.2.0: final batch of For v10.2.0 picks#13460
cmcfarlen merged 11 commits into
apache:10.2.xfrom
cmcfarlen:10.2.x-picks-20260730

Conversation

@cmcfarlen

@cmcfarlen cmcfarlen commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Draft — CI verification

Final batch of last-minute adds for 10.2.0: the 10 PRs sitting at For v10.2.0 in the
ATS v10.2.x project, cherry-picked with -x in master merge order.

PR Title
#13349 Support OpenSSL 3.0 APIs for Diffie-Hellman
#13363 Fix H2 origin payload handling
#13416 Allow dynamic TLS record sizing
#13423 Fix Fedora OTEL and WAMR builds
#13429 doc: clarify run-plugin argument is fixed at load time
#13156 Add Prometheus v2 labeled stats
#13213 Add H3 quiche traffic handling tests and provide fixes
#13391 TSHttpAltInfoQualitySet: add an autest
#13402 Extend pipeline autest to verify HTTP/1.1 request framing
#13453 Fix PluginVC server connection cast
#13461 Fix JSONRPC server shutdown race

None carry the Incompatible label; none were already present on 10.2.x.

Adaptations for 10.2.x

  • Allow dynamic TLS record sizing #13416 — kept 10.2.x's ssl.session_cache.* records block; applied only the
    validation-range change ([0-16383][-1-16383]) that the fix is about.
  • Add H3 quiche traffic handling tests and provide fixes #13213 — three small conflicts resolved (_close_transactions() in the HQSession
    dtor, _handle_error() on the read-vio error path, and the SNI test's cert setup).
    Its new h3 autests used the ssl_multicert.yaml Disk API, which the 10.2.x autest
    harness doesn't provide; converted h3_curl and h3_go_client to the flat
    ssl_multicert.config form (the other new h3 tests already guard with hasattr).

Testing

Local build clean; ctest 162/163 — the single failure is the pre-existing macOS-local
test_jsonrpcserver unix-socket flake, unrelated to these picks.

#13461 was added after the first CI run: test_jsonrpcserver failed on Fedora with a
hang/timeout, and #13461 fixes exactly that shutdown/startup race. It is also a
For v10.2.0 item. Note it does NOT resolve the separate macOS-local failure of that test
(a /var/folders unix-socket ENOENT/EPERM), which is why local runs can't validate it —
this CI run is the check.

Note this build had QUIC disabled locally, so CI is the real signal for #13213's H3
changes
(and for the new h3 autests generally).

JosiahWI and others added 11 commits July 30, 2026 14:21
* Add unit tests for SSL Diffie-Hellman key configuration

* Support OpenSSL 3.0 APIs for Diffie-Hellman

* Move DH keygen to SSLKeyUtils.{h,cc}

(cherry picked from commit ec93038)
HTTP/2 origin responses can legally carry a non-zero Content-Length
when no payload is sent, such as responses to HEAD requests. ATS
discarded the outbound request method after encoding the H2 HEADERS
frame and could therefore reject a valid no-body response as a
payload-length error.

An H2 DATA sender also treated every byte visible through its
IOBufferReader as eligible for the current write. Reader availability is
independent of the finite VIO operation: VIO::ntodo() is the
authoritative boundary, and the ordinary network VConnection already
caps writes to it. Without that cap, the regression sent 327,675 bytes
for a 300,000-byte PUT and the H2 origin returned GOAWAY with
PROTOCOL_ERROR.

This retains the outbound request method on the H2 stream for response
validation. It also caps DATA payloads to the remaining write VIO bytes,
still setting END_STREAM when the final authorized bytes are sent, and
extends the H2 origin replay coverage with HEAD and large PUT cases.

(cherry picked from commit dcb1850)
The documented -1 value for proxy.config.ssl.max_record_size is
incorrectly rejected by records validation, leaving dynamic TLS record
sizing unreachable from records.yaml.

This widens the accepted range to include the dynamic sentinel,
clarifies the documented modes, and extends TLS wire-level coverage to
verify the small-to-large record transition.

Fixes: apache#13288
(cherry picked from commit a0ece04)
Fedora CI could silently omit the OTEL and WAMR plugins, while
BoringSSL builds mixed system OpenSSL headers and libraries. This made
the updated dependency image appear usable without proving either
plugin could load.

This makes the Fedora presets require both plugins and selects the
matching curl and TLS roots for system OpenSSL and BoringSSL builds.

This also distinguishes BoringSSL from OpenSSL 3 during configuration
and gives the WASM targets explicit OpenSSL dependencies so both TLS
variants build and load consistently.

(cherry picked from commit fa297c3)
The run-plugin action's plugin-argument is parsed once when the
rule loads, like a remap.config argument, and is never
re-evaluated per request, so variable interpolation (e.g.
%{HEADER:bar}) does not work there.

(cherry picked from commit 9a847a5)
Prometheus consumers currently only get flat ATS stat names, which makes
related counters hard to aggregate and can hide broken output behind
parser leniency. The draft v2 output also exposed that risk by letting
`completed` look like a request method and by interleaving samples from
the same family.

This adds a Prometheus v2 response format that groups samples by metric
family and derives labels for methods, directions, status codes, cache
results, time buckets, and cache volumes. This keeps lifecycle counters
such as completed requests as their own metrics while preserving the
existing v1 output.

This extends the stats_over_http AuTest and Prometheus ingester to
validate both the raw v2 exposition and the parser's view of it. This
catches split families, missing TYPE metadata, malformed labels, and
regressions in the expected labeled samples.

(cherry picked from commit 8d35fd2)
This patch extends the HTTP/3 autest coverage, using curl, Go,
Python/aioquic, and Proxy Verifier HTTP/3 clients to generate their
implementations of H3 traffic. It also adds request and response bodies
of various sizes, including "large" 300k bodies to exercise multiple
packet, buffer, and flow control ATS HTTP/3 implementations. It also
exercises interesting requests and responses, such as HEAD, 204, PUT,
DELETE, OPTIONS, range responses over cached objects, and malformed
HTTP/3 frame behavior. This patch also includes the various production
fixes needed for these tests.

Large request and response bodies exposed a UDP receive starvation bug
in the UDP read path. On systems using `recvmmsg()` with
edge-triggered readiness, ATS could read one full batch of datagrams
and then leave the rest queued in the kernel without another readable
event to wake the QUIC stack.

This changes
`UDPNetProcessorInternal::read_multiple_messages_from_net()` in
`src/iocore/net/UnixUDPNet.cc` to return whether the kernel supplied a
full batch. `udp_read_from_net()` now processes a bounded number of
full batches per event, preserving UDP batching for H3 while avoiding
both unread UDP bursts and unbounded net-thread monopolization under
sustained QUIC load.

The stream write path consumed the `QUICStreamVCAdapter` write reader
inside `_read()`, before `QUICStream::send_data()` knew whether
`quiche_conn_stream_send()` had accepted the bytes. When quiche accepted
only a partial write or returned a flow-control error, ATS could lose
stream data and report write progress too early.

This makes `QUICStream::send_data()` keep a pending
`IOBufferBlock`/FIN pair until quiche reports successful consumption,
and only then calls the new `QUICStreamAdapter::consume()` hook. The
concrete reader accounting lives in `QUICStreamVCAdapter::_consume()`,
while `QUICStream::has_data_to_send()`, `QUICStream::on_write()`, and
`QUICNetVConnection::on_stream_updated()` make newly writable stream
data schedule packet writes again. This also treats completed finite
writes with only FIN left as writable stream state, so empty bodies and
fully consumed bodies still close the H3 stream cleanly.

The large-body tests exposed that `QUICStreamVCAdapter::_read()` could
hand more data to the transaction than the read VIO requested. That was
usually hidden by small bodies, but larger reads made finite
request-body accounting fragile.

This clamps cloned input blocks in `QUICStreamVCAdapter::_read()` to the
requested and available byte count before filling the read VIO. The
adapter now also checks for a missing reader before touching the read
buffer, which makes late stream cleanup paths more defensive.

The timeout and stream lifetime tests exposed cases where an
`HQTransaction` could be deleted while an event handler was still
active, or while the QUIC stream adapter still had read/write cleanup to
finish. That left later stream-close and timeout paths touching state
that had already been torn down.

This makes the transaction and stream closed state derive from the
active event handlers instead of separate booleans that could drift from
the adapter state. `Http3App::on_stream_close()` now calls
`HQTransaction::stream_closed()` while holding the transaction mutex,
and `HQTransaction::_delete_if_possible()` waits until the transaction
is done, the stream is closed or no longer readable, and pending writes
have flushed before deleting the transaction.

The aioquic edge-case probes found malformed request streams that were
correctly rejected at the H3 layer but still left partially constructed
transactions attached to the session. Session teardown then either
asserted because the transaction list was not empty or touched the H3
session after `Http3Session` had already nulled its network connection.

This adds `HQSession::_close_transactions()` and drains any remaining
transactions before destroying the H3 session-specific state. It also
lets `Http3App::on_stream_close()` attach a cleanup callback to the
transaction so the application stream map is erased when the transaction
is actually destroyed, rather than when quiche first reports stream
closure.

The H3 request read path could signal completion before asynchronous
QPACK header decode and buffered DATA delivery had finished updating the
sink VIO. That showed up around HEAD, 204, and stream-close timing
because the HTTP state machine needed a stable view of whether headers
were decoded and whether a request body existed.

This updates `Http3HeaderVIOAdaptor::_on_qpack_decode_complete()` to add
the printed header length to the sink VIO and notify
`Http3Transaction::on_header_decode_complete()`, which schedules the
appropriate read event. `Http3StreamDataVIOAdaptor::finalize()` now uses
a persistent reader, writes buffered DATA into the sink VIO exactly
once, and updates `ndone`/`nbytes` consistently before the transaction
is signaled.

The aioquic client can write raw QUIC stream data, which exposed gaps in
ATS's HTTP/3 frame validation. Reserved frames on request streams,
DATA-before-HEADERS, client-created push streams, and duplicate control
streams did not all reliably close the QUIC connection with an H3
application error.

This adds request-stream enforcement through `Http3ProtocolEnforcer` in
`Http3Transaction`, recognizes reserved HTTP/3 frame types in
`Http3Frame`, and routes connection-level errors through
`Http3App::_handle_error()` and `Http3Transaction::_handle_error()` to
close the QUIC connection. The transaction signal path now also avoids
calling the HTTP state machine through closed transactions or the
initial zero-byte write VIO created before the HTTP response handler is
installed.

The HEAD, 204, and quic-go coverage exposed that ATS's static QPACK
table was not the table used by external HTTP/3 implementations. The
extra zstd entry and modified `accept-encoding` value in
`src/proxy/http3/QPACK.cc` shifted later static indexes, so an
externally encoded `:status 204` could decode as a different status.

This restores the standard static table entries by using
`accept-encoding: gzip, deflate, br` and removing the non-standard
`content-encoding: zstd` entry. The new 204 cases in
`tests/gold_tests/h3/replays/h3_proxy_verifier.replay.yaml`,
`tests/gold_tests/h3/replays/h3_server_for_go_client.replay.yaml`, and
`tests/gold_tests/h3/replays/h3_server_for_python_client.replay.yaml`
cover this interoperability point with Proxy Verifier, quic-go, and
aioquic.

(cherry picked from commit 3076c17)
This adds an AuTest to verify the TSHttpAltInfoQualitySet plugin API.
The test verifies alternate creation and selection through observable
cached responses.

Closes: apache#7205
(cherry picked from commit 799c4cc)
Traffic Server already handles these request shapes correctly; this
patch adds no product code and changes no behavior. It only adds test
coverage so that the currently correct handling cannot regress
unnoticed.

A proxy that disagrees with its origin about where a request body ends
can be desynchronized from that origin. Two common shapes provoke this:
pipelining a body-less POST (Content-Length: 0) ahead of a second
request, and sending a request with conflicting Content-Length header
fields. Traffic Server must keep such request boundaries intact.

This extends the pipeline autest with two runs that exercise those
shapes. This confirms that Traffic Server delivers the pipelined
requests to the origin as two independent requests, so the second
request cannot be folded into the first, and that it rejects the
ambiguously-framed request rather than forwarding it.

(cherry picked from commit 991524a)
Plugin intercepts provide a PluginVC for the server connection, but
HttpSM treats it as a UnixNetVConnection. This invalid downcast can
trigger libc++ RTTI diagnostics and is undefined behavior.

This keeps the connection at its NetVConnection base type, which
provides both TLS service lookups needed at that point.

Fixes: apache#8105
(cherry picked from commit 43e0b5d)
JSONRPC server shutdown can race with worker thread startup. When the
worker starts after stop_thread(), it restores the running flag and
polls a closed socket indefinitely. This causes test_jsonrpcserver and
process shutdown to hang.

This marks the socket server as running before the worker is created,
so a concurrent stop cannot be overwritten. It also passes the owning
server to the worker instead of relying on the mutable global server
pointer.

(cherry picked from commit 8b9a007)
@cmcfarlen

Copy link
Copy Markdown
Contributor Author

[approve ci]

@cmcfarlen

Copy link
Copy Markdown
Contributor Author

[approve ci rocky]

@cmcfarlen cmcfarlen closed this Jul 31, 2026
@cmcfarlen cmcfarlen reopened this Jul 31, 2026
@cmcfarlen

Copy link
Copy Markdown
Contributor Author

[approve ci rocky]

@cmcfarlen
cmcfarlen merged commit 1427ebf into apache:10.2.x Jul 31, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants