]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
vquic: drop support for OpenSSL-QUIC 20226/head
authorDaniel Stenberg <daniel@haxx.se>
Sat, 17 Jan 2026 21:49:28 +0000 (22:49 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 17 Jan 2026 21:49:34 +0000 (22:49 +0100)
- It is slower and uses more memory than the alternatives and is only
  experimental in curl.
- We disable a few tests for OpenSSL-QUIC because of flakiness
- It gets little attention from OpenSSL and we have no expectation of the
  major flaws getting corrected anytime soon.
- No one has spoken up for keeping it
- curl users building with vanilla OpenSSL can still use QUIC through the
  means of ngtcp2

Closes #20226

22 files changed:
.github/workflows/http3-linux.yml
.github/workflows/macos.yml
.github/workflows/windows.yml
CMakeLists.txt
RELEASE-NOTES
configure.ac
docs/DEPRECATE.md
docs/HTTP3.md
docs/INSTALL-CMAKE.md
lib/Makefile.inc
lib/curl_config-cmake.h.in
lib/vquic/curl_osslq.c [deleted file]
lib/vquic/curl_osslq.h [deleted file]
lib/vquic/vquic.c
lib/vtls/openssl.c
m4/curl-openssl.m4
tests/http/test_02_download.py
tests/http/test_03_goaway.py
tests/http/test_05_errors.py
tests/http/test_07_upload.py
tests/http/test_14_auth.py
tests/http/testenv/curl.py

index b6b9359d57e6d939d194621e52f62a7b8d965691..181ba34303c091dbf15cedadb02d7179b094e0c8 100644 (file)
@@ -439,21 +439,6 @@ jobs:
               -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON
               -DUSE_ECH=ON
 
-          - name: 'openssl-quic'
-            install_steps: skipall
-            PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig
-            tflags: '--min=1640'
-            configure: >-
-              LDFLAGS=-Wl,-rpath,/home/runner/openssl/build/lib
-              --with-openssl=/home/runner/openssl/build --with-openssl-quic
-
-          - name: 'openssl-quic'
-            PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig
-            generate: >-
-              -DOPENSSL_ROOT_DIR=/home/runner/openssl/build -DUSE_OPENSSL_QUIC=ON
-              -DCURL_DISABLE_LDAP=ON
-              -DCMAKE_UNITY_BUILD=ON
-
           - name: 'quiche'
             install_steps: skipall
             PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig
index 09305fe3ea256aa9bd25df102436086ee330bbfc..9ad31ff62947f6867f7d017c3f36b301006a9109 100644 (file)
@@ -232,7 +232,7 @@ jobs:
           - name: 'OpenSSL libssh'
             compiler: llvm@18
             install: libssh libnghttp3
-            generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_OPENSSL_QUIC=ON -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF
+            generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF
           - name: '!ssl c-ares'
             compiler: clang
             configure: --enable-debug --enable-ares --without-ssl
@@ -330,19 +330,19 @@ jobs:
             compiler: clang
             install: libnghttp3
             install_steps: torture
-            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_OPENSSL_QUIC=ON
+            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl
             tflags: '-t --shallow=25 --min=480 1 to 500'
           - name: 'OpenSSL torture 2'
             compiler: clang
             install: libnghttp3
             install_steps: torture
-            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_OPENSSL_QUIC=ON
+            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl
             tflags: '-t --shallow=25 --min=730 501 to 1250'
           - name: 'OpenSSL torture 3'
             compiler: clang
             install: libnghttp3
             install_steps: torture
-            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_OPENSSL_QUIC=ON
+            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl
             tflags: '-t --shallow=25 --min=628 1251 to 9999'
 
     steps:
index 16abc5ae17a2c71825c7c7510cbb50b23f91f926..48de62739d83daf87e66acb86f6a99cc085fce11 100644 (file)
@@ -200,7 +200,7 @@ jobs:
           - { build: 'autotools', sys: 'msys'      , env: 'x86_64'       , tflags: ''          , config: '--with-openssl', install: 'openssl-devel libssh2-devel', name: 'default R' }
           # MinGW
           - { build: 'autotools', sys: 'mingw64'   , env: 'x86_64'       , tflags: 'skiprun'   , config: '--enable-debug --with-openssl --disable-threaded-resolver --disable-curldebug --enable-static --without-zlib', install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-libssh2', name: 'default' }
-          - { build: 'autotools', sys: 'mingw64'   , env: 'x86_64'       , tflags: ''          , config: '--enable-debug --with-openssl --enable-windows-unicode --enable-ares --with-openssl-quic --enable-static --disable-shared --enable-ca-native', install: 'mingw-w64-x86_64-c-ares mingw-w64-x86_64-openssl mingw-w64-x86_64-nghttp3 mingw-w64-x86_64-libssh2', name: 'c-ares U' }
+          - { build: 'autotools', sys: 'mingw64'   , env: 'x86_64'       , tflags: ''          , config: '--enable-debug --with-openssl --enable-windows-unicode --enable-ares --enable-static --disable-shared --enable-ca-native', install: 'mingw-w64-x86_64-c-ares mingw-w64-x86_64-openssl mingw-w64-x86_64-nghttp3 mingw-w64-x86_64-libssh2', name: 'c-ares U' }
           - { build: 'cmake'    , sys: 'mingw64'   , env: 'x86_64'       , tflags: '--min=1650', config: '-DENABLE_DEBUG=ON  -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', install: 'mingw-w64-x86_64-c-ares mingw-w64-x86_64-libssh2', type: 'Debug', name: 'schannel c-ares U' }
           # MinGW torture
           - { build: 'cmake'    , sys: 'mingw64'   , env: 'x86_64'       , tflags: '-t --shallow=13 --min=700 1 to 950'   , config: '-DENABLE_DEBUG=ON  -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', install: 'mingw-w64-x86_64-c-ares mingw-w64-x86_64-libssh2', type: 'Debug', name: 'schannel U torture 1' }
index 3b2427ee62bfa69b9726d88bea9bd84125994a61..e21f1384f2c737aed96234fc5af0249659990e79 100644 (file)
@@ -686,11 +686,6 @@ else()
   set(_openssl_default ON)
 endif()
 cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${_openssl_default} CURL_ENABLE_SSL OFF)
-option(USE_OPENSSL_QUIC "Use OpenSSL and nghttp3 libraries for HTTP/3 support" OFF)
-if(USE_OPENSSL_QUIC AND NOT CURL_USE_OPENSSL)
-  message(WARNING "OpenSSL QUIC has been requested, but without enabling OpenSSL. Will not enable QUIC.")
-  set(USE_OPENSSL_QUIC OFF)
-endif()
 option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF)
 
 curl_count_true(_enabled_ssl_options_count
@@ -959,7 +954,7 @@ endmacro()
 
 # Ensure that OpenSSL (or fork) or wolfSSL actually supports QUICTLS API.
 macro(curl_openssl_check_quic)
-  if(USE_OPENSSL AND NOT USE_OPENSSL_QUIC)
+  if(USE_OPENSSL)
     if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0)
       if(NOT DEFINED HAVE_SSL_SET_QUIC_TLS_CBS)
         curl_openssl_check_exists("SSL_set_quic_tls_cbs" HAVE_SSL_SET_QUIC_TLS_CBS)
@@ -1057,7 +1052,7 @@ if(USE_NGTCP2)
       find_package(NGTCP2 REQUIRED COMPONENTS "wolfSSL")
     elseif(HAVE_BORINGSSL OR HAVE_AWSLC)
       find_package(NGTCP2 REQUIRED COMPONENTS "BoringSSL")
-    elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0 AND NOT USE_OPENSSL_QUIC)
+    elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0)
       find_package(NGTCP2 REQUIRED COMPONENTS "ossl")
       if(NGTCP2_VERSION VERSION_LESS 1.12.0)
         message(FATAL_ERROR "ngtcp2 1.12.0 or upper required for OpenSSL")
@@ -1106,19 +1101,6 @@ if(USE_QUICHE)
   endif()
 endif()
 
-if(USE_OPENSSL_QUIC)
-  if(USE_NGTCP2 OR USE_QUICHE)
-    message(FATAL_ERROR "Only one HTTP/3 backend can be selected")
-  elseif(CURL_WITH_MULTI_SSL)
-    message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
-  endif()
-  find_package(OpenSSL 3.3.0 REQUIRED)
-
-  find_package(NGHTTP3 REQUIRED)
-  set(USE_NGHTTP3 ON)
-  list(APPEND CURL_LIBS CURL::nghttp3)
-endif()
-
 if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP))
   set(USE_TLS_SRP 1)
 endif()
@@ -1991,7 +1973,7 @@ curl_add_if("NTLM"          NOT CURL_DISABLE_NTLM AND
                             (_use_curl_ntlm_core OR USE_WINDOWS_SSPI))
 curl_add_if("TLS-SRP"       USE_TLS_SRP)
 curl_add_if("HTTP2"         USE_NGHTTP2)
-curl_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC)
+curl_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
 curl_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
 curl_add_if("HTTPS-proxy"   NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS
                             OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR
index d0232115a83474a659d7b82069ce85f53bd16f82..73831b9ede03f95369ec3bbd88b77faec8886171 100644 (file)
@@ -77,7 +77,6 @@ For all changes ever done in curl:
 
 Planned upcoming removals include:
 
- o OpenSSL-QUIC
  o RTMP support
  o Support for c-ares versions before 1.16.0
  o Support for Windows XP/2003
index aa4a37ef09a1f21245c6935496b36f5fdff950bc..eb009c4fdbc303bba1c3ea5365f3c56979ae4099 100644 (file)
@@ -170,7 +170,7 @@ curl_unix_sockets_msg="no       (--enable-unix-sockets)"
          ssl_backends=
           curl_h1_msg="enabled  (internal)"
           curl_h2_msg="no       (--with-nghttp2)"
-          curl_h3_msg="no       (--with-ngtcp2 --with-nghttp3, --with-quiche, --with-openssl-quic)"
+          curl_h3_msg="no       (--with-ngtcp2 --with-nghttp3, --with-quiche)"
 
 enable_altsvc="yes"
 hsts="yes"
@@ -3607,53 +3607,14 @@ if test "$USE_NGTCP2" = "1" && test "$WOLFSSL_ENABLED" = "1"; then
   fi
 fi
 
-dnl **********************************************************************
-dnl Check for OpenSSL QUIC
-dnl **********************************************************************
-
-OPT_OPENSSL_QUIC="no"
-
-if test "$disable_http" = "yes" || test "$OPENSSL_ENABLED" != "1"; then
-  # without HTTP or without openssl, no use
-  OPT_OPENSSL_QUIC="no"
-fi
-
-AC_ARG_WITH(openssl-quic,
-AS_HELP_STRING([--with-openssl-quic],[Enable OpenSSL QUIC usage])
-AS_HELP_STRING([--without-openssl-quic],[Disable OpenSSL QUIC usage]),
-  [OPT_OPENSSL_QUIC=$withval])
-case "$OPT_OPENSSL_QUIC" in
-  no)
-    dnl --without-openssl-quic option used
-    want_openssl_quic="no"
-    ;;
-  yes)
-    dnl --with-openssl-quic option used
-    want_openssl_quic="yes"
-    ;;
-esac
-
-curl_openssl_quic_msg="no      (--with-openssl-quic)"
-if test "$want_openssl_quic" = "yes"; then
-
-  if test "$USE_NGTCP2" = "1"; then
-    AC_MSG_ERROR([--with-openssl-quic and --with-ngtcp2 are mutually exclusive])
-  fi
-  if test "$have_openssl_quic" != "1"; then
-    AC_MSG_ERROR([--with-openssl-quic requires quic support and OpenSSL >= 3.3.0])
-  fi
-  AC_DEFINE(USE_OPENSSL_QUIC, 1, [if openssl QUIC is in use])
-  USE_OPENSSL_QUIC=1
-fi
-
 dnl **********************************************************************
 dnl Check for nghttp3 (HTTP/3 with ngtcp2)
 dnl **********************************************************************
 
 OPT_NGHTTP3="yes"
 
-if test "$USE_NGTCP2" != "1" && test "$USE_OPENSSL_QUIC" != "1"; then
-  # without ngtcp2 or openssl quic, nghttp3 is of no use for us
+if test "$USE_NGTCP2" != "1"; then
+  # without ngtcp2, nghttp3 is of no use for us
   OPT_NGHTTP3="no"
   want_nghttp3="no"
 fi
@@ -3682,9 +3643,9 @@ esac
 curl_http3_msg="no      (--with-nghttp3)"
 if test "$want_nghttp3" != "no"; then
 
