Skip to content

HTTP/3 support via OpenSSL 3.5#13186

Open
bneradt wants to merge 3 commits into
apache:masterfrom
bneradt:openssl35-native-quic-support
Open

HTTP/3 support via OpenSSL 3.5#13186
bneradt wants to merge 3 commits into
apache:masterfrom
bneradt:openssl35-native-quic-support

Conversation

@bneradt

@bneradt bneradt commented May 20, 2026

Copy link
Copy Markdown
Contributor

First Commit: HTTP/3 via OpenSSL 3.5 + quiche

Fedora now ships OpenSSL 3.5 with the third-party QUIC TLS
callback API, but quiche still links against the older
quictls/BoringSSL symbols. ATS therefore could not use the system
OpenSSL library for downstream HTTP/3 without dragging in a different
TLS stack.

This adds CMake detection for the OpenSSL callback API and provides a
private compatibility layer that maps quiche's legacy hooks onto
SSL_set_quic_tls_cbs. This requires static quiche in that mode so ATS
resolves the shim symbols locally and links the final binaries against
the system OpenSSL libraries.

This also relaxes verifier-only HTTP/3 AuTest gates that do not execute
curl, so those tests can run when ATS has QUIC support but the installed
curl lacks HTTP/3.

Second Commit: HTTP/3 via OpenSSL 3.5 QUIC

OpenSSL 3.5 can terminate QUIC connections directly, but ATS only had a
quiche-backed HTTP/3 listener. Operators who want to use the system
OpenSSL QUIC stack needed a separate downstream backend without changing
the existing quiche path or origin HTTP/3 scope.

This adds an optional ENABLE_OPENSSL_QUIC backend that uses OpenSSL's
native QUIC listener and stream APIs for downstream HTTP/3. This keeps
the backend mutually exclusive with quiche, exposes TS_HAS_OPENSSL_QUIC,
and shares ATS's existing HTTP/3 stream handling above the transport.

This also installs native-QUIC TLS callbacks for ALPN and SNI
certificate selection before ATS has a QUIC NetVC to bind. OpenSSL
native QUIC does not make a selected SSL_CTX certificate active via
SSL_set_SSL_CTX alone, so this applies the selected cert, key, and chain
to the connection SSL.

This also broadens client-side HTTP/3 tests to run with either backend
and hardens transaction cleanup when OpenSSL closes stream state before
ATS finishes teardown. This caches stream identifiers, declines
listener-time QUIC tickets until a NetVC is bound, and adds focused H3
lifecycle and session-ticket coverage.

Testing

Production Testing

This is currently running in docs for https://docs.trafficserver.apache.org/ targets via Alt-Svc header rewrite rules to negotiate h3 if it is supported.

CI AuTests/Fedora 44 Note

This patch is built for openssl 3.5. It assumes CI is running the autests in a fedora:44 container which has that as the system openssl. The autests will fail to build as such until we switch over CI to fedora:44, which is hopefully soon.

@bneradt bneradt added this to the 11.0.0 milestone May 20, 2026
@bneradt bneradt self-assigned this May 20, 2026
@bneradt bneradt added HTTP/3 QUIC TLS Build work related to build configuration or environment Tests labels May 20, 2026
@bneradt bneradt force-pushed the openssl35-native-quic-support branch 12 times, most recently from 6901c2f to 0a42b65 Compare May 28, 2026 00:00
@bneradt bneradt force-pushed the openssl35-native-quic-support branch 2 times, most recently from 841b545 to f00cdf3 Compare May 28, 2026 02:27
@bneradt bneradt removed the Tests label May 28, 2026
@bneradt bneradt marked this pull request as ready for review May 28, 2026 15:19
Copilot AI review requested due to automatic review settings May 28, 2026 15:19
@bneradt bneradt changed the title Add OpenSSL native QUIC backend HTTP/3 support via OpenSSL 3.5 May 28, 2026
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from f00cdf3 to 0c99cfc Compare May 28, 2026 15:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Apache Traffic Server’s HTTP/3 / QUIC support to work with upstream OpenSSL 3.5 by (1) adding a quiche-to-OpenSSL 3.5 QUIC TLS callback compatibility shim and (2) introducing an optional OpenSSL-native QUIC backend that is mutually exclusive with quiche. It also broadens and adds gold tests so core HTTP/3 scenarios can run across either QUIC backend where appropriate.

