← Commit list

d1702a24b3

Stefan Eissing
Stefan Eissing
2026-08-25
SUCCESS Duration: 430s

Subject

ngtcp2+openssL: fix early data

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md+0 -1
@@ -103,7 +103,6 @@ BoringSSL). It works on TCP and QUIC connections using ngtcp2.
This option works on a best effort basis,
in cases when it was not possible to send early data the request is resent
normally post-handshake.
-This option does not work when using QUIC.
(Added in 8.11.0 for GnuTLS and 8.13.0 for OpenSSL, quictls and wolfSSL)
# DEFAULT
lib/vquic/cf-ngtcp2-cmn.c+29 -7
@@ -274,15 +274,24 @@ static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
}
if(ctx->use_earlydata) {
#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
- ctx->earlydata_accepted =
- (SSL_get_early_data_status(ctx->tls.ossl.ssl) !=
- SSL_EARLY_DATA_REJECTED);
+ /* Check for bug that OpenSSL did not even send the early data. */
+ if(SSL_get_early_data_status(ctx->tls.ossl.ssl) == SSL_EARLY_DATA_NOT_SENT)
+ CURL_TRC_CF(data, cf, "OpenSSL did not send early data");
#endif
-#ifdef USE_GNUTLS
+
+#if NGTCP2_VERSION_NUM >= 0x011700
+ ctx->earlydata_accepted =
+ !ngtcp2_conn_get_tls_early_data_rejected2(ctx->qconn);
+#else /* older NGTCP2 */
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
+ int ossl_early_status = SSL_get_early_data_status(ctx->tls.ossl.ssl);
+ if(ossl_early_status == SSL_EARLY_DATA_NOT_SENT)
+ CURL_TRC_CF(data, cf, "OpenSSL did not send early data");
+ ctx->earlydata_accepted = (ossl_early_status == SSL_EARLY_DATA_ACCEPTED);
+#elif defined(USE_GNUTLS)
int flags = gnutls_session_get_flags(ctx->tls.gtls.session);
ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA);
-#endif
-#ifdef USE_WOLFSSL
+#elif defined(USE_WOLFSSL)
#ifdef WOLFSSL_EARLY_DATA
ctx->earlydata_accepted =
(wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) !=
@@ -291,7 +300,8 @@ static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
DEBUGASSERT(0); /* should not come here if ED is disabled. */
ctx->earlydata_accepted = FALSE;
#endif /* WOLFSSL_EARLY_DATA */
-#endif
+#endif /* OPENSSL or GNUTLS or WOLFSSL */
+#endif /* older NGTCP2 */
CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data",
ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip);
Curl_pgrsEarlyData(data, ctx->earlydata_accepted ?
@@ -1068,6 +1078,18 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
#error "ngtcp2 TLS backend not defined"
#endif
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) && \
+ defined(OPENSSL_QUIC_API2)
+ /* We need to tell OpenSSL to *really* use Early Data for QUIC and
+ * this only works *after* ngtcp2 has tweaked all SSL parameters,
+ * otherwise OpenSSL does not accept it. */
+ if(ctx->use_earlydata &&
+ !SSL_set_quic_tls_early_data_enabled(ctx->tls.ossl.ssl, 1)) {
+ CURL_TRC_CF(data, cf, "OpenSSL refused to use early data");
+ ctx->use_earlydata = FALSE;
+ cf->connected = FALSE;
+ }
+#endif
ngtcp2_ccerr_default(&ctx->last_error);
return CURLE_OK;
tests/http/test_02_download.py+2 -8
@@ -526,7 +526,7 @@ class TestDownload:
assert r.total_connects <= 3, r.dump_logs()
# nghttpx is the only server we have that supports TLS early data
- @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx")
+ @pytest.mark.skipif(condition=not Env.have_h3_server(), reason="no nghttpx with QUIC")
@pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug")
@pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings")
@pytest.mark.parametrize("proto", Env.http_protos())
@@ -535,12 +535,6 @@ class TestDownload:
pytest.skip('TLS earlydata not implemented')
if proto == 'h3' and not env.curl_can_h3_early_data():
pytest.skip("h3 early data not supported")
- if proto != 'h3' and sys.platform.startswith('darwin') and env.ci_run:
- pytest.skip('failing on macOS CI runners')
- if proto == 'h3' and env.curl_uses_lib('wolfssl'):
- pytest.skip('h3 wolfssl early data failing')
- if proto == 'h3' and env.curl_uses_lib('gnutls'):
- pytest.skip('h3 gnutls early data failing')
count = 2
docname = 'data-10k'
# we want this test to always connect to nghttpx, since it is
@@ -571,7 +565,7 @@ class TestDownload:
if m:
earlydata[int(m.group(1))] = int(m.group(2))
continue
- if re.match(r'\[1-1] \* SSL reusing session.*', line):
+ if re.match(r'\[1-1] \* (\[1-1] )?SSL reusing session.*', line):
reused_session = True
assert reused_session, 'session was not reused for 2nd transfer'
assert earlydata[0] == 0, f'{earlydata}'