-  if test "$USE_NGTCP2" != "1" && test "$USE_OPENSSL_QUIC" != "1"; then
-    # without ngtcp2 or openssl quic, nghttp3 is of no use for us
-    AC_MSG_ERROR([nghttp3 enabled without a QUIC library; enable ngtcp2 or OpenSSL-QUIC])
+  if test "x$USE_NGTCP2" != "x1"; then
+    # without ngtcp2, nghttp3 is of no use for us
+    AC_MSG_ERROR([nghttp3 enabled without a QUIC library; enable ngtcp2])
   fi
 
   dnl backup the pre-nghttp3 variables
@@ -3753,17 +3714,6 @@ if test "$USE_NGTCP2" = "1" && test "$USE_NGHTTP3" = "1"; then
   curl_h3_msg="enabled (ngtcp2 + nghttp3)"
 fi
 
-dnl **********************************************************************
-dnl Check for OpenSSL and nghttp3 (HTTP/3 with nghttp3 using OpenSSL QUIC)
-dnl **********************************************************************
-
-if test "$USE_OPENSSL_QUIC" = "1" && test "$USE_NGHTTP3" = "1"; then
-  experimental="$experimental HTTP3"
-  USE_OPENSSL_H3=1
-  AC_MSG_NOTICE([HTTP3 support is experimental])
-  curl_h3_msg="enabled (openssl + nghttp3)"
-fi
-
 dnl **********************************************************************
 dnl Check for quiche (QUIC)
 dnl **********************************************************************
index 8ba75acee4811e2a702bbf6e60c791be8ca4a1f0..6ad0b7ad0676fc9857f685cff88d31694f2d6fce 100644 (file)
@@ -16,21 +16,6 @@ how your use case cannot be satisfied properly using a workaround.
 
 In March 2026, we drop support for all c-ares versions before 1.16.0.
 
-## OpenSSL-QUIC
-
-OpenSSL-QUIC is what we call the curl QUIC backend that uses the OpenSSL QUIC
-stack.
-
- - It is slower and uses more memory than the alternatives and is only
-   experimental in curl.
- - It gets little attention from OpenSSL and we have no expectation of the
-   major flaws getting corrected anytime soon.
- - No one has spoken up for keeping it
- - curl users building with vanilla OpenSSL can still use QUIC through the
-   means of ngtcp2
-
-We remove the OpenSSL-QUIC backend in January 2026.
-
 ## RTMP
 
 RTMP in curl is powered by the 3rd party library librtmp.
@@ -71,3 +56,4 @@ CMake 3.18 was released on 2020-07-15.
  - Support for Visual Studio 2008 (removed in 8.18.0)
  - OpenSSL 1.1.1 and older (removed in 8.18.0)
  - Support for Windows XP (removed in 8.19.0)
+ - OpenSSL-QUIC (removed in 8.19.0)
index 59fba0e5ce7e760e0346e15c5e7f741d96fa248e..53c200c0883de46b090cc419d2650d8dee3da421 100644 (file)
@@ -250,54 +250,6 @@ Build curl:
  If `make install` results in `Permission denied` error, you need to prepend
  it with `sudo`.
 
-# OpenSSL version
-
-QUIC support is **EXPERIMENTAL**
-
-Use OpenSSL 3.3.1 or newer (QUIC support was added in 3.3.0, with
-shortcomings on some platforms like macOS). 3.4.1 or newer is recommended.
-Build via:
-
-     % cd ..
-     % git clone -b $OPENSSL_VERSION https://github.com/openssl/openssl
-     % cd openssl
-     % ./config enable-tls1_3 --prefix=<somewhere> --libdir=lib
-     % make
-     % make install
-
-Build nghttp3:
-
-     % cd ..
-     % git clone -b $NGHTTP3_VERSION https://github.com/ngtcp2/nghttp3
-     % cd nghttp3
-     % git submodule update --init
-     % autoreconf -fi
-     % ./configure --prefix=<somewhere2> --enable-lib-only
-     % make
-     % make install
-
-Build curl:
-
-     % cd ..
-     % git clone https://github.com/curl/curl
-     % cd curl
-     % autoreconf -fi
-     % LDFLAGS="-Wl,-rpath,<somewhere>/lib" ./configure --with-openssl=<somewhere> --with-openssl-quic --with-nghttp3=<somewhere2>
-     % make
-     % make install
-
-You can build curl with cmake:
-
-     % cd ..
-     % git clone https://github.com/curl/curl
-     % cd curl
-     % cmake -B bld -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON
-     % cmake --build bld
-     % cmake --install bld
-
- If `make install` results in `Permission denied` error, you need to prepend
- it with `sudo`.
-
 # `--http3`
 
 Use only HTTP/3:
index 8ff3f30146878b6ed2a7f78c563ef182db752e71..bc62da242f7660e8f9720056b6d739620c5419c8 100644 (file)
@@ -275,7 +275,6 @@ target_link_libraries(my_target PRIVATE CURL::libcurl)
 - `ENABLE_UNIX_SOCKETS`:                    Enable Unix domain sockets support. Default: `ON`
 - `USE_ECH`:                                Enable ECH support. Default: `OFF`
 - `USE_HTTPSRR`:                            Enable HTTPS RR support. Default: `OFF`
-- `USE_OPENSSL_QUIC`:                       Use OpenSSL and nghttp3 libraries for HTTP/3 support. Default: `OFF`
 - `USE_SSLS_EXPORT`:                        Enable experimental SSL session import/export. Default: `OFF`
 
 ## Disabling features
index 402a5be229fb921e00fc7c973d38c35f2c449afc..36befa325053f93a4f4c684677bcb557eb005a5a 100644 (file)
@@ -118,14 +118,12 @@ LIB_VTLS_HFILES =           \
 
 LIB_VQUIC_CFILES = \
   vquic/curl_ngtcp2.c   \
-  vquic/curl_osslq.c   \
   vquic/curl_quiche.c   \
   vquic/vquic.c \
   vquic/vquic-tls.c
 
 LIB_VQUIC_HFILES = \
   vquic/curl_ngtcp2.h   \
-  vquic/curl_osslq.h   \
   vquic/curl_quiche.h   \
   vquic/vquic.h    \
   vquic/vquic_int.h \
index dbbe7e353aafb1d9a9815f723644a5dfa54b388e..1f5d9399e3a0e2cab9289fdb971c6f47e9f0480a 100644 (file)
@@ -749,9 +749,6 @@ ${SIZEOF_TIME_T_CODE}
 /* to enable quiche */
 #cmakedefine USE_QUICHE 1
 
-/* to enable openssl + nghttp3 */
-#cmakedefine USE_OPENSSL_QUIC 1
-
 /* to enable openssl + ngtcp2 + nghttp3 */
 #cmakedefine OPENSSL_QUIC_API2 1
 
diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c
deleted file mode 100644 (file)
index 958b9a4..0000000
+++ /dev/null
@@ -1,2457 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "../curl_setup.h"
-
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_OPENSSL_QUIC) && \
-  defined(USE_NGHTTP3)
-
-#include <openssl/ssl.h>
-#include <openssl/bio.h>
-#include <openssl/err.h>
-#include <nghttp3/nghttp3.h>
-
-#include "../urldata.h"
-#include "../curl_trc.h"
-#include "../multiif.h"
-#include "../cfilters.h"
-#include "../cf-socket.h"
-#include "../connect.h"
-#include "../progress.h"
-#include "../curlx/dynbuf.h"
-#include "../http1.h"
-#include "../select.h"
-#include "../uint-hash.h"
-#include "vquic.h"
-#include "vquic_int.h"
-#include "vquic-tls.h"
-#include "../vtls/keylog.h"
-#include "../vtls/vtls.h"
-#include "../vtls/openssl.h"
-#include "curl_osslq.h"
-#include "../url.h"
-#include "../bufref.h"
-#include "../curlx/strerr.h"
-#include "../curlx/strcopy.h"
-
-/* A stream window is the maximum amount we need to buffer for
- * each active transfer. We use HTTP/3 flow control and only ACK
- * when we take things out of the buffer.
- * Chunk size is large enough to take a full DATA frame */
-#define H3_STREAM_WINDOW_SIZE (128 * 1024)
-#define H3_STREAM_CHUNK_SIZE   (16 * 1024)
-/* The pool keeps spares around and half of a full stream window
- * seems good. More does not seem to improve performance.
- * The benefit of the pool is that stream buffer to not keep
- * spares. Memory consumption goes down when streams run empty,
- * have a large upload done, etc. */
-#define H3_STREAM_POOL_SPARES \
-  (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) / 2
-/* Receive and Send max number of chunks just follows from the
- * chunk size and window size */
-#define H3_STREAM_RECV_CHUNKS \
-  (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
-#define H3_STREAM_SEND_CHUNKS \
-  (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
-
-#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
-typedef uint32_t sslerr_t;
-#else
-typedef unsigned long sslerr_t;
-#endif
-
-
-/* How to access `call_data` from a cf_osslq filter */
-#undef CF_CTX_CALL_DATA
-#define CF_CTX_CALL_DATA(cf) ((struct cf_osslq_ctx *)(cf)->ctx)->call_data
-
-static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data);
-
-static const char *osslq_SSL_ERROR_to_str(int err)
-{
-  switch(err) {
-  case SSL_ERROR_NONE:
-    return "SSL_ERROR_NONE";
-  case SSL_ERROR_SSL:
-    return "SSL_ERROR_SSL";
-  case SSL_ERROR_WANT_READ:
-    return "SSL_ERROR_WANT_READ";
-  case SSL_ERROR_WANT_WRITE:
-    return "SSL_ERROR_WANT_WRITE";
-  case SSL_ERROR_WANT_X509_LOOKUP:
-    return "SSL_ERROR_WANT_X509_LOOKUP";
-  case SSL_ERROR_SYSCALL:
-    return "SSL_ERROR_SYSCALL";
-  case SSL_ERROR_ZERO_RETURN:
-    return "SSL_ERROR_ZERO_RETURN";
-  case SSL_ERROR_WANT_CONNECT:
-    return "SSL_ERROR_WANT_CONNECT";
-  case SSL_ERROR_WANT_ACCEPT:
-    return "SSL_ERROR_WANT_ACCEPT";
-#ifdef SSL_ERROR_WANT_ASYNC  /* OpenSSL 1.1.0+, LibreSSL 3.6.0+ */
-  case SSL_ERROR_WANT_ASYNC:
-    return "SSL_ERROR_WANT_ASYNC";
-#endif
-#ifdef SSL_ERROR_WANT_ASYNC_JOB  /* OpenSSL 1.1.0+, LibreSSL 3.6.0+ */
-  case SSL_ERROR_WANT_ASYNC_JOB:
-    return "SSL_ERROR_WANT_ASYNC_JOB";
-#endif
-#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB  /* OpenSSL 1.1.1, LibreSSL 3.6.0+ */
-  case SSL_ERROR_WANT_CLIENT_HELLO_CB:
-    return "SSL_ERROR_WANT_CLIENT_HELLO_CB";
-#endif
-  default:
-    return "SSL_ERROR unknown";
-  }
-}
-
-/* Return error string for last OpenSSL error */
-static char *osslq_strerror(unsigned long error, char *buf, size_t size)
-{
-  DEBUGASSERT(size);
-  *buf = '\0';
-
-#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
-  ERR_error_string_n((uint32_t)error, buf, size);
-#else
-  ERR_error_string_n(error, buf, size);
-#endif
-
-  if(!*buf) {
-    const char *msg = error ? "Unknown error" : "No error";
-    if(strlen(msg) < size)
-      curlx_strcopy(buf, size, msg, strlen(msg));
-  }
-
-  return buf;
-}
-
-static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
-                              const struct Curl_sockaddr_ex *addr)
-{
-  BIO_ADDR *bio_addr;
-  CURLcode result = CURLE_FAILED_INIT;
-
-  bio_addr = BIO_ADDR_new();
-  if(!bio_addr) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  switch(addr->family) {
-  case AF_INET: {
-    struct sockaddr_in * const sin =
-      (struct sockaddr_in * const)CURL_UNCONST(&addr->curl_sa_addr);
-    if(!BIO_ADDR_rawmake(bio_addr, AF_INET, &sin->sin_addr,
-                         sizeof(sin->sin_addr), sin->sin_port)) {
-      goto out;
-    }
-    result = CURLE_OK;
-    break;
-  }
-#ifdef USE_IPV6
-  case AF_INET6: {
-    struct sockaddr_in6 * const sin =
-      (struct sockaddr_in6 * const)CURL_UNCONST(&addr->curl_sa_addr);
-    if(!BIO_ADDR_rawmake(bio_addr, AF_INET6, &sin->sin6_addr,
-                         sizeof(sin->sin6_addr), sin->sin6_port)) {
-      goto out;
-    }
-    result = CURLE_OK;
-    break;
-  }
-#endif /* USE_IPV6 */
-  default:
-    /* sunsupported */
-    DEBUGASSERT(0);
-    break;
-  }
-
-out:
-  if(result && bio_addr) {
-    BIO_ADDR_free(bio_addr);
-    bio_addr = NULL;
-  }
-  *pbio_addr = bio_addr;
-  return result;
-}
-
-/* QUIC stream (not necessarily H3) */
-struct cf_osslq_stream {
-  int64_t id;
-  SSL *ssl;
-  struct bufq recvbuf; /* QUIC war data recv buffer */
-  BIT(recvd_eos);
-  BIT(closed);
-  BIT(reset);
-  BIT(send_blocked);
-};
-
-static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s,
-                                     SSL *conn,
-                                     uint64_t flags,
-                                     struct bufc_pool *bufcp,
-                                     void *user_data)
-{
-  DEBUGASSERT(!s->ssl);
-  Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE);
-  s->ssl = SSL_new_stream(conn, flags);
-  if(!s->ssl) {
-    return CURLE_FAILED_INIT;
-  }
-  s->id = SSL_get_stream_id(s->ssl);
-  SSL_set_app_data(s->ssl, user_data);
-  return CURLE_OK;
-}
-
-static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s)
-{
-  if(s->ssl) {
-    SSL_set_app_data(s->ssl, NULL);
-    SSL_free(s->ssl);
-  }
-  Curl_bufq_free(&s->recvbuf);
-  memset(s, 0, sizeof(*s));
-}
-
-static void cf_osslq_stream_close(struct cf_osslq_stream *s)
-{
-  if(s->ssl) {
-    SSL_free(s->ssl);
-    s->ssl = NULL;
-  }
-}
-
-struct cf_osslq_h3conn {
-  nghttp3_conn *conn;
-  nghttp3_settings settings;
-  struct cf_osslq_stream s_ctrl;
-  struct cf_osslq_stream s_qpack_enc;
-  struct cf_osslq_stream s_qpack_dec;
-  struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */
-  size_t remote_ctrl_n; /* number of peer streams opened */
-};
-
-static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3)
-{
-  size_t i;
-
-  if(h3->conn)
-    nghttp3_conn_del(h3->conn);
-  cf_osslq_stream_cleanup(&h3->s_ctrl);
-  cf_osslq_stream_cleanup(&h3->s_qpack_enc);
-  cf_osslq_stream_cleanup(&h3->s_qpack_dec);
-  for(i = 0; i < h3->remote_ctrl_n; ++i) {
-    cf_osslq_stream_cleanup(&h3->remote_ctrl[i]);
-  }
-}
-
-struct cf_osslq_ctx {
-  struct cf_quic_ctx q;
-  struct ssl_peer peer;
-  struct curl_tls_ctx tls;
-  struct cf_call_data call_data;
-  struct cf_osslq_h3conn h3;
-  struct curltime started_at;        /* time the current attempt started */
-  struct curltime handshake_at;      /* time connect handshake finished */
-  struct curltime first_byte_at;     /* when first byte was recvd */
-  struct bufc_pool stream_bufcp;     /* chunk pool for streams */
-  struct uint_hash streams;          /* hash `data->mid` to `h3_stream_ctx` */
-  size_t max_stream_window;          /* max flow window for one stream */
-  SSL_POLL_ITEM *poll_items;         /* Array for polling on writable state */
-  struct Curl_easy **curl_items;     /* Array of easy objs */
-  size_t items_max;                  /* max elements in poll/curl_items */
-  BIT(initialized);
-  BIT(got_first_byte);               /* if first byte was received */
-  BIT(x509_store_setup);             /* if x509 store has been set up */
-  BIT(protocol_shutdown);            /* QUIC connection is shut down */
-  BIT(need_recv);                    /* QUIC connection needs to receive */
-  BIT(need_send);                    /* QUIC connection needs to send */
-};
-
-static void h3_stream_hash_free(unsigned int id, void *stream);
-
-static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx)
-{
-  DEBUGASSERT(!ctx->initialized);
-  Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
-                  H3_STREAM_POOL_SPARES);
-  Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free);
-  ctx->poll_items = NULL;
-  ctx->curl_items = NULL;
-  ctx->items_max = 0;
-  ctx->initialized = TRUE;
-}
-
-static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx)
-{
-  if(ctx && ctx->initialized) {
-    Curl_bufcp_free(&ctx->stream_bufcp);
-    Curl_uint32_hash_destroy(&ctx->streams);
-    Curl_ssl_peer_cleanup(&ctx->peer);
-    curlx_free(ctx->poll_items);
-    curlx_free(ctx->curl_items);
-  }
-  curlx_free(ctx);
-}
-
-static void cf_osslq_ctx_close(struct cf_osslq_ctx *ctx)
-{
-  struct cf_call_data save = ctx->call_data;
-
-  cf_osslq_h3conn_cleanup(&ctx->h3);
-  Curl_vquic_tls_cleanup(&ctx->tls);
-  vquic_ctx_free(&ctx->q);
-  ctx->call_data = save;
-}
-
-static CURLcode cf_osslq_shutdown(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data, bool *done)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct cf_call_data save;
-  CURLcode result = CURLE_OK;
-  int rc;
-
-  CF_DATA_SAVE(save, cf, data);
-
-  if(cf->shutdown || ctx->protocol_shutdown) {
-    *done = TRUE;
-    return CURLE_OK;
-  }
-
-  CF_DATA_SAVE(save, cf, data);
-  *done = FALSE;
-  ctx->need_send = FALSE;
-  ctx->need_recv = FALSE;
-
-  rc = SSL_shutdown_ex(ctx->tls.ossl.ssl,
-                       SSL_SHUTDOWN_FLAG_NO_BLOCK, NULL, 0);
-  if(rc == 0) {  /* ongoing */
-    CURL_TRC_CF(data, cf, "shutdown ongoing");
-    ctx->need_recv = TRUE;
-    goto out;
-  }
-  else if(rc == 1) {  /* done */
-    CURL_TRC_CF(data, cf, "shutdown finished");
-    *done = TRUE;
-    goto out;
-  }
-  else {
-    long sslerr;
-    char err_buffer[256];
-    int err = SSL_get_error(ctx->tls.ossl.ssl, rc);
-
-    switch(err) {
-    case SSL_ERROR_NONE:
-    case SSL_ERROR_ZERO_RETURN:
-      CURL_TRC_CF(data, cf, "shutdown not received, but closed");
-      *done = TRUE;
-      goto out;
-    case SSL_ERROR_WANT_READ:
-      /* SSL has send its notify and now wants to read the reply
-       * from the server. We are not really interested in that. */
-      CURL_TRC_CF(data, cf, "shutdown sent, want receive");
-      ctx->need_recv = TRUE;
-      goto out;
-    case SSL_ERROR_WANT_WRITE:
-      CURL_TRC_CF(data, cf, "shutdown send blocked");
-      ctx->need_send = TRUE;
-      goto out;
-    default:
-      /* We give up on this. */
-      sslerr = ERR_get_error();
-      CURL_TRC_CF(data, cf, "shutdown, ignore recv error: '%s', errno %d",
-                  (sslerr ?
-                   osslq_strerror(sslerr, err_buffer, sizeof(err_buffer)) :
-                   osslq_SSL_ERROR_to_str(err)),
-                  SOCKERRNO);
-      *done = TRUE;
-      result = CURLE_OK;
-      goto out;
-    }
-  }
-out:
-  CF_DATA_RESTORE(cf, save);
-  return result;
-}
-
-static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct cf_call_data save;
-
-  CF_DATA_SAVE(save, cf, data);
-  if(ctx && ctx->tls.ossl.ssl) {
-    CURL_TRC_CF(data, cf, "cf_osslq_close()");
-    if(!cf->shutdown && !ctx->protocol_shutdown) {
-      /* last best effort, which OpenSSL calls a "rapid" shutdown. */
-      SSL_shutdown_ex(ctx->tls.ossl.ssl,
-                      (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID),
-                      NULL, 0);
-    }
-    cf_osslq_ctx_close(ctx);
-  }
-
-  cf->connected = FALSE;
-  CF_DATA_RESTORE(cf, save);
-}
-
-static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct cf_call_data save;
-
-  CF_DATA_SAVE(save, cf, data);
-  CURL_TRC_CF(data, cf, "destroy");
-  if(ctx) {
-    CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
-    if(ctx->tls.ossl.ssl)
-      cf_osslq_ctx_close(ctx);
-    cf_osslq_ctx_free(ctx);
-  }
-  cf->ctx = NULL;
-  /* No CF_DATA_RESTORE(cf, save) possible */
-  (void)save;
-}
-
-static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
-                                           SSL *stream_ssl,
-                                           struct Curl_cfilter *cf,
-                                           struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  int64_t stream_id = (int64_t)SSL_get_stream_id(stream_ssl);
-  int stype = SSL_get_stream_type(stream_ssl);
-  /* This could be a GREASE stream, e.g. HTTP/3 rfc9114 ch 6.2.3
-   * reserved stream type that is supposed to be discarded silently.
-   * BUT OpenSSL does not offer this information to us. So, we silently
-   * ignore all such streams we do not expect. */
-  switch(stype) {
-  case SSL_STREAM_TYPE_READ: {
-    struct cf_osslq_stream *nstream;
-    if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) {
-      /* rejected, we are full */
-      CURL_TRC_CF(data, cf, "[%" PRId64 "] reject remote uni stream",
-                  stream_id);
-      SSL_free(stream_ssl);
-      return CURLE_OK;
-    }
-    nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
-    nstream->id = stream_id;
-    nstream->ssl = stream_ssl;
-    Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted remote uni stream",
-                stream_id);
-    return CURLE_OK;
-  }
-  default:
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] reject remote %s"
-                " stream, type=%x", stream_id,
-                (stype == SSL_STREAM_TYPE_BIDI) ? "bidi" : "write", stype);
-    SSL_free(stream_ssl);
-    return CURLE_OK;
-  }
-}
-
-static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 int detail, CURLcode def_result)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = def_result;
-  sslerr_t errdetail;
-  char ebuf[256] = "unknown";
-  const char *err_descr = ebuf;
-  long lerr;
-  int lib;
-  int reason;
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-
-  errdetail = ERR_get_error();
-  lib = ERR_GET_LIB(errdetail);
-  reason = ERR_GET_REASON(errdetail);
-
-  if((lib == ERR_LIB_SSL) &&
-     ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
-      (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
-    result = CURLE_PEER_FAILED_VERIFICATION;
-
-    lerr = SSL_get_verify_result(ctx->tls.ossl.ssl);
-    if(lerr != X509_V_OK) {
-      ssl_config->certverifyresult = lerr;
-      curl_msnprintf(ebuf, sizeof(ebuf), "SSL certificate problem: %s",
-                     X509_verify_cert_error_string(lerr));
-    }
-    else
-      err_descr = "SSL certificate verification failed";
-  }
-#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
-  /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
-     OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
-  else if((lib == ERR_LIB_SSL) &&
-          (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
-    /* If client certificate is required, communicate the
-       error to client */
-    result = CURLE_SSL_CLIENTCERT;
-    osslq_strerror(errdetail, ebuf, sizeof(ebuf));
-  }
-#endif
-  else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
-    ctx->protocol_shutdown = TRUE;
-    err_descr = "QUIC connection has been shut down";
-    result = def_result;
-  }
-  else {
-    result = def_result;
-    osslq_strerror(errdetail, ebuf, sizeof(ebuf));
-  }
-
-  /* detail is already set to the SSL error above */
-
-  /* If we e.g. use SSLv2 request-method and the server does not like us
-   * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
-   * the SO_ERROR is also lost.
-   */
-  if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
-    char extramsg[80] = "";
-    int sockerr = SOCKERRNO;
-    struct ip_quadruple ip;
-
-    if(sockerr && detail == SSL_ERROR_SYSCALL)
-      curlx_strerror(sockerr, extramsg, sizeof(extramsg));
-    if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
-      failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
-            extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail),
-            ctx->peer.dispname, ip.remote_port, ip.remote_ip);
-  }
-  else {
-    /* Could be a CERT problem */
-    failf(data, "%s", err_descr);
-  }
-  return result;
-}
-
-static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
-}
-
-/**
- * All about the H3 internals of a stream
- */
-struct h3_stream_ctx {
-  struct cf_osslq_stream s;
-  struct bufq sendbuf;          /* h3 request body */
-  struct bufq recvbuf;          /* h3 response body */
-  struct h1_req_parser h1;      /* h1 request parsing */
-  size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
-  size_t recv_buf_nonflow;      /* buffered bytes,
-                                   not counting for flow control */
-  uint64_t error3;              /* HTTP/3 stream error code */
-  curl_off_t upload_left;       /* number of request bytes left to upload */
-  curl_off_t download_recvd;    /* number of response DATA bytes received */
-  int status_code;              /* HTTP status code */
-  BIT(resp_hds_complete);       /* we have a complete, final response */
-  BIT(closed);                  /* TRUE on stream close */
-  BIT(reset);                   /* TRUE on stream reset */
-  BIT(send_closed);             /* stream is local closed */
-  BIT(quic_flow_blocked);       /* stream is blocked by QUIC flow control */
-};
-
-static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
-{
-  cf_osslq_stream_cleanup(&stream->s);
-  Curl_bufq_free(&stream->sendbuf);
-  Curl_bufq_free(&stream->recvbuf);
-  Curl_h1_req_parse_free(&stream->h1);
-  curlx_free(stream);
-}
-
-static void h3_stream_hash_free(unsigned int id, void *stream)
-{
-  (void)id;
-  DEBUGASSERT(stream);
-  h3_stream_ctx_free((struct h3_stream_ctx *)stream);
-}
-
-static CURLcode h3_data_setup(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
-  if(!data)
-    return CURLE_FAILED_INIT;
-
-  if(stream)
-    return CURLE_OK;
-
-  stream = curlx_calloc(1, sizeof(*stream));
-  if(!stream)
-    return CURLE_OUT_OF_MEMORY;
-
-  stream->s.id = -1;
-  /* on send, we control how much we put into the buffer */
-  Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
-                  H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
-  stream->sendbuf_len_in_flight = 0;
-  /* on recv, we need a flexible buffer limit since we also write
-   * headers to it that are not counted against the nghttp3 flow limits. */
-  Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
-                  H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  stream->recv_buf_nonflow = 0;
-  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
-
-  if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) {
-    h3_stream_ctx_free(stream);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  return CURLE_OK;
-}
-
-static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
-  (void)cf;
-  if(stream) {
-    CURL_TRC_CF(data, cf, "[%" PRIu64 "] easy handle is done", stream->s.id);
-    if(ctx->h3.conn && (stream->s.id >= 0) && !stream->closed) {
-      nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
-      nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
-                                NGHTTP3_H3_REQUEST_CANCELLED);
-      nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL);
-      stream->closed = TRUE;
-    }
-
-    Curl_uint32_hash_remove(&ctx->streams, data->mid);
-  }
-}
-
-struct cf_ossq_find_ctx {
-  int64_t stream_id;
-  struct h3_stream_ctx *stream;
-};
-
-static bool cf_osslq_find_stream(uint32_t mid, void *val, void *user_data)
-{
-  struct h3_stream_ctx *stream = val;
-  struct cf_ossq_find_ctx *fctx = user_data;
-
-  (void)mid;
-  if(stream && stream->s.id == fctx->stream_id) {
-    fctx->stream = stream;
-    return FALSE; /* stop iterating */
-  }
-  return TRUE;
-}
-
-static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
-                                                    struct Curl_easy *data,
-                                                    int64_t stream_id)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
-  if(stream && stream->s.id == stream_id) {
-    return &stream->s;
-  }
-  else if(ctx->h3.s_ctrl.id == stream_id) {
-    return &ctx->h3.s_ctrl;
-  }
-  else if(ctx->h3.s_qpack_enc.id == stream_id) {
-    return &ctx->h3.s_qpack_enc;
-  }
-  else if(ctx->h3.s_qpack_dec.id == stream_id) {
-    return &ctx->h3.s_qpack_dec;
-  }
-  else {
-    struct cf_ossq_find_ctx fctx;
-    fctx.stream_id = stream_id;
-    fctx.stream = NULL;
-    Curl_uint32_hash_visit(&ctx->streams, cf_osslq_find_stream, &fctx);
-    if(fctx.stream)
-      return &fctx.stream->s;
-  }
-  return NULL;
-}
-
-static CURLcode h3_data_pause(struct Curl_cfilter *cf,
-                              struct Curl_easy *data,
-                              bool pause)
-{
-  (void)cf;
-  if(!pause) {
-    /* unpaused. make it run again right away */
-    Curl_multi_mark_dirty(data);
-  }
-  return CURLE_OK;
-}
-
-static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
-                              uint64_t app_error_code, void *user_data,
-                              void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  (void)conn;
-  (void)stream_id;
-
-  /* we might be called by nghttp3 after we already cleaned up */
-  if(!stream)
-    return 0;
-
-  stream->closed = TRUE;
-  stream->error3 = app_error_code;
-  if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
-    stream->reset = TRUE;
-    stream->send_closed = TRUE;
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64,
-                stream->s.id, stream->error3);
-  }
-  else {
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id);
-  }
-  Curl_multi_mark_dirty(data);
-  return 0;
-}
-
-/*
- * write_resp_raw() copies response data in raw format to the `data`'s
-  * receive buffer. If not enough space is available, it appends to the
- * `data`'s overflow buffer.
- */
-static CURLcode write_resp_raw(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               const void *mem, size_t memlen,
-                               bool flow)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  CURLcode result = CURLE_OK;
-  size_t nwritten;
-
-  (void)cf;
-  if(!stream) {
-    return CURLE_RECV_ERROR;
-  }
-  result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten);
-  if(result)
-    return result;
-
-  if(!flow)
-    stream->recv_buf_nonflow += nwritten;
-
-  if(nwritten < memlen) {
-    /* This MUST not happen. Our recbuf is dimensioned to hold the
-     * full max_stream_window and then some for this reason. */
-    DEBUGASSERT(0);
-    return CURLE_RECV_ERROR;
-  }
-  return result;
-}
-
-static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
-                           const uint8_t *buf, size_t buflen,
-                           void *user_data, void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  CURLcode result;
-
-  (void)conn;
-  (void)stream3_id;
-
-  if(!stream)
-    return NGHTTP3_ERR_CALLBACK_FAILURE;
-
-  result = write_resp_raw(cf, data, buf, buflen, TRUE);
-  if(result) {
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR %d",
-                stream->s.id, buflen, result);
-    return NGHTTP3_ERR_CALLBACK_FAILURE;
-  }
-  stream->download_recvd += (curl_off_t)buflen;
-  CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%" FMT_OFF_T,
-              stream->s.id, buflen, stream->download_recvd);
-  Curl_multi_mark_dirty(data);
-  return 0;
-}
-
-static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
-                                  size_t consumed, void *user_data,
-                                  void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
-  (void)conn;
-  (void)stream_id;
-  if(stream)
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes",
-                stream->s.id, consumed);
-  return 0;
-}
-
-static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
-                             int32_t token, nghttp3_rcbuf *name,
-                             nghttp3_rcbuf *value, uint8_t flags,
-                             void *user_data, void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
-  nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  CURLcode result = CURLE_OK;
-  (void)conn;
-  (void)stream_id;
-  (void)token;
-  (void)flags;
-  (void)cf;
-
-  /* we might have cleaned up this transfer already */
-  if(!stream)
-    return 0;
-
-  if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
-    char line[14]; /* status line is always 13 characters long */
-    size_t ncopy;
-
-    result = Curl_http_decode_status(&stream->status_code,
-                                     (const char *)h3val.base, h3val.len);
-    if(result)
-      return -1;
-    ncopy = curl_msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
-                           stream->status_code);
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
-    result = write_resp_raw(cf, data, line, ncopy, FALSE);
-    if(result) {
-      return -1;
-    }
-  }
-  else {
-    /* store as an HTTP1-style header */
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
-                stream_id, (int)h3name.len, h3name.base,
-                (int)h3val.len, h3val.base);
-    result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
-    if(result) {
-      return -1;
-    }
-    result = write_resp_raw(cf, data, ": ", 2, FALSE);
-    if(result) {
-      return -1;
-    }
-    result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
-    if(result) {
-      return -1;
-    }
-    result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
-    if(result) {
-      return -1;
-    }
-  }
-  return 0;
-}
-
-static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
-                             int fin, void *user_data, void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  CURLcode result = CURLE_OK;
-  (void)conn;
-  (void)stream_id;
-  (void)fin;
-  (void)cf;
-
-  if(!stream)
-    return 0;
-  /* add a CRLF only if we have received some headers */
-  result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
-  if(result) {
-    return -1;
-  }
-
-  CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d",
-              stream_id, stream->status_code);
-  if(stream->status_code / 100 != 1) {
-    stream->resp_hds_complete = TRUE;
-  }
-  Curl_multi_mark_dirty(data);
-  return 0;
-}
-
-static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
-                              uint64_t app_error_code, void *user_data,
-                              void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  (void)conn;
-  (void)app_error_code;
-
-  if(!stream || !stream->s.ssl)
-    return 0;
-
-  CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id);
-  cf_osslq_stream_close(&stream->s);
-  return 0;
-}
-
-static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
-                              uint64_t app_error_code, void *user_data,
-                              void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  int rv;
-  (void)conn;
-
-  if(stream && stream->s.ssl) {
-    SSL_STREAM_RESET_ARGS args = { 0 };
-    args.quic_error_code = app_error_code;
-    rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args));
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
-    if(!rv) {
-      return NGHTTP3_ERR_CALLBACK_FAILURE;
-    }
-  }
-  return 0;
-}
-
-static nghttp3_ssize cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
-                                         nghttp3_vec *vec, size_t veccnt,
-                                         uint32_t *pflags, void *user_data,
-                                         void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  ssize_t nwritten = 0;
-  size_t nvecs = 0;
-  (void)cf;
-  (void)conn;
-  (void)stream_id;
-  (void)user_data;
-  (void)veccnt;
-
-  if(!stream)
-    return NGHTTP3_ERR_CALLBACK_FAILURE;
-  /* nghttp3 keeps references to the sendbuf data until it is ACKed
-   * by the server (see `cb_h3_acked_req_body()` for updates).
-   * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
-   * that we have already passed to nghttp3, but which have not been
-   * ACKed yet.
-   * Any amount beyond `sendbuf_len_in_flight` we need still to pass
-   * to nghttp3. Do that now, if we can. */
-  if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
-    nvecs = 0;
-    while(nvecs < veccnt &&
-          Curl_bufq_peek_at(&stream->sendbuf,
-                            stream->sendbuf_len_in_flight,
-                            CURL_UNCONST(&vec[nvecs].base),
-                            &vec[nvecs].len)) {
-      stream->sendbuf_len_in_flight += vec[nvecs].len;
-      nwritten += vec[nvecs].len;
-      ++nvecs;
-    }
-    DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */
-  }
-
-  if(nwritten > 0 && stream->upload_left != -1)
-    stream->upload_left -= nwritten;
-
-  /* When we stopped sending and everything in `sendbuf` is "in flight",
-   * we are at the end of the request body. */
-  if(stream->upload_left == 0) {
-    *pflags = NGHTTP3_DATA_FLAG_EOF;
-    stream->send_closed = TRUE;
-  }
-  else if(!nwritten) {
-    /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN",
-                stream->s.id);
-    return NGHTTP3_ERR_WOULDBLOCK;
-  }
-
-  CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
-              "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")",
-              stream->s.id, (int)nvecs,
-              *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "",
-              nwritten, Curl_bufq_len(&stream->sendbuf),
-              stream->upload_left);
-  return (nghttp3_ssize)nvecs;
-}
-
-static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
-                                   uint64_t datalen, void *user_data,
-                                   void *stream_user_data)
-{
-  struct Curl_cfilter *cf = user_data;
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct Curl_easy *data = stream_user_data;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  size_t skiplen;
-
-  (void)cf;
-  if(!stream)
-    return 0;
-  /* The server acknowledged `datalen` of bytes from our request body.
-   * This is a delta. We have kept this data in `sendbuf` for
-   * re-transmissions and can free it now. */
-  if(datalen >= (uint64_t)stream->sendbuf_len_in_flight)
-    skiplen = stream->sendbuf_len_in_flight;
-  else
-    skiplen = (size_t)datalen;
-  Curl_bufq_skip(&stream->sendbuf, skiplen);
-  stream->sendbuf_len_in_flight -= skiplen;
-
-  /* Resume upload processing if we have more data to send */
-  if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
-    int rv = nghttp3_conn_resume_stream(conn, stream_id);
-    if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
-      return NGHTTP3_ERR_CALLBACK_FAILURE;
-    }
-  }
-  return 0;
-}
-
-static nghttp3_callbacks ngh3_callbacks = {
-  cb_h3_acked_stream_data,
-  cb_h3_stream_close,
-  cb_h3_recv_data,
-  cb_h3_deferred_consume,
-  NULL, /* begin_headers */
-  cb_h3_recv_header,
-  cb_h3_end_headers,
-  NULL, /* begin_trailers */
-  cb_h3_recv_header,
-  NULL, /* end_trailers */
-  cb_h3_stop_sending,
-  NULL, /* end_stream */
-  cb_h3_reset_stream,
-  NULL, /* shutdown */
-  NULL, /* recv_settings (deprecated) */
-#ifdef NGHTTP3_CALLBACKS_V2  /* nghttp3 v1.11.0+ */
-  NULL, /* recv_origin */
-  NULL, /* end_origin */
-  NULL, /* rand */
-#endif
-#ifdef NGHTTP3_CALLBACKS_V3  /* nghttp3 v1.14.0+ */
-  NULL, /* recv_settings2 */
-#endif
-};
-
-static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn,
-                                     void *user_data)
-{
-  struct cf_osslq_h3conn *h3 = &ctx->h3;
-  CURLcode result;
-  int rc;
-
-  nghttp3_settings_default(&h3->settings);
-  rc = nghttp3_conn_client_new(&h3->conn,
-                               &ngh3_callbacks,
-                               &h3->settings,
-                               Curl_nghttp3_mem(),
-                               user_data);
-  if(rc) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  result = cf_osslq_stream_open(&h3->s_ctrl, conn,
-                                SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI,
-                                &ctx->stream_bufcp, NULL);
-  if(result) {
-    result = CURLE_QUIC_CONNECT_ERROR;
-    goto out;
-  }
-  result = cf_osslq_stream_open(&h3->s_qpack_enc, conn,
-                                SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI,
-                                &ctx->stream_bufcp, NULL);
-  if(result) {
-    result = CURLE_QUIC_CONNECT_ERROR;
-    goto out;
-  }
-  result = cf_osslq_stream_open(&h3->s_qpack_dec, conn,
-                                SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI,
-                                &ctx->stream_bufcp, NULL);
-  if(result) {
-    result = CURLE_QUIC_CONNECT_ERROR;
-    goto out;
-  }
-
-  rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id);
-  if(rc) {
-    result = CURLE_QUIC_CONNECT_ERROR;
-    goto out;
-  }
-  rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id,
-                                       h3->s_qpack_dec.id);
-  if(rc) {
-    result = CURLE_QUIC_CONNECT_ERROR;
-    goto out;
-  }
-
-  result = CURLE_OK;
-out:
-  return result;
-}
-
-static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result;
-  int rv;
-  const struct Curl_sockaddr_ex *peer_addr = NULL;
-  BIO *bio = NULL;
-  BIO_ADDR *baddr = NULL;
-  static const struct alpn_spec ALPN_SPEC_H3 = {{ "h3" }, 1};
-
-  DEBUGASSERT(ctx->initialized);
-
-#define H3_ALPN "\x2h3"
-  result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
-                               &ALPN_SPEC_H3, NULL, NULL, NULL, NULL);
-  if(result)
-    goto out;
-
-  result = vquic_ctx_init(data, &ctx->q);
-  if(result)
-    goto out;
-
-  result = CURLE_QUIC_CONNECT_ERROR;
-  if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL) ||
-     !peer_addr)
-    goto out;
-
-  ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
-  rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
-                   &ctx->q.local_addrlen);
-  if(rv == -1)
-    goto out;
-
-  result = make_bio_addr(&baddr, peer_addr);
-  if(result) {
-    failf(data, "error creating BIO_ADDR from sockaddr");
-    goto out;
-  }
-
-  /* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit
-   * Win32 systems, Microsoft defines SOCKET as `unsigned long long`.
-   */
-#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
-  if(ctx->q.sockfd > INT_MAX) {
-    failf(data, "Windows socket identifier larger than MAX_INT, "
-          "unable to set in OpenSSL dgram API.");
-    result = CURLE_QUIC_CONNECT_ERROR;
-    goto out;
-  }
-  bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE);
-#else
-  bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
-#endif
-  if(!bio) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) {
-    failf(data, "failed to set the initial peer address");
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-  if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) {
-    failf(data, "failed to turn off blocking mode");
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-
-  SSL_set_bio(ctx->tls.ossl.ssl, bio, bio);
-  bio = NULL;
-  SSL_set_connect_state(ctx->tls.ossl.ssl);
-  SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl,
-                                 SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
-  /* from our side, there is no idle timeout */
-  SSL_set_value_uint(ctx->tls.ossl.ssl,
-                     SSL_VALUE_CLASS_FEATURE_REQUEST,
-                     SSL_VALUE_QUIC_IDLE_TIMEOUT, 0);
-  /* setup the H3 things on top of the QUIC connection */
-  result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf);
-
-out:
-  if(bio)
-    BIO_free(bio);
-  if(baddr)
-    BIO_ADDR_free(baddr);
-  CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result);
-  return result;
-}
-
-struct h3_quic_recv_ctx {
-  struct Curl_cfilter *cf;
-  struct Curl_easy *data;
-  struct cf_osslq_stream *s;
-};
-
-static CURLcode h3_quic_recv(void *reader_ctx,
-                             unsigned char *buf, size_t len,
-                             size_t *pnread)
-{
-  struct h3_quic_recv_ctx *x = reader_ctx;
-  int rv;
-
-  rv = SSL_read_ex(x->s->ssl, buf, len, pnread);
-  if(rv <= 0) {
-    int detail = SSL_get_error(x->s->ssl, rv);
-    if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) {
-      return CURLE_AGAIN;
-    }
-    else if(detail == SSL_ERROR_ZERO_RETURN) {
-      CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS",
-                  x->s->id);
-      x->s->recvd_eos = TRUE;
-      return CURLE_OK;
-    }
-    else if(SSL_get_stream_read_state(x->s->ssl) ==
-            SSL_STREAM_STATE_RESET_REMOTE) {
-      uint64_t app_error_code = NGHTTP3_H3_NO_ERROR;
-      if(!SSL_get_stream_read_error_code(x->s->ssl, &app_error_code)) {
-        x->s->reset = TRUE;
-        return CURLE_RECV_ERROR;
-      }
-      else {
-        CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
-                    "rv=%d, app_err=%" PRIu64,
-                    x->s->id, rv, app_error_code);
-        if(app_error_code != NGHTTP3_H3_NO_ERROR)
-          x->s->reset = TRUE;
-      }
-      x->s->recvd_eos = TRUE;
-      return CURLE_OK;
-    }
-    else {
-      return cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR);
-    }
-  }
-  return CURLE_OK;
-}
-
-static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
-                                     struct Curl_cfilter *cf,
-                                     struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-  ssize_t nread;
-  size_t n;
-  struct h3_quic_recv_ctx x;
-  bool eagain = FALSE;
-  size_t total_recv_len = 0;
-
-  DEBUGASSERT(s);
-  if(s->closed)
-    return CURLE_OK;
-
-  x.cf = cf;
-  x.data = data;
-  x.s = s;
-  while(s->ssl && !s->closed && !eagain &&
-        (total_recv_len < H3_STREAM_CHUNK_SIZE)) {
-    if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) {
-      while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) {
-        result = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &n);
-        if(result) {
-          if(result != CURLE_AGAIN)
-            goto out;
-          result = CURLE_OK;
-          eagain = TRUE;
-        }
-      }
-    }
-
-    /* Forward what we have to nghttp3 */
-    if(!Curl_bufq_is_empty(&s->recvbuf)) {
-      const unsigned char *buf;
-      size_t blen;
-
-      while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) {
-        nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id,
-                                         buf, blen, 0);
-        CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes "
-                    "to nghttp3 -> %zd", s->id, blen, nread);
-        if(nread < 0) {
-          failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s",
-                blen, nghttp3_strerror((int)nread));
-          result = CURLE_RECV_ERROR;
-          goto out;
-        }
-        /* success, `nread` is the flow for QUIC to count as "consumed",
-         * not sure how that will work with OpenSSL. Anyways, without error,
-         * all data that we passed is not owned by nghttp3. */
-        Curl_bufq_skip(&s->recvbuf, blen);
-        total_recv_len += blen;
-      }
-    }
-
-    /* When we forwarded everything, handle RESET/EOS */
-    if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) {
-      int rv;
-      result = CURLE_OK;
-      if(s->reset) {
-        uint64_t app_error;
-        if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) {
-          failf(data, "SSL_get_stream_read_error_code returned error");
-          result = CURLE_RECV_ERROR;
-          goto out;
-        }
-        rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error);
-        s->closed = TRUE;
-        if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
-          failf(data, "nghttp3_conn_close_stream returned error: %s",
-                nghttp3_strerror(rv));
-          result = CURLE_RECV_ERROR;
-          goto out;
-        }
-      }
-      else if(s->recvd_eos) {
-        rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id,
-                                       NGHTTP3_H3_NO_ERROR);
-        s->closed = TRUE;
-        CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d",
-                    s->id, rv);
-        if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
-          failf(data, "nghttp3_conn_close_stream returned error: %s",
-                nghttp3_strerror(rv));
-          result = CURLE_RECV_ERROR;
-          goto out;
-        }
-      }
-    }
-  }
-out:
-  if(result)
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d",
-                s->id, result);
-  return result;
-}
-
-struct cf_ossq_recv_ctx {
-  struct Curl_cfilter *cf;
-  struct Curl_multi *multi;
-  CURLcode result;
-};
-
-static bool cf_osslq_iter_recv(uint32_t mid, void *val, void *user_data)
-{
-  struct h3_stream_ctx *stream = val;
-  struct cf_ossq_recv_ctx *rctx = user_data;
-
-  (void)mid;
-  if(stream && !stream->closed && !Curl_bufq_is_full(&stream->recvbuf)) {
-    struct Curl_easy *sdata = Curl_multi_get_easy(rctx->multi, mid);
-    if(sdata) {
-      rctx->result = cf_osslq_stream_recv(&stream->s, rctx->cf, sdata);
-      if(rctx->result)
-        return FALSE; /* abort iteration */
-    }
-  }
-  return TRUE;
-}
-
-static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-
-  if(!ctx->tls.ossl.ssl)
-    goto out;
-
-  ERR_clear_error();
-
-  /* 1. Check for new incoming streams */
-  while(1) {
-    SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl,
-                                  SSL_ACCEPT_STREAM_NO_BLOCK);
-    if(!snew)
-      break;
-
-    result = cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
-    if(result)
-      goto out;
-  }
-
-  if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
-    int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
-    result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
-    if(result)
-      goto out;
-  }
-
-  if(ctx->h3.conn) {
-    size_t i;
-    for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) {
-      result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data);
-      if(result)
-        goto out;
-    }
-  }
-
-  if(ctx->h3.conn) {
-    struct cf_ossq_recv_ctx rctx;
-
-    DEBUGASSERT(data->multi);
-    rctx.cf = cf;
-    rctx.multi = data->multi;
-    rctx.result = CURLE_OK;
-    Curl_uint32_hash_visit(&ctx->streams, cf_osslq_iter_recv, &rctx);
-    result = rctx.result;
-  }
-
-out:
-  CURL_TRC_CF(data, cf, "progress_ingress -> %d", result);
-  return result;
-}
-
-struct cf_ossq_fill_ctx {
-  struct cf_osslq_ctx *ctx;
-  struct Curl_multi *multi;
-  size_t n;
-};
-
-static bool cf_osslq_collect_block_send(uint32_t mid, void *val,
-                                        void *user_data)
-{
-  struct h3_stream_ctx *stream = val;
-  struct cf_ossq_fill_ctx *fctx = user_data;
-  struct cf_osslq_ctx *ctx = fctx->ctx;
-
-  if(fctx->n >= ctx->items_max)  /* should not happen, prevent mayhem */
-    return FALSE;
-
-  if(stream && stream->s.ssl && stream->s.send_blocked) {
-    struct Curl_easy *sdata = Curl_multi_get_easy(fctx->multi, mid);
-    if(sdata) {
-      ctx->poll_items[fctx->n].desc = SSL_as_poll_descriptor(stream->s.ssl);
-      ctx->poll_items[fctx->n].events = SSL_POLL_EVENT_W;
-      ctx->curl_items[fctx->n] = sdata;
-      fctx->n++;
-    }
-  }
-  return TRUE;
-}
-
-/* Iterate over all streams and check if blocked can be unblocked */
-static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
-                                           struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream;
-  size_t poll_count;
-  size_t result_count = 0;
-  size_t idx_count = 0;
-  CURLcode res = CURLE_OK;
-  struct timeval timeout;
-  void *tmpptr;
-
-  if(ctx->h3.conn) {
-    struct cf_ossq_fill_ctx fill_ctx;
-
-    if(ctx->items_max < Curl_uint32_hash_count(&ctx->streams)) {
-      size_t nmax = Curl_uint32_hash_count(&ctx->streams);
-      ctx->items_max = 0;
-      tmpptr = curlx_realloc(ctx->poll_items, nmax * sizeof(SSL_POLL_ITEM));
-      if(!tmpptr) {
-        curlx_free(ctx->poll_items);
-        ctx->poll_items = NULL;
-        res = CURLE_OUT_OF_MEMORY;
-        goto out;
-      }
-      ctx->poll_items = tmpptr;
-
-      tmpptr = curlx_realloc(ctx->curl_items,
-                             nmax * sizeof(struct Curl_easy *));
-      if(!tmpptr) {
-        curlx_free(ctx->curl_items);
-        ctx->curl_items = NULL;
-        res = CURLE_OUT_OF_MEMORY;
-        goto out;
-      }
-      ctx->curl_items = tmpptr;
-      ctx->items_max = nmax;
-    }
-
-    fill_ctx.ctx = ctx;
-    fill_ctx.multi = data->multi;
-    fill_ctx.n = 0;
-    Curl_uint32_hash_visit(&ctx->streams, cf_osslq_collect_block_send,
-                           &fill_ctx);
-    poll_count = fill_ctx.n;
-    if(poll_count) {
-      CURL_TRC_CF(data, cf, "polling %zu blocked streams", poll_count);
-
-      memset(&timeout, 0, sizeof(struct timeval));
-      res = CURLE_UNRECOVERABLE_POLL;
-      if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM),
-                   &timeout, 0, &result_count))
-        goto out;
-
-      res = CURLE_OK;
-
-      for(idx_count = 0; idx_count < poll_count && result_count > 0;
-          idx_count++) {
-        if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) {
-          stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]);
-          DEBUGASSERT(stream);  /* should still exist */
-          if(stream) {
-            nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
-            stream->s.send_blocked = FALSE;
-            Curl_multi_mark_dirty(ctx->curl_items[idx_count]);
-            CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked");
-          }
-          result_count--;
-        }
-      }
-    }
-  }
-
-out:
-  return res;
-}
-
-static CURLcode h3_send_streams(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-
-  if(!ctx->tls.ossl.ssl || !ctx->h3.conn)
-    goto out;
-
-  for(;;) {
-    struct cf_osslq_stream *s = NULL;
-    nghttp3_vec vec[16];
-    nghttp3_ssize n, i;
-    int64_t stream_id;
-    size_t written;
-    int eos, ok, rv;
-    size_t total_len, acked_len = 0;
-    bool blocked = FALSE, eos_written = FALSE;
-
-    n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
-                                   vec, CURL_ARRAYSIZE(vec));
-    if(n < 0) {
-      failf(data, "nghttp3_conn_writev_stream returned error: %s",
-            nghttp3_strerror((int)n));
-      result = CURLE_SEND_ERROR;
-      goto out;
-    }
-    if(stream_id < 0) {
-      result = CURLE_OK;
-      goto out;
-    }
-
-    /* Get the stream for this data */
-    s = cf_osslq_get_qstream(cf, data, stream_id);
-    if(!s) {
-      failf(data, "nghttp3_conn_writev_stream gave unknown stream %"
-            PRId64, stream_id);
-      result = CURLE_SEND_ERROR;
-      goto out;
-    }
-    /* Now write the data to the stream's SSL*, it may not all fit! */
-    DEBUGASSERT(s->id == stream_id);
-    for(i = 0, total_len = 0; i < n; ++i) {
-      total_len += vec[i].len;
-    }
-    for(i = 0; (i < n) && !blocked; ++i) {
-      /* Without stream->s.ssl, we closed that already, so
-       * pretend the write did succeed. */
-      uint64_t flags = (eos && ((i + 1) == n)) ? SSL_WRITE_FLAG_CONCLUDE : 0;
-      written = vec[i].len;
-      ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags,
-                                    &written);
-      if(ok && flags & SSL_WRITE_FLAG_CONCLUDE)
-        eos_written = TRUE;
-      if(ok) {
-        /* As OpenSSL buffers the data, we count this as acknowledged
-         * from nghttp3's point of view */
-        CURL_TRC_CF(data, cf, "[%" PRId64 "] send %zu bytes to QUIC ok",
-                    s->id, vec[i].len);
-        acked_len += vec[i].len;
-      }
-      else {
-        int detail = SSL_get_error(s->ssl, 0);
-        switch(detail) {
-        case SSL_ERROR_WANT_WRITE:
-        case SSL_ERROR_WANT_READ:
-          /* QUIC blocked us from writing more */
-          CURL_TRC_CF(data, cf, "[%" PRId64 "] send %zu bytes to "
-                      "QUIC blocked", s->id, vec[i].len);
-          written = 0;
-          nghttp3_conn_block_stream(ctx->h3.conn, s->id);
-          s->send_blocked = blocked = TRUE;
-          break;
-        default:
-          failf(data, "[%" PRId64 "] send %zu bytes to QUIC, SSL error %d",
-                s->id, vec[i].len, detail);
-          result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3);
-          goto out;
-        }
-      }
-    }
-
-    if(acked_len > 0 || (eos && !s->send_blocked)) {
-      /* Since QUIC buffers the data written internally, we can tell
-       * nghttp3 that it can move forward on it */
-      ctx->q.last_io = *Curl_pgrs_now(data);
-      rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
-      if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
-        failf(data, "nghttp3_conn_add_write_offset returned error: %s",
-              nghttp3_strerror(rv));
-        result = CURLE_SEND_ERROR;
-        goto out;
-      }
-      rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len);
-      if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
-        failf(data, "nghttp3_conn_add_ack_offset returned error: %s",
-              nghttp3_strerror(rv));
-        result = CURLE_SEND_ERROR;
-        goto out;
-      }
-      CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes "
-                  "to QUIC, eos=%d", s->id, acked_len, total_len, eos);
-    }
-
-    if(eos && !s->send_blocked && !eos_written) {
-      /* wrote everything and H3 indicates end of stream */
-      CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
-      SSL_stream_conclude(s->ssl, 0);
-    }
-  }
-
-out:
-  CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result);
-  return result;
-}
-
-static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-
-  if(!ctx->tls.ossl.ssl)
-    goto out;
-
-  ERR_clear_error();
-  result = h3_send_streams(cf, data);
-  if(result)
-    goto out;
-
-  if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
-    int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
-    result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
-  }
-
-  result = cf_osslq_check_and_unblock(cf, data);
-
-out:
-  CURL_TRC_CF(data, cf, "progress_egress -> %d", result);
-  return result;
-}
-
-static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-  struct timeval tv;
-  timediff_t timeoutms;
-  int is_infinite = 1;
-
-  if(ctx->tls.ossl.ssl &&
-     SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) &&
-     !is_infinite) {
-    timeoutms = curlx_tvtoms(&tv);
-    /* QUIC want to be called again latest at the returned timeout */
-    if(timeoutms <= 0) {
-      result = cf_progress_ingress(cf, data);
-      if(result)
-        goto out;
-      result = cf_progress_egress(cf, data);
-      if(result)
-        goto out;
-      if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) {
-        timeoutms = curlx_tvtoms(&tv);
-      }
-    }
-    if(!is_infinite) {
-      Curl_expire(data, timeoutms, EXPIRE_QUIC);
-      CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms);
-    }
-  }
-out:
-  return result;
-}
-
-static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 bool *done)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-  struct cf_call_data save;
-  int err;
-
-  if(cf->connected) {
-    *done = TRUE;
-    return CURLE_OK;
-  }
-
-  /* Connect the UDP filter first */
-  if(!cf->next->connected) {
-    result = Curl_conn_cf_connect(cf->next, data, done);
-    if(result || !*done)
-      return result;
-  }
-
-  *done = FALSE;
-  CF_DATA_SAVE(save, cf, data);
-
-  if(!ctx->tls.ossl.ssl) {
-    ctx->started_at = *Curl_pgrs_now(data);
-    result = cf_osslq_ctx_start(cf, data);
-    if(result)
-      goto out;
-  }
-
-  if(!ctx->got_first_byte) {
-    int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
-    if(readable > 0 && (readable & CURL_CSELECT_IN)) {
-      ctx->got_first_byte = TRUE;
-      ctx->first_byte_at = *Curl_pgrs_now(data);
-    }
-  }
-
-  /* Since OpenSSL does its own send/recv internally, we may miss the
-   * moment to populate the x509 store right before the server response.
-   * Do it instead before we start the handshake, at the loss of the
-   * time to set this up. */
-  result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
-  if(result)
-    goto out;
-
-  ERR_clear_error();
-  err = SSL_do_handshake(ctx->tls.ossl.ssl);
-
-  if(err == 1) {
-    /* connected */
-    if(!ctx->got_first_byte) {
-      /* if not recorded yet, take the timestamp before we called
-       * SSL_do_handshake() as the time we received the first packet. */
-      ctx->got_first_byte = TRUE;
-      ctx->first_byte_at = *Curl_pgrs_now(data);
-    }
-    /* Record the handshake complete with a new time stamp. */
-    ctx->handshake_at = *Curl_pgrs_now(data);
-    ctx->q.last_io = *Curl_pgrs_now(data);
-    CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms",
-                curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started_at));
-    result = cf_osslq_verify_peer(cf, data);
-    if(!result) {
-      CURL_TRC_CF(data, cf, "peer verified");
-      cf->connected = TRUE;
-      *done = TRUE;
-    }
-  }
-  else {
-    int detail = SSL_get_error(ctx->tls.ossl.ssl, err);
-    switch(detail) {
-    case SSL_ERROR_WANT_READ:
-      ctx->q.last_io = *Curl_pgrs_now(data);
-      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
-      goto out;
-    case SSL_ERROR_WANT_WRITE:
-      ctx->q.last_io = *Curl_pgrs_now(data);
-      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
-      result = CURLE_OK;
-      goto out;
-#ifdef SSL_ERROR_WANT_ASYNC
-    case SSL_ERROR_WANT_ASYNC:
-      ctx->q.last_io = *Curl_pgrs_now(data);
-      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
-      result = CURLE_OK;
-      goto out;
-#endif
-#ifdef SSL_ERROR_WANT_RETRY_VERIFY
-    case SSL_ERROR_WANT_RETRY_VERIFY:
-      result = CURLE_OK;
-      goto out;
-#endif
-    default:
-      result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT);
-      goto out;
-    }
-  }
-
-out:
-  if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl &&
-     ctx->protocol_shutdown) {
-    /* When a QUIC server instance is shutting down, it may send us a
-     * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
-     * state. The CONNECT may work in the near future again. Indicate
-     * that as a "weird" reply. */
-    result = CURLE_WEIRD_SERVER_REPLY;
-  }
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  if(result) {
-    struct ip_quadruple ip;
-
-    if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
-      infof(data, "QUIC connect to %s port %u failed: %s",
-            ip.remote_ip, ip.remote_port, curl_easy_strerror(result));
-  }
-#endif
-  if(!result)
-    result = check_and_set_expiry(cf, data);
-  if(result || *done)
-    CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
-  CF_DATA_RESTORE(cf, save);
-  return result;
-}
-
-static CURLcode h3_stream_open(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               const uint8_t *buf, size_t len,
-                               size_t *pnwritten)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream = NULL;
-  struct dynhds h2_headers;
-  size_t nheader;
-  nghttp3_nv *nva = NULL;
-  int rc = 0;
-  unsigned int i;
-  nghttp3_data_reader reader;
-  nghttp3_data_reader *preader = NULL;
-  CURLcode result;
-
-  Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
-
-  result = h3_data_setup(cf, data);
-  if(result)
-    goto out;
-  stream = H3_STREAM_CTX(ctx, data);
-  DEBUGASSERT(stream);
-  if(!stream) {
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-
-  result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL,
-                                  !data->state.http_ignorecustom ?
-                                  data->set.str[STRING_CUSTOMREQUEST] : NULL,
-                                  0, pnwritten);
-  if(result)
-    goto out;
-  if(!stream->h1.done) {
-    /* need more data */
-    goto out;
-  }
-  DEBUGASSERT(stream->h1.req);
-
-  result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
-  if(result)
-    goto out;
-  /* no longer needed */
-  Curl_h1_req_parse_free(&stream->h1);
-
-  nheader = Curl_dynhds_count(&h2_headers);
-  nva = curlx_malloc(sizeof(nghttp3_nv) * nheader);
-  if(!nva) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  for(i = 0; i < nheader; ++i) {
-    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
-    nva[i].name = (unsigned char *)e->name;
-    nva[i].namelen = e->namelen;
-    nva[i].value = (unsigned char *)e->value;
-    nva[i].valuelen = e->valuelen;
-    nva[i].flags = NGHTTP3_NV_FLAG_NONE;
-  }
-
-  DEBUGASSERT(stream->s.id == -1);
-  result = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0,
-                                &ctx->stream_bufcp, data);
-  if(result) {
-    failf(data, "cannot get bidi streams");
-    result = CURLE_SEND_ERROR;
-    goto out;
-  }
-
-  switch(data->state.httpreq) {
-  case HTTPREQ_POST:
-  case HTTPREQ_POST_FORM:
-  case HTTPREQ_POST_MIME:
-  case HTTPREQ_PUT:
-    /* known request body size or -1 */
-    if(data->state.infilesize != -1)
-      stream->upload_left = data->state.infilesize;
-    else
-      /* data sending without specifying the data amount up front */
-      stream->upload_left = -1; /* unknown */
-    break;
-  default:
-    /* there is not request body */
-    stream->upload_left = 0; /* no request body */
-    break;
-  }
-
-  stream->send_closed = (stream->upload_left == 0);
-  if(!stream->send_closed) {
-    reader.read_data = cb_h3_read_req_body;
-    preader = &reader;
-  }
-
-  rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id,
-                                   nva, nheader, preader, data);
-  if(rc) {
-    switch(rc) {
-    case NGHTTP3_ERR_CONN_CLOSING:
-      CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, "
-                  "connection is closing", stream->s.id);
-      break;
-    default:
-      CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)",
-                  stream->s.id, rc, nghttp3_strerror(rc));
-      break;
-    }
-    result = CURLE_SEND_ERROR;
-    goto out;
-  }
-
-  if(Curl_trc_is_verbose(data)) {
-    infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
-          stream->s.id, Curl_bufref_ptr(&data->state.url));
-    for(i = 0; i < nheader; ++i) {
-      infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]",
-            stream->s.id,
-            (int)nva[i].namelen, nva[i].name,
-            (int)nva[i].valuelen, nva[i].value);
-    }
-  }
-
-out:
-  curlx_free(nva);
-  Curl_dynhds_free(&h2_headers);
-  return result;
-}
-
-static CURLcode cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
-                              const uint8_t *buf, size_t len, bool eos,
-                              size_t *pnwritten)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream = NULL;
-  struct cf_call_data save;
-  CURLcode result = CURLE_OK;
-
-  (void)eos; /* use to end stream */
-  CF_DATA_SAVE(save, cf, data);
-  DEBUGASSERT(cf->connected);
-  DEBUGASSERT(ctx->tls.ossl.ssl);
-  DEBUGASSERT(ctx->h3.conn);
-  *pnwritten = 0;
-
-  result = cf_progress_ingress(cf, data);
-  if(result)
-    goto out;
-
-  result = cf_progress_egress(cf, data);
-  if(result)
-    goto out;
-
-  stream = H3_STREAM_CTX(ctx, data);
-  if(!stream || stream->s.id < 0) {
-    result = h3_stream_open(cf, data, buf, len, pnwritten);
-    if(result) {
-      CURL_TRC_CF(data, cf, "failed to open stream -> %d", result);
-      goto out;
-    }
-    stream = H3_STREAM_CTX(ctx, data);
-  }
-  else if(stream->closed) {
-    if(stream->resp_hds_complete) {
-      /* Server decided to close the stream after having sent us a final
-       * response. This is valid if it is not interested in the request
-       * body. This happens on 30x or 40x responses.
-       * We silently discard the data sent, since this is not a transport
-       * error situation. */
-      CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
-                  "on closed stream with response", stream->s.id);
-      result = CURLE_OK;
-      *pnwritten = len;
-      goto out;
-    }
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
-                "-> stream closed", stream->s.id, len);
-    result = CURLE_HTTP3;
-    goto out;
-  }
-  else {
-    result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten);
-    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to "
-                "sendbuf(len=%zu) -> %d, %zu",
-                stream->s.id, len, result, *pnwritten);
-    if(result)
-      goto out;
-    (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
-  }
-
-  result = Curl_1st_err(result, cf_progress_egress(cf, data));
-
-out:
-  result = Curl_1st_err(result, check_and_set_expiry(cf, data));
-
-  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu",
-              stream ? stream->s.id : -1, len, result, *pnwritten);
-  CF_DATA_RESTORE(cf, save);
-  return result;
-}
-
-static CURLcode recv_closed_stream(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data,
-                                   struct h3_stream_ctx *stream,
-                                   size_t *pnread)
-{
-  (void)cf;
-  *pnread = 0;
-  if(stream->reset) {
-    failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64
-          " %s)", stream->s.id, stream->error3,
-          vquic_h3_err_str(stream->error3));
-    return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3;
-  }
-  else if(!stream->resp_hds_complete) {
-    failf(data,
-          "HTTP/3 stream %" PRId64
-          " was closed cleanly, but before getting"
-          " all response header fields, treated as error",
-          stream->s.id);
-    return CURLE_HTTP3;
-  }
-  return CURLE_OK;
-}
-
-static CURLcode cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                              char *buf, size_t len, size_t *pnread)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream;
-  struct cf_call_data save;
-  CURLcode result = CURLE_OK;
-
-  CF_DATA_SAVE(save, cf, data);
-  DEBUGASSERT(cf->connected);
-  DEBUGASSERT(ctx->tls.ossl.ssl);
-  DEBUGASSERT(ctx->h3.conn);
-  *pnread = 0;
-
-  stream = H3_STREAM_CTX(ctx, data);
-  if(!stream) {
-    result = CURLE_RECV_ERROR;
-    goto out;
-  }
-
-  if(!Curl_bufq_is_empty(&stream->recvbuf)) {
-    result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread);
-    if(result) {
-      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) -> %d, %zu",
-                  stream->s.id, len, result, *pnread);
-      goto out;
-    }
-  }
-
-  result = Curl_1st_err(result, cf_progress_ingress(cf, data));
-  if(result)
-    goto out;
-
-  /* recvbuf had nothing before, maybe after progressing ingress? */
-  if(!*pnread && !Curl_bufq_is_empty(&stream->recvbuf)) {
-    result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread);
-    if(result) {
-      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) -> %d, %zu",
-                  stream->s.id, len, result, *pnread);
-      goto out;
-    }
-  }
-
-  if(*pnread) {
-    Curl_multi_mark_dirty(data);
-  }
-  else {
-    if(stream->closed) {
-      result = recv_closed_stream(cf, data, stream, pnread);
-      goto out;
-    }
-    result = CURLE_AGAIN;
-  }
-
-out:
-  result = Curl_1st_err(result, cf_progress_egress(cf, data));
-  result = Curl_1st_err(result, check_and_set_expiry(cf, data));
-
-  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu",
-              stream ? stream->s.id : -1, len, result, *pnread);
-  CF_DATA_RESTORE(cf, save);
-  return result;
-}
-
-/*
- * Called from transfer.c:data_pending to know if we should keep looping
- * to receive more data from the connection.
- */
-static bool cf_osslq_data_pending(struct Curl_cfilter *cf,
-                                  const struct Curl_easy *data)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  const struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-  (void)cf;
-  return stream && !Curl_bufq_is_empty(&stream->recvbuf);
-}
-
-static CURLcode cf_osslq_cntrl(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               int event, int arg1, void *arg2)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-  struct cf_call_data save;
-
-  CF_DATA_SAVE(save, cf, data);
-  (void)arg1;
-  (void)arg2;
-  switch(event) {
-  case CF_CTRL_DATA_SETUP:
-    break;
-  case CF_CTRL_DATA_PAUSE:
-    result = h3_data_pause(cf, data, (arg1 != 0));
-    break;
-  case CF_CTRL_DATA_DONE:
-    h3_data_done(cf, data);
-    break;
-  case CF_CTRL_DATA_DONE_SEND: {
-    struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-    if(stream && !stream->send_closed) {
-      stream->send_closed = TRUE;
-      stream->upload_left = Curl_bufq_len(&stream->sendbuf) -
-        stream->sendbuf_len_in_flight;
-      (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
-    }
-    break;
-  }
-  case CF_CTRL_CONN_INFO_UPDATE:
-    if(!cf->sockindex && cf->connected) {
-      cf->conn->httpversion_seen = 30;
-      Curl_conn_set_multiplex(cf->conn);
-    }
-    break;
-  default:
-    break;
-  }
-  CF_DATA_RESTORE(cf, save);
-  return result;
-}
-
-static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data,
-                                   bool *input_pending)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  bool alive = FALSE;
-  struct cf_call_data save;
-
-  CF_DATA_SAVE(save, cf, data);
-  *input_pending = FALSE;
-  if(!ctx->tls.ossl.ssl)
-    goto out;
-
-#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
-  /* Added in OpenSSL v3.3.x */
-  {
-    timediff_t idletime;
-    uint64_t idle_ms = 0;
-    if(!SSL_get_value_uint(ctx->tls.ossl.ssl,
-                           SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
-                           SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) {
-      CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, "
-                  "assume connection is dead.");
-      goto out;
-    }
-    CURL_TRC_CF(data, cf, "negotiated idle timeout: %" PRIu64 "ms", idle_ms);
-    idletime = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io);
-    if(idle_ms && idletime > 0 && (uint64_t)idletime > idle_ms)
-      goto out;
-  }
-
-#endif
-
-  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
-    goto out;
-
-  alive = TRUE;
-  if(*input_pending) {
-    CURLcode result;
-    /* This happens before we have sent off a request and the connection is
-       not in use by any other transfer, there should not be any data here,
-       only "protocol frames" */
-    *input_pending = FALSE;
-    result = cf_progress_ingress(cf, data);
-    CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
-    alive = result ? FALSE : TRUE;
-  }
-
-out:
-  CF_DATA_RESTORE(cf, save);
-  return alive;
-}
-
-static CURLcode cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
-                                        struct Curl_easy *data,
-                                        struct easy_pollset *ps)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-
-  if(!ctx->tls.ossl.ssl) {
-    /* NOP */
-  }
-  else if(!cf->connected) {
-    /* during handshake, transfer has not started yet. we always
-     * add our socket for polling if SSL wants to send/recv */
-    result = Curl_pollset_set(data, ps, ctx->q.sockfd,
-                              SSL_net_read_desired(ctx->tls.ossl.ssl),
-                              SSL_net_write_desired(ctx->tls.ossl.ssl));
-  }
-  else {
-    /* once connected, we only modify the socket if it is present.
-     * this avoids adding it for paused transfers. */
-    bool want_recv, want_send;
-    Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
-    if(want_recv || want_send) {
-      result = Curl_pollset_set(data, ps, ctx->q.sockfd,
-                                SSL_net_read_desired(ctx->tls.ossl.ssl),
-                                SSL_net_write_desired(ctx->tls.ossl.ssl));
-    }
-    else if(ctx->need_recv || ctx->need_send) {
-      result = Curl_pollset_set(data, ps, ctx->q.sockfd,
-                                ctx->need_recv, ctx->need_send);
-    }
-  }
-  return result;
-}
-
-static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               int query, int *pres1, void *pres2)
-{
-  struct cf_osslq_ctx *ctx = cf->ctx;
-
-  switch(query) {
-  case CF_QUERY_MAX_CONCURRENT: {
-#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL
-    /* Added in OpenSSL v3.3.x */
-    uint64_t v = 0;
-    if(ctx->tls.ossl.ssl &&
-       !SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC,
-                           SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) {
-      CURL_TRC_CF(data, cf, "error getting available local bidi streams");
-      return CURLE_HTTP3;
-    }
-    /* we report avail + in_use */
-    v += cf->conn->attached_xfers;
-    *pres1 = (v > INT_MAX) ? INT_MAX : (int)v;
-#else
-    *pres1 = 100;
-#endif
-    CURL_TRC_CF(data, cf, "query max_concurrent -> %d", *pres1);
-    return CURLE_OK;
-  }
-  case CF_QUERY_CONNECT_REPLY_MS:
-    if(ctx->got_first_byte) {
-      timediff_t ms = curlx_ptimediff_ms(&ctx->first_byte_at,
-                                         &ctx->started_at);
-      *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
-    }
-    else
-      *pres1 = -1;
-    return CURLE_OK;
-  case CF_QUERY_TIMER_CONNECT: {
-    struct curltime *when = pres2;
-    if(ctx->got_first_byte)
-      *when = ctx->first_byte_at;
-    return CURLE_OK;
-  }
-  case CF_QUERY_TIMER_APPCONNECT: {
-    struct curltime *when = pres2;
-    if(cf->connected)
-      *when = ctx->handshake_at;
-    return CURLE_OK;
-  }
-  case CF_QUERY_HTTP_VERSION:
-    *pres1 = 30;
-    return CURLE_OK;
-  case CF_QUERY_SSL_INFO:
-  case CF_QUERY_SSL_CTX_INFO: {
-    struct curl_tlssessioninfo *info = pres2;
-    if(Curl_vquic_tls_get_ssl_info(&ctx->tls,
-                                   (query == CF_QUERY_SSL_CTX_INFO), info))
-      return CURLE_OK;
-    break;
-  }
-  case CF_QUERY_ALPN_NEGOTIATED: {
-    const char **palpn = pres2;
-    DEBUGASSERT(palpn);
-    *palpn = cf->connected ? "h3" : NULL;
-    return CURLE_OK;
-  }
-  default:
-    break;
-  }
-  return cf->next ?
-    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
-    CURLE_UNKNOWN_OPTION;
-}
-
-struct Curl_cftype Curl_cft_http3 = {
-  "HTTP/3",
-  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
-  0,
-  cf_osslq_destroy,
-  cf_osslq_connect,
-  cf_osslq_close,
-  cf_osslq_shutdown,
-  cf_osslq_adjust_pollset,
-  cf_osslq_data_pending,
-  cf_osslq_send,
-  cf_osslq_recv,
-  cf_osslq_cntrl,
-  cf_osslq_conn_is_alive,
-  Curl_cf_def_conn_keep_alive,
-  cf_osslq_query,
-};
-
-CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
-                              struct Curl_easy *data,
-                              struct connectdata *conn,
-                              const struct Curl_addrinfo *ai)
-{
-  struct cf_osslq_ctx *ctx = NULL;
-  struct Curl_cfilter *cf = NULL;
-  CURLcode result;
-
-  ctx = curlx_calloc(1, sizeof(*ctx));
-  if(!ctx) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-  cf_osslq_ctx_init(ctx);
-
-  result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
-  if(result)
-    goto out;
-  cf->conn = conn;
-
-  result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC);
-  if(result)
-    goto out;
-
-  cf->next->conn = cf->conn;
-  cf->next->sockindex = cf->sockindex;
-
-out:
-  *pcf = (!result) ? cf : NULL;
-  if(result) {
-    if(cf)
-      Curl_conn_cf_discard_chain(&cf, data);
-    else if(ctx)
-      cf_osslq_ctx_free(ctx);
-  }
-  return result;
-}
-
-bool Curl_conn_is_osslq(const struct Curl_easy *data,
-                        const struct connectdata *conn,
-                        int sockindex)
-{
-  struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
-
-  (void)data;
-  for(; cf; cf = cf->next) {
-    if(cf->cft == &Curl_cft_http3)
-      return TRUE;
-    if(cf->cft->flags & CF_TYPE_IP_CONNECT)
-      return FALSE;
-  }
-  return FALSE;
-}
-
-/*
- * Store ngtcp2 version info in this buffer.
- */
-void Curl_osslq_ver(char *p, size_t len)
-{
-  const nghttp3_info *ht3 = nghttp3_version(0);
-  (void)curl_msnprintf(p, len, "nghttp3/%s", ht3->version_str);
-}
-
-#endif /* !CURL_DISABLE_HTTP && USE_OPENSSL_QUIC && USE_NGHTTP3 */
diff --git a/lib/vquic/curl_osslq.h b/lib/vquic/curl_osslq.h
deleted file mode 100644 (file)
index 7aa93d6..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef HEADER_CURL_VQUIC_CURL_OSSLQ_H
-#define HEADER_CURL_VQUIC_CURL_OSSLQ_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "../curl_setup.h"
-
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_OPENSSL_QUIC) && \
-  defined(USE_NGHTTP3)
-
-#ifdef HAVE_NETINET_UDP_H
-#include <netinet/udp.h>
-#endif
-
-struct Curl_cfilter;
-
-#include "../urldata.h"
-
-void Curl_osslq_ver(char *p, size_t len);
-
-CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
-                              struct Curl_easy *data,
-                              struct connectdata *conn,
-                              const struct Curl_addrinfo *ai);
-
-bool Curl_conn_is_osslq(const struct Curl_easy *data,
-                        const struct connectdata *conn,
-                        int sockindex);
-#endif
-
-#endif /* HEADER_CURL_VQUIC_CURL_OSSLQ_H */
index 96a3bcb594df12130f3ed5135c5039c8dcdd9efe..9d45833820d73fafdddc9ab9e3fca03b7a9669c5 100644 (file)
@@ -36,7 +36,6 @@
 #include "../cfilters.h"
 #include "../curl_trc.h"
 #include "curl_ngtcp2.h"
-#include "curl_osslq.h"
 #include "curl_quiche.h"
 #include "../multiif.h"
 #include "../progress.h"
@@ -66,8 +65,6 @@ void Curl_quic_ver(char *p, size_t len)
 {
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
   Curl_ngtcp2_ver(p, len);
-#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
-  Curl_osslq_ver(p, len);
 #elif defined(USE_QUICHE)
   Curl_quiche_ver(p, len);
 #endif
@@ -703,8 +700,6 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
   DEBUGASSERT(transport == TRNSPRT_QUIC);
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
   return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
-#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
-  return Curl_cf_osslq_create(pcf, data, conn, ai);
 #elif defined(USE_QUICHE)
   return Curl_cf_quiche_create(pcf, data, conn, ai);
 #else
index c88e2b81d956810eb83b4488d3baca81a0f80c54..bab1b9a3a1f6dcb1ea1fd97648387deedd31ed8f 100644 (file)
@@ -3652,11 +3652,7 @@ static CURLcode ossl_init_method(struct Curl_cfilter *cf,
       return CURLE_SSL_CONNECT_ERROR;
     }
 
-#ifdef USE_OPENSSL_QUIC
-    *pmethod = OSSL_QUIC_client_method();
-#else
     *pmethod = TLS_method();
-#endif
     break;
   default:
     failf(data, "unsupported transport %d in SSL init", peer->transport);
index be30c2c01be40c289ec87729ca0a9d9e5641050a..ee63f46c0a658dbd7ab971c5ead99b040f589569 100644 (file)
@@ -394,24 +394,5 @@ AS_HELP_STRING([--disable-openssl-auto-load-config],[Disable automatic loading o
     fi
   ])
 
-  dnl ---
-  dnl We may use OpenSSL QUIC.
-  dnl ---
-  AC_MSG_CHECKING([for QUIC support and OpenSSL >= 3.3])
-  AC_LINK_IFELSE([
-    AC_LANG_PROGRAM([[
-      #include <openssl/ssl.h>
-    ]],[[
-      #if (OPENSSL_VERSION_NUMBER < 0x30300000L)
-      #error need at least version 3.3.0
-      #endif
-      OSSL_QUIC_client_method();
-    ]])
-  ],[
-    AC_MSG_RESULT([yes])
-    have_openssl_quic=1
-  ],[
-    AC_MSG_RESULT([no])
-  ])
 fi
 ])