Changes:

  • Add CMake feature detection + build options for OpenSSL 3.5 QUIC (native QUIC + QUIC TLS callbacks compatibility for quiche).
  • Introduce an OpenSSL-native QUIC NetProcessor/NetVC/PacketHandler path and wire it into existing QUIC/HTTP3 plumbing.
  • Expand/add gold tests and ATSReplay support for HTTP/3, plus minor HTTP/3 transaction / stream lifecycle fixes.

Reviewed changes

Copilot reviewed 59 out of 59 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/gold_tests/timeout/quic_no_activity_timeout.test.py Generalize QUIC gating to TS_USE_QUIC and keep quiche-specific assertions conditional.
tests/gold_tests/timeout/active_timeout.test.py Run HTTP/3 active-timeout curl path under TS_USE_QUIC (still requires curl http3).
tests/gold_tests/headers/via.test.py Run HTTP/3 portion of Via header test under TS_USE_QUIC.
tests/gold_tests/h3/replays/h3_stream_lifetime.replay.yaml New Proxy Verifier replay covering concurrent stream lifetime behavior.
tests/gold_tests/h3/replays/h3_sni.replay.yaml Adjust SNI/authority values used by the HTTP/3 SNI replay.
tests/gold_tests/h3/replays/h3_proxy_verifier.replay.yaml New replay covering multi-connection HTTP/3 proxy verifier interop.
tests/gold_tests/h3/replays/h3_active_timeout.replay.yaml New replay exercising HTTP/3 active-timeout cleanup behavior.
tests/gold_tests/h3/h3_stream_lifetime.test.py New ATSReplay-based test for stream lifetime handling.
tests/gold_tests/h3/h3_sni_check.test.py Update QUIC gating + cert material for the SNI behavior test.
tests/gold_tests/h3/h3_session_ticket.test.py New OpenSSL QUIC s_client-based session ticket test (OpenSSL 3.5+).
tests/gold_tests/h3/h3_proxy_verifier.test.py New ATSReplay-based test for proxy verifier client interop.
tests/gold_tests/h3/h3_curl.test.py New curl-based HTTP/3 interop test (requires curl http3 + --http3-only).
tests/gold_tests/h3/h3_active_timeout.test.py New verifier-based HTTP/3 active-timeout cleanup test.
tests/gold_tests/autest-site/ats_replay.test.ext Extend ATSReplay to pass http3_ports when QUIC is enabled in replay config.
src/traffic_server/traffic_server.cc Switch QUIC include gate to TS_USE_QUIC.
src/traffic_layout/info.cc Expose TS_HAS_OPENSSL_QUIC in feature reporting.
src/proxy/http3/test/test_Http3FrameDispatcher.cc Adjust dispatcher test expectations for incremental feed behavior.
src/proxy/http3/Http3Transaction.cc Improve stream-closure, timeout, and deferred read/write event handling; add safe self-delete gating.
src/proxy/http3/Http3StreamDataVIOAdaptor.cc Fix reader lifecycle and make finalize() idempotent.
src/proxy/http3/Http3Session.cc Avoid iterator invalidation while iterating transactions on session close.
src/proxy/http3/Http3HeaderVIOAdaptor.cc Notify transaction when header decode completes (to schedule pending reads).
src/proxy/http3/Http3Frame.cc Add DATA frame readiness parsing based on available payload bytes.
src/proxy/http3/Http3App.cc Inform transaction on QUIC stream close for cleanup.
src/iocore/net/SSLSessionTicket.cc Avoid hard-failing ticket callback when OpenSSL QUIC listener parses tickets before NetVC binding.
src/iocore/net/QUICNetVConnection.cc Refactor stream send/recv to use QUICStreamIO interface and add stream I/O wrappers.
src/iocore/net/QUICMultiCertConfigLoader.cc Add OpenSSL QUIC-specific SNI/cert selection + ALPN/curves hooks and TLS1.3-only enforcement for QUIC.
src/iocore/net/quic/QUICStreamVCAdapter.cc Split read vs consume semantics and improve write reenable behavior.
src/iocore/net/quic/QUICStreamManager.cc Implement local uni/bidi stream id allocation.
src/iocore/net/quic/QUICStreamAdapter.cc Add consume() API and move on_read() notification there.
src/iocore/net/quic/QUICStream.cc Abstract stream I/O via QUICStreamIO, support partial sends with pending blocks, and add on-write notification.
src/iocore/net/quic/QUICGlobals.cc Adjust session handling compilation gates for new QUIC backends.
src/iocore/net/quic/QUICConfig.cc Add QUIC server SSL_CTX creation via OpenSSL native QUIC method; gate quiche-only config APIs.
src/iocore/net/quic/OpenSSLQuicCompat.cc New compatibility shim exporting quiche-expected legacy QUIC TLS callback symbols via OpenSSL 3.5 QUIC TLS callbacks API.
src/iocore/net/quic/CMakeLists.txt Make quiche linkage conditional and add static compat shim library when required.
src/iocore/net/P_QUICPacketHandler.h Add OpenSSL QUIC includes and OpenSSL-specific handler members/methods under feature guards.
src/iocore/net/P_QUICNetVConnection.h Add OpenSSL QUIC init/stream plumbing, introduce QUICStreamIO, and add feature guards for quiche-only fields.
src/iocore/net/P_QUICNetProcessor.h Make quiche-only members conditional and adjust signatures for new handler creation paths.
src/iocore/net/OpenSSLQUICPacketHandler.cc New OpenSSL QUIC listener-based inbound packet handler implementation.
src/iocore/net/OpenSSLQUICNetVConnection.cc New OpenSSL QUIC-based NetVC implementing stream accept/read/write via OpenSSL QUIC stream APIs.
src/iocore/net/OpenSSLQUICNetProcessor.cc New OpenSSL QUIC NetProcessor wiring for inbound QUIC accept.
src/iocore/net/CMakeLists.txt Select OpenSSL QUIC vs quiche QUIC sources and link dependencies based on feature flags.
include/tscore/ink_config.h.cmake.in Add TS_HAS_OPENSSL_QUIC feature macro.
include/proxy/http3/Http3Transaction.h Add stream close notification, new read scheduling helper, close-state tracking, and safe delete gating.
include/proxy/http3/Http3StreamDataVIOAdaptor.h Track reader and finalized state for safe finalize/destruction.
include/proxy/http3/Http3HeaderVIOAdaptor.h Minor formatting cleanup.
include/proxy/http3/Http3Frame.h Allow DATA frame to override parsing readiness behavior.
include/iocore/net/QUICMultiCertConfigLoader.h Add OpenSSL QUIC hook overrides behind TS_HAS_OPENSSL_QUIC.
include/iocore/net/quic/QUICStreamVCAdapter.h Add _consume() override hook.
include/iocore/net/quic/QUICStreamManager.h Make stream-creation methods virtual with out-params; track next local stream ids.
include/iocore/net/quic/QUICStreamAdapter.h Add consume() API and require _consume() implementations.
include/iocore/net/quic/QUICStream.h Introduce QUICStreamIO interface and pending-send tracking for partial writes.
include/iocore/net/quic/QUICConnection.h Add optional on_stream_updated() hook.
include/iocore/net/quic/QUICConfig.h Gate quiche-only APIs and declare quic_new_server_ssl_ctx().
include/iocore/net/quic/Mock.h Update mock stream adapter behavior to align with read/consume split.
CMakePresets.json Add CI preset wiring for OpenSSL QUIC enablement and adjust Fedora preset inheritance.
CMakeLists.txt Add ENABLE_OPENSSL_QUIC, OpenSSL QUIC feature checks, mutual exclusivity with quiche, and quiche static selection under compat mode.
cmake/Findquiche.cmake Support selecting static quiche when required by compat shim linkage needs.
cmake/CheckOpenSSLHasQuicTlsCbs.cmake New compile-check for OpenSSL QUIC TLS callback API availability.
cmake/CheckOpenSSLHasNativeQuic.cmake New compile-check for OpenSSL native QUIC listener/stream APIs availability.

