Skip to content

Commit 9426649

Browse files
author
bneradt
committed
HTTP/3 via OpenSSL 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, keeps H3 streams open across informational responses, 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.
1 parent 64ca285 commit 9426649

37 files changed

Lines changed: 2498 additions & 85 deletions

CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ set(ENABLE_TPROXY
215215
'X' where X is a number to use as the IP_TRANSPARENT sockopt,
216216
anything else to enable."
217217
)
218+
option(ENABLE_OPENSSL_QUIC "Use OpenSSL native QUIC (default OFF)")
218219
option(ENABLE_QUICHE "Use quiche (default OFF)")
219220

220221
option(ENABLE_EXAMPLE "Build example directory (default OFF)")
@@ -284,6 +285,7 @@ include(CheckOpenSSLIsBoringSSL)
284285
include(CheckOpenSSLIsQuictls)
285286
include(CheckOpenSSLIsAwsLc)
286287
include(CheckOpenSSLHasQuicTlsCbs)
288+
include(CheckOpenSSLHasNativeQuic)
287289
find_package(OpenSSL REQUIRED)
288290
check_openssl_is_boringssl(SSLLIB_IS_BORINGSSL BORINGSSL_VERSION "${OPENSSL_INCLUDE_DIR}")
289291
check_openssl_is_awslc(SSLLIB_IS_AWSLC AWSLC_VERSION "${OPENSSL_INCLUDE_DIR}")
@@ -326,6 +328,8 @@ if(SSLLIB_HAS_QUIC_TLS_CBS
326328
add_compile_definitions(TS_OPENSSL_QUIC_TLS_CBS_COMPAT)
327329
endif()
328330

331+
check_openssl_has_native_quic(SSLLIB_HAS_NATIVE_QUIC "${OPENSSL_INCLUDE_DIR}")
332+
329333
if(ENABLE_PROFILER)
330334
find_package(profiler REQUIRED)
331335
set(TS_HAS_PROFILER ${profiler_FOUND})
@@ -341,6 +345,25 @@ elseif(TS_HAS_MIMALLOC)
341345
link_libraries(mimalloc)
342346
endif()
343347

348+
if(ENABLE_OPENSSL_QUIC AND ENABLE_QUICHE)
349+
message(FATAL_ERROR "ENABLE_OPENSSL_QUIC and ENABLE_QUICHE are mutually exclusive QUIC backends")
350+
endif()
351+
352+
if(ENABLE_OPENSSL_QUIC)
353+
if(NOT SSLLIB_HAS_NATIVE_QUIC)
354+
message(FATAL_ERROR "OpenSSL native QUIC support requires OpenSSL 3.5 or newer with OSSL_QUIC_server_method")
355+
endif()
356+
if(SSLLIB_IS_BORINGSSL
357+
OR SSLLIB_IS_AWSLC
358+
OR SSLLIB_IS_QUICTLS
359+
)
360+
message(FATAL_ERROR "OpenSSL native QUIC support requires upstream OpenSSL 3.5 or newer")
361+
endif()
362+
set(TS_HAS_OPENSSL_QUIC TRUE)
363+
set(TS_USE_QUIC TRUE)
364+
message(STATUS "Using OpenSSL native QUIC")
365+
endif()
366+
344367
if(ENABLE_QUICHE)
345368
if(TS_OPENSSL_QUIC_TLS_CBS_COMPAT)
346369
set(quiche_USE_STATIC TRUE)

CMakePresets.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@
191191
"wamr_ROOT": "/opt",
192192
"ENABLE_OTEL_TRACER": "ON",
193193
"ENABLE_WASM_WAMR": "ON",
194-
"ENABLE_CRIPTS": "ON"
194+
"ENABLE_CRIPTS": "ON",
195+
"ENABLE_OPENSSL_QUIC": "ON"
195196
}
196197
},
197198
{
@@ -207,7 +208,7 @@
207208
"name": "ci-fedora-quiche",
208209
"displayName": "CI Fedora Quiche",
209210
"description": "CI Pipeline config for Fedora Linux (quiche build)",
210-
"inherits": ["ci"],
211+
"inherits": ["ci-fedora"],
211212
"cacheVariables": {
212213
"OPENSSL_ROOT_DIR": "/opt/h3-tools-boringssl/boringssl",
213214
"quiche_ROOT": "/opt/h3-tools-boringssl/quiche",
@@ -217,6 +218,7 @@
217218
"ENABLE_OTEL_TRACER": "ON",
218219
"ENABLE_WASM_WAMR": "ON",
219220
"CMAKE_INSTALL_PREFIX": "/tmp/ats-quiche",
221+
"ENABLE_OPENSSL_QUIC": "OFF",
220222
"ENABLE_QUICHE": "ON"
221223
}
222224
},
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#######################
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one or more contributor license
4+
# agreements. See the NOTICE file distributed with this work for additional information regarding
5+
# copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software distributed under the License
12+
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
# or implied. See the License for the specific language governing permissions and limitations under
14+
# the License.
15+
#
16+
#######################
17+
18+
function(CHECK_OPENSSL_HAS_NATIVE_QUIC OUT_VAR OPENSSL_INCLUDE_DIR)
19+
set(CHECK_PROGRAM
20+
"
21+
#include <openssl/ssl.h>
22+
#include <openssl/quic.h>
23+
#include <cstdint>
24+
25+
int main() {
26+
const SSL_METHOD *method = OSSL_QUIC_server_method();
27+
SSL *ssl = nullptr;
28+
uint64_t value = 0;
29+
return method == nullptr ||
30+
SSL_new_listener == nullptr ||
31+
SSL_listen == nullptr ||
32+
SSL_handle_events == nullptr ||
33+
SSL_accept_connection == nullptr ||
34+
SSL_accept_stream == nullptr ||
35+
SSL_new_stream == nullptr ||
36+
SSL_stream_conclude == nullptr ||
37+
SSL_get_stream_id == nullptr ||
38+
SSL_get_stream_type == nullptr ||
39+
SSL_get_stream_read_state == nullptr ||
40+
SSL_get_stream_write_buf_avail(ssl, &value) ||
41+
SSL_get_conn_close_info == nullptr ||
42+
SSL_shutdown_ex == nullptr ||
43+
SSL_set_default_stream_mode == nullptr ||
44+
SSL_set_blocking_mode == nullptr ||
45+
SSL_set_event_handling_mode(ssl, SSL_VALUE_EVENT_HANDLING_MODE_EXPLICIT) ||
46+
SSL_set_feature_request_uint(ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT, value) ||
47+
SSL_set_incoming_stream_policy == nullptr;
48+
}
49+
"
50+
)
51+
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
52+
set(CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
53+
include(CheckCXXSourceCompiles)
54+
check_cxx_source_compiles("${CHECK_PROGRAM}" ${OUT_VAR})
55+
set(${OUT_VAR}
56+
${${OUT_VAR}}
57+
PARENT_SCOPE
58+
)
59+
endfunction()

include/iocore/net/QUICMultiCertConfigLoader.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525

2626
#include "iocore/net/SSLMultiCertConfigLoader.h"
2727
#include "iocore/eventsystem/ConfigProcessor.h"
28+
#include "tscore/ink_config.h"
29+
30+
#include <string_view>
2831

2932
class QUICCertConfig
3033
{
@@ -40,6 +43,10 @@ class QUICCertConfig
4043
static int _config_id;
4144
};
4245

46+
#if TS_HAS_OPENSSL_QUIC
47+
std::string_view quic_sni_server_name(SSL *ssl);
48+
#endif
49+
4350
class QUICMultiCertConfigLoader : public SSLMultiCertConfigLoader
4451
{
4552
public:
@@ -48,7 +55,12 @@ class QUICMultiCertConfigLoader : public SSLMultiCertConfigLoader
4855
virtual SSL_CTX *default_server_ssl_ctx() override;
4956

5057
private:
51-
const char *_debug_tag() const override;
58+
const char *_debug_tag() const override;
59+
#if TS_HAS_OPENSSL_QUIC
60+
virtual void _set_handshake_callbacks(SSL_CTX *ctx) override;
61+
virtual bool _set_alpn_callback(SSL_CTX *ctx) override;
62+
virtual bool _set_curves(SSL_CTX *ctx) override;
63+
#endif
5264
virtual bool _setup_session_cache(SSL_CTX *ctx) override;
5365
virtual bool _set_cipher_suites_for_legacy_versions(SSL_CTX *ctx) override;
5466
virtual bool _set_info_callback(SSL_CTX *ctx) override;

include/iocore/net/TLSSNISupport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ class TLSSNISupport
123123
* @return True if the servername was set successfully, false otherwise.
124124
*/
125125
bool set_sni_server_name(SSL *ssl, char const *name);
126+
void set_sni_server_name(std::string_view name);
126127

127128
/**
128129
* Get the server name in SNI

include/iocore/net/quic/QUICConfig.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
#pragma once
2525

2626
#include <openssl/ssl.h>
27+
#include "tscore/ink_config.h"
28+
29+
#if TS_HAS_QUICHE
2730
#include <quiche.h>
31+
#endif
2832

2933
#include "iocore/eventsystem/ConfigProcessor.h"
3034
#include "iocore/net/SSLTypes.h"
@@ -89,7 +93,9 @@ class QUICConfigParams : public ConfigInfo
8993

9094
bool disable_http_0_9() const;
9195

96+
#if TS_HAS_QUICHE
9297
quiche_cc_algorithm get_cc_algorithm() const;
98+
#endif
9399

94100
private:
95101
static int _connection_table_size;
@@ -163,3 +169,4 @@ class QUICConfig
163169
};
164170

165171
SSL_CTX *quic_new_ssl_ctx();
172+
SSL_CTX *quic_new_server_ssl_ctx();

include/iocore/net/quic/QUICStream.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,25 @@
3131
#include "iocore/net/quic/QUICConnection.h"
3232
#include "iocore/net/quic/QUICDebugNames.h"
3333

34-
#include <quiche.h>
34+
#include <cstddef>
35+
#include <cstdint>
3536

3637
class QUICStreamAdapter;
3738
class QUICStreamStateListener;
3839

40+
class QUICStreamIO
41+
{
42+
public:
43+
using ErrorCode = uint64_t;
44+
45+
virtual ~QUICStreamIO() = default;
46+
47+
virtual int64_t read_stream(QUICStreamId stream_id, uint8_t *buf, size_t len, bool &fin, ErrorCode &error_code) = 0;
48+
virtual bool stream_read_finished(QUICStreamId stream_id) = 0;
49+
virtual int64_t stream_write_capacity(QUICStreamId stream_id) = 0;
50+
virtual int64_t write_stream(QUICStreamId stream_id, uint8_t const *buf, size_t len, bool fin, ErrorCode &error_code) = 0;
51+
};
52+
3953
/**
4054
* @brief QUIC Stream
4155
* TODO: This is similar to Http2Stream. Need to think some integration.
@@ -61,8 +75,8 @@ class QUICStream
6175
void stop_sending(QUICStreamErrorUPtr error);
6276
void reset(QUICStreamErrorUPtr error);
6377

64-
void receive_data(quiche_conn *quiche_con);
65-
void send_data(quiche_conn *quiche_con);
78+
void receive_data(QUICStreamIO &stream_io);
79+
int64_t send_data(QUICStreamIO &stream_io);
6680

6781
/*
6882
* QUICApplication need to call one of these functions when it process VC_EVENT_*

include/iocore/net/quic/QUICStreamManager.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ class QUICStreamManager : public QUICStreamStateListener
4848

4949
QUICStream *create_stream(QUICStreamId stream_id, QUICConnectionError &err);
5050

51-
QUICConnectionErrorUPtr create_uni_stream(QUICStreamId new_stream_id);
52-
QUICConnectionErrorUPtr create_bidi_stream(QUICStreamId new_stream_id);
53-
QUICConnectionErrorUPtr delete_stream(QUICStreamId new_stream_id);
54-
void reset_stream(QUICStreamId stream_id, QUICStreamErrorUPtr error);
51+
virtual QUICConnectionErrorUPtr create_uni_stream(QUICStreamId &new_stream_id);
52+
virtual QUICConnectionErrorUPtr create_bidi_stream(QUICStreamId &new_stream_id);
53+
QUICConnectionErrorUPtr delete_stream(QUICStreamId new_stream_id);
54+
void reset_stream(QUICStreamId stream_id, QUICStreamErrorUPtr error);
5555

5656
void set_default_application(QUICApplication *app);
5757

@@ -63,4 +63,6 @@ class QUICStreamManager : public QUICStreamStateListener
6363
protected:
6464
QUICContext *_context = nullptr;
6565
QUICApplicationMap *_app_map = nullptr;
66+
QUICStreamId _next_local_bidi_stream_id{0};
67+
QUICStreamId _next_local_uni_stream_id{0};
6668
};

include/proxy/http3/Http3Frame.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ class Http3DataFrame : public Http3Frame
9898
// Head of IOBufferBlock chain to send
9999
Ptr<IOBufferBlock> _whole_frame;
100100
uint64_t _payload_len = 0;
101+
102+
protected:
103+
bool _parse() override;
101104
};
102105

103106
//

include/proxy/http3/Http3HeaderFramer.h

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,21 @@ class Http3HeaderFramer : public Http3FrameGenerator
4444
Http3FrameUPtr generate_frame() override;
4545
bool is_done() const override;
4646

47+
void reset();
48+
bool is_final_header_sent() const;
49+
4750
private:
48-
Http3Transaction *_transaction = nullptr;
49-
VIO *_source_vio = nullptr;
50-
QPACK *_qpack = nullptr;
51-
MIOBuffer *_header_block = nullptr;
52-
IOBufferReader *_header_block_reader = nullptr;
53-
uint64_t _header_block_len = 0;
54-
uint64_t _header_block_wrote = 0;
55-
uint64_t _stream_id = 0;
56-
bool _sent_all_data = false;
51+
Http3Transaction *_transaction = nullptr;
52+
VIO *_source_vio = nullptr;
53+
QPACK *_qpack = nullptr;
54+
MIOBuffer *_header_block = nullptr;
55+
IOBufferReader *_header_block_reader = nullptr;
56+
uint64_t _header_block_len = 0;
57+
uint64_t _header_block_wrote = 0;
58+
uint64_t _stream_id = 0;
59+
bool _sent_all_data = false;
60+
bool _is_current_header_final = false;
61+
bool _is_final_header_sent = false;
5762
HTTPParser _http_parser;
5863
HTTPHdr _header;
5964
VersionConverter _hvc;

0 commit comments

Comments
 (0)