index bb0f2f31f1ff7eb2e8ba9c80c46cd8b855909f7b..ba11880b0b55193ed9a37fd4060f75cd88003fd9 100644 (file)
@@ -314,8 +314,6 @@ class TestDownload:
     # download, several at a time, pause and abort paused
     @pytest.mark.parametrize("proto", Env.http_protos())
     def test_02_23a_lib_abort_paused(self, env: Env, httpd, nghttpx, proto):
-        if proto == 'h3' and env.curl_uses_ossl_quic():
-            pytest.skip('OpenSSL QUIC fails here')
         if proto == 'h3' and env.ci_run and env.curl_uses_lib('quiche'):
             pytest.skip("fails in CI, but works locally for unknown reasons")
         count = 10
@@ -341,8 +339,6 @@ class TestDownload:
     # download, several at a time, abort after n bytes
     @pytest.mark.parametrize("proto", Env.http_protos())
     def test_02_23b_lib_abort_offset(self, env: Env, httpd, nghttpx, proto):
-        if proto == 'h3' and env.curl_uses_ossl_quic():
-            pytest.skip('OpenSSL QUIC fails here')
         if proto == 'h3' and env.ci_run and env.curl_uses_lib('quiche'):
             pytest.skip("fails in CI, but works locally for unknown reasons")
         count = 10
@@ -368,8 +364,6 @@ class TestDownload:
     # download, several at a time, abort after n bytes
     @pytest.mark.parametrize("proto", Env.http_protos())
     def test_02_23c_lib_fail_offset(self, env: Env, httpd, nghttpx, proto):
-        if proto == 'h3' and env.curl_uses_ossl_quic():
-            pytest.skip('OpenSSL QUIC fails here')
         if proto == 'h3' and env.ci_run and env.curl_uses_lib('quiche'):
             pytest.skip("fails in CI, but works locally for unknown reasons")
         count = 10
index 2e1d6a58020c5987c8c4c54da029e46a5c1d6fce..524f11e9539b2e2119960fce0bc6443565ee4aaf 100644 (file)
@@ -75,8 +75,6 @@ class TestGoAway:
     @pytest.mark.skipif(condition=not Env.have_h3(), reason="h3 not supported")
     def test_03_02_h3_goaway(self, env: Env, httpd, nghttpx):
         proto = 'h3'
-        if proto == 'h3' and env.curl_uses_ossl_quic():
-            pytest.skip('OpenSSL QUIC fails here')
         count = 3
         self.r = None
 
index 29225983e9b16a69fd99b893115ee28cf0ba8551..5b3d8f4a58fc036ba802333a562fc777ccb368ea 100644 (file)
@@ -58,8 +58,6 @@ class TestErrors:
     # download files, check that we get CURLE_PARTIAL_FILE for all
     @pytest.mark.parametrize("proto", Env.http_mplx_protos())
     def test_05_02_partial_20(self, env: Env, httpd, nghttpx, proto):