Comment thread tests/gold_tests/h3/h3_session_ticket.test.py Outdated
Comment thread tests/gold_tests/timeout/quic_no_activity_timeout.test.py
Comment thread include/iocore/net/quic/QUICStreamAdapter.h
Comment thread src/iocore/net/OpenSSLQUICPacketHandler.cc
Comment thread src/iocore/net/OpenSSLQUICNetVConnection.cc
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from 0c99cfc to 1ecea18 Compare May 28, 2026 16:19
@bneradt bneradt requested a review from Copilot May 28, 2026 17:21
@zwoop

zwoop commented May 29, 2026

Copy link
Copy Markdown
Contributor

[approve ci autest 2]

Copilot AI review requested due to automatic review settings May 30, 2026 17:01
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from 7b26164 to 8da4a30 Compare May 30, 2026 17:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 71 out of 72 changed files in this pull request and generated 3 comments.

Comment thread src/proxy/http3/Http3Session.cc Outdated
Comment on lines 675 to 679
UDPNetProcessorInternal::udp_read_from_net(UDPNetHandler *nh, UDPConnection *xuc)
{
#if HAVE_RECVMMSG
read_multiple_messages_from_net(nh, xuc);
while (read_multiple_messages_from_net(nh, xuc)) {}
#else
Comment thread tests/gold_tests/h3/h3_go_client.test.py
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from 8da4a30 to c161a49 Compare May 30, 2026 19:11
Copilot AI review requested due to automatic review settings May 30, 2026 19:54
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from c161a49 to 006cd26 Compare May 30, 2026 19:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 73 out of 74 changed files in this pull request and generated 2 comments.

Comment thread src/proxy/http3/Http3Session.cc Outdated
Comment thread src/proxy/http3/Http3App.cc
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from 006cd26 to a2e5c6e Compare June 5, 2026 03:32
Copilot AI review requested due to automatic review settings June 5, 2026 04:22
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from a2e5c6e to 2342450 Compare June 5, 2026 04:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 76 out of 77 changed files in this pull request and generated 3 comments.

Comment thread src/iocore/net/quic/QUICConfig.cc
Comment on lines 674 to 679
void
UDPNetProcessorInternal::udp_read_from_net(UDPNetHandler *nh, UDPConnection *xuc)
{
#if HAVE_RECVMMSG
read_multiple_messages_from_net(nh, xuc);
while (read_multiple_messages_from_net(nh, xuc)) {}
#else
Comment thread tests/gold_tests/autest-site/conditions.test.ext Outdated
@bneradt bneradt force-pushed the openssl35-native-quic-support branch 2 times, most recently from 952254e to ae8c052 Compare June 6, 2026 01:47
Copilot AI review requested due to automatic review settings June 6, 2026 01:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 74 out of 75 changed files in this pull request and generated 2 comments.

Comment thread src/iocore/net/quic/QUICStreamVCAdapter.cc
Comment thread src/iocore/net/quic/QUICStreamVCAdapter.cc Outdated
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from ae8c052 to 43b7945 Compare June 7, 2026 02:53
Copilot AI review requested due to automatic review settings June 7, 2026 04:02
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from 43b7945 to 45b6530 Compare June 7, 2026 04:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 92 out of 93 changed files in this pull request and generated 2 comments.

Comment on lines +307 to +309
if (error && error->cls != Http3ErrorClass::UNDEFINED) {
this->_handle_error(*error);
}
Comment on lines 30 to +33
return {Http3FrameType::DATA, Http3FrameType::HEADERS, Http3FrameType::X_RESERVED_1, Http3FrameType::CANCEL_PUSH,
Http3FrameType::SETTINGS, Http3FrameType::PUSH_PROMISE, Http3FrameType::X_RESERVED_2, Http3FrameType::GOAWAY,
Http3FrameType::X_RESERVED_3, Http3FrameType::X_RESERVED_4, Http3FrameType::MAX_PUSH_ID, Http3FrameType::X_MAX_DEFINED,
Http3FrameType::UNKNOWN};
Http3FrameType::RESERVED, Http3FrameType::UNKNOWN};
bneradt added 3 commits June 8, 2026 16:23
# Overview
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, H3-to-H2 origin forwarding, range responses over cached
objects, and malformed HTTP/3 frame behavior. This patch also includes
the various production fixes needed for these tests.

# Issues Found and their Fixes

## UDP batches could stall large H3 transfers
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.

## QUIC stream writes consumed data before quiche accepted it
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.

## QUIC stream reads could expose bytes beyond the VIO request
The new H3-to-H2 and 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 and protocol translation 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.

## H3 transaction cleanup raced with stream closure
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 adds explicit transaction lifetime state in `HQTransaction`:
`_closed`, `_stream_closed`, `_event_handler_active`, and
`_is_write_buffer_flushed()`. `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.

## Malformed H3 streams could leave transactions behind
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.

## H3 read completion could run before headers and DATA were settled
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.

## Malformed H3 frames were not consistently enforced
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.

## H3-to-H2 origin traffic exposed H2 body accounting bugs
The H3-to-H2 origin coverage found that HEAD and large request-body
translation depended on HTTP/2 knowing both the original request method
and the exact remaining write VIO byte count. Without that, an H2 origin
stream could send DATA past the finite request body or mishandle
no-body HEAD semantics.

This records the sent request method in `Http2Stream` and uses it when
validating response body framing.
`Http2ConnectionState::send_a_data_frame()` now caps DATA payloads to
the write VIO `ntodo()` value and sends END_STREAM when a finite body
has been exhausted, even if the reader has additional buffered bytes.

## The QPACK static table had drifted from the standard table
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.
Fedora now ships OpenSSL 3.5 with the third-party QUIC TLS
callback API, but quiche still links against the older
quictls/BoringSSL symbols. ATS therefore could not use the system
OpenSSL library for downstream HTTP/3 without dragging in a different
TLS stack.

This adds CMake detection for the OpenSSL callback API and provides a
private compatibility layer that maps quiche's legacy hooks onto
SSL_set_quic_tls_cbs. This requires static quiche in that mode so ATS
resolves the shim symbols locally and links the final binaries against
the system OpenSSL libraries.

This also relaxes verifier-only HTTP/3 AuTest gates that do not execute
curl, so those tests can run when ATS has QUIC support but the installed
curl lacks HTTP/3.
OpenSSL 3.5 can terminate QUIC connections directly, but ATS only had a
quiche-backed HTTP/3 listener. Operators who want to use the system
OpenSSL QUIC stack needed a separate downstream backend without changing
the existing quiche path or origin HTTP/3 scope.

This adds an optional ENABLE_OPENSSL_QUIC backend that uses OpenSSL's
native QUIC listener and stream APIs for downstream HTTP/3. This keeps
the backend mutually exclusive with quiche, exposes TS_HAS_OPENSSL_QUIC,
and shares ATS's existing HTTP/3 stream handling above the transport.

This also installs native-QUIC TLS callbacks for ALPN and SNI
certificate selection before ATS has a QUIC NetVC to bind. OpenSSL
native QUIC does not make a selected SSL_CTX certificate active via
SSL_set_SSL_CTX alone, so this applies the selected cert, key, and chain
to the connection SSL.

This also broadens client-side HTTP/3 tests to run with either backend
and hardens transaction cleanup when OpenSSL closes stream state before
ATS finishes teardown. This caches stream identifiers, declines
listener-time QUIC tickets until a NetVC is bound, and adds focused H3
lifecycle and session-ticket coverage.
@bneradt bneradt force-pushed the openssl35-native-quic-support branch from 45b6530 to 7b994e9 Compare June 8, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build work related to build configuration or environment HTTP/3 QUIC TLS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants