.github/workflows/ngtcp2*,\
.github/workflows/quiche*,\
.github/workflows/osslq*,\
- CMake/FindMSH3.cmake,\
CMake/FindNGHTTP3.cmake,\
CMake/FindNGTCP2.cmake,\
docs/HTTP3.md,\
'#define HAVE_LIBSSL 1' => 1,
'#define HAVE_LIBWOLFSSH' => 1,
'#define HAVE_LIBZSTD 1' => 1,
- '#define HAVE_MSH3_H 1' => 1,
'#define HAVE_NGHTTP2_NGHTTP2_H 1' => 1,
'#define HAVE_NGHTTP3_NGHTTP3_H 1' => 1,
'#define HAVE_NGTCP2_NGTCP2_CRYPTO_H 1' => 1,
WOLFSSH_VERSION: 1.4.19
# renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com
MBEDTLS_VERSION: 3.6.4
- # renovate: datasource=github-tags depName=nibanks/msh3 versioning=semver registryUrl=https://github.com
- MSH3_VERSION: 0.6.0
# renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com
AWSLC_VERSION: 1.55.0
# handled in renovate.json
install_steps: skipall
generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON -DCURL_USE_PKGCONFIG=OFF -DCURL_COMPLETION_FISH=ON -DCURL_COMPLETION_ZSH=ON
- - name: 'msh3'
- install_packages: zlib1g-dev
- install_steps: quictls msh3
- LDFLAGS: -Wl,-rpath,/home/runner/msh3/lib -Wl,-rpath,/home/runner/quictls/lib
- configure: --with-msh3=/home/runner/msh3 --with-openssl=/home/runner/quictls --enable-debug
-
- - name: 'msh3'
- install_packages: zlib1g-dev
- install_steps: quictls msh3 skipall
- PKG_CONFIG_PATH: /home/runner/msh3/lib/pkgconfig # Broken as of v0.6.0
- generate: -DOPENSSL_ROOT_DIR=/home/runner/quictls -DUSE_MSH3=ON -DMSH3_INCLUDE_DIR=/home/runner/msh3/include -DMSH3_LIBRARY=/home/runner/msh3/lib/libmsh3.so -DENABLE_DEBUG=ON
-
- - name: 'awslc'
+ - name: awslc
install_packages: zlib1g-dev
install_steps: awslc pytest
configure: LDFLAGS=-Wl,-rpath,/home/runner/awslc/lib --with-openssl=/home/runner/awslc --enable-ech
make
make -j1 install_sw
- - name: 'cache msh3'
- if: ${{ contains(matrix.build.install_steps, 'msh3') }}
- uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
- id: cache-msh3
- env:
- cache-name: cache-msh3
- with:
- path: ~/msh3
- key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MSH3_VERSION }}
-
- - name: 'build msh3'
- if: ${{ contains(matrix.build.install_steps, 'msh3') && steps.cache-msh3.outputs.cache-hit != 'true' }}
- run: |
- git clone --quiet --depth=1 -b "v${MSH3_VERSION}" --recursive https://github.com/nibanks/msh3
- cd msh3
- cmake -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/home/runner/msh3
- cmake --build .
- cmake --install .
-
- name: 'cache awslc'
if: ${{ contains(matrix.build.install_steps, 'awslc') }}
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
+++ /dev/null
-#***************************************************************************
-# _ _ ____ _
-# 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
-#
-###########################################################################
-# Find the msh3 library
-#
-# Input variables:
-#
-# - `MSH3_INCLUDE_DIR`: The msh3 include directory.
-# - `MSH3_LIBRARY`: Path to `msh3` library.
-#
-# Result variables:
-#
-# - `MSH3_FOUND`: System has msh3.
-# - `MSH3_INCLUDE_DIRS`: The msh3 include directories.
-# - `MSH3_LIBRARIES`: The msh3 library names.
-# - `MSH3_LIBRARY_DIRS`: The msh3 library directories.
-# - `MSH3_PC_REQUIRES`: The msh3 pkg-config packages.
-# - `MSH3_CFLAGS`: Required compiler flags.
-# - `MSH3_VERSION`: Version of msh3.
-
-set(MSH3_PC_REQUIRES "libmsh3")
-
-if(CURL_USE_PKGCONFIG AND
- NOT DEFINED MSH3_INCLUDE_DIR AND
- NOT DEFINED MSH3_LIBRARY)
- find_package(PkgConfig QUIET)
- pkg_check_modules(MSH3 ${MSH3_PC_REQUIRES})
-endif()
-
-if(MSH3_FOUND)
- string(REPLACE ";" " " MSH3_CFLAGS "${MSH3_CFLAGS}")
- message(STATUS "Found MSH3 (via pkg-config): ${MSH3_INCLUDE_DIRS} (found version \"${MSH3_VERSION}\")")
-else()
- set(MSH3_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config
-
- find_path(MSH3_INCLUDE_DIR NAMES "msh3.h")
- find_library(MSH3_LIBRARY NAMES "msh3")
-
- include(FindPackageHandleStandardArgs)
- find_package_handle_standard_args(MSH3
- REQUIRED_VARS
- MSH3_INCLUDE_DIR
- MSH3_LIBRARY
- )
-
- if(MSH3_FOUND)
- set(MSH3_INCLUDE_DIRS ${MSH3_INCLUDE_DIR})
- set(MSH3_LIBRARIES ${MSH3_LIBRARY})
- endif()
-
- mark_as_advanced(MSH3_INCLUDE_DIR MSH3_LIBRARY)
-endif()
endif()
endif()
-option(USE_MSH3 "Use msh3/msquic library for HTTP/3 support" OFF)
-if(USE_MSH3)
- if(USE_NGTCP2 OR USE_QUICHE)
- message(FATAL_ERROR "Only one HTTP/3 backend can be selected")
- endif()
- if(NOT WIN32)
- if(NOT USE_OPENSSL)
- message(FATAL_ERROR "msh3/msquic requires OpenSSL fork with QUIC API")
- endif()
- curl_openssl_check_quic()
- endif()
- find_package(MSH3 REQUIRED)
- list(APPEND CURL_LIBS ${MSH3_LIBRARIES})
- list(APPEND CURL_LIBDIRS ${MSH3_LIBRARY_DIRS})
- list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${MSH3_PC_REQUIRES})
- include_directories(SYSTEM ${MSH3_INCLUDE_DIRS})
- link_directories(${MSH3_LIBRARY_DIRS})
- if(MSH3_CFLAGS)
- string(APPEND CMAKE_C_FLAGS " ${MSH3_CFLAGS}")
- endif()
-endif()
-
if(USE_OPENSSL_QUIC)
- if(USE_NGTCP2 OR USE_QUICHE OR USE_MSH3)
+ if(USE_NGTCP2 OR USE_QUICHE)
message(FATAL_ERROR "Only one HTTP/3 backend can be selected")
endif()
find_package(OpenSSL 3.3.0 REQUIRED)
endif()
endif()
-if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC))
+if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC))
message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
endif()
(_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_MSH3 OR USE_OPENSSL_QUIC)
+curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC)
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
CMake/FindLibssh.cmake \
CMake/FindLibssh2.cmake \
CMake/FindLibuv.cmake \
- CMake/FindMSH3.cmake \
CMake/FindMbedTLS.cmake \
CMake/FindNGHTTP2.cmake \
CMake/FindNGHTTP3.cmake \
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, --with-msh3)"
+ curl_h3_msg="no (--with-ngtcp2 --with-nghttp3, --with-quiche, --with-openssl-quic)"
enable_altsvc="yes"
hsts="yes"
fi
fi
-dnl **********************************************************************
-dnl Check for msh3/msquic (QUIC)
-dnl **********************************************************************
-
-OPT_MSH3="no"
-
-if test "x$disable_http" = "xyes" -o "x$USE_NGTCP" = "x1"; then
- # without HTTP or with ngtcp2, msh3 is no use
- OPT_MSH3="no"
-fi
-
-AC_ARG_WITH(msh3,
-AS_HELP_STRING([--with-msh3=PATH],[Enable msh3 usage])
-AS_HELP_STRING([--without-msh3],[Disable msh3 usage]),
- [OPT_MSH3=$withval])
-case "$OPT_MSH3" in
- no)
- dnl --without-msh3 option used
- want_msh3="no"
- ;;
- yes)
- dnl --with-msh3 option used without path
- want_msh3="default"
- want_msh3_path=""
- ;;
- *)
- dnl --with-msh3 option used with path
- want_msh3="yes"
- want_msh3_path="$withval"
- ;;
-esac
-
-if test X"$want_msh3" != Xno; then
-
- dnl msh3 on non-Windows needs an OpenSSL with the QUIC API
- if test "$curl_cv_native_windows" != "yes"; then
- if test "$QUIC_ENABLED" != "yes"; then
- AC_MSG_ERROR([the detected TLS library does not support QUIC, making --with-msh3 a no-no])
- fi
- if test "$OPENSSL_ENABLED" != "1"; then
- AC_MSG_ERROR([msh3/msquic requires OpenSSL])
- fi
- fi
-
- if test "$NGHTTP3_ENABLED" = 1; then
- AC_MSG_ERROR([--with-msh3 and --with-ngtcp2 are mutually exclusive])
- fi
- if test "$USE_QUICHE" = 1; then
- AC_MSG_ERROR([--with-msh3 and --with-quiche are mutually exclusive])
- fi
-
- dnl backup the pre-msh3 variables
- CLEANLDFLAGS="$LDFLAGS"
- CLEANLDFLAGSPC="$LDFLAGSPC"
- CLEANCPPFLAGS="$CPPFLAGS"
- CLEANLIBS="$LIBS"
-
- if test -n "$want_msh3_path"; then
- LD_MSH3="-L$want_msh3_path/lib"
- CPP_MSH3="-I$want_msh3_path/include"
- DIR_MSH3="$want_msh3_path/lib"
- LDFLAGS="$LDFLAGS $LD_MSH3"
- LDFLAGSPC="$LDFLAGSPC $LD_MSH3"
- CPPFLAGS="$CPPFLAGS $CPP_MSH3"
- fi
- LIBS="-lmsh3 $LIBS"
-
- AC_CHECK_LIB(msh3, MsH3ApiOpen,
- [
- AC_CHECK_HEADERS(msh3.h,
- curl_h3_msg="enabled (msh3)"
- AC_DEFINE(USE_MSH3, 1, [if msh3 is in use])
- USE_MSH3=1
- CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_MSH3"
- export CURL_LIBRARY_PATH
- AC_MSG_NOTICE([Added $DIR_MSH3 to CURL_LIBRARY_PATH])
- dnl FIXME: Enable when msh3 was detected via pkg-config
- if false; then
- LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libmsh3"
- fi
- experimental="$experimental HTTP3"
- )
- ],
- dnl not found, revert back to clean variables
- LDFLAGS=$CLEANLDFLAGS
- LDFLAGSPC=$CLEANLDFLAGSPC
- CPPFLAGS=$CLEANCPPFLAGS
- LIBS=$CLEANLIBS
- )
-fi
-
dnl **********************************************************************
dnl libuv is only ever used for debug purposes
dnl **********************************************************************
fi
if test "x$USE_NGTCP2_H3" = "x1" -o "x$USE_QUICHE" = "x1" \
- -o "x$USE_OPENSSL_H3" = "x1" -o "x$USE_MSH3" = "x1"; then
+ -o "x$USE_OPENSSL_H3" = "x1"; then
if test "x$CURL_WITH_MULTI_SSL" = "x1"; then
AC_MSG_ERROR([MultiSSL cannot be enabled with HTTP/3 and vice versa])
fi
Nothing is currently scheduled to be removed.
-## msh3 support
-
-The msh3 backed for QUIC and HTTP/3 was introduced in April 2022 but has never
-been made to work properly. It has seen no visible traction or developer
-activity from the msh3 main author (or anyone else seemingly interested) in
-two years. As a non-functional backend, it only adds friction and "weight" to
-the development and maintenance.
-
-Meanwhile, we have a fully working backend in the ngtcp2 one and we have two
-fully working backends in OpenSSL-QUIC and quiche well on their way of ending
-their experimental status in a future.
-
-We remove msh3 support from the curl source tree in July 2025.
-
## winbuild build system
curl drops support for the winbuild build method after September 2025.
- Support for Visual Studio 2005 and older (removed in 8.13.0)
- Secure Transport (removed in 8.15.0)
- BearSSL (removed in 8.15.0)
+ - msh3 (removed in 8.16.0)
[OpenSSL 3.2+ QUIC](https://github.com/openssl/openssl) - **EXPERIMENTAL**
-[msh3](https://github.com/nibanks/msh3) (with [msquic](https://github.com/microsoft/msquic)) - **EXPERIMENTAL**
-
## Experimental
HTTP/3 support in curl is considered **EXPERIMENTAL** until further notice
-when built to use *quiche* or *msh3*. Only the *ngtcp2* backend is not
-experimental.
+when built to use *quiche*. Only the *ngtcp2* backend is not experimental.
Further development and tweaking of the HTTP/3 support in curl happens in the
master branch using pull-requests, just like ordinary changes.
If `make install` results in `Permission denied` error, you need to prepend
it with `sudo`.
-# msh3 (msquic) version
-
-**Note**: The msquic HTTP/3 backend is immature and is not properly functional
-one as of September 2023. Feel free to help us test it and improve it, but
-there is no point in filing bugs about it just yet.
-
-msh3 support is **EXPERIMENTAL**
-
-## Build Linux (with quictls fork of OpenSSL)
-
-Build msh3:
-
- % git clone -b v0.6.0 --depth 1 --recursive https://github.com/nibanks/msh3
- % cd msh3 && mkdir build && cd build
- % cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
- % cmake --build .
- % cmake --install .
-
-Build curl:
-
- % git clone https://github.com/curl/curl
- % cd curl
- % autoreconf -fi
- % ./configure LDFLAGS="-Wl,-rpath,/usr/local/lib" --with-msh3=/usr/local --with-openssl
- % make
- % make install
-
-Run from `/usr/local/bin/curl`.
-
-## Build Windows
-
-Build msh3:
-
- % git clone -b v0.6.0 --depth 1 --recursive https://github.com/nibanks/msh3
- % cd msh3 && mkdir build && cd build
- % cmake -G 'Visual Studio 17 2022' -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
- % cmake --build . --config Release
- % cmake --install . --config Release
-
-**Note** - On Windows, Schannel is used for TLS support by default. If you
-with to use (the quictls fork of) OpenSSL, specify the `-DQUIC_TLS=openssl`
-option to the generate command above. Also note that OpenSSL brings with it an
-additional set of build dependencies not specified here.
-
-Build curl (in [Visual Studio Command
-prompt](../winbuild/README.md#open-a-command-prompt)):
-
- % git clone https://github.com/curl/curl
- % cd curl/winbuild
- % nmake /f Makefile.vc mode=dll WITH_MSH3=dll MSH3_PATH="C:/Program Files/msh3" MACHINE=x64
-
-Run in the `C:/Program Files/msh3/lib` directory, copy `curl.exe` to that
-directory, or copy `msquic.dll` and `msh3.dll` from that directory to the
-`curl.exe` directory. For example:
-
- % C:\Program Files\msh3\lib> F:\curl\builds\libcurl-vc-x64-release-dll-ipv6-sspi-schannel-msh3\bin\curl.exe --http3 https://curl.se/
-
# `--http3`
Use only HTTP/3:
- `USE_APPLE_IDN`: Use Apple built-in IDN support. Default: `OFF`
- `USE_LIBIDN2`: Use libidn2 for IDN support. Default: `ON`
- `USE_LIBRTMP`: Enable librtmp from rtmpdump. Default: `OFF`
-- `USE_MSH3`: Use msh3/msquic library for HTTP/3 support. Default: `OFF`
- `USE_NGHTTP2`: Use nghttp2 library. Default: `ON`
- `USE_NGTCP2`: Use ngtcp2 and nghttp3 libraries for HTTP/3 support. Default: `OFF`
- `USE_QUICHE`: Use quiche library for HTTP/3 support. Default: `OFF`
- `MBEDTLS_LIBRARY`: Path to `mbedtls` library.
- `MBEDX509_LIBRARY`: Path to `mbedx509` library.
- `MBEDCRYPTO_LIBRARY`: Path to `mbedcrypto` library.
-- `MSH3_INCLUDE_DIR`: The msh3 include directory.
-- `MSH3_LIBRARY`: Path to `msh3` library.
- `NGHTTP2_INCLUDE_DIR`: The nghttp2 include directory.
- `NGHTTP2_LIBRARY`: Path to `nghttp2` library.
- `NGHTTP3_INCLUDE_DIR`: The nghttp3 include directory.
`RTLIBCFG`: `static` | `CURL_STATIC_CRT=ON`
`ENABLE_IDN` | `USE_WIN32_IDN=ON`
`ENABLE_IPV6` | `ENABLE_IPV6=ON`
-`ENABLE_MSH3` | `USE_MSH3=ON`
`ENABLE_NGHTTP2` | `USE_NGHTTP2=ON`
`ENABLE_OPENSSL_AUTO_LOAD_CONFIG` | `CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG=OFF` (default)
`ENABLE_SCHANNEL` | `CURL_USE_SCHANNEL=ON`
`WITH_DEVEL` | see individual `*_INCLUDE_DIR` and `*_LIBRARY` options and `OPENSSL_ROOT_DIR`
`WITH_CARES`, `CARES_PATH` | `ENABLE_ARES=ON`, optional: `CARES_INCLUDE_DIR`, `CARES_LIBRARY`
`WITH_MBEDTLS`, `MBEDTLS_PATH` | `CURL_USE_MBEDTLS=ON`, optional: `MBEDTLS_INCLUDE_DIR`, `MBEDTLS_LIBRARY`, `MBEDX509_LIBRARY`, `MBEDCRYPTO_LIBRARY`
-`WITH_MSH3`, `MSH_PATH` | `USE_MSH3=ON`, optional: `MSH3_INCLUDE_DIR`, `MSH3_LIBRARY`
`WITH_NGHTTP2`, `NGHTTP2_PATH` | `USE_NGHTTP2=ON`, optional: `NGHTTP2_INCLUDE_DIR`, `NGHTTP2_LIBRARY`
`WITH_SSH`, `SSH_PATH` | `CURL_USE_LIBSSH=ON`, optional: `LIBSSH_INCLUDE_DIR`, `LIBSSH_LIBRARY`
`WITH_SSH2`, `SSH2_PATH` | `CURL_USE_LIBSSH2=ON`, optional: `LIBSSH2_INCLUDE_DIR`, `LIBSSH2_LIBRARY`
vtls/x509asn1.h
LIB_VQUIC_CFILES = \
- vquic/curl_msh3.c \
vquic/curl_ngtcp2.c \
vquic/curl_osslq.c \
vquic/curl_quiche.c \
vquic/vquic-tls.c
LIB_VQUIC_HFILES = \
- vquic/curl_msh3.h \
vquic/curl_ngtcp2.h \
vquic/curl_osslq.h \
vquic/curl_quiche.h \
* Assign the address `ai` to the Curl_sockaddr_ex `dest` and
* set the transport used.
*/
-CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
- const struct Curl_addrinfo *ai,
- int transport)
+static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest,
+ const struct Curl_addrinfo *ai,
+ int transport)
{
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
/* if the caller does not want info back, use a local temp copy */
addr = &dummy;
- result = Curl_sock_assign_addr(addr, ai, transport);
+ result = sock_assign_addr(addr, ai, transport);
if(result)
return result;
ctx->sock = CURL_SOCKET_BAD;
ctx->transport = transport;
- result = Curl_sock_assign_addr(&ctx->addr, ai, transport);
+ result = sock_assign_addr(&ctx->addr, ai, transport);
if(result)
return result;
#define Curl_sndbuf_init(y) Curl_nop_stmt
#endif
-/**
- * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
- * set the transport used.
- */
-CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
- const struct Curl_addrinfo *ai,
- int transport);
-
/**
* Creates a cfilter that opens a TCP socket to the given address
* when calling its `connect` implementation.
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS
-/* Define to enable HTTP3 support (experimental, requires NGTCP2, quiche or
- MSH3) */
-#undef USE_HTTP3
-
/* Version number of package */
#undef VERSION
/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
-/* to enable msh3 */
-#cmakedefine USE_MSH3 1
-
/* if Unix domain sockets are enabled */
#cmakedefine USE_UNIX_SOCKETS 1
#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
(defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \
- defined(USE_QUICHE) || defined(USE_MSH3)
+ defined(USE_QUICHE)
#ifdef CURL_WITH_MULTI_SSL
#error "MultiSSL combined with QUIC is not supported"
***************************************************************************/
#include "curl_setup.h"
-#if defined(USE_MSH3) && !defined(_WIN32)
-#include <pthread.h>
-#endif
-
#include "bufq.h"
#include "dynhds.h"
#include "ws.h"
#ifndef CURL_DISABLE_SOCKETPAIR
#ifdef HAVE_SOCKETPAIR
+#ifdef USE_SOCKETPAIR
int Curl_socketpair(int domain, int type, int protocol,
curl_socket_t socks[2], bool nonblocking)
{
#endif
return 0;
}
+#endif /* USE_SOCKETPAIR */
#else /* !HAVE_SOCKETPAIR */
#ifdef _WIN32
/*
#define SOCKETPAIR_TYPE SOCK_STREAM
#endif
-#define wakeup_create(p,nb)\
-Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p, nb)
+#define USE_SOCKETPAIR
+#define wakeup_create(p,nb) \
+ Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p, nb)
#endif /* USE_EVENTFD */
+++ /dev/null
-/***************************************************************************
- * _ _ ____ _
- * 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"
-
-#ifdef USE_MSH3
-
-#include "../urldata.h"
-#include "../hash.h"
-#include "../uint-hash.h"
-#include "../curlx/timeval.h"
-#include "../multiif.h"
-#include "../sendf.h"
-#include "../curl_trc.h"
-#include "../cfilters.h"
-#include "../cf-socket.h"
-#include "../connect.h"
-#include "../progress.h"
-#include "../http1.h"
-#include "curl_msh3.h"
-#include "../socketpair.h"
-#include "../vtls/vtls.h"
-#include "vquic.h"
-#include "vquic_int.h"
-
-/* The last 3 #include files should be in this order */
-#include "../curl_printf.h"
-#include "../curl_memory.h"
-#include "../memdebug.h"
-
-#ifdef CURL_DISABLE_SOCKETPAIR
-#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
-#endif
-
-#define H3_STREAM_WINDOW_SIZE (128 * 1024)
-#define H3_STREAM_CHUNK_SIZE (16 * 1024)
-#define H3_STREAM_RECV_CHUNKS \
- (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
-
-#ifdef _WIN32
-#define msh3_lock CRITICAL_SECTION
-#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
-#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
-#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
-#define msh3_lock_release(lock) LeaveCriticalSection(lock)
-#else /* !_WIN32 */
-#include <pthread.h>
-#define msh3_lock pthread_mutex_t
-#define msh3_lock_initialize(lock) do { \
- pthread_mutexattr_t attr; \
- pthread_mutexattr_init(&attr); \
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
- pthread_mutex_init(lock, &attr); \
- pthread_mutexattr_destroy(&attr); \
-} while(0)
-#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
-#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
-#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
-#endif /* _WIN32 */
-
-
-static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
- void *IfContext);
-static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
- void *IfContext);
-static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
- void *IfContext,
- MSH3_REQUEST *Request);
-static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
- void *IfContext,
- const MSH3_HEADER *Header);
-static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
- void *IfContext, uint32_t *Length,
- const uint8_t *Data);
-static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
- bool Aborted, uint64_t AbortError);
-static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
- void *IfContext);
-static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
- void *IfContext, void *SendContext);
-
-
-void Curl_msh3_ver(char *p, size_t len)
-{
- uint32_t v[4];
- MsH3Version(v);
- (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
-}
-
-#define SP_LOCAL 0
-#define SP_REMOTE 1
-
-struct cf_msh3_ctx {
- MSH3_API *api;
- MSH3_CONNECTION *qconn;
- struct Curl_sockaddr_ex addr;
- curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
- char l_ip[MAX_IPADR_LEN]; /* local IP as string */
- int l_port; /* local port number */
- struct cf_call_data call_data;
- struct curltime connect_started; /* time the current attempt started */
- struct curltime handshake_at; /* time connect handshake finished */
- struct uint_hash streams; /* hash `data->mid` to `stream_ctx` */
- /* Flags written by msh3/msquic thread */
- BIT(handshake_complete);
- BIT(handshake_succeeded);
- BIT(connected);
- BIT(initialized);
- /* Flags written by curl thread */
- BIT(verbose);
- BIT(active);
-};
-
-static void h3_stream_hash_free(unsigned int id, void *stream);
-
-static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
- const struct Curl_addrinfo *ai)
-{
- CURLcode result;
-
- DEBUGASSERT(!ctx->initialized);
- Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
-
- result = Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
- if(result)
- return result;
-
- ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
- ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
- ctx->initialized = TRUE;
-
- return result;
-}
-
-static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx)
-{
- if(ctx && ctx->initialized) {
- Curl_uint_hash_destroy(&ctx->streams);
- }
- free(ctx);
-}
-
-static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data);
-
-/* How to access `call_data` from a cf_msh3 filter */
-#undef CF_CTX_CALL_DATA
-#define CF_CTX_CALL_DATA(cf) \
- ((struct cf_msh3_ctx *)(cf)->ctx)->call_data
-
-/**
- * All about the H3 internals of a stream
- */
-struct h3_stream_ctx {
- struct MSH3_REQUEST *req;
- struct bufq recvbuf; /* h3 response */
-#ifdef _WIN32
- CRITICAL_SECTION recv_lock;
-#else /* !_WIN32 */
- pthread_mutex_t recv_lock;
-#endif /* _WIN32 */
- uint64_t error3; /* HTTP/3 stream error code */
- int status_code; /* HTTP status code */
- CURLcode recv_error;
- BIT(closed);
- BIT(reset);
- BIT(upload_done);
- BIT(firstheader); /* FALSE until headers arrive */
- BIT(recv_header_complete);
-};
-
-static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
-{
- Curl_bufq_free(&stream->recvbuf);
- 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_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
- if(stream)
- return CURLE_OK;
-
- stream = calloc(1, sizeof(*stream));
- if(!stream)
- return CURLE_OUT_OF_MEMORY;
-
- stream->req = ZERO_NULL;
- msh3_lock_initialize(&stream->recv_lock);
- Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
- H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
- CURL_TRC_CF(data, cf, "data setup");
-
- if(!Curl_uint_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_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
- (void)cf;
- if(stream) {
- CURL_TRC_CF(data, cf, "easy handle is done");
- Curl_uint_hash_remove(&ctx->streams, data->mid);
- }
-}
-
-static void drain_stream_from_other_thread(struct Curl_easy *data,
- struct h3_stream_ctx *stream)
-{
- (void)data;
- (void)stream;
- /* cannot expire from other thread.
- here is the disconnect between msh3 and curl */
-}
-
-static void h3_drain_stream(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- (void)cf;
- Curl_multi_mark_dirty(data);
-}
-
-static const MSH3_CONNECTION_IF msh3_conn_if = {
- msh3_conn_connected,
- msh3_conn_shutdown_complete,
- msh3_conn_new_request
-};
-
-static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
- void *IfContext)
-{
- struct Curl_cfilter *cf = IfContext;
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct Curl_easy *data = CF_DATA_CURRENT(cf);
- (void)Connection;
-
- CURL_TRC_CF(data, cf, "[MSH3] connected");
- ctx->handshake_succeeded = TRUE;
- ctx->connected = TRUE;
- ctx->handshake_complete = TRUE;
-}
-
-static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
- void *IfContext)
-{
- struct Curl_cfilter *cf = IfContext;
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct Curl_easy *data = CF_DATA_CURRENT(cf);
-
- (void)Connection;
- CURL_TRC_CF(data, cf, "[MSH3] shutdown complete");
- ctx->connected = FALSE;
- ctx->handshake_complete = TRUE;
-}
-
-static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
- void *IfContext,
- MSH3_REQUEST *Request)
-{
- (void)Connection;
- (void)IfContext;
- (void)Request;
-}
-
-static const MSH3_REQUEST_IF msh3_request_if = {
- msh3_header_received,
- msh3_data_received,
- msh3_complete,
- msh3_shutdown_complete,
- msh3_data_sent
-};
-
-/* Decode HTTP status code. Returns -1 if no valid status code was
- decoded. (duplicate from http2.c) */
-static int decode_status_code(const char *value, size_t len)
-{
- int i;
- int res;
-
- if(len != 3) {
- return -1;
- }
-
- res = 0;
-
- for(i = 0; i < 3; ++i) {
- char c = value[i];
-
- if(c < '0' || c > '9') {
- return -1;
- }
-
- res *= 10;
- res += c - '0';
- }
-
- return res;
-}
-
-/*
- * 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_easy *data,
- const void *mem, size_t memlen)
-{
- struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- CURLcode result = CURLE_OK;
- size_t nwritten;
-
- if(!stream)
- return CURLE_RECV_ERROR;
-
- result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten);
- if(result)
- return result;
-
- if(nwritten < memlen) {
- /* This MUST not happen. Our recbuf is dimensioned to hold the
- * full max_stream_window and then some for this very reason. */
- DEBUGASSERT(0);
- return CURLE_RECV_ERROR;
- }
- return result;
-}
-
-static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
- void *userp,
- const MSH3_HEADER *hd)
-{
- struct Curl_easy *data = userp;
- struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- CURLcode result;
- (void)Request;
-
- DEBUGF(infof(data, "[MSH3] header received, stream=%d", !!stream));
- if(!stream || stream->recv_header_complete) {
- return;
- }
-
- msh3_lock_acquire(&stream->recv_lock);
-
- if((hd->NameLength == 7) &&
- !strncmp(HTTP_PSEUDO_STATUS, (const char *)hd->Name, 7)) {
- char line[14]; /* status line is always 13 characters long */
- size_t ncopy;
-
- DEBUGASSERT(!stream->firstheader);
- stream->status_code = decode_status_code(hd->Value, hd->ValueLength);
- DEBUGASSERT(stream->status_code != -1);
- ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
- stream->status_code);
- result = write_resp_raw(data, line, ncopy);
- if(result)
- stream->recv_error = result;
- stream->firstheader = TRUE;
- }
- else {
- /* store as an HTTP1-style header */
- DEBUGASSERT(stream->firstheader);
- result = write_resp_raw(data, hd->Name, hd->NameLength);
- if(!result)
- result = write_resp_raw(data, ": ", 2);
- if(!result)
- result = write_resp_raw(data, hd->Value, hd->ValueLength);
- if(!result)
- result = write_resp_raw(data, "\r\n", 2);
- if(result) {
- stream->recv_error = result;
- }
- }
-
- drain_stream_from_other_thread(data, stream);
- msh3_lock_release(&stream->recv_lock);
-}
-
-static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
- void *IfContext, uint32_t *buflen,
- const uint8_t *buf)
-{
- struct Curl_easy *data = IfContext;
- struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- CURLcode result;
- bool rv = FALSE;
-
- /* We would like to limit the amount of data we are buffer here. There seems
- * to be no mechanism in msh3 to adjust flow control and it is undocumented
- * what happens if we return FALSE here or less length (buflen is an inout
- * parameter).
- */
- (void)Request;
- if(!stream)
- return FALSE;
-
- msh3_lock_acquire(&stream->recv_lock);
-
- if(!stream->recv_header_complete) {
- result = write_resp_raw(data, "\r\n", 2);
- if(result) {
- stream->recv_error = result;
- goto out;
- }
- stream->recv_header_complete = TRUE;
- }
-
- result = write_resp_raw(data, buf, *buflen);
- if(result) {
- stream->recv_error = result;
- }
- rv = TRUE;
-
-out:
- msh3_lock_release(&stream->recv_lock);
- return rv;
-}
-
-static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
- bool aborted, uint64_t error)
-{
- struct Curl_easy *data = IfContext;
- struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
- (void)Request;
- if(!stream)
- return;
- msh3_lock_acquire(&stream->recv_lock);
- stream->closed = TRUE;
- stream->recv_header_complete = TRUE;
- if(error)
- stream->error3 = error;
- if(aborted)
- stream->reset = TRUE;
- msh3_lock_release(&stream->recv_lock);
-}
-
-static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
- void *IfContext)
-{
- struct Curl_easy *data = IfContext;
- struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
- if(!stream)
- return;
- (void)Request;
- (void)stream;
-}
-
-static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
- void *IfContext, void *SendContext)
-{
- struct Curl_easy *data = IfContext;
- struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- if(!stream)
- return;
- (void)Request;
- (void)stream;
- (void)SendContext;
-}
-
-static CURLcode recv_closed_stream(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- size_t *pnread)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
- (void)cf;
- *pnread = 0;
- if(!stream)
- return CURLE_RECV_ERROR;
-
- if(stream->reset) {
- failf(data, "HTTP/3 stream reset by server");
- CURL_TRC_CF(data, cf, "cf_recv, was reset");
- return CURLE_PARTIAL_FILE;
- }
- else if(stream->error3) {
- failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)",
- (ssize_t)stream->error3);
- CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly");
- return CURLE_HTTP3;
- }
- else {
- CURL_TRC_CF(data, cf, "cf_recv, closed ok");
- }
- return CURLE_OK;
-}
-
-static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
-
- /* we have no indication from msh3 when it would be a good time
- * to juggle the connection again. So, we compromise by calling
- * us again every some milliseconds. */
- (void)cf;
- if(stream && stream->req && !stream->closed) {
- Curl_expire(data, 10, EXPIRE_QUIC);
- }
- else {
- Curl_expire(data, 50, EXPIRE_QUIC);
- }
-}
-
-static CURLcode cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
- char *buf, size_t len, size_t *pnread)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- struct cf_call_data save;
- CURLcode result = CURLE_OK;
-
- *pnread = 0;
- CURL_TRC_CF(data, cf, "cf_recv(len=%zu), stream=%d", len, !!stream);
- if(!stream)
- return CURLE_RECV_ERROR;
- CF_DATA_SAVE(save, cf, data);
-
- msh3_lock_acquire(&stream->recv_lock);
-
- if(stream->recv_error) {
- failf(data, "request aborted");
- result = stream->recv_error;
- goto out;
- }
-
- if(!Curl_bufq_is_empty(&stream->recvbuf)) {
- result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread);
- CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %d, %zu",
- len, result, *pnread);
- if(result)
- goto out;
- if(stream->closed)
- h3_drain_stream(cf, data);
- }
- else if(stream->closed) {
- result = recv_closed_stream(cf, data, pnread);
- goto out;
- }
- else {
- CURL_TRC_CF(data, cf, "req: nothing here, call again");
- result = CURLE_AGAIN;
- }
-
-out:
- msh3_lock_release(&stream->recv_lock);
- set_quic_expire(cf, data);
- CF_DATA_RESTORE(cf, save);
- return result;
-}
-
-static CURLcode cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, bool eos,
- size_t *pnwritten)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- struct h1_req_parser h1;
- struct dynhds h2_headers;
- MSH3_HEADER *nva = NULL;
- size_t nheader, i;
- ssize_t nwritten = -1;
- struct cf_call_data save;
- CURLcode result = CURLE_OK;
-
- *pnwritten = 0;
- CF_DATA_SAVE(save, cf, data);
-
- Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
-
- /* Sizes must match for cast below to work" */
- DEBUGASSERT(stream);
- CURL_TRC_CF(data, cf, "req: send %zu bytes", len);
-
- if(!stream->req) {
- /* The first send on the request contains the headers and possibly some
- data. Parse out the headers and create the request, then if there is
- any data left over go ahead and send it too. */
- nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, &result);
- if(nwritten < 0)
- goto out;
- DEBUGASSERT(h1.done);
- DEBUGASSERT(h1.req);
- *pnwritten = (size_t)nwritten;
-
- result = Curl_http_req_to_h2(&h2_headers, h1.req, data);
- if(result)
- goto out;
-
- nheader = Curl_dynhds_count(&h2_headers);
- nva = malloc(sizeof(MSH3_HEADER) * 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 = e->name;
- nva[i].NameLength = e->namelen;
- nva[i].Value = e->value;
- nva[i].ValueLength = e->valuelen;
- }
-
- CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
- stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
- nva, nheader,
- eos ? MSH3_REQUEST_FLAG_FIN :
- MSH3_REQUEST_FLAG_NONE);
- if(!stream->req) {
- failf(data, "request open failed");
- result = CURLE_SEND_ERROR;
- }
- result = CURLE_OK;
- *pnwritten = len;
- goto out;
- }
- else {
- /* request is open */
- CURL_TRC_CF(data, cf, "req: send %zu body bytes", len);
- if(len > 0xFFFFFFFF) {
- len = 0xFFFFFFFF;
- }
-
- if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf,
- (uint32_t)len, stream)) {
- result = CURLE_SEND_ERROR;
- goto out;
- }
-
- /* msh3/msquic will hold onto this memory until the send complete event.
- How do we make sure curl does not free it until then? */
- result = CURLE_OK;
- *pnwritten = len;
- }
-
-out:
- set_quic_expire(cf, data);
- free(nva);
- Curl_h1_req_parse_free(&h1);
- Curl_dynhds_free(&h2_headers);
- CF_DATA_RESTORE(cf, save);
- return result;
-}
-
-static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct easy_pollset *ps)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- struct cf_call_data save;
-
- CF_DATA_SAVE(save, cf, data);
- if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
- if(stream->recv_error) {
- Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]);
- h3_drain_stream(cf, data);
- }
- else if(stream->req) {
- Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]);
- h3_drain_stream(cf, data);
- }
- }
-}
-
-static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- struct cf_call_data save;
- bool pending = FALSE;
-
- CF_DATA_SAVE(save, cf, data);
-
- (void)cf;
- if(stream && stream->req) {
- msh3_lock_acquire(&stream->recv_lock);
- CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf,
- "data pending = %zu",
- Curl_bufq_len(&stream->recvbuf));
- pending = !Curl_bufq_is_empty(&stream->recvbuf);
- msh3_lock_release(&stream->recv_lock);
- if(pending)
- h3_drain_stream(cf, (struct Curl_easy *)CURL_UNCONST(data));
- }
-
- CF_DATA_RESTORE(cf, save);
- return pending;
-}
-
-static CURLcode h3_data_pause(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool pause)
-{
- if(!pause) {
- h3_drain_stream(cf, data);
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
- }
- return CURLE_OK;
-}
-
-static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int event, int arg1, void *arg2)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- struct cf_call_data save;
- CURLcode result = CURLE_OK;
-
- CF_DATA_SAVE(save, cf, data);
-
- (void)arg1;
- (void)arg2;
- switch(event) {
- case CF_CTRL_DATA_SETUP:
- result = h3_data_setup(cf, data);
- 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:
- CURL_TRC_CF(data, cf, "req: send done");
- if(stream) {
- stream->upload_done = TRUE;
- if(stream->req) {
- char buf[1];
- if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN,
- buf, 0, data)) {
- result = CURLE_SEND_ERROR;
- }
- }
- }
- break;
- default:
- break;
- }
-
- CF_DATA_RESTORE(cf, save);
- return result;
-}
-
-static CURLcode cf_connect_start(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct ssl_primary_config *conn_config;
- MSH3_ADDR addr = {0};
- CURLcode result;
- bool verify;
-
- DEBUGASSERT(ctx->initialized);
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config)
- return CURLE_FAILED_INIT;
- verify = !!conn_config->verifypeer;
-
- memcpy(&addr, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
- MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
-
- if(verify && (conn_config->CAfile || conn_config->CApath)) {
- /* Note there's currently no way to provide trust anchors to MSH3 and
- that causes tests to fail. */
- CURL_TRC_CF(data, cf, "non-standard CA not supported, "
- "attempting with built-in verification");
- }
-
- CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
- cf->conn->host.name, (int)cf->conn->remote_port, verify);
-
- ctx->api = MsH3ApiOpen();
- if(!ctx->api) {
- failf(data, "cannot create msh3 api");
- return CURLE_FAILED_INIT;
- }
-
- ctx->qconn = MsH3ConnectionOpen(ctx->api,
- &msh3_conn_if,
- cf,
- cf->conn->host.name,
- &addr,
- !verify);
- if(!ctx->qconn) {
- failf(data, "cannot create msh3 connection");
- if(ctx->api) {
- MsH3ApiClose(ctx->api);
- ctx->api = NULL;
- }
- return CURLE_FAILED_INIT;
- }
-
- result = h3_data_setup(cf, data);
- if(result)
- return result;
-
- return CURLE_OK;
-}
-
-static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct cf_call_data save;
- CURLcode result = CURLE_OK;
-
- if(cf->connected) {
- *done = TRUE;
- return CURLE_OK;
- }
-
- CF_DATA_SAVE(save, cf, data);
-
- if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
- if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0], FALSE) < 0) {
- ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
- ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
- return CURLE_COULDNT_CONNECT;
- }
- }
-
- *done = FALSE;
- if(!ctx->qconn) {
- ctx->connect_started = curlx_now();
- result = cf_connect_start(cf, data);
- if(result)
- goto out;
- }
-
- if(ctx->handshake_complete) {
- ctx->handshake_at = curlx_now();
- if(ctx->handshake_succeeded) {
- CURL_TRC_CF(data, cf, "handshake succeeded");
- cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- cf->connected = TRUE;
- cf->conn->alpn = CURL_HTTP_VERSION_3;
- *done = TRUE;
- connkeep(cf->conn, "HTTP/3 default");
- Curl_pgrsTime(data, TIMER_APPCONNECT);
- }
- else {
- failf(data, "failed to connect, handshake failed");
- result = CURLE_COULDNT_CONNECT;
- }
- }
-
-out:
- CF_DATA_RESTORE(cf, save);
- return result;
-}
-
-static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
- struct cf_call_data save;
-
- (void)data;
- CF_DATA_SAVE(save, cf, data);
-
- if(ctx) {
- CURL_TRC_CF(data, cf, "destroying");
- if(ctx->qconn) {
- MsH3ConnectionClose(ctx->qconn);
- ctx->qconn = NULL;
- }
- if(ctx->api) {
- MsH3ApiClose(ctx->api);
- ctx->api = NULL;
- }
-
- if(ctx->active) {
- /* We share our socket at cf->conn->sock[cf->sockindex] when active.
- * If it is no longer there, someone has stolen (and hopefully
- * closed it) and we just forget about it.
- */
- ctx->active = FALSE;
- if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
- CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active",
- (int)ctx->sock[SP_LOCAL]);
- cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
- }
- else {
- CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at "
- "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]);
- ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
- }
- }
- if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
- sclose(ctx->sock[SP_LOCAL]);
- }
- if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
- sclose(ctx->sock[SP_REMOTE]);
- }
- ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
- ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
- }
- CF_DATA_RESTORE(cf, save);
-}
-
-static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- struct cf_call_data save;
-
- CF_DATA_SAVE(save, cf, data);
- cf_msh3_close(cf, data);
- if(cf->ctx) {
- cf_msh3_ctx_free(cf->ctx);
- cf->ctx = NULL;
- }
- /* no CF_DATA_RESTORE(cf, save); its gone */
-}
-
-static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int query, int *pres1, void *pres2)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
-
- switch(query) {
- case CF_QUERY_MAX_CONCURRENT: {
- /* We do not have access to this so far, fake it */
- (void)ctx;
- *pres1 = 100;
- return CURLE_OK;
- }
- case CF_QUERY_TIMER_CONNECT: {
- struct curltime *when = pres2;
- /* we do not know when the first byte arrived */
- if(cf->connected)
- *when = ctx->handshake_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;
- default:
- break;
- }
- return cf->next ?
- cf->next->cft->query(cf->next, data, query, pres1, pres2) :
- CURLE_UNKNOWN_OPTION;
-}
-
-static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *input_pending)
-{
- struct cf_msh3_ctx *ctx = cf->ctx;
-
- (void)data;
- *input_pending = FALSE;
- return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
- ctx->connected;
-}
-
-struct Curl_cftype Curl_cft_http3 = {
- "HTTP/3",
- CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
- 0,
- cf_msh3_destroy,
- cf_msh3_connect,
- cf_msh3_close,
- Curl_cf_def_shutdown,
- cf_msh3_adjust_pollset,
- cf_msh3_data_pending,
- cf_msh3_send,
- cf_msh3_recv,
- cf_msh3_data_event,
- cf_msh3_conn_is_alive,
- Curl_cf_def_conn_keep_alive,
- cf_msh3_query,
-};
-
-static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data)
-{
- if(data && data->conn) {
- struct Curl_cfilter *cf = data->conn->cfilter[FIRSTSOCKET];
- while(cf) {
- if(cf->cft == &Curl_cft_http3)
- return cf->ctx;
- cf = cf->next;
- }
- }
- DEBUGF(infof(data, "no filter context found"));
- return NULL;
-}
-
-CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
- struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_addrinfo *ai)
-{
- struct cf_msh3_ctx *ctx = NULL;
- struct Curl_cfilter *cf = NULL;
- CURLcode result;
-
- (void)data;
- (void)conn;
- (void)ai; /* msh3 resolves itself? */
- ctx = calloc(1, sizeof(*ctx));
- if(!ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- result = cf_msh3_ctx_init(ctx, ai);
- if(result)
- goto out;
-
- result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
-
-out:
- *pcf = (!result) ? cf : NULL;
- if(result) {
- Curl_safefree(cf);
- cf_msh3_ctx_free(ctx);
- }
-
- return result;
-}
-
-bool Curl_conn_is_msh3(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;
-}
-
-#endif /* USE_MSH3 */
+++ /dev/null
-#ifndef HEADER_CURL_VQUIC_CURL_MSH3_H
-#define HEADER_CURL_VQUIC_CURL_MSH3_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"
-
-#ifdef USE_MSH3
-
-#include <msh3.h>
-
-void Curl_msh3_ver(char *p, size_t len);
-
-CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
- struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_addrinfo *ai);
-
-bool Curl_conn_is_msh3(const struct Curl_easy *data,
- const struct connectdata *conn,
- int sockindex);
-
-#endif /* USE_MSQUIC */
-
-#endif /* HEADER_CURL_VQUIC_CURL_MSH3_H */
#include "../curlx/dynbuf.h"
#include "../cfilters.h"
#include "../curl_trc.h"
-#include "curl_msh3.h"
#include "curl_ngtcp2.h"
#include "curl_osslq.h"
#include "curl_quiche.h"
Curl_osslq_ver(p, len);
#elif defined(USE_QUICHE)
Curl_quiche_ver(p, len);
-#elif defined(USE_MSH3)
- Curl_msh3_ver(p, len);
#endif
}
return Curl_cf_osslq_create(pcf, data, conn, ai);
#elif defined(USE_QUICHE)
return Curl_cf_quiche_create(pcf, data, conn, ai);
-#elif defined(USE_MSH3)
- return Curl_cf_msh3_create(pcf, data, conn, ai);
#else
*pcf = NULL;
(void)data;
#define OSSL_PACKAGE "BoringSSL"
#elif defined(OPENSSL_IS_AWSLC)
#define OSSL_PACKAGE "AWS-LC"
-#elif (defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \
- !defined(OPENSSL_QUIC_API2)) || defined(USE_MSH3)
+#elif (defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \
+ !defined(OPENSSL_QUIC_API2))
#define OSSL_PACKAGE "quictls"
#else
#define OSSL_PACKAGE "OpenSSL"
score['meta']['protocol'] = 'h3'
if not self.env.have_h3_curl():
raise ScoreCardError('curl does not support HTTP/3')
- for lib in ['ngtcp2', 'quiche', 'msh3', 'nghttp3']:
+ for lib in ['ngtcp2', 'quiche', 'nghttp3']:
if self.env.curl_uses_lib(lib):
score['meta']['implementation'] = lib
break
def test_02_05_download_many_sequential(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 shaky here")
if proto == 'h2' and env.curl_uses_lib('mbedtls') and \
sys.platform.startswith('darwin') and env.ci_run:
pytest.skip('mbedtls 3.6.3 fails this test on macOS CI runners')
def test_02_11_10MB_parallel(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
count = 3
urln = f'https://{env.authority_for(env.domain1, proto)}/data-10m?[0-{count-1}]'
curl = CurlClient(env=env)
def test_02_14_not_found(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
count = 5
urln = f'https://{env.authority_for(env.domain1, proto)}/not-found?[0-{count-1}]'
curl = CurlClient(env=env)
def test_02_15_fail_not_found(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
count = 5
urln = f'https://{env.authority_for(env.domain1, proto)}/not-found?[0-{count-1}]'
curl = CurlClient(env=env)
@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_lib('msh3'):
- pytest.skip("msh3 stalls here")
if proto == 'h3' and env.curl_uses_ossl_quic():
pytest.skip('OpenSSL QUIC fails here')
count = 3
def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
count = 1
curl = CurlClient(env=env)
urln = f'https://{env.authority_for(env.domain1, proto)}' \
pytest.skip("h3 not supported")
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('msh3'):
- pytest.skip("msh3 stalls here")
count = 20
curl = CurlClient(env=env)
urln = f'https://{env.authority_for(env.domain1, proto)}' \
def test_07_01_upload_1_small(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
data = '0123456789'
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
def test_07_02_upload_1_large(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
fdata = os.path.join(env.gen_dir, 'data-100k')
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
def test_07_10_upload_sequential(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
count = 20
data = '0123456789'
curl = CurlClient(env=env)
def test_07_11_upload_parallel(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
# limit since we use a separate connection in h1
count = 20
data = '0123456789'
def test_07_12_upload_seq_large(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
fdata = os.path.join(env.gen_dir, 'data-100k')
count = 10
curl = CurlClient(env=env)
def test_07_13_upload_seq_large(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
fdata = os.path.join(env.gen_dir, 'data-10m')
count = 2
curl = CurlClient(env=env)
def test_07_14_upload_stdin(self, env: Env, httpd, nghttpx, proto, indata):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
count = 1
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]'
def test_07_20_upload_parallel(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
# limit since we use a separate connection in h1
count = 10
data = '0123456789'
def test_07_21_upload_parallel_large(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
fdata = os.path.join(env.gen_dir, 'data-100k')
# limit since we use a separate connection in h1
count = 10
def test_07_22_upload_parallel_fail(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
fdata = os.path.join(env.gen_dir, 'data-10m')
count = 20
curl = CurlClient(env=env)
def test_07_30_put_100k(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
fdata = os.path.join(env.gen_dir, 'data-100k')
count = 1
curl = CurlClient(env=env)
def test_07_31_put_10m(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
fdata = os.path.join(env.gen_dir, 'data-10m')
count = 1
curl = CurlClient(env=env)
def test_07_32_issue_10591(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
fdata = os.path.join(env.gen_dir, 'data-10m')
count = 1
curl = CurlClient(env=env)
pytest.skip("h3 not supported")
if proto == 'h3' and env.curl_uses_ossl_quic():
pytest.skip("OpenSSL's own QUIC is flaky here")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
data = '0123456789' * 10
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo{redir}?id=[0-0]'
pytest.skip("h3 not supported")
if proto == 'h3' and env.curl_uses_ossl_quic():
pytest.skip("OpenSSL's own QUIC is flaky here")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
data = '0123456789' * 10
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo307?id=[0-0]'
def test_07_38_form_small(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
r = curl.http_form(urls=[url], alpn_proto=proto, form={
def test_07_39_post_urlenc_small(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
fdata = os.path.join(env.gen_dir, 'data-63k')
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
def test_07_40_post_urlenc_large(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
fdata = os.path.join(env.gen_dir, 'data-64k')
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
def test_07_41_post_urlenc_small(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
if proto == 'h3' and env.curl_uses_lib('quiche'):
pytest.skip("quiche has CWND issues with large requests")
fdata = os.path.join(env.gen_dir, 'data-63k')
def test_07_42a_upload_disconnect(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
client = LocalClient(name='upload_pausing', env=env, timeout=60)
if not client.exists():
pytest.skip(f'example client not built: {client.name}')
def test_07_42b_upload_disconnect(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
client = LocalClient(name='upload_pausing', env=env, timeout=60)
if not client.exists():
pytest.skip(f'example client not built: {client.name}')
def test_07_42c_upload_disconnect(self, env: Env, httpd, nghttpx, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
client = LocalClient(name='upload_pausing', env=env, timeout=60)
if not client.exists():
pytest.skip(f'example client not built: {client.name}')
pytest.skip("h3 not supported")
if proto == 'h3' and env.curl_uses_ossl_quic():
pytest.skip("openssl-quic is flaky in filed PUTs")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 fails here")
fdata = os.path.join(env.gen_dir, 'data-10m')
count = 1
max_upload = 128 * 1024
def test_08_01_download_1(self, env: Env, caddy: Caddy, proto):
if proto == 'h3' and not env.have_h3_curl():
pytest.skip("h3 not supported in curl")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 itself crashes")
curl = CurlClient(env=env)
url = f'https://{env.domain1}:{caddy.port}/data.json'
r = curl.http_download(urls=[url], alpn_proto=proto)
def test_08_02_download_1mb_sequential(self, env: Env, caddy: Caddy, proto):
if proto == 'h3' and not env.have_h3_curl():
pytest.skip("h3 not supported in curl")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 itself crashes")
count = 50
curl = CurlClient(env=env)
urln = f'https://{env.domain1}:{caddy.port}/data1.data?[0-{count-1}]'
def test_08_03_download_1mb_parallel(self, env: Env, caddy: Caddy, proto):
if proto == 'h3' and not env.have_h3_curl():
pytest.skip("h3 not supported in curl")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 itself crashes")
count = 20
curl = CurlClient(env=env)
urln = f'https://{env.domain1}:{caddy.port}/data1.data?[0-{count-1}]'
def test_08_04a_download_10mb_sequential(self, env: Env, caddy: Caddy, proto):
if proto == 'h3' and not env.have_h3_curl():
pytest.skip("h3 not supported in curl")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 itself crashes")
count = 40
curl = CurlClient(env=env)
urln = f'https://{env.domain1}:{caddy.port}/data5.data?[0-{count-1}]'
def test_08_04b_download_10mb_sequential(self, env: Env, caddy: Caddy, proto):
if proto == 'h3' and not env.have_h3_curl():
pytest.skip("h3 not supported in curl")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 itself crashes")
count = 20
curl = CurlClient(env=env)
urln = f'https://{env.domain1}:{caddy.port}/data10.data?[0-{count-1}]'
def test_08_05_download_1mb_parallel(self, env: Env, caddy: Caddy, proto):
if proto == 'h3' and not env.have_h3_curl():
pytest.skip("h3 not supported in curl")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 itself crashes")
if proto == 'http/1.1' and env.curl_uses_lib('mbedtls'):
pytest.skip("mbedtls 3.6.0 fails on 50 connections with: "
"ssl_handshake returned: (-0x7F00) SSL - Memory allocation failed")
def test_08_06_post_parallel(self, env: Env, httpd, caddy, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
# limit since we use a separate connection in h1
count = 20
data = '0123456789'
def test_08_07_put_large(self, env: Env, httpd, caddy, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
- if proto == 'h3' and env.curl_uses_lib('msh3'):
- pytest.skip("msh3 stalls here")
# limit since we use a separate connection in h1<
count = 1
fdata = os.path.join(env.gen_dir, 'data-10m')
USE_NGHTTP2 = false
!ENDIF
-!IF "$(ENABLE_MSH3)"=="yes"
-# compatibility bit, WITH_MSH3 is the correct flag
-WITH_MSH3 = dll
-USE_MSH3 = true
-MSH3 = dll
-!ELSEIF "$(WITH_MSH3)"=="dll"
-USE_MSH3 = true
-MSH3 = dll
-!ELSEIF "$(WITH_MSH3)"=="static"
-USE_MSH3 = true
-MSH3 = static
-!ENDIF
-
-!IFNDEF USE_MSH3
-USE_MSH3 = false
-!ENDIF
-
!IF "$(WITH_MBEDTLS)"=="dll" || "$(WITH_MBEDTLS)"=="static"
USE_MBEDTLS = true
MBEDTLS = $(WITH_MBEDTLS)
CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-nghttp2-$(NGHTTP2)
!ENDIF
-!IF "$(USE_MSH3)"=="true"
-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-msh3
-!ENDIF
-
!MESSAGE configuration name: $(CONFIG_NAME_LIB)
# Note these directories are removed by this makefile's 'clean' so they should
@SET CONFIG_NAME_LIB=$(CONFIG_NAME_LIB)
@SET MACHINE=$(MACHINE)
@SET USE_NGHTTP2=$(USE_NGHTTP2)
- @SET USE_MSH3=$(USE_MSH3)
@SET USE_IDN=$(USE_IDN)
@SET USE_IPV6=$(USE_IPV6)
@SET USE_SSPI=$(USE_SSPI)
!ENDIF
!ENDIF
-!IFDEF MSH3_PATH
-MSH3_INC_DIR = $(MSH3_PATH)\include
-MSH3_LIB_DIR = $(MSH3_PATH)\lib
-MSH3_LFLAGS = $(MSH3_LFLAGS) "/LIBPATH:$(MSH3_LIB_DIR)"
-!ELSE
-MSH3_INC_DIR = $(DEVEL_INCLUDE)
-MSH3_LIB_DIR = $(DEVEL_LIB)
-!ENDIF
-
-!IF "$(WITH_MSH3)"=="dll"
-MSH3_CFLAGS = /DUSE_MSH3 /I"$(MSH3_INC_DIR)"
-MSH3_LIBS = msh3.lib
-!ELSEIF "$(WITH_MSH3)"=="static"
-MSH3_CFLAGS = /DUSE_MSH3 /DMSH3_STATICLIB /I"$(MSH3_INC_DIR)"
-!IF EXISTS("$(NGHTTP2_LIB_DIR)\msh3_static.lib")
-MSH3_LIBS = msh3_static.lib
-!ELSE
-MSH3_LIBS = msh3.lib
-!ENDIF
-!ENDIF
-
!IFDEF MBEDTLS_PATH
MBEDTLS_INC_DIR = $(MBEDTLS_PATH)\include
MBEDTLS_LIB_DIR = $(MBEDTLS_PATH)\lib
LFLAGS = $(LFLAGS) $(NGHTTP2_LFLAGS) $(NGHTTP2_LIBS)
!ENDIF
-!IF "$(USE_MSH3)"=="true"
-CFLAGS = $(CFLAGS) $(MSH3_CFLAGS)
-LFLAGS = $(LFLAGS) $(MSH3_LFLAGS) $(MSH3_LIBS)
-!ENDIF
-
!IF "$(GEN_PDB)"=="true"
CFLAGS = $(CFLAGS) $(CFLAGS_PDB) /Fd"$(LIB_DIROBJ)\$(PDB)"
LFLAGS = $(LFLAGS) $(LFLAGS_PDB)
$(TARGET): $(LIB_OBJS) $(LIB_DIROBJ) $(DIRDIST)
@echo Using SSL: $(USE_SSL)
@echo Using NGHTTP2: $(USE_NGHTTP2)
- @echo Using MSH3: $(USE_MSH3)
@echo Using c-ares: $(USE_CARES)
@echo Using SSH2: $(USE_SSH2)
@echo Using SSH: $(USE_SSH)
Defaults to sibling directory: `../deps`\r
- `WITH_SSL=<dll/static>` - Enable OpenSSL support, DLL or static\r
- `WITH_NGHTTP2=<dll/static>` - Enable HTTP/2 support, DLL or static\r
- - `WITH_MSH3=<dll/static>` - Enable (experimental) HTTP/3 support, DLL or static\r
- `WITH_MBEDTLS=<dll/static>` - Enable mbedTLS support, DLL or static\r
- `WITH_WOLFSSL=<dll/static>` - Enable wolfSSL support, DLL or static\r
- `WITH_CARES=<dll/static>` - Enable c-ares support, DLL or static\r
- `MBEDTLS_PATH=<path>` - Custom path for mbedTLS\r
- `WOLFSSL_PATH=<path>` - Custom path for wolfSSL\r
- `NGHTTP2_PATH=<path>` - Custom path for nghttp2\r
- - `MSH3_PATH=<path>` - Custom path for msh3\r
- `SSH_PATH=<path>` - Custom path for libssh\r
- `SSH2_PATH=<path>` - Custom path for libssh2\r
- `SSL_PATH=<path>` - Custom path for OpenSSL\r