-        if proto == 'h3' and env.curl_uses_ossl_quic():
-            pytest.skip("openssl-quic is flaky in yielding proper error codes")
         if proto == 'h3' and env.curl_uses_lib('quiche') and \
                 not env.curl_lib_version_at_least('quiche', '0.24.8'):
             pytest.skip("quiche issue #2277 not fixed")
index 9a21a8bb7e834ce32f801160a34c734212401fc6..070afeb757b459bf78b672485bf781996a9bc1ca 100644 (file)
@@ -507,8 +507,6 @@ class TestUpload:
 
     @pytest.mark.parametrize("proto", Env.http_protos())
     def test_07_43_upload_denied(self, env: Env, httpd, nghttpx, proto):
-        if proto == 'h3' and env.curl_uses_ossl_quic():
-            pytest.skip("openssl-quic is flaky in filed PUTs")
         fdata = os.path.join(env.gen_dir, 'data-10m')
         count = 1
         max_upload = 128 * 1024
index 9f2ae7a27f0fb33c23303d5eebf85a14b64e3783..da3e716a688873fb78ad623dd83bb53030ea60f8 100644 (file)
@@ -65,8 +65,6 @@ class TestAuth:
     def test_14_03_digest_put_auth(self, env: Env, httpd, nghttpx, proto):
         if not env.curl_has_feature('digest'):
             pytest.skip("curl built without digest")
-        if proto == 'h3' and env.curl_uses_ossl_quic():
-            pytest.skip("openssl-quic is flaky in retrying POST")
         data='0123456789'
         curl = CurlClient(env=env)
         url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json'
@@ -97,7 +95,7 @@ class TestAuth:
     def test_14_05_basic_large_pw(self, env: Env, httpd, nghttpx, proto):
         if proto == 'h3' and not env.curl_uses_lib('ngtcp2'):
             # See <https://github.com/cloudflare/quiche/issues/1573>
-            pytest.skip("quiche/openssl-quic have problems with large requests")
+            pytest.skip("quiche has problems with large requests")
         # just large enough that nghttp2 will submit
         password = 'x' * (47 * 1024)
         fdata = os.path.join(env.gen_dir, 'data-10m')
index 129abf0ec412a8c5011ee5795d05185095ea4dec..4fc11c7923dcace122481230d69422656ed7ea2a 100644 (file)
@@ -515,7 +515,7 @@ class ExecResult:
         s = self._stats[idx]
 
         url = s['url_effective']
-        # connect time is sometimes reported as 0 by openssl-quic (sigh)
+
         self.check_stat_positive_or_0(s, idx, 'time_connect')
         # all stat keys which reporting timings
         all_keys = {