From ca15b7512e8d1199e55fbaa206ef01e64b8f147d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 29 Nov 2022 16:41:15 +0100 Subject: [PATCH] tests: add HTTP/3 test case, custom location for proper nghttpx - adding support for HTTP/3 test cases via a nghttpx server that is build with ngtcp2 and nghttp3. - test2500 is the first test case, performing a simple GET. - nghttpx is checked for support and the 'feature' nghttpx-h3 is set accordingly. test2500 will only run, when supported. - a specific nghttpx location can be given in the environment variable NGHTTPX or via the configure option --with-test-nghttpx= Extend NGHTTPX config to H2 tests as well * use $ENV{NGHTTPX} and the configured default also in http2 server starts * always provide the empty test/nghttpx.conf to nghttpx. as it defaults to reading /etc/nghttpx/nghttpx.conf otherwise. Added nghttpx to CI ngtcp2 jobs to run h3 tests. Closes #9031 --- .github/workflows/ngtcp2-gnutls.yml | 41 ++++++-- .github/workflows/ngtcp2-quictls.yml | 98 +++++++++++++++++++ .github/workflows/ngtcp2-wolfssl.yml | 39 ++++++-- .gitignore | 2 + configure.ac | 11 +++ tests/.gitignore | 1 + tests/README.md | 7 +- tests/config.in | 24 +++++ tests/data/Makefile.inc | 2 + tests/data/test2500 | 78 +++++++++++++++ tests/getpart.pm | 15 +++ tests/http2-server.pl | 8 ++ tests/http3-server.pl | 111 +++++++++++++++++++++ tests/nghttpx.conf | 26 +++++ tests/runtests.pl | 141 +++++++++++++++++++++++++-- tests/serverhelp.pm | 2 +- 16 files changed, 582 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/ngtcp2-quictls.yml create mode 100644 tests/config.in create mode 100644 tests/data/test2500 create mode 100644 tests/http3-server.pl create mode 100644 tests/nghttpx.conf diff --git a/.github/workflows/ngtcp2-gnutls.yml b/.github/workflows/ngtcp2-gnutls.yml index 6e90781243..bec3851ba0 100644 --- a/.github/workflows/ngtcp2-gnutls.yml +++ b/.github/workflows/ngtcp2-gnutls.yml @@ -28,9 +28,21 @@ jobs: matrix: build: - name: gnutls - install: nettle-dev libp11-kit-dev libtspi-dev libunistring-dev guile-2.2-dev libtasn1-bin libtasn1-6-dev libidn2-0-dev gawk gperf libtss2-dev dns-root-data bison gtk-doc-tools texinfo texlive texlive-extra-utils autopoint libev-dev - configure: LDFLAGS="-Wl,-rpath,$HOME/all/lib" --with-gnutls=$HOME/all --enable-debug - gnutls-configure: --with-included-libtasn1 --with-included-unistring --disable-guile --disable-doc --disable-tests + install: >- + libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libev-dev libc-ares-dev + nettle-dev libp11-kit-dev libtspi-dev libunistring-dev guile-2.2-dev libtasn1-bin + libtasn1-6-dev libidn2-0-dev gawk gperf libtss2-dev dns-root-data bison gtk-doc-tools + texinfo texlive texlive-extra-utils autopoint libev-dev + configure: >- + PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/all/lib" + --with-ngtcp2=$HOME/all --enable-warnings --enable-werror --enable-debug + --with-test-nghttpx="$HOME/all/bin/nghttpx" + ngtcp2-configure: >- + --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-lib-only + gnutls-configure: >- + PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/all/lib -L$HOME/all/lib" + --with-included-libtasn1 --with-included-unistring + --disable-guile --disable-doc --disable-tests --disable-tools steps: - run: | @@ -39,6 +51,13 @@ jobs: sudo python3 -m pip install impacket name: 'install prereqs and impacket' + - run: | + git clone --depth=1 -b openssl-3.0.7+quic https://github.com/quictls/openssl + cd openssl + ./config --prefix=$HOME/all --libdir=$HOME/all/lib + make install_sw + name: 'install quictls' + - run: | git clone --depth=1 https://gitlab.com/gnutls/nettle.git cd nettle @@ -51,7 +70,7 @@ jobs: git clone --depth=1 -b 3.7.7 https://github.com/gnutls/gnutls.git cd gnutls ./bootstrap - ./configure PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/all/lib -L$HOME/all/lib" --prefix=$HOME/all ${{ matrix.build.gnutls-configure }} --disable-tools + ./configure ${{ matrix.build.gnutls-configure }} --prefix=$HOME/all make install name: 'install gnutls' @@ -59,7 +78,7 @@ jobs: git clone --depth=1 https://github.com/ngtcp2/nghttp3 cd nghttp3 autoreconf -fi - ./configure --prefix=$HOME/all --enable-lib-only + ./configure --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-lib-only make install name: 'install nghttp3' @@ -67,16 +86,24 @@ jobs: git clone --depth=1 https://github.com/ngtcp2/ngtcp2 cd ngtcp2 autoreconf -fi - ./configure PKG_CONFIG_PATH=$HOME/all/lib/pkgconfig LDFLAGS="-Wl,-rpath,$HOME/all/lib" --prefix=$HOME/all --enable-lib-only --with-gnutls=$HOME/all + ./configure ${{ matrix.build.ngtcp2-configure }} --with-openssl --with-gnutls make install name: 'install ngtcp2' + - run: | + git clone --depth=1 https://github.com/nghttp2/nghttp2 + cd nghttp2 + autoreconf -fi + ./configure --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-http3 + make install + name: 'install nghttp2' + - uses: actions/checkout@v3 - run: autoreconf -fi name: 'autoreconf' - - run: ./configure --enable-warnings --enable-werror ${{ matrix.build.configure }} + - run: ./configure --with-gnutls=$HOME/all ${{ matrix.build.configure }} name: 'configure' - run: make V=1 diff --git a/.github/workflows/ngtcp2-quictls.yml b/.github/workflows/ngtcp2-quictls.yml new file mode 100644 index 0000000000..0662dea78c --- /dev/null +++ b/.github/workflows/ngtcp2-quictls.yml @@ -0,0 +1,98 @@ +# Copyright (C) 2000 - 2022 Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +name: ngtcp2 + +on: + push: + branches: + - master + - '*/ci' + pull_request: + branches: + - master + +concurrency: + # Hardcoded workflow filename as workflow name above is just Linux again + group: ngtcp2-openssl-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +jobs: + autotools: + name: ${{ matrix.build.name }} + runs-on: 'ubuntu-latest' + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + build: + - name: quictls + install: >- + libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libev-dev libc-ares-dev + configure: >- + PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/all/lib" + --with-ngtcp2=$HOME/all --enable-warnings --enable-werror --enable-debug + --with-test-nghttpx="$HOME/all/bin/nghttpx" + ngtcp2-configure: >- + --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-lib-only + + steps: + - run: | + sudo apt-get update + sudo apt-get install libtool autoconf automake pkg-config stunnel4 ${{ matrix.build.install }} + sudo python3 -m pip install impacket + name: 'install prereqs and impacket' + + - run: | + git clone --depth=1 -b openssl-3.0.7+quic https://github.com/quictls/openssl + cd openssl + ./config --prefix=$HOME/all --libdir=$HOME/all/lib + make install_sw + name: 'install quictls' + + - run: | + git clone --depth=1 https://github.com/ngtcp2/nghttp3 + cd nghttp3 + autoreconf -fi + ./configure --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-lib-only + make install + name: 'install nghttp3' + + - run: | + git clone --depth=1 https://github.com/ngtcp2/ngtcp2 + cd ngtcp2 + autoreconf -fi + ./configure ${{ matrix.build.ngtcp2-configure }} --with-openssl + make install + name: 'install ngtcp2' + + - run: | + git clone --depth=1 https://github.com/nghttp2/nghttp2 + cd nghttp2 + autoreconf -fi + ./configure --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-http3 + make install + name: 'install nghttp2' + + - uses: actions/checkout@v3 + + - run: autoreconf -fi + name: 'autoreconf' + + - run: ./configure --with-openssl=$HOME/all ${{ matrix.build.configure }} + name: 'configure' + + - run: make V=1 + name: 'make' + + - run: make V=1 examples + name: 'make examples' + + - run: make V=1 -C tests + name: 'make tests' + + - run: make V=1 test-ci + name: 'run tests' + env: + TFLAGS: "${{ matrix.build.tflags }}" diff --git a/.github/workflows/ngtcp2-wolfssl.yml b/.github/workflows/ngtcp2-wolfssl.yml index 3f5d576123..e3d263c006 100644 --- a/.github/workflows/ngtcp2-wolfssl.yml +++ b/.github/workflows/ngtcp2-wolfssl.yml @@ -28,9 +28,17 @@ jobs: matrix: build: - name: wolfssl - install: - configure: LDFLAGS="-Wl,-rpath,$HOME/all/lib" --with-wolfssl=$HOME/all --enable-debug - wolfssl-configure: --enable-quic --enable-session-ticket --enable-earlydata --enable-psk --enable-harden --enable-altcertchains + install: >- + libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libev-dev libc-ares-dev + configure: >- + PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/all/lib" + --with-ngtcp2=$HOME/all --enable-warnings --enable-werror --enable-debug + --with-test-nghttpx="$HOME/all/bin/nghttpx" + ngtcp2-configure: >- + --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-lib-only + wolfssl-configure: >- + --enable-quic --enable-session-ticket --enable-earlydata --enable-psk + --enable-harden --enable-altcertchains steps: - run: | @@ -48,27 +56,42 @@ jobs: name: 'install wolfssl' - run: | - git clone https://github.com/ngtcp2/nghttp3 + git clone --depth=1 -b openssl-3.0.7+quic https://github.com/quictls/openssl + cd openssl + ./config --prefix=$HOME/all --libdir=$HOME/all/lib + make install_sw + name: 'install quictls' + + - run: | + git clone --depth=1 https://github.com/ngtcp2/nghttp3 cd nghttp3 autoreconf -fi - ./configure --prefix=$HOME/all --enable-lib-only + ./configure --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-lib-only make install name: 'install nghttp3' - run: | - git clone https://github.com/ngtcp2/ngtcp2 + git clone --depth=1 https://github.com/ngtcp2/ngtcp2 cd ngtcp2 autoreconf -fi - ./configure PKG_CONFIG_PATH=$HOME/all/lib/pkgconfig LDFLAGS="-Wl,-rpath,$HOME/all/lib" --prefix=$HOME/all --enable-lib-only --with-wolfssl=$HOME/all + ./configure ${{ matrix.build.ngtcp2-configure }} --with-openssl --with-wolfssl make install name: 'install ngtcp2' + - run: | + git clone --depth=1 https://github.com/nghttp2/nghttp2 + cd nghttp2 + autoreconf -fi + ./configure --prefix=$HOME/all PKG_CONFIG_PATH="$HOME/all/lib/pkgconfig" --enable-http3 + make install + name: 'install nghttp2' + - uses: actions/checkout@v3 - run: autoreconf -fi name: 'autoreconf' - - run: ./configure --enable-warnings --enable-werror ${{ matrix.build.configure }} + - run: ./configure --with-wolfssl=$HOME/all ${{ matrix.build.configure }} name: 'configure' - run: make V=1 diff --git a/.gitignore b/.gitignore index 973e868d7b..4198d13a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,5 @@ curl_fuzzer curl_fuzzer_seed_corpus.zip libstandaloneengine.a tests/string +tests/config + diff --git a/configure.ac b/configure.ac index bfbf828f00..5b82d439b0 100644 --- a/configure.ac +++ b/configure.ac @@ -301,6 +301,16 @@ AS_HELP_STRING([--with-nss=PATH],[where to look for NSS, PATH points to the inst fi ) +TEST_NGHTTPX=nghttpx +AC_ARG_WITH(test-nghttpx,dnl +AS_HELP_STRING([--with-test-nghttpx=PATH],[where to find nghttpx for testing]), + TEST_NGHTTPX=$withval + if test X"$OPT_TEST_NGHTTPX" = "Xno" ; then + TEST_NGHTTPX="" + fi +) +AC_SUBST(TEST_NGHTTPX) + dnl If no TLS choice has been made, check if it was explicitly disabled or dnl error out to force the user to decide. if test -z "$TLSCHOICE"; then @@ -4568,6 +4578,7 @@ AC_CONFIG_FILES([Makefile \ lib/libcurl.vers \ lib/libcurl.plist \ tests/Makefile \ + tests/config \ tests/certs/Makefile \ tests/certs/scripts/Makefile \ tests/data/Makefile \ diff --git a/tests/.gitignore b/tests/.gitignore index 07590a33c5..d75f9e2ec8 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -24,3 +24,4 @@ runtests.pdf testcurl.html testcurl.pdf *.port +config \ No newline at end of file diff --git a/tests/README.md b/tests/README.md index 7fff0a534b..a2a1c54070 100644 --- a/tests/README.md +++ b/tests/README.md @@ -16,7 +16,7 @@ SPDX-License-Identifier: curl - diff (when a test fails, a diff is shown) - stunnel (for HTTPS and FTPS tests) - OpenSSH or SunSSH (for SCP, SFTP and SOCKS4/5 tests) - - nghttpx (for HTTP/2 tests) + - nghttpx (for HTTP/2 and HTTP/3 tests) - nroff (for --manual tests) - An available `en_US.UTF-8` locale @@ -69,6 +69,11 @@ SPDX-License-Identifier: curl The HTTP server supports listening on a Unix domain socket, the default location is 'http.sock'. + + For HTTP/2 and HTTP/3 testing an installed `nghttpx` is used. HTTP/3 + tests check if nghttpx supports the protocol. To override the nghttpx + used, set the environment variable `NGHTTPX`. The default can also be + changed by specifying `--with-test-nghttpx=` as argument to `configure`. ### Run diff --git a/tests/config.in b/tests/config.in new file mode 100644 index 0000000000..d08ffb7b6d --- /dev/null +++ b/tests/config.in @@ -0,0 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2022, Daniel Stenberg, , 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 +# +########################################################################### +NGHTTPX: @TEST_NGHTTPX@ diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 2f13f15ee5..0e2ac5a595 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -243,6 +243,8 @@ test2200 test2201 test2202 test2203 test2204 test2205 \ \ test2300 test2301 test2302 test2303 \ \ +test2500 \ +\ test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 \ test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 \ test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 \ diff --git a/tests/data/test2500 b/tests/data/test2500 new file mode 100644 index 0000000000..f726ebdba9 --- /dev/null +++ b/tests/data/test2500 @@ -0,0 +1,78 @@ + + + +HTTP +HTTP GET +HTTP/3 + + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# +# Client-side + + +http/3 +nghttpx-h3 + + +http +http/3 + + +HTTP/3 GET: + + +--cacert %SRCDIR/certs/EdelCurlRoot-ca.crt --http3 --resolve localhost:%HTTP3PORT:%HOSTIP https://localhost:%HTTP3PORT/%TESTNUMBER + + + +# +# Verify data after the test has been "shot" + + +^X-Forwarded-Proto:.* +^Via:.* + + +GET https://localhost:%HTTP3PORT/%TESTNUMBER HTTP/1.1 +Host: localhost:%HTTP3PORT +User-Agent: curl/%VERSION +Accept: */* + + + +HTTP/3 200 +date: Tue, 09 Nov 2010 14:49:00 GMT +last-modified: Tue, 13 Jun 2000 12:10:00 GMT +etag: "21025-dc7-39462498" +accept-ranges: bytes +content-length: 6 +content-type: text/html +funny-head: yesyes +via: 1.1 nghttpx + +-foo- + + +s/^server: nghttpx.*\r?\n// + + + diff --git a/tests/getpart.pm b/tests/getpart.pm index f8cb6effc7..29d783efdc 100644 --- a/tests/getpart.pm +++ b/tests/getpart.pm @@ -41,6 +41,21 @@ sub decode_hex { return $s; } +sub testcaseattr { + my %hash; + for(@xml) { + if(($_ =~ /^ *\]*)/)) { + my $attr=$1; + while($attr =~ s/ *([^=]*)= *(\"([^\"]*)\"|([^\> ]*))//) { + my ($var, $cont)=($1, $2); + $cont =~ s/^\"(.*)\"$/$1/; + $hash{$var}=$cont; + } + } + } + return %hash; +} + sub getpartattr { # if $part is undefined (ie only one argument) then # return the attributes of the section diff --git a/tests/http2-server.pl b/tests/http2-server.pl index 09ada660bb..a1afc3e02d 100755 --- a/tests/http2-server.pl +++ b/tests/http2-server.pl @@ -31,6 +31,7 @@ my $logfile = "log/http2.log"; my $nghttpx = "nghttpx"; my $listenport = 9015; my $connect = "127.0.0.1,8990"; +my $conf = "nghttpx.conf"; #*************************************************************************** # Process command line options @@ -70,6 +71,12 @@ while(@ARGV) { shift @ARGV; } } + elsif($ARGV[0] eq '--conf') { + if($ARGV[1]) { + $conf = $ARGV[1]; + shift @ARGV; + } + } else { print STDERR "\nWarning: http2-server.pl unknown parameter: $ARGV[0]\n"; } @@ -80,6 +87,7 @@ my $cmdline="$nghttpx --backend=$connect ". "--frontend=\"*,$listenport;no-tls\" ". "--log-level=INFO ". "--pid-file=$pidfile ". + "--conf=$conf ". "--errorlog-file=$logfile"; print "RUN: $cmdline\n" if($verbose); system("$cmdline 2>/dev/null"); diff --git a/tests/http3-server.pl b/tests/http3-server.pl new file mode 100644 index 0000000000..67c854ff89 --- /dev/null +++ b/tests/http3-server.pl @@ -0,0 +1,111 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 2016 - 2022, Daniel Stenberg, , 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 +# +#*************************************************************************** + +# This script invokes nghttpx properly to have it serve HTTP/3 for us. +# nghttpx runs as a proxy in front of our "actual" HTTP/1 server. + +use Cwd; +use Cwd 'abs_path'; + +my $pidfile = "log/nghttpx.pid"; +my $logfile = "log/http3.log"; +my $nghttpx = "nghttpx"; +my $listenport = 9015; +my $connect = "127.0.0.1,8990"; +my $cert = "Server-localhost-sv"; +my $conf = "nghttpx.conf"; + +#*************************************************************************** +# Process command line options +# +while(@ARGV) { + if($ARGV[0] eq '--verbose') { + $verbose = 1; + } + elsif($ARGV[0] eq '--pidfile') { + if($ARGV[1]) { + $pidfile = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--nghttpx') { + if($ARGV[1]) { + $nghttpx = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--port') { + if($ARGV[1]) { + $listenport = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--connect') { + if($ARGV[1]) { + $connect = $ARGV[1]; + $connect =~ s/:/,/; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--cert') { + if($ARGV[1]) { + $cert = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--logfile') { + if($ARGV[1]) { + $logfile = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--conf') { + if($ARGV[1]) { + $conf = $ARGV[1]; + shift @ARGV; + } + } + else { + print STDERR "\nWarning: http3-server.pl unknown parameter: $ARGV[0]\n"; + } + shift @ARGV; +} + +my $path = getcwd(); +my $srcdir = $path; +$certfile = "$srcdir/certs/$cert.pem"; +$keyfile = "$srcdir/certs/$cert.key"; +$certfile = abs_path($certfile); +$keyfile = abs_path($keyfile); + +my $cmdline="$nghttpx --http2-proxy --backend=$connect ". + "--frontend=\"*,$listenport;quic\" ". + "--log-level=INFO ". + "--pid-file=$pidfile ". + "--errorlog-file=$logfile ". + "--conf=$conf ". + "$keyfile $certfile"; +print "RUN: $cmdline\n" if($verbose); +system("$cmdline 2>/dev/null"); diff --git a/tests/nghttpx.conf b/tests/nghttpx.conf new file mode 100644 index 0000000000..605872d739 --- /dev/null +++ b/tests/nghttpx.conf @@ -0,0 +1,26 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2022, Daniel Stenberg, , 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 +# +########################################################################### + +# nghttpx reads /etc/nghttpx/nghttpx.conf if we do not give it another one. +# This is what this otherwise empty file is for. diff --git a/tests/runtests.pl b/tests/runtests.pl index ae8b25e526..43d4c629a6 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -159,6 +159,7 @@ my $HTTPTLSPORT=$noport; # HTTP TLS (non-stunnel) server port my $HTTPTLS6PORT=$noport; # HTTP TLS (non-stunnel) IPv6 server port my $HTTPPROXYPORT=$noport; # HTTP proxy port, when using CONNECT my $HTTP2PORT=$noport; # HTTP/2 server port +my $HTTP3PORT=$noport; # HTTP/3 server port my $DICTPORT=$noport; # DICT server port my $SMBPORT=$noport; # SMB server port my $SMBSPORT=$noport; # SMBS server port @@ -262,6 +263,7 @@ my $has_charconv; # set if libcurl is built with CharConv support my $has_tls_srp; # set if libcurl is built with TLS-SRP support my $has_http2; # set if libcurl is built with HTTP2 support my $has_h2c; # set if libcurl is built with h2c support +my $has_http3; # set if libcurl is built with HTTP3 support my $has_httpsproxy; # set if libcurl is built with HTTPS-proxy support my $has_crypto; # set if libcurl is built with cryptographic support my $has_cares; # set if built with c-ares @@ -315,7 +317,7 @@ my %ignored_keywords; # key words of tests to ignore results my %enabled_keywords; # key words of tests to run my %disabled; # disabled test cases my %ignored; # ignored results of test cases - +my $crlf_http = 0; # always convert HTTP heaaders to cr+lf my $sshdid; # for socks server, ssh daemon version id my $sshdvernum; # for socks server, ssh daemon version number my $sshdverstr; # for socks server, ssh daemon version string @@ -438,12 +440,36 @@ delete $ENV{'SSL_CERT_DIR'} if($ENV{'SSL_CERT_DIR'}); delete $ENV{'SSL_CERT_PATH'} if($ENV{'SSL_CERT_PATH'}); delete $ENV{'CURL_CA_BUNDLE'} if($ENV{'CURL_CA_BUNDLE'}); +# provide defaults from our config file for ENV vars not explicitly +# set by the caller +if (open(my $fd, "< config")) { + while(my $line = <$fd>) { + next if ($line =~ /^#/); + chomp $line; + my ($name, $val) = split(/\s*:\s*/, $line, 2); + $ENV{$name} = $val if(!$ENV{$name}); + } + close($fd); +} + +# Check if we have nghttpx available and if it talks http/3 +my $nghttpx_h3 = 0; +if (!$ENV{"NGHTTPX"}) { + $ENV{"NGHTTPX"} = checktestcmd("nghttpx"); +} +if ($ENV{"NGHTTPX"}) { + my $nghttpx_version=join(' ', runclientoutput("$ENV{'NGHTTPX'} -v")); + $nghttpx_h3 = $nghttpx_version =~ /nghttp3\//; + logmsg "nghttpx_h3=$nghttpx_h3, output=$nghttpx_version\n"; +} + + ####################################################################### # Load serverpidfile and serverportfile hashes with file names for all # possible servers. # sub init_serverpidfile_hash { - for my $proto (('ftp', 'gopher', 'http', 'imap', 'pop3', 'smtp', 'http/2')) { + for my $proto (('ftp', 'gopher', 'http', 'imap', 'pop3', 'smtp', 'http/2', 'http/3')) { for my $ssl (('', 's')) { for my $ipvnum ((4, 6)) { for my $idnum ((1, 2, 3)) { @@ -468,7 +494,7 @@ sub init_serverpidfile_hash { } } } - for my $proto (('http', 'imap', 'pop3', 'smtp', 'http/2')) { + for my $proto (('http', 'imap', 'pop3', 'smtp', 'http/2', 'http/3')) { for my $ssl (('', 's')) { my $serv = servername_id("$proto$ssl", "unix", 1); my $pidf = server_pidfilename("$proto$ssl", "unix", 1); @@ -840,10 +866,11 @@ sub stopserver { push @killservers, "socks${2}"; } if($server eq "http") { - # since the http2 server is a proxy that needs to know about the + # since the http2+3 server is a proxy that needs to know about the # dynamic http port it too needs to get restarted when the http server # is killed push @killservers, "http/2"; + push @killservers, "http/3"; } push @killservers, $server; # @@ -1528,6 +1555,7 @@ sub runhttp2server { $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + $flags .= "--nghttpx \"$ENV{'NGHTTPX'}\" "; $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; $flags .= "--connect $HOSTIP:$HTTPPORT "; $flags .= $verbose_flag if($debugprotocol); @@ -1561,6 +1589,76 @@ sub runhttp2server { return ($http2pid, $pid2, $port); } +####################################################################### +# start the http3 server +# +sub runhttp3server { + my ($verbose, $cert) = @_; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + my $proto="http/3"; + my $ipvnum = 4; + my $idnum = 0; + my $exe = "$perl $srcdir/http3-server.pl"; + my $verbose_flag = "--verbose "; + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0, 0, 0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--nghttpx \"$ENV{'NGHTTPX'}\" "; + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--connect $HOSTIP:$HTTPPORT "; + $flags .= "--cert \"$cert\" " if($cert); + $flags .= $verbose_flag if($debugprotocol); + + my ($http3pid, $pid3); + my $port = 24113; + for(1 .. 10) { + $port += int(rand(900)); + my $aflags = "--port $port $flags"; + + my $cmd = "$exe $aflags"; + ($http3pid, $pid3) = startnew($cmd, $pidfile, 15, 0); + + if($http3pid <= 0 || !pidexists($http3pid)) { + # it is NOT alive + stopserver($server, "$pid3"); + $doesntrun{$pidfile} = 1; + $http3pid = $pid3 = 0; + next; + } + $doesntrun{$pidfile} = 0; + + if($verbose) { + logmsg "RUN: $srvrname server PID $http3pid port $port\n"; + } + last; + } + + logmsg "RUN: failed to start the $srvrname server\n" if(!$http3pid); + + return ($http3pid, $pid3, $port); +} + ####################################################################### # start the http server # @@ -2899,6 +2997,7 @@ sub setupfeatures { $feature{"h2c"} = $has_h2c; $feature{"HSTS"} = $has_hsts; $feature{"http/2"} = $has_http2; + $feature{"http/3"} = $has_http3; $feature{"https-proxy"} = $has_httpsproxy; $feature{"hyper"} = $has_hyper; $feature{"idn"} = $has_idn; @@ -2959,6 +3058,8 @@ sub setupfeatures { $feature{"wakeup"} = 1; $feature{"headers-api"} = 1; $feature{"xattr"} = 1; + $feature{"nghttpx"} = !!$ENV{'NGHTTPX'}; + $feature{"nghttpx-h3"} = !!$nghttpx_h3; } ####################################################################### @@ -3213,6 +3314,12 @@ sub checksystem { push @protocols, 'http/2'; } + if($feat =~ /HTTP3/) { + # http3 enabled + $has_http3=1; + + push @protocols, 'http/3'; + } if($feat =~ /HTTPS-proxy/) { $has_httpsproxy=1; @@ -3400,6 +3507,7 @@ sub subVariables { $$thing =~ s/${prefix}HTTPSPORT/$HTTPSPORT/g; $$thing =~ s/${prefix}HTTPSPROXYPORT/$HTTPSPROXYPORT/g; $$thing =~ s/${prefix}HTTP2PORT/$HTTP2PORT/g; + $$thing =~ s/${prefix}HTTP3PORT/$HTTP3PORT/g; $$thing =~ s/${prefix}HTTPPORT/$HTTPPORT/g; $$thing =~ s/${prefix}PROXYPORT/$HTTPPROXYPORT/g; $$thing =~ s/${prefix}MQTTPORT/$MQTTPORT/g; @@ -3510,7 +3618,8 @@ sub subNewlines { # as well, all test comparisons will survive without knowing about this # little quirk. - if(($$thing =~ /^HTTP\/(1.1|1.0|2) [1-5][^\x0d]*\z/) || + if(($$thing =~ /^HTTP\/(1.1|1.0|2|3) [1-5][^\x0d]*\z/) || + ($$thing =~ /^(GET|POST|PUT|DELETE) \S+ HTTP\/\d+(\.\d+)?/) || (($$thing =~ /^[a-z0-9_-]+: [^\x0d]*\z/i) && # skip curl error messages ($$thing !~ /^curl: \(\d+\) /))) { @@ -3587,6 +3696,8 @@ sub prepro { my (@entiretest) = @_; my $show = 1; my @out; + my $crlf_header = ($crlf_http || ($has_hyper && ($keywords{"HTTP"} + || $keywords{"HTTPS"}))); for my $s (@entiretest) { my $f = $s; if($s =~ /^ *%if (.*)/) { @@ -3612,8 +3723,7 @@ sub prepro { if($show) { subVariables(\$s, $testnum, "%"); subBase64(\$s); - subNewlines(\$s) if($has_hyper && ($keywords{"HTTP"} || - $keywords{"HTTPS"})); + subNewlines(\$s) if($crlf_header); push @out, $s; } } @@ -3807,6 +3917,12 @@ sub singletest { $why = serverfortest($testnum); } + $crlf_http = 0; + my %hash = testcaseattr(); + if($hash{'http-crlf'}) { + $crlf_http = 1; + } + # Save a preprocessed version of the entire test file. This allows more # "basic" test case readers to enjoy variable replacements. my @entiretest = fulltest(); @@ -4958,6 +5074,17 @@ sub startservers { $run{'gopher-ipv6'}="$pid $pid2"; } } + elsif($what eq "http/3") { + if(!$run{'http/3'}) { + ($pid, $pid2, $HTTP3PORT) = runhttp3server($verbose); + if($pid <= 0) { + return "failed starting HTTP/3 server"; + } + logmsg sprintf ("* pid http/3 => %d %d\n", $pid, $pid2) + if($verbose); + $run{'http/3'}="$pid $pid2"; + } + } elsif($what eq "http/2") { if(!$run{'http/2'}) { ($pid, $pid2, $HTTP2PORT) = runhttp2server($verbose); diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index 7d312cc032..84673b97c7 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -108,7 +108,7 @@ sub servername_str { $proto = uc($proto) if($proto); die "unsupported protocol: '$proto'" unless($proto && - ($proto =~ /^(((FTP|HTTP|HTTP\/2|IMAP|POP3|GOPHER|SMTP|HTTP-PIPE)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|HTTPTLS|DICT|SMB|SMBS|TELNET|MQTT))$/)); + ($proto =~ /^(((FTP|HTTP|HTTP\/2|HTTP\/3|IMAP|POP3|GOPHER|SMTP|HTTP-PIPE)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|HTTPTLS|DICT|SMB|SMBS|TELNET|MQTT))$/)); $ipver = (not $ipver) ? 'ipv4' : lc($ipver); die "unsupported IP version: '$ipver'" unless($ipver && -- 2.47.3