nghttp2 v1.55.0

-----BEGIN PGP SIGNATURE-----
 
 iF0EABECAB0WIQT087kUdNHrKYib0O9+hAPV1nPDZgUCZK5jPAAKCRB+hAPV1nPD
 ZpPZAJ9zCed2FWlUUon5ha1Mdf3/zNT3hwCgpRRnGWA6ipu7VH3qDvLyvnnX7Yo=
 =gkZl
 -----END PGP SIGNATURE-----

Merge tag 'v1.55.0' of https://github.com/nghttp2/nghttp2 into xj_1014_2

nghttp2 v1.55.0
This commit is contained in:
xujie 2023-10-14 22:16:50 +08:00
commit 16810ecd9f
97 changed files with 4042 additions and 2583 deletions

View File

@ -2,26 +2,195 @@ name: build
on: [push, pull_request]
permissions: read-all
env:
LIBBPF_VERSION: v1.2.0
OPENSSL1_VERSION: 1_1_1u+quic
OPENSSL3_VERSION: 3.1.0+quic
BORINGSSL_VERSION: 80dcb67d4481fb1194b9669917e35580c32dc388
NGHTTP3_VERSION: v0.13.0
NGTCP2_VERSION: v0.17.0
jobs:
build:
build-cache:
strategy:
matrix:
os: [ubuntu-22.04, macos-11]
os: [ubuntu-22.04, macos-12]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Restore libbpf cache
id: cache-libbpf
uses: actions/cache@v3
if: runner.os == 'Linux'
with:
path: libbpf/build
key: ${{ runner.os }}-libbpf-${{ env.LIBBPF_VERSION }}
- name: Restore OpenSSL v1.1.1 cache
id: cache-openssl1
uses: actions/cache@v3
with:
path: openssl1/build
key: ${{ runner.os }}-openssl-${{ env.OPENSSL1_VERSION }}
- name: Restore OpenSSL v3.x cache
id: cache-openssl3
uses: actions/cache@v3
with:
path: openssl3/build
key: ${{ runner.os }}-openssl-${{ env.OPENSSL3_VERSION }}
- name: Restore BoringSSL cache
id: cache-boringssl
uses: actions/cache@v3
with:
path: |
boringssl/build/crypto/libcrypto.a
boringssl/build/ssl/libssl.a
boringssl/include
key: ${{ runner.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
- name: Restore nghttp3 cache
id: cache-nghttp3
uses: actions/cache@v3
with:
path: nghttp3/build
key: ${{ runner.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
- name: Restore ngtcp2 + quictls/openssl v1.1.1 cache
id: cache-ngtcp2-openssl1
uses: actions/cache@v3
with:
path: ngtcp2-openssl1/build
key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
- name: Restore ngtcp2 + quictls/openssl v3.x cache
id: cache-ngtcp2-openssl3
uses: actions/cache@v3
with:
path: ngtcp2-openssl3/build
key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL3_VERSION }}
- id: settings
if: |
(steps.cache-libbpf.outputs.cache-hit != 'true' && runner.os == 'Linux') ||
steps.cache-openssl1.outputs.cache-hit != 'true' ||
steps.cache-openssl3.outputs.cache-hit != 'true' ||
steps.cache-boringssl.outputs.cache-hit != 'true' ||
steps.cache-nghttp3.outputs.cache-hit != 'true' ||
steps.cache-ngtcp2-openssl1.outputs.cache-hit != 'true' ||
steps.cache-ngtcp2-openssl3.outputs.cache-hit != 'true'
run: |
echo 'needs-build=true' >> $GITHUB_OUTPUT
- name: Linux setup
if: runner.os == 'Linux' && steps.settings.outputs.needs-build == 'true'
run: |
sudo apt-get install \
g++-12 \
clang-14 \
autoconf \
automake \
autotools-dev \
libtool \
pkg-config \
libelf-dev \
cmake \
cmake-data
- name: MacOS setup
if: runner.os == 'macOS' && steps.settings.outputs.needs-build == 'true'
run: |
brew install \
autoconf \
automake \
pkg-config \
libtool
- name: Build libbpf
if: steps.cache-libbpf.outputs.cache-hit != 'true' && runner.os == 'Linux'
run: |
git clone -b ${{ env.LIBBPF_VERSION }} https://github.com/libbpf/libbpf
cd libbpf
make -C src install PREFIX=$PWD/build
- name: Build quictls/openssl v1.1.1
if: steps.cache-openssl1.outputs.cache-hit != 'true'
run: |
git clone --depth 1 -b OpenSSL_${{ env.OPENSSL1_VERSION }} https://github.com/quictls/openssl openssl1
cd openssl1
./config --prefix=$PWD/build
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
make install_sw
- name: Build quictls/openssl v3.x
if: steps.cache-openssl3.outputs.cache-hit != 'true'
run: |
git clone --depth 1 -b openssl-${{ env.OPENSSL3_VERSION }} https://github.com/quictls/openssl openssl3
cd openssl3
./config enable-ktls --prefix=$PWD/build --libdir=$PWD/build/lib
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
make install_sw
- name: Build BoringSSL
if: steps.cache-boringssl.outputs.cache-hit != 'true'
run: |
git clone https://boringssl.googlesource.com/boringssl
cd boringssl
git checkout ${{ env.BORINGSSL_VERSION }}
mkdir build
cd build
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
- name: Build nghttp3
if: steps.cache-nghttp3.outputs.cache-hit != 'true'
run: |
git clone --depth 1 -b ${{ env.NGHTTP3_VERSION}} https://github.com/ngtcp2/nghttp3
cd nghttp3
autoreconf -i
./configure --prefix=$PWD/build --enable-lib-only
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
make install
- name: Build ngtcp2 + quictls/openssl v1.1.1
if: steps.cache-ngtcp2-openssl1.outputs.cache-hit != 'true'
run: |
git clone --depth 1 -b ${{ env.NGTCP2_VERSION }} https://github.com/ngtcp2/ngtcp2 ngtcp2-openssl1
cd ngtcp2-openssl1
autoreconf -i
./configure --prefix=$PWD/build --enable-lib-only \
PKG_CONFIG_PATH="../openssl1/build/lib/pkgconfig" \
BORINGSSL_CFLAGS="-I$PWD/../boringssl/include/" \
BORINGSSL_LIBS="-L$PWD/../boringssl/build/ssl -lssl -L$PWD/../boringssl/build/crypto -lcrypto" \
--with-boringssl
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
make install
- name: Build ngtcp2 + quictls/openssl v3.x
if: steps.cache-ngtcp2-openssl3.outputs.cache-hit != 'true'
run: |
git clone --depth 1 -b ${{ env.NGTCP2_VERSION }} https://github.com/ngtcp2/ngtcp2 ngtcp2-openssl3
cd ngtcp2-openssl3
autoreconf -i
./configure --prefix=$PWD/build --enable-lib-only \
PKG_CONFIG_PATH="../openssl3/build/lib/pkgconfig" \
BORINGSSL_CFLAGS="-I$PWD/../boringssl/include/" \
BORINGSSL_LIBS="-L$PWD/../boringssl/build/ssl -lssl -L$PWD/../boringssl/build/crypto -lcrypto" \
--with-boringssl
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
make install
build:
needs:
- build-cache
strategy:
matrix:
os: [ubuntu-22.04, macos-12]
compiler: [gcc, clang]
buildtool: [autotools, cmake]
http3: [http3, no-http3]
openssl: [openssl1, openssl3, boringssl]
exclude:
- os: macos-11
- os: macos-12
openssl: openssl3
- http3: no-http3
openssl: openssl3
- os: macos-11
- os: macos-12
compiler: gcc
- # disable macos cmake because of include path issue
os: macos-11
os: macos-12
buildtool: cmake
- os: macos-11
- os: macos-12
openssl: boringssl
- openssl: boringssl
buildtool: cmake
@ -91,83 +260,87 @@ jobs:
run: |
echo 'CC=gcc' >> $GITHUB_ENV
echo 'CXX=g++' >> $GITHUB_ENV
- name: Build libbpf
- name: Restore libbpf cache
uses: actions/cache/restore@v3
if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
with:
path: libbpf/build
key: ${{ runner.os }}-libbpf-${{ env.LIBBPF_VERSION }}
fail-on-cache-miss: true
- name: Set libbpf variables
if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
run: |
git clone -b v1.1.0 https://github.com/libbpf/libbpf
cd libbpf
PREFIX=$PWD/build make -C src install
EXTRA_AUTOTOOLS_OPTS="--with-libbpf"
EXTRA_CMAKE_OPTS="-DWITH_LIBBPF=1"
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
- name: Build quictls/openssl v1.1.1
- name: Restore quictls/openssl v1.1.1 cache
uses: actions/cache/restore@v3
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl1'
run: |
git clone --depth 1 -b OpenSSL_1_1_1t+quic https://github.com/quictls/openssl
cd openssl
./config --prefix=$PWD/build
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
make install_sw
- name: Build quictls/openssl v3.0.x
with:
path: openssl1/build
key: ${{ runner.os }}-openssl-${{ env.OPENSSL1_VERSION }}
fail-on-cache-miss: true
- name: Restore quictls/openssl v3.x cache
uses: actions/cache/restore@v3
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3'
run: |
unset CPPFLAGS
unset LDFLAGS
git clone --depth 1 -b openssl-3.0.8+quic https://github.com/quictls/openssl
cd openssl
./config enable-ktls --prefix=$PWD/build --libdir=$PWD/build/lib
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
make install_sw
- name: Build BoringSSL
with:
path: openssl3/build
key: ${{ runner.os }}-openssl-${{ env.OPENSSL3_VERSION }}
fail-on-cache-miss: true
- name: Restore BoringSSL cache
uses: actions/cache/restore@v3
if: matrix.openssl == 'boringssl'
with:
path: |
boringssl/build/crypto/libcrypto.a
boringssl/build/ssl/libssl.a
boringssl/include
key: ${{ runner.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
fail-on-cache-miss: true
- name: Set BoringSSL variables
if: matrix.openssl == 'boringssl'
run: |
git clone https://boringssl.googlesource.com/boringssl
cd boringssl
git checkout 80a243e07ef77156af66efa7d22ac35aba44c1b3
mkdir build
cd build
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
cd ..
OPENSSL_CFLAGS="-I$PWD/include/"
OPENSSL_LIBS="-L$PWD/build/ssl -lssl -L$PWD/build/crypto -lcrypto -pthread"
EXTRA_NGTCP2_OPTS="$EXTRA_NGTCP2_OPTS --without-openssl --with-boringssl"
EXTRA_AUTOTOOLS_OPTS="$EXTRA_AUTOTOOLS_OPTS --without-neverbleed --without-jemalloc"
echo 'OPENSSL_CFLAGS='"$OPENSSL_CFLAGS" >> $GITHUB_ENV
echo 'OPENSSL_LIBS='"$OPENSSL_LIBS" >> $GITHUB_ENV
echo 'BORINGSSL_CFLAGS='"$OPENSSL_CFLAGS" >> $GITHUB_ENV
echo 'BORINGSSL_LIBS='"$OPENSSL_LIBS" >> $GITHUB_ENV
echo 'EXTRA_NGTCP2_OPTS='"$EXTRA_NGTCP2_OPTS" >> "$GITHUB_ENV"
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
- name: Build nghttp3
- name: Restore nghttp3 cache
uses: actions/cache/restore@v3
if: matrix.http3 == 'http3'
run: |
git clone --depth 1 -b v0.8.0 https://github.com/ngtcp2/nghttp3
cd nghttp3
autoreconf -i
./configure --prefix=$PWD/build --enable-lib-only
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
make install
- name: Build ngtcp2
if: matrix.http3 == 'http3'
run: |
git clone --depth 1 -b v0.13.1 https://github.com/ngtcp2/ngtcp2
cd ngtcp2
autoreconf -i
./configure --prefix=$PWD/build --enable-lib-only PKG_CONFIG_PATH="../openssl/build/lib/pkgconfig" $EXTRA_NGTCP2_OPTS
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
make install
with:
path: nghttp3/build
key: ${{ runner.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
fail-on-cache-miss: true
- name: Restore ngtcp2 + quictls/openssl v1.1.1 cache
uses: actions/cache/restore@v3
if: matrix.http3 == 'http3' && (matrix.openssl == 'openssl1' || matrix.openssl == 'boringssl')
with:
path: ngtcp2-openssl1/build
key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
fail-on-cache-miss: true
- name: Restore ngtcp2 + quictls/openssl v3.x cache
uses: actions/cache/restore@v3
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3'
with:
path: ngtcp2-openssl3/build
key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL3_VERSION }}
fail-on-cache-miss: true
- name: Setup extra environment variables for HTTP/3
if: matrix.http3 == 'http3'
run: |
PKG_CONFIG_PATH="$PWD/openssl/build/lib/pkgconfig:$PWD/nghttp3/build/lib/pkgconfig:$PWD/ngtcp2/build/lib/pkgconfig:$PWD/libbpf/build/lib64/pkgconfig:$PKG_CONFIG_PATH"
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/openssl/build/lib -Wl,-rpath,$PWD/libbpf/build/lib64"
PKG_CONFIG_PATH="$PWD/openssl1/build/lib/pkgconfig:$PWD/openssl3/build/lib/pkgconfig:$PWD/nghttp3/build/lib/pkgconfig:$PWD/ngtcp2-openssl1/build/lib/pkgconfig:$PWD/ngtcp2-openssl3/build/lib/pkgconfig:$PWD/libbpf/build/lib64/pkgconfig:$PKG_CONFIG_PATH"
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/openssl1/build/lib -Wl,-rpath,$PWD/openssl3/build/lib -Wl,-rpath,$PWD/libbpf/build/lib64"
EXTRA_AUTOTOOLS_OPTS="--enable-http3 $EXTRA_AUTOTOOLS_OPTS"
EXTRA_CMAKE_OPTS="-DENABLE_HTTP3=1 $EXTRA_CMAKE_OPTS"
@ -221,6 +394,10 @@ jobs:
cd $NGHTTP2_CMAKE_DIR
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
- uses: actions/setup-go@v3
if: matrix.buildtool == 'cmake'
with:
go-version-file: go.mod
- name: Integration test
# Integration tests for nghttpx; autotools erases build
# artifacts.
@ -277,15 +454,28 @@ jobs:
wine main.exe
build-windows:
strategy:
matrix:
arch: [x86, x64]
include:
- arch: x86
platform: Win32
- arch: x64
platform: x64
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1
- run: |
vcpkg --triplet=${{ matrix.arch }}-windows install cunit
- name: Configure cmake
run: |
mkdir build
cd build
cmake ..
cmake -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_GENERATOR_PLATFORM=${{ matrix.platform }} -DVCPKG_TARGET_TRIPLET=${{ matrix.arch}}-windows ..
- name: Build nghttp2
run: |
cmake --build build
cmake --build build --target check

View File

@ -1,5 +1,6 @@
name: CIFuzz
on: [pull_request]
permissions: read-all
jobs:
Fuzzing:
runs-on: ubuntu-latest

View File

@ -24,12 +24,12 @@
cmake_minimum_required(VERSION 3.0)
# XXX using 1.8.90 instead of 1.9.0-DEV
project(nghttp2 VERSION 1.52.0)
project(nghttp2 VERSION 1.55.0)
# See versioning rule:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
set(LT_CURRENT 38)
set(LT_REVISION 1)
set(LT_REVISION 2)
set(LT_AGE 24)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
@ -61,9 +61,9 @@ find_package(Libev 4.11)
find_package(Libcares 1.7.5)
find_package(ZLIB 1.2.3)
find_package(Libngtcp2 0.0.0)
find_package(Libngtcp2_crypto_openssl 0.0.0)
if(LIBNGTCP2_CRYPTO_OPENSSL_FOUND)
set(HAVE_LIBNGTCP2_CRYPTO_OPENSSL 1)
find_package(Libngtcp2_crypto_quictls 0.0.0)
if(LIBNGTCP2_CRYPTO_QUICTLS_FOUND)
set(HAVE_LIBNGTCP2_CRYPTO_QUICTLS 1)
endif()
find_package(Libnghttp3 0.0.0)
if(WITH_LIBBPF)
@ -238,9 +238,9 @@ if(ENABLE_APP AND NOT (ZLIB_FOUND AND OPENSSL_FOUND AND LIBEV_FOUND))
message(FATAL_ERROR "Applications were requested (ENABLE_APP=1) but dependencies are not met.")
endif()
# HTTP/3 requires quictls/openssl, libngtcp2, libngtcp2_crypto_openssl
# HTTP/3 requires quictls/openssl, libngtcp2, libngtcp2_crypto_quictls
# and libnghttp3.
if(ENABLE_HTTP3 AND NOT (HAVE_SSL_IS_QUIC AND LIBNGTCP2_FOUND AND LIBNGTCP2_CRYPTO_OPENSSL_FOUND AND LIBNGHTTP3_FOUND))
if(ENABLE_HTTP3 AND NOT (HAVE_SSL_IS_QUIC AND LIBNGTCP2_FOUND AND LIBNGTCP2_CRYPTO_QUICTLS_FOUND AND LIBNGHTTP3_FOUND))
message(FATAL_ERROR "HTTP/3 was requested (ENABLE_HTTP3=1) but dependencies are not met.")
endif()
@ -513,7 +513,7 @@ message(STATUS "summary of build options:
Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
Libngtcp2: ${HAVE_LIBNGTCP2} (LIBS='${LIBNGTCP2_LIBRARIES}')
Libngtcp2_crypto_openssl: ${HAVE_LIBNGTCP2_CRYPTO_OPENSSL} (LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}')
Libngtcp2_crypto_quictls: ${HAVE_LIBNGTCP2_CRYPTO_QUICTLS} (LIBS='${LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES}')
Libnghttp3: ${HAVE_LIBNGHTTP3} (LIBS='${LIBNGHTTP3_LIBRARIES}')
Libbpf: ${HAVE_LIBBPF} (LIBS='${LIBBPF_LIBRARIES}')
Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')

View File

@ -44,7 +44,7 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-env \
cmake/FindLibbpf.cmake \
cmake/FindLibnghttp3.cmake \
cmake/FindLibngtcp2.cmake \
cmake/FindLibngtcp2_crypto_openssl.cmake
cmake/FindLibngtcp2_crypto_quictls.cmake
.PHONY: clang-format

View File

@ -127,11 +127,11 @@ To enable the experimental HTTP/3 support for h2load and nghttpx, the
following libraries are required:
* `OpenSSL with QUIC support
<https://github.com/quictls/openssl/tree/OpenSSL_1_1_1t+quic>`_; or
<https://github.com/quictls/openssl/tree/OpenSSL_1_1_1u+quic>`_; or
`BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit
80a243e07ef77156af66efa7d22ac35aba44c1b3)
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ >= 0.13.0
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 0.7.0
b0341041b03ea71d8371a9692aedae263fc06ee9)
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ 0.17.x
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ 0.13.x
Use ``--enable-http3`` configure option to enable HTTP/3 feature for
h2load and nghttpx.
@ -146,7 +146,7 @@ Use ``--with-libbpf`` configure option to build eBPF program.
libelf-dev is needed to build libbpf.
For Ubuntu 20.04, you can build libbpf from `the source code
<https://github.com/libbpf/libbpf/releases/tag/v1.1.0>`_. nghttpx
<https://github.com/libbpf/libbpf/releases/tag/v1.2.0>`_. nghttpx
requires eBPF program for reloading its configuration and hot swapping
its executable.
@ -343,7 +343,7 @@ Build custom OpenSSL:
.. code-block:: text
$ git clone --depth 1 -b OpenSSL_1_1_1t+quic https://github.com/quictls/openssl
$ git clone --depth 1 -b OpenSSL_1_1_1u+quic https://github.com/quictls/openssl
$ cd openssl
$ ./config --prefix=$PWD/build --openssldir=/etc/ssl
$ make -j$(nproc)
@ -354,7 +354,7 @@ Build nghttp3:
.. code-block:: text
$ git clone --depth 1 -b v0.8.0 https://github.com/ngtcp2/nghttp3
$ git clone --depth 1 -b v0.13.0 https://github.com/ngtcp2/nghttp3
$ cd nghttp3
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only
@ -366,7 +366,7 @@ Build ngtcp2:
.. code-block:: text
$ git clone --depth 1 -b v0.13.1 https://github.com/ngtcp2/ngtcp2
$ git clone --depth 1 -b v0.17.0 https://github.com/ngtcp2/ngtcp2
$ cd ngtcp2
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only \
@ -380,7 +380,7 @@ from source:
.. code-block:: text
$ git clone --depth 1 -b v1.1.0 https://github.com/libbpf/libbpf
$ git clone --depth 1 -b v1.2.0 https://github.com/libbpf/libbpf
$ cd libbpf
$ PREFIX=$PWD/build make -C src install
$ cd ..

View File

@ -1,43 +0,0 @@
# - Try to find libngtcp2_crypto_openssl
# Once done this will define
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND - System has libngtcp2_crypto_openssl
# LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS - The libngtcp2_crypto_openssl include directories
# LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES - The libraries needed to use libngtcp2_crypto_openssl
find_package(PkgConfig QUIET)
pkg_check_modules(PC_LIBNGTCP2_CRYPTO_OPENSSL QUIET libngtcp2_crypto_openssl)
find_path(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
NAMES ngtcp2/ngtcp2_crypto_openssl.h
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
)
find_library(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
NAMES ngtcp2_crypto_openssl
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY_DIRS}
)
if(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR)
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
file(STRINGS "${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR}/ngtcp2/version.h"
LIBNGTCP2_CRYPTO_OPENSSL_VERSION REGEX "${_version_regex}")
string(REGEX REPLACE "${_version_regex}" "\\1"
LIBNGTCP2_CRYPTO_OPENSSL_VERSION "${LIBNGTCP2_CRYPTO_OPENSSL_VERSION}")
unset(_version_regex)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND to TRUE if all listed variables are
# TRUE and the requested version matches.
find_package_handle_standard_args(Libngtcp2_crypto_openssl REQUIRED_VARS
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
VERSION_VAR LIBNGTCP2_CRYPTO_OPENSSL_VERSION)
if(LIBNGTCP2_CRYPTO_OPENSSL_FOUND)
set(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES ${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY})
set(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR})
endif()
mark_as_advanced(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY)

View File

@ -0,0 +1,43 @@
# - Try to find libngtcp2_crypto_quictls
# Once done this will define
# LIBNGTCP2_CRYPTO_QUICTLS_FOUND - System has libngtcp2_crypto_quictls
# LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS - The libngtcp2_crypto_quictls include directories
# LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES - The libraries needed to use libngtcp2_crypto_quictls
find_package(PkgConfig QUIET)
pkg_check_modules(PC_LIBNGTCP2_CRYPTO_QUICTLS QUIET libngtcp2_crypto_quictls)
find_path(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR
NAMES ngtcp2/ngtcp2_crypto_quictls.h
HINTS ${PC_LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS}
)
find_library(LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY
NAMES ngtcp2_crypto_quictls
HINTS ${PC_LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY_DIRS}
)
if(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR)
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
file(STRINGS "${LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR}/ngtcp2/version.h"
LIBNGTCP2_CRYPTO_QUICTLS_VERSION REGEX "${_version_regex}")
string(REGEX REPLACE "${_version_regex}" "\\1"
LIBNGTCP2_CRYPTO_QUICTLS_VERSION "${LIBNGTCP2_CRYPTO_QUICTLS_VERSION}")
unset(_version_regex)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set
# LIBNGTCP2_CRYPTO_QUICTLS_FOUND to TRUE if all listed variables are
# TRUE and the requested version matches.
find_package_handle_standard_args(Libngtcp2_crypto_quictls REQUIRED_VARS
LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY
LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR
VERSION_VAR LIBNGTCP2_CRYPTO_QUICTLS_VERSION)
if(LIBNGTCP2_CRYPTO_QUICTLS_FOUND)
set(LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES ${LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY})
set(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR})
endif()
mark_as_advanced(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR
LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY)

View File

@ -88,5 +88,8 @@
/* Define to 1 if you have enum bpf_stats_type in linux/bpf.h. */
#cmakedefine HAVE_BPF_STATS_TYPE 1
/* Define to 1 if you have `libngtcp2_crypto_openssl` library. */
#cmakedefine HAVE_LIBNGTCP2_CRYPTO_OPENSSL
/* Define to 1 if you have `libngtcp2_crypto_quictls` library. */
#cmakedefine HAVE_LIBNGTCP2_CRYPTO_QUICTLS
/* Define to 1 if you have `libev` library. */
#cmakedefine HAVE_LIBEV 1

View File

@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.52.0], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.55.0], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
@ -45,7 +45,7 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl See versioning rule:
dnl https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
AC_SUBST(LT_CURRENT, 38)
AC_SUBST(LT_REVISION, 1)
AC_SUBST(LT_REVISION, 2)
AC_SUBST(LT_AGE, 24)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
@ -433,6 +433,10 @@ if test "x${request_libev}" != "xno"; then
else
have_libev=yes
fi
if test "x${have_libev}" = "xyes"; then
AC_DEFINE([HAVE_LIBEV], [1], [Define to 1 if you have `libev` library.])
fi
fi
if test "x${request_libev}" = "xyes" &&
@ -504,7 +508,7 @@ fi
# ngtcp2 (for src)
have_libngtcp2=no
if test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.13.0], [have_libngtcp2=yes],
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.17.0], [have_libngtcp2=yes],
[have_libngtcp2=no])
if test "x${have_libngtcp2}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS)
@ -516,26 +520,26 @@ if test "x${request_libngtcp2}" = "xyes" &&
AC_MSG_ERROR([libngtcp2 was requested (--with-libngtcp2) but not found])
fi
# ngtcp2_crypto_openssl (for src)
have_libngtcp2_crypto_openssl=no
# ngtcp2_crypto_quictls (for src)
have_libngtcp2_crypto_quictls=no
if test "x${have_ssl_is_quic}" = "xyes" &&
test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_OPENSSL],
[libngtcp2_crypto_openssl >= 0.13.0],
[have_libngtcp2_crypto_openssl=yes],
[have_libngtcp2_crypto_openssl=no])
if test "x${have_libngtcp2_crypto_openssl}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_OPENSSL_PKG_ERRORS)
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_QUICTLS],
[libngtcp2_crypto_quictls >= 0.17.0],
[have_libngtcp2_crypto_quictls=yes],
[have_libngtcp2_crypto_quictls=no])
if test "x${have_libngtcp2_crypto_quictls}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_QUICTLS_PKG_ERRORS)
else
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_OPENSSL], [1],
[Define to 1 if you have `libngtcp2_crypto_openssl` library.])
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_QUICTLS], [1],
[Define to 1 if you have `libngtcp2_crypto_quictls` library.])
fi
fi
if test "x${have_ssl_is_quic}" = "xyes" &&
test "x${request_libngtcp2}" = "xyes" &&
test "x${have_libngtcp2_crypto_openssl}" != "xyes"; then
AC_MSG_ERROR([libngtcp2_crypto_openssl was requested (--with-libngtcp2) but not found])
test "x${have_libngtcp2_crypto_quictls}" != "xyes"; then
AC_MSG_ERROR([libngtcp2_crypto_quictls was requested (--with-libngtcp2) but not found])
fi
# ngtcp2_crypto_boringssl (for src)
@ -563,7 +567,7 @@ fi
# nghttp3 (for src)
have_libnghttp3=no
if test "x${request_libnghttp3}" != "xno"; then
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.7.0], [have_libnghttp3=yes],
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.13.0], [have_libnghttp3=yes],
[have_libnghttp3=no])
if test "x${have_libnghttp3}" = "xno"; then
AC_MSG_NOTICE($LIBNGHTTP3_PKG_ERRORS)
@ -751,7 +755,7 @@ if test "x${request_http3}" != "xno" &&
(test "x${have_ssl_is_quic}" = "xyes" ||
test "x${have_boringssl_quic}" = "xyes") &&
test "x${have_libngtcp2}" = "xyes" &&
(test "x${have_libngtcp2_crypto_openssl}" = "xyes" ||
(test "x${have_libngtcp2_crypto_quictls}" = "xyes" ||
test "x${have_libngtcp2_crypto_boringssl}" = "xyes") &&
test "x${have_libnghttp3}" = "xyes"; then
enable_http3=yes
@ -1072,6 +1076,7 @@ AC_CONFIG_FILES([
tests/testdata/Makefile
third-party/Makefile
src/Makefile
src/testdata/Makefile
bpf/Makefile
examples/Makefile
integration-tests/Makefile
@ -1139,7 +1144,7 @@ AC_MSG_NOTICE([summary of build options:
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}')
libngtcp2_crypto_openssl: ${have_libngtcp2_crypto_openssl} (CFLAGS='${LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBS}')
libngtcp2_crypto_quictls: ${have_libngtcp2_crypto_quictls} (CFLAGS='${LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_QUICTLS_LIBS}')
libngtcp2_crypto_boringssl: ${have_libngtcp2_crypto_boringssl} (CFLAGS='${LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_BORINGSSL_LIBS}')
libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}')
libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}')

View File

@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "H2LOAD" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
.TH "H2LOAD" "1" "Jul 12, 2023" "1.55.0" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.SH SYNOPSIS
@ -246,7 +246,7 @@ mutually exclusive.
Specify URI from which the scheme, host and port will be
used for all requests. The base URI overrides all
values defined either at the command line or inside
input files. If argument starts with "unix:", then the
input files. If argument starts with \(dqunix:\(dq, then the
rest of the argument will be treated as UNIX domain
socket path. The connection is made through that path
instead of TCP. In this case, scheme is inferred from
@ -305,9 +305,9 @@ to buffering. Status code is \-1 for failed streams.
.B \-\-qlog\-file\-base=<PATH>
Enable qlog output and specify base file name for qlogs.
Qlog is emitted for each connection. For a given base
name "base", each output file name becomes
"base.M.N.sqlog" where M is worker ID and N is client ID
(e.g. "base.0.3.sqlog"). Only effective in QUIC runs.
name \(dqbase\(dq, each output file name becomes
\(dqbase.M.N.sqlog\(dq where M is worker ID and N is client ID
(e.g. \(dqbase.0.3.sqlog\(dq). Only effective in QUIC runs.
.UNINDENT
.INDENT 0.0
.TP
@ -408,7 +408,7 @@ The number of status code h2load received.
.INDENT 7.0
.TP
.B total
The number of bytes received from the server "on the wire". If
The number of bytes received from the server \(dqon the wire\(dq. If
requests were made via TLS, this value is the number of decrypted
bytes.
.TP

View File

@ -307,7 +307,6 @@ def arg_repl(matchobj):
def transform_content(content):
content = re.sub(r'^\s+\* ?', '', content)
content = re.sub(r'\|([^\s|]+)\|', arg_repl, content)
content = re.sub(r':enum:', ':macro:', content)
return content
if __name__ == '__main__':

View File

@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTP" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
.TH "NGHTTP" "1" "Jul 12, 2023" "1.55.0" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.SH SYNOPSIS
@ -280,7 +280,7 @@ the easy way to test out the dependency priority in server
implementation.
.sp
When connection is established, nghttp sends 5 PRIORITY frames to idle
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
streams 3, 5, 7, 9 and 11 to create \(dqanchor\(dq nodes in dependency
tree:
.INDENT 0.0
.INDENT 3.5
@ -320,8 +320,8 @@ URI given in command\-line as html, and extracts resource links from
it. When requesting those resources, nghttp uses dependency according
to its resource type.
.sp
For CSS, and Javascript files inside "head" element, they depend on
stream 3 with the weight 2. The Javascript files outside "head"
For CSS, and Javascript files inside \(dqhead\(dq element, they depend on
stream 3 with the weight 2. The Javascript files outside \(dqhead\(dq
element depend on stream 5 with the weight 2. The mages depend on
stream 11 with the weight 12. The other resources (e.g., icon) depend
on stream 11 with the weight 2.

View File

@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTPD" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
.TH "NGHTTPD" "1" "Jul 12, 2023" "1.55.0" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 server
.SH SYNOPSIS

View File

@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTPX" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
.TH "NGHTTPX" "1" "Jul 12, 2023" "1.55.0" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.SH SYNOPSIS
@ -40,13 +40,13 @@ A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.
.TP
.B <PRIVATE_KEY>
Set path to server\(aqs private key. Required unless
"no\-tls" parameter is used in \fI\%\-\-frontend\fP option.
\(dqno\-tls\(dq parameter is used in \fI\%\-\-frontend\fP option.
.UNINDENT
.INDENT 0.0
.TP
.B <CERT>
Set path to server\(aqs certificate. Required unless
"no\-tls" parameter is used in \fI\%\-\-frontend\fP option. To
\(dqno\-tls\(dq parameter is used in \fI\%\-\-frontend\fP option. To
make OCSP stapling work, this must be an absolute path.
.UNINDENT
.SH OPTIONS
@ -59,56 +59,56 @@ The options are categorized into several groups.
Set backend host and port. The multiple backend
addresses are accepted by repeating this option. UNIX
domain socket can be specified by prefixing path name
with "unix:" (e.g., unix:/var/run/backend.sock).
with \(dqunix:\(dq (e.g., unix:/var/run/backend.sock).
.sp
Optionally, if <PATTERN>s are given, the backend address
is only used if request matches the pattern. The
pattern matching is closely designed to ServeMux in
net/http package of Go programming language. <PATTERN>
consists of path, host + path or just host. The path
must start with "\fI/\fP". If it ends with "\fI/\fP", it matches
must start with \(dq\fI/\fP\(dq. If it ends with \(dq\fI/\fP\(dq, it matches
all request path in its subtree. To deal with the
request to the directory without trailing slash, the
path which ends with "\fI/\fP" also matches the request path
which only lacks trailing \(aq\fI/\fP\(aq (e.g., path "\fI/foo/\fP"
matches request path "\fI/foo\fP"). If it does not end with
"\fI/\fP", it performs exact match against the request path.
path which ends with \(dq\fI/\fP\(dq also matches the request path
which only lacks trailing \(aq\fI/\fP\(aq (e.g., path \(dq\fI/foo/\fP\(dq
matches request path \(dq\fI/foo\fP\(dq). If it does not end with
\(dq\fI/\fP\(dq, it performs exact match against the request path.
If host is given, it performs a match against the
request host. For a request received on the frontend
listener with "sni\-fwd" parameter enabled, SNI host is
listener with \(dqsni\-fwd\(dq parameter enabled, SNI host is
used instead of a request host. If host alone is given,
"\fI/\fP" is appended to it, so that it matches all request
paths under the host (e.g., specifying "nghttp2.org"
equals to "nghttp2.org/"). CONNECT method is treated
\(dq\fI/\fP\(dq is appended to it, so that it matches all request
paths under the host (e.g., specifying \(dqnghttp2.org\(dq
equals to \(dqnghttp2.org/\(dq). CONNECT method is treated
specially. It does not have path, and we don\(aqt allow
empty path. To workaround this, we assume that CONNECT
method has "\fI/\fP" as path.
method has \(dq\fI/\fP\(dq as path.
.sp
Patterns with host take precedence over patterns with
just path. Then, longer patterns take precedence over
shorter ones.
.sp
Host can include "*" in the left most position to
Host can include \(dq*\(dq in the left most position to
indicate wildcard match (only suffix match is done).
The "*" must match at least one character. For example,
host pattern "*.nghttp2.org" matches against
"www.nghttp2.org" and "git.ngttp2.org", but does not
match against "nghttp2.org". The exact hosts match
The \(dq*\(dq must match at least one character. For example,
host pattern \(dq*.nghttp2.org\(dq matches against
\(dqwww.nghttp2.org\(dq and \(dqgit.ngttp2.org\(dq, but does not
match against \(dqnghttp2.org\(dq. The exact hosts match
takes precedence over the wildcard hosts match.
.sp
If path part ends with "*", it is treated as wildcard
If path part ends with \(dq*\(dq, it is treated as wildcard
path. The wildcard path behaves differently from the
normal path. For normal path, match is made around the
boundary of path component separator,"\fI/\fP". On the other
boundary of path component separator,\(dq\fI/\fP\(dq. On the other
hand, the wildcard path does not take into account the
path component separator. All paths which include the
wildcard path without last "*" as prefix, and are
strictly longer than wildcard path without last "*" are
matched. "*" must match at least one character. For
example, the pattern "\fI/foo*\fP" matches "\fI/foo/\fP" and
"\fI/foobar\fP". But it does not match "\fI/foo\fP", or "\fI/fo\fP".
wildcard path without last \(dq*\(dq as prefix, and are
strictly longer than wildcard path without last \(dq*\(dq are
matched. \(dq*\(dq must match at least one character. For
example, the pattern \(dq\fI/foo*\fP\(dq matches \(dq\fI/foo/\fP\(dq and
\(dq\fI/foobar\fP\(dq. But it does not match \(dq\fI/foo\fP\(dq, or \(dq\fI/fo\fP\(dq.
.sp
If <PATTERN> is omitted or empty string, "\fI/\fP" is used as
If <PATTERN> is omitted or empty string, \(dq\fI/\fP\(dq is used as
pattern, which matches all request paths (catch\-all
pattern). The catch\-all backend must be given.
.sp
@ -116,16 +116,16 @@ When doing a match, nghttpx made some normalization to
pattern, request host and path. For host part, they are
converted to lower case. For path part, percent\-encoded
unreserved characters defined in RFC 3986 are decoded,
and any dot\-segments (".." and ".") are resolved and
and any dot\-segments (\(dq..\(dq and \(dq.\(dq) are resolved and
removed.
.sp
For example, \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org/httpbin/\(aq
matches the request host "nghttp2.org" and the request
path "\fI/httpbin/get\fP", but does not match the request host
"nghttp2.org" and the request path "\fI/index.html\fP".
matches the request host \(dqnghttp2.org\(dq and the request
path \(dq\fI/httpbin/get\fP\(dq, but does not match the request host
\(dqnghttp2.org\(dq and the request path \(dq\fI/index.html\fP\(dq.
.sp
The multiple <PATTERN>s can be specified, delimiting
them by ":". Specifying
them by \(dq:\(dq. Specifying
\fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org:www.nghttp2.org\(aq has the
same effect to specify \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org\(aq
and \fI\%\-b\fP\(aq127.0.0.1,8080;www.nghttp2.org\(aq.
@ -134,45 +134,45 @@ The backend addresses sharing same <PATTERN> are grouped
together forming load balancing group.
.sp
Several parameters <PARAM> are accepted after <PATTERN>.
The parameters are delimited by ";". The available
parameters are: "proto=<PROTO>", "tls",
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
"affinity=<METHOD>", "dns", "redirect\-if\-not\-tls",
"upgrade\-scheme", "mruby=<PATH>",
"read\-timeout=<DURATION>", "write\-timeout=<DURATION>",
"group=<GROUP>", "group\-weight=<N>", "weight=<N>", and
"dnf". The parameter consists of keyword, and
optionally followed by "=" and value. For example, the
parameter "proto=h2" consists of the keyword "proto" and
value "h2". The parameter "tls" consists of the keyword
"tls" without value. Each parameter is described as
The parameters are delimited by \(dq;\(dq. The available
parameters are: \(dqproto=<PROTO>\(dq, \(dqtls\(dq,
\(dqsni=<SNI_HOST>\(dq, \(dqfall=<N>\(dq, \(dqrise=<N>\(dq,
\(dqaffinity=<METHOD>\(dq, \(dqdns\(dq, \(dqredirect\-if\-not\-tls\(dq,
\(dqupgrade\-scheme\(dq, \(dqmruby=<PATH>\(dq,
\(dqread\-timeout=<DURATION>\(dq, \(dqwrite\-timeout=<DURATION>\(dq,
\(dqgroup=<GROUP>\(dq, \(dqgroup\-weight=<N>\(dq, \(dqweight=<N>\(dq, and
\(dqdnf\(dq. The parameter consists of keyword, and
optionally followed by \(dq=\(dq and value. For example, the
parameter \(dqproto=h2\(dq consists of the keyword \(dqproto\(dq and
value \(dqh2\(dq. The parameter \(dqtls\(dq consists of the keyword
\(dqtls\(dq without value. Each parameter is described as
follows.
.sp
The backend application protocol can be specified using
optional "proto" parameter, and in the form of
"proto=<PROTO>". <PROTO> should be one of the following
list without quotes: "h2", "http/1.1". The default
value of <PROTO> is "http/1.1". Note that usually "h2"
optional \(dqproto\(dq parameter, and in the form of
\(dqproto=<PROTO>\(dq. <PROTO> should be one of the following
list without quotes: \(dqh2\(dq, \(dqhttp/1.1\(dq. The default
value of <PROTO> is \(dqhttp/1.1\(dq. Note that usually \(dqh2\(dq
refers to HTTP/2 over TLS. But in this option, it may
mean HTTP/2 over cleartext TCP unless "tls" keyword is
mean HTTP/2 over cleartext TCP unless \(dqtls\(dq keyword is
used (see below).
.sp
TLS can be enabled by specifying optional "tls"
TLS can be enabled by specifying optional \(dqtls\(dq
parameter. TLS is not enabled by default.
.sp
With "sni=<SNI_HOST>" parameter, it can override the TLS
With \(dqsni=<SNI_HOST>\(dq parameter, it can override the TLS
SNI field value with given <SNI_HOST>. This will
default to the backend <HOST> name
.sp
The feature to detect whether backend is online or
offline can be enabled using optional "fall" and "rise"
parameters. Using "fall=<N>" parameter, if nghttpx
offline can be enabled using optional \(dqfall\(dq and \(dqrise\(dq
parameters. Using \(dqfall=<N>\(dq parameter, if nghttpx
cannot connect to a this backend <N> times in a row,
this backend is assumed to be offline, and it is
excluded from load balancing. If <N> is 0, this backend
never be excluded from load balancing whatever times
nghttpx cannot connect to it, and this is the default.
There is also "rise=<N>" parameter. After backend was
There is also \(dqrise=<N>\(dq parameter. After backend was
excluded from load balancing group, nghttpx periodically
attempts to make a connection to the failed backend, and
if the connection is made successfully <N> times in a
@ -182,80 +182,80 @@ backend is permanently offline, once it goes in that
state, and this is the default behaviour.
.sp
The session affinity is enabled using
"affinity=<METHOD>" parameter. If "ip" is given in
\(dqaffinity=<METHOD>\(dq parameter. If \(dqip\(dq is given in
<METHOD>, client IP based session affinity is enabled.
If "cookie" is given in <METHOD>, cookie based session
affinity is enabled. If "none" is given in <METHOD>,
If \(dqcookie\(dq is given in <METHOD>, cookie based session
affinity is enabled. If \(dqnone\(dq is given in <METHOD>,
session affinity is disabled, and this is the default.
The session affinity is enabled per <PATTERN>. If at
least one backend has "affinity" parameter, and its
<METHOD> is not "none", session affinity is enabled for
least one backend has \(dqaffinity\(dq parameter, and its
<METHOD> is not \(dqnone\(dq, session affinity is enabled for
all backend servers sharing the same <PATTERN>. It is
advised to set "affinity" parameter to all backend
advised to set \(dqaffinity\(dq parameter to all backend
explicitly if session affinity is desired. The session
affinity may break if one of the backend gets
unreachable, or backend settings are reloaded or
replaced by API.
.sp
If "affinity=cookie" is used, the additional
If \(dqaffinity=cookie\(dq is used, the additional
configuration is required.
"affinity\-cookie\-name=<NAME>" must be used to specify a
\(dqaffinity\-cookie\-name=<NAME>\(dq must be used to specify a
name of cookie to use. Optionally,
"affinity\-cookie\-path=<PATH>" can be used to specify a
\(dqaffinity\-cookie\-path=<PATH>\(dq can be used to specify a
path which cookie is applied. The optional
"affinity\-cookie\-secure=<SECURE>" controls the Secure
attribute of a cookie. The default value is "auto", and
\(dqaffinity\-cookie\-secure=<SECURE>\(dq controls the Secure
attribute of a cookie. The default value is \(dqauto\(dq, and
the Secure attribute is determined by a request scheme.
If a request scheme is "https", then Secure attribute is
set. Otherwise, it is not set. If <SECURE> is "yes",
If a request scheme is \(dqhttps\(dq, then Secure attribute is
set. Otherwise, it is not set. If <SECURE> is \(dqyes\(dq,
the Secure attribute is always set. If <SECURE> is
"no", the Secure attribute is always omitted.
"affinity\-cookie\-stickiness=<STICKINESS>" controls
\(dqno\(dq, the Secure attribute is always omitted.
\(dqaffinity\-cookie\-stickiness=<STICKINESS>\(dq controls
stickiness of this affinity. If <STICKINESS> is
"loose", removing or adding a backend server might break
\(dqloose\(dq, removing or adding a backend server might break
the affinity and the request might be forwarded to a
different backend server. If <STICKINESS> is "strict",
different backend server. If <STICKINESS> is \(dqstrict\(dq,
removing the designated backend server breaks affinity,
but adding new backend server does not cause breakage.
If the designated backend server becomes unavailable,
new backend server is chosen as if the request does not
have an affinity cookie. <STICKINESS> defaults to
"loose".
\(dqloose\(dq.
.sp
By default, name resolution of backend host name is done
at start up, or reloading configuration. If "dns"
at start up, or reloading configuration. If \(dqdns\(dq
parameter is given, name resolution takes place
dynamically. This is useful if backend address changes
frequently. If "dns" is given, name resolution of
frequently. If \(dqdns\(dq is given, name resolution of
backend host name at start up, or reloading
configuration is skipped.
.sp
If "redirect\-if\-not\-tls" parameter is used, the matched
If \(dqredirect\-if\-not\-tls\(dq parameter is used, the matched
backend requires that frontend connection is TLS
encrypted. If it isn\(aqt, nghttpx responds to the request
with 308 status code, and https URI the client should
use instead is included in Location header field. The
port number in redirect URI is 443 by default, and can
be changed using \fI\%\-\-redirect\-https\-port\fP option. If at
least one backend has "redirect\-if\-not\-tls" parameter,
least one backend has \(dqredirect\-if\-not\-tls\(dq parameter,
this feature is enabled for all backend servers sharing
the same <PATTERN>. It is advised to set
"redirect\-if\-no\-tls" parameter to all backends
\(dqredirect\-if\-no\-tls\(dq parameter to all backends
explicitly if this feature is desired.
.sp
If "upgrade\-scheme" parameter is used along with "tls"
If \(dqupgrade\-scheme\(dq parameter is used along with \(dqtls\(dq
parameter, HTTP/2 :scheme pseudo header field is changed
to "https" from "http" when forwarding a request to this
to \(dqhttps\(dq from \(dqhttp\(dq when forwarding a request to this
particular backend. This is a workaround for a backend
server which requires "https" :scheme pseudo header
server which requires \(dqhttps\(dq :scheme pseudo header
field on TLS encrypted connection.
.sp
"mruby=<PATH>" parameter specifies a path to mruby
\(dqmruby=<PATH>\(dq parameter specifies a path to mruby
script file which is invoked when this pattern is
matched. All backends which share the same pattern must
have the same mruby path.
.sp
"read\-timeout=<DURATION>" and "write\-timeout=<DURATION>"
\(dqread\-timeout=<DURATION>\(dq and \(dqwrite\-timeout=<DURATION>\(dq
parameters specify the read and write timeout of the
backend connection when this pattern is matched. All
backends which share the same pattern must have the same
@ -263,42 +263,42 @@ timeouts. If these timeouts are entirely omitted for a
pattern, \fI\%\-\-backend\-read\-timeout\fP and
\fI\%\-\-backend\-write\-timeout\fP are used.
.sp
"group=<GROUP>" parameter specifies the name of group
\(dqgroup=<GROUP>\(dq parameter specifies the name of group
this backend address belongs to. By default, it belongs
to the unnamed default group. The name of group is
unique per pattern. "group\-weight=<N>" parameter
unique per pattern. \(dqgroup\-weight=<N>\(dq parameter
specifies the weight of the group. The higher weight
gets more frequently selected by the load balancing
algorithm. <N> must be [1, 256] inclusive. The weight
8 has 4 times more weight than 2. <N> must be the same
for all addresses which share the same <GROUP>. If
"group\-weight" is omitted in an address, but the other
\(dqgroup\-weight\(dq is omitted in an address, but the other
address which belongs to the same group specifies
"group\-weight", its weight is used. If no
"group\-weight" is specified for all addresses, the
weight of a group becomes 1. "group" and "group\-weight"
\(dqgroup\-weight\(dq, its weight is used. If no
\(dqgroup\-weight\(dq is specified for all addresses, the
weight of a group becomes 1. \(dqgroup\(dq and \(dqgroup\-weight\(dq
are ignored if session affinity is enabled.
.sp
"weight=<N>" parameter specifies the weight of the
\(dqweight=<N>\(dq parameter specifies the weight of the
backend address inside a group which this address
belongs to. The higher weight gets more frequently
selected by the load balancing algorithm. <N> must be
[1, 256] inclusive. The weight 8 has 4 times more
weight than weight 2. If this parameter is omitted,
weight becomes 1. "weight" is ignored if session
weight becomes 1. \(dqweight\(dq is ignored if session
affinity is enabled.
.sp
If "dnf" parameter is specified, an incoming request is
If \(dqdnf\(dq parameter is specified, an incoming request is
not forwarded to a backend and just consumed along with
the request body (actually a backend server never be
contacted). It is expected that the HTTP response is
generated by mruby script (see "mruby=<PATH>" parameter
above). "dnf" is an abbreviation of "do not forward".
generated by mruby script (see \(dqmruby=<PATH>\(dq parameter
above). \(dqdnf\(dq is an abbreviation of \(dqdo not forward\(dq.
.sp
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. In order to include ":"
in <PATTERN>, one has to specify "%3A" (which is
percent\-encoded from of ":") instead. Since ";" has
Since \(dq;\(dq and \(dq:\(dq are used as delimiter, <PATTERN> must
not contain these characters. In order to include \(dq:\(dq
in <PATTERN>, one has to specify \(dq%3A\(dq (which is
percent\-encoded from of \(dq:\(dq) instead. Since \(dq;\(dq has
special meaning in shell, the option value must be
quoted.
.sp
@ -310,23 +310,23 @@ Default: \fB127.0.0.1,80\fP
Set frontend host and port. If <HOST> is \(aq*\(aq, it
assumes all addresses including both IPv4 and IPv6.
UNIX domain socket can be specified by prefixing path
name with "unix:" (e.g., unix:/var/run/nghttpx.sock).
name with \(dqunix:\(dq (e.g., unix:/var/run/nghttpx.sock).
This option can be used multiple times to listen to
multiple addresses.
.sp
This option can take 0 or more parameters, which are
described below. Note that "api" and "healthmon"
described below. Note that \(dqapi\(dq and \(dqhealthmon\(dq
parameters are mutually exclusive.
.sp
Optionally, TLS can be disabled by specifying "no\-tls"
Optionally, TLS can be disabled by specifying \(dqno\-tls\(dq
parameter. TLS is enabled by default.
.sp
If "sni\-fwd" parameter is used, when performing a match
If \(dqsni\-fwd\(dq parameter is used, when performing a match
to select a backend server, SNI host name received from
the client is used instead of the request host. See
\fI\%\-\-backend\fP option about the pattern match.
.sp
To make this frontend as API endpoint, specify "api"
To make this frontend as API endpoint, specify \(dqapi\(dq
parameter. This is disabled by default. It is
important to limit the access to the API frontend.
Otherwise, someone may change the backend server, and
@ -334,18 +334,18 @@ break your services, or expose confidential information
to the outside the world.
.sp
To make this frontend as health monitor endpoint,
specify "healthmon" parameter. This is disabled by
specify \(dqhealthmon\(dq parameter. This is disabled by
default. Any requests which come through this address
are replied with 200 HTTP status, without no body.
.sp
To accept PROXY protocol version 1 and 2 on frontend
connection, specify "proxyproto" parameter. This is
connection, specify \(dqproxyproto\(dq parameter. This is
disabled by default.
.sp
To receive HTTP/3 (QUIC) traffic, specify "quic"
To receive HTTP/3 (QUIC) traffic, specify \(dqquic\(dq
parameter. It makes nghttpx listen on UDP port rather
than TCP port. UNIX domain socket, "api", and
"healthmon" parameters cannot be used with "quic"
than TCP port. UNIX domain socket, \(dqapi\(dq, and
\(dqhealthmon\(dq parameters cannot be used with \(dqquic\(dq
parameter.
.sp
Default: \fB*,3000\fP
@ -361,9 +361,9 @@ Default: \fB65536\fP
.TP
.B \-\-backend\-address\-family=(auto|IPv4|IPv6)
Specify address family of backend connections. If
"auto" is given, both IPv4 and IPv6 are considered. If
"IPv4" is given, only IPv4 address is considered. If
"IPv6" is given, only IPv6 address is considered.
\(dqauto\(dq is given, both IPv4 and IPv6 are considered. If
\(dqIPv4\(dq is given, only IPv4 address is considered. If
\(dqIPv6\(dq is given, only IPv6 address is considered.
.sp
Default: \fBauto\fP
.UNINDENT
@ -538,7 +538,7 @@ Default: \fB128K\fP
.INDENT 0.0
.TP
.B \-\-fastopen=<N>
Enables "TCP Fast Open" for the listening socket and
Enables \(dqTCP Fast Open\(dq for the listening socket and
limits the maximum length for the queue of connections
that have not yet completed the three\-way handshake. If
value is 0 then fast open is disabled.
@ -664,7 +664,7 @@ Default: \fB10s\fP
.TP
.B \-\-backend\-max\-backoff=<DURATION>
Specify maximum backoff interval. This is used when
doing health check against offline backend (see "fail"
doing health check against offline backend (see \(dqfail\(dq
parameter in \fI\%\-\-backend\fP option). It is also used to
limit the maximum interval to temporarily disable
backend when nghttpx failed to connect to it. These
@ -766,9 +766,9 @@ used multiple times. To make OCSP stapling work,
<CERTPATH> must be absolute path.
.sp
Additional parameter can be specified in <PARAM>. The
available <PARAM> is "sct\-dir=<DIR>".
available <PARAM> is \(dqsct\-dir=<DIR>\(dq.
.sp
"sct\-dir=<DIR>" specifies the path to directory which
\(dqsct\-dir=<DIR>\(dq specifies the path to directory which
contains *.sct files for TLS
signed_certificate_timestamp extension (RFC 6962). This
feature requires OpenSSL >= 1.0.2. See also
@ -833,7 +833,7 @@ done in case\-insensitive manner. The versions between
\fI\%\-\-tls\-min\-proto\-version\fP and \fI\%\-\-tls\-max\-proto\-version\fP are
enabled. If the protocol list advertised by client does
not overlap this range, you will receive the error
message "unknown protocol". If a protocol version lower
message \(dqunknown protocol\(dq. If a protocol version lower
than TLSv1.2 is specified, make sure that the compatible
ciphers are included in \fI\%\-\-ciphers\fP option. The default
cipher list only includes ciphers compatible with
@ -850,7 +850,7 @@ done in case\-insensitive manner. The versions between
\fI\%\-\-tls\-min\-proto\-version\fP and \fI\%\-\-tls\-max\-proto\-version\fP are
enabled. If the protocol list advertised by client does
not overlap this range, you will receive the error
message "unknown protocol". The available versions are:
message \(dqunknown protocol\(dq. The available versions are:
TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
.sp
Default: \fBTLSv1.3\fP
@ -891,18 +891,18 @@ ticket key generator is required. nghttpx just gets TLS
ticket keys from memcached, and use them, possibly
replacing current set of keys. It is up to extern TLS
ticket key generator to rotate keys frequently. See
"TLS SESSION TICKET RESUMPTION" section in manual page
\(dqTLS SESSION TICKET RESUMPTION\(dq section in manual page
to know the data format in memcached entry. Optionally,
memcached connection can be encrypted with TLS by
specifying "tls" parameter.
specifying \(dqtls\(dq parameter.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-ticket\-key\-memcached\-address\-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to get
TLS ticket keys. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
TLS ticket keys. If \(dqauto\(dq is given, both IPv4 and IPv6
are considered. If \(dqIPv4\(dq is given, only IPv4 address
is considered. If \(dqIPv6\(dq is given, only IPv6 address is
considered.
.sp
Default: \fBauto\fP
@ -920,7 +920,7 @@ Default: \fB10m\fP
Set maximum number of consecutive retries before
abandoning TLS ticket key retrieval. If this number is
reached, the attempt is considered as failure, and
"failure" count is incremented by 1, which contributed
\(dqfailure\(dq count is incremented by 1, which contributed
to the value controlled
\fI\%\-\-tls\-ticket\-key\-memcached\-max\-fail\fP option.
.sp
@ -993,16 +993,16 @@ Disable OCSP stapling.
Specify address of memcached server to store session
cache. This enables shared session cache between
multiple nghttpx instances. Optionally, memcached
connection can be encrypted with TLS by specifying "tls"
connection can be encrypted with TLS by specifying \(dqtls\(dq
parameter.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-session\-cache\-memcached\-address\-family=(auto|IPv4|IPv6)
Specify address family of memcached connections to store
session cache. If "auto" is given, both IPv4 and IPv6
are considered. If "IPv4" is given, only IPv4 address
is considered. If "IPv6" is given, only IPv6 address is
session cache. If \(dqauto\(dq is given, both IPv4 and IPv6
are considered. If \(dqIPv4\(dq is given, only IPv4 address
is considered. If \(dqIPv6\(dq is given, only IPv6 address is
considered.
.sp
Default: \fBauto\fP
@ -1113,7 +1113,7 @@ By default, except for QUIC connections, nghttpx
postpones forwarding HTTP requests sent in early data,
including those sent in partially in it, until TLS
handshake finishes. If all backend server recognizes
"Early\-Data" header field, using this option makes
\(dqEarly\-Data\(dq header field, using this option makes
nghttpx not postpone forwarding request and get full
potential of 0\-RTT data.
.UNINDENT
@ -1274,7 +1274,7 @@ Default: \fB4K\fP
.INDENT 0.0
.TP
.B (default mode)
Accept HTTP/2, and HTTP/1.1 over SSL/TLS. "no\-tls"
Accept HTTP/2, and HTTP/1.1 over SSL/TLS. \(dqno\-tls\(dq
parameter is used in \fI\%\-\-frontend\fP option, accept HTTP/2
and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1
connection can be upgraded to HTTP/2 through HTTP
@ -1365,16 +1365,16 @@ $tls_protocol: protocol for SSL/TLS connection.
.IP \(bu 2
$tls_session_id: session ID for SSL/TLS connection.
.IP \(bu 2
$tls_session_reused: "r" if SSL/TLS session was
reused. Otherwise, "."
$tls_session_reused: \(dqr\(dq if SSL/TLS session was
reused. Otherwise, \(dq.\(dq
.IP \(bu 2
$tls_sni: SNI server name for SSL/TLS connection.
.IP \(bu 2
$backend_host: backend host used to fulfill the
request. "\-" if backend host is not available.
request. \(dq\-\(dq if backend host is not available.
.IP \(bu 2
$backend_port: backend port used to fulfill the
request. "\-" if backend host is not available.
request. \(dq\-\(dq if backend host is not available.
.IP \(bu 2
$method: HTTP method
.IP \(bu 2
@ -1389,10 +1389,10 @@ $protocol_version: HTTP version (e.g., HTTP/1.1,
HTTP/2)
.UNINDENT
.sp
The variable can be enclosed by "{" and "}" for
The variable can be enclosed by \(dq{\(dq and \(dq}\(dq for
disambiguation (e.g., ${remote_addr}).
.sp
Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP
Default: \fB$remote_addr \- \- [$time_local] \(dq$request\(dq $status $body_bytes_sent \(dq$http_referer\(dq \(dq$http_user_agent\(dq\fP
.UNINDENT
.INDENT 0.0
.TP
@ -1456,8 +1456,8 @@ client requests.
.B \-\-add\-forwarded=<LIST>
Append RFC 7239 Forwarded header field with parameters
specified in comma delimited list <LIST>. The supported
parameters are "by", "for", "host", and "proto". By
default, the value of "by" and "for" parameters are
parameters are \(dqby\(dq, \(dqfor\(dq, \(dqhost\(dq, and \(dqproto\(dq. By
default, the value of \(dqby\(dq and \(dqfor\(dq parameters are
obfuscated string. See \fI\%\-\-forwarded\-by\fP and
\fI\%\-\-forwarded\-for\fP options respectively. Note that nghttpx
does not translate non\-standard X\-Forwarded\-* header
@ -1472,15 +1472,15 @@ requests.
.INDENT 0.0
.TP
.B \-\-forwarded\-by=(obfuscated|ip|<VALUE>)
Specify the parameter value sent out with "by" parameter
of Forwarded header field. If "obfuscated" is given,
the string is randomly generated at startup. If "ip" is
Specify the parameter value sent out with \(dqby\(dq parameter
of Forwarded header field. If \(dqobfuscated\(dq is given,
the string is randomly generated at startup. If \(dqip\(dq is
given, the interface address of the connection,
including port number, is sent with "by" parameter. In
case of UNIX domain socket, "localhost" is used instead
including port number, is sent with \(dqby\(dq parameter. In
case of UNIX domain socket, \(dqlocalhost\(dq is used instead
of address and port. User can also specify the static
obfuscated string. The limitation is that it must start
with "_", and only consists of character set
with \(dq_\(dq, and only consists of character set
[A\-Za\-z0\-9._\-], as described in RFC 7239.
.sp
Default: \fBobfuscated\fP
@ -1488,13 +1488,13 @@ Default: \fBobfuscated\fP
.INDENT 0.0
.TP
.B \-\-forwarded\-for=(obfuscated|ip)
Specify the parameter value sent out with "for"
parameter of Forwarded header field. If "obfuscated" is
Specify the parameter value sent out with \(dqfor\(dq
parameter of Forwarded header field. If \(dqobfuscated\(dq is
given, the string is randomly generated for each client
connection. If "ip" is given, the remote client address
connection. If \(dqip\(dq is given, the remote client address
of the connection, without port number, is sent with
"for" parameter. In case of UNIX domain socket,
"localhost" is used instead of address.
\(dqfor\(dq parameter. In case of UNIX domain socket,
\(dqlocalhost\(dq is used instead of address.
.sp
Default: \fBobfuscated\fP
.UNINDENT
@ -1534,7 +1534,7 @@ they are treated as nothing is specified. They are
advertised in alt\-svc header field only in HTTP/1.1
frontend. This option can be used multiple times to
specify multiple alternative services.
Example: \fI\%\-\-altsvc\fP="h2,443,,,ma=3600; persist=1"
Example: \fI\%\-\-altsvc\fP=\(dqh2,443,,,ma=3600; persist=1\(dq
.UNINDENT
.INDENT 0.0
.TP
@ -1549,7 +1549,7 @@ Specify additional header field to add to request header
set. This option just appends header field and won\(aqt
replace anything already set. This option can be used
several times to specify multiple header fields.
Example: \fI\%\-\-add\-request\-header\fP="foo: bar"
Example: \fI\%\-\-add\-request\-header\fP=\(dqfoo: bar\(dq
.UNINDENT
.INDENT 0.0
.TP
@ -1558,7 +1558,7 @@ Specify additional header field to add to response
header set. This option just appends header field and
won\(aqt replace anything already set. This option can be
used several times to specify multiple header fields.
Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
Example: \fI\%\-\-add\-response\-header\fP=\(dqfoo: bar\(dq
.UNINDENT
.INDENT 0.0
.TP
@ -1604,7 +1604,7 @@ Default: \fB500\fP
Set file path to custom error page served when nghttpx
originally generates HTTP error status code <CODE>.
<CODE> must be greater than or equal to 400, and at most
599. If "*" is used instead of <CODE>, it matches all
599. If \(dq*\(dq is used instead of <CODE>, it matches all
HTTP status code. If error status code comes from
backend server, the custom error pages are not used.
.UNINDENT
@ -1627,7 +1627,7 @@ regardless of this option.
.B \-\-redirect\-https\-port=<PORT>
Specify the port number which appears in Location header
field when redirect to HTTPS URI is made due to
"redirect\-if\-not\-tls" parameter in \fI\%\-\-backend\fP option.
\(dqredirect\-if\-not\-tls\(dq parameter in \fI\%\-\-backend\fP option.
.sp
Default: \fB443\fP
.UNINDENT
@ -1807,7 +1807,7 @@ Default: \fB/usr/local/lib/nghttp2/reuseport_kern.o\fP
.TP
.B \-\-frontend\-quic\-early\-data
Enable early data on frontend QUIC connections. nghttpx
sends "Early\-Data" header field to a backend server if a
sends \(dqEarly\-Data\(dq header field to a backend server if a
request is received in early data and handshake has not
finished. All backend servers should deal with possibly
replayed requests.
@ -1818,8 +1818,8 @@ replayed requests.
Specify a directory where a qlog file is written for
frontend QUIC connections. A qlog file is created per
each QUIC connection. The file name is ISO8601 basic
format, followed by "\-", server Source Connection ID and
".sqlog".
format, followed by \(dq\-\(dq, server Source Connection ID and
\(dq.sqlog\(dq.
.UNINDENT
.INDENT 0.0
.TP
@ -1832,8 +1832,8 @@ NEW_TOKEN frame in the previous connection.
.TP
.B \-\-frontend\-quic\-congestion\-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be one of "cubic", "bbr",
and "bbr2".
QUIC connection. <CC> should be one of \(dqcubic\(dq, \(dqbbr\(dq,
and \(dqbbr2\(dq.
.sp
Default: \fBcubic\fP
.UNINDENT
@ -2008,7 +2008,7 @@ instead of:
.sp
.nf
.ft C
add\-request\-header="foo: bar"
add\-request\-header=\(dqfoo: bar\(dq
.ft P
.fi
.UNINDENT
@ -2127,7 +2127,7 @@ Link: </css/theme.css>; rel=preload
Currently, the following restriction is applied for server push:
.INDENT 0.0
.IP 1. 3
The associated stream must have method "GET" or "POST". The
The associated stream must have method \(dqGET\(dq or \(dqPOST\(dq. The
associated stream\(aqs status code must be 200.
.UNINDENT
.sp
@ -2295,7 +2295,7 @@ response without forwarding request to backend servers.
There are 2 levels of mruby script invocations: global and
per\-pattern. The global mruby script is set by \fI\%\-\-mruby\-file\fP
option and is called for all requests. The per\-pattern mruby script
is set by "mruby" parameter in \fI\%\-b\fP option. It is invoked for
is set by \(dqmruby\(dq parameter in \fI\%\-b\fP option. It is invoked for
a request which matches the particular pattern. The order of hook
invocation is: global request phase hook, per\-pattern request phase
hook, per\-pattern response phase hook, and finally global response
@ -2362,7 +2362,7 @@ Return the current phase.
.TP
.B attribute [R] remote_addr
Return IP address of a remote client. If connection is made
via UNIX domain socket, this returns the string "localhost".
via UNIX domain socket, this returns the string \(dqlocalhost\(dq.
.UNINDENT
.INDENT 7.0
.TP
@ -2504,7 +2504,7 @@ not include authority component of URI. This may include
query component. nghttpx makes certain normalization for
path. It decodes percent\-encoding for unreserved characters
(see \fI\%https://tools.ietf.org/html/rfc3986#section\-2.3\fP), and
resolves ".." and ".". But it may leave characters which
resolves \(dq..\(dq and \(dq.\(dq. But it may leave characters which
should be percent\-encoded as is. So be careful when comparing
path against desired string.
.UNINDENT
@ -2646,7 +2646,7 @@ Modify request path:
.ft C
class App
def on_req(env)
env.req.path = "/apps#{env.req.path}"
env.req.path = \(dq/apps#{env.req.path}\(dq
end
end
@ -2667,12 +2667,12 @@ addresses:
.ft C
class App
def on_req(env)
allowed_clients = ["127.0.0.1", "::1"]
allowed_clients = [\(dq127.0.0.1\(dq, \(dq::1\(dq]
if env.req.path.start_with?("/log/") &&
if env.req.path.start_with?(\(dq/log/\(dq) &&
!allowed_clients.include?(env.remote_addr) then
env.resp.status = 404
env.resp.return "permission denied"
env.resp.return \(dqpermission denied\(dq
end
end
end
@ -2686,7 +2686,7 @@ App.new
.sp
nghttpx exposes API endpoints to manipulate it via HTTP based API. By
default, API endpoint is disabled. To enable it, add a dedicated
frontend for API using \fI\%\-\-frontend\fP option with "api"
frontend for API using \fI\%\-\-frontend\fP option with \(dqapi\(dq
parameter. All requests which come from this frontend address, will
be treated as API request.
.sp
@ -2713,7 +2713,7 @@ HTTP status code
Additionally, depending on the API endpoint, \fBdata\fP key may be
present, and its value contains the API endpoint specific data.
.sp
We wrote "normally", since nghttpx may return ordinal HTML response in
We wrote \(dqnormally\(dq, since nghttpx may return ordinal HTML response in
some cases where the error has occurred before reaching API endpoint
(e.g., header field is too large).
.sp
@ -2737,7 +2737,7 @@ connections or requests. It also avoids any process creation as is
the case with hot swapping with signals.
.sp
The one limitation is that only numeric IP address is allowed in
\fI\%backend\fP in request body unless "dns" parameter
\fI\%backend\fP in request body unless \(dqdns\(dq parameter
is used while non numeric hostname is allowed in command\-line or
configuration file is read using \fI\%\-\-conf\fP\&.
.SS GET /api/v1beta1/configrevision

View File

@ -7,7 +7,7 @@ RUN apt-get update && \
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
libelf-dev
RUN git clone --depth 1 -b OpenSSL_1_1_1t+quic https://github.com/quictls/openssl && \
RUN git clone --depth 1 -b OpenSSL_1_1_1u+quic https://github.com/quictls/openssl && \
cd openssl && \
./config --openssldir=/etc/ssl && \
make -j$(nproc) && \
@ -15,7 +15,7 @@ RUN git clone --depth 1 -b OpenSSL_1_1_1t+quic https://github.com/quictls/openss
cd .. && \
rm -rf openssl
RUN git clone --depth 1 -b v0.8.0 https://github.com/ngtcp2/nghttp3 && \
RUN git clone --depth 1 -b v0.13.0 https://github.com/ngtcp2/nghttp3 && \
cd nghttp3 && \
autoreconf -i && \
./configure --enable-lib-only && \
@ -24,7 +24,7 @@ RUN git clone --depth 1 -b v0.8.0 https://github.com/ngtcp2/nghttp3 && \
cd .. && \
rm -rf nghttp3
RUN git clone --depth 1 -b v0.13.1 https://github.com/ngtcp2/ngtcp2 && \
RUN git clone --depth 1 -b v0.17.0 https://github.com/ngtcp2/ngtcp2 && \
cd ngtcp2 && \
autoreconf -i && \
./configure --enable-lib-only \
@ -36,7 +36,7 @@ RUN git clone --depth 1 -b v0.13.1 https://github.com/ngtcp2/ngtcp2 && \
cd .. && \
rm -rf ngtcp2
RUN git clone --depth 1 -b v1.1.0 https://github.com/libbpf/libbpf && \
RUN git clone --depth 1 -b v1.2.0 https://github.com/libbpf/libbpf && \
cd libbpf && \
PREFIX=/usr/local make -C src install && \
cd .. && \

26
go.mod
View File

@ -1,26 +1,26 @@
module github.com/nghttp2/nghttp2
go 1.18
go 1.20
require (
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d
github.com/lucas-clemente/quic-go v0.30.0
github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746
github.com/quic-go/quic-go v0.35.1
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20150408091349-4742878d9c90
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
golang.org/x/net v0.10.0
)
require (
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/marten-seemann/qpack v0.3.0 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.12 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.6.0 // indirect
)

48
go.sum
View File

@ -1,5 +1,5 @@
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746 h1:wAIE/kN63Oig1DdOzN7O+k4AbFh2cCJoKMFXrwRJtzk=
github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -15,19 +15,19 @@ github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/lucas-clemente/quic-go v0.30.0 h1:nwLW0h8ahVQ5EPTIM7uhl/stHqQDea15oRlYKZmw2O0=
github.com/lucas-clemente/quic-go v0.30.0/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk=
github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=
github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@ -36,18 +36,18 @@ github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20150408091349-4742878d9c90/go.mod h1:Y
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -56,18 +56,18 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -594,6 +594,76 @@ func TestH1H1ReqPhaseReturn(t *testing.T) {
}
}
// TestH1H1ReqPhaseReturnCONNECTMethod tests that mruby request phase
// hook resets llhttp HPE_PAUSED_UPGRADE.
func TestH1H1ReqPhaseReturnCONNECTMethod(t *testing.T) {
opts := options{
args: []string{"--mruby-file=" + testDir + "/req-return.rb"},
handler: func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("request should not be forwarded")
},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1ReqPhaseReturnCONNECTMethod\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusNotFound; got != want {
t.Errorf("status: %v; want %v", got, want)
}
hdCheck := func() {
hdtests := []struct {
k, v string
}{
{"content-length", "20"},
{"from", "mruby"},
}
for _, tt := range hdtests {
if got, want := resp.Header.Get(tt.k), tt.v; got != want {
t.Errorf("%v = %v; want %v", tt.k, got, want)
}
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
hdCheck()
if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1ReqPhaseReturnCONNECTMethod\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err = http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusNotFound; got != want {
t.Errorf("status: %v; want %v", got, want)
}
hdCheck()
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies
// response header fields.
func TestH1H1RespPhaseSetHeader(t *testing.T) {
@ -737,6 +807,54 @@ func TestH1H1POSTRequests(t *testing.T) {
}
}
// TestH1H1CONNECTMethodFailure tests that CONNECT method failure
// resets llhttp HPE_PAUSED_UPGRADE.
func TestH1H1CONNECTMethodFailure(t *testing.T) {
opts := options{
handler: func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("required-header") == "" {
w.WriteHeader(http.StatusNotFound)
}
},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1CONNECTMethodFailure\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusNotFound; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1CONNECTMethodFailure\r\nHost: 127.0.0.1:443\r\nrequired-header: foo\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err = http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// // TestH1H2ConnectFailure tests that server handles the situation that
// // connection attempt to HTTP/2 backend failed.
// func TestH1H2ConnectFailure(t *testing.T) {

View File

@ -22,7 +22,7 @@ import (
"testing"
"time"
"github.com/lucas-clemente/quic-go/http3"
"github.com/quic-go/quic-go/http3"
"github.com/tatsuhiro-t/go-nghttp2"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"

View File

@ -25,6 +25,7 @@ set(NGHTTP2_SOURCES
nghttp2_rcbuf.c
nghttp2_extpri.c
nghttp2_debug.c
sfparse.c
)
set(NGHTTP2_RES "")

View File

@ -51,7 +51,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_http.c \
nghttp2_rcbuf.c \
nghttp2_extpri.c \
nghttp2_debug.c
nghttp2_debug.c \
sfparse.c
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_frame.h \
@ -68,7 +69,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_http.h \
nghttp2_rcbuf.h \
nghttp2_extpri.h \
nghttp2_debug.h
nghttp2_debug.h \
sfparse.h
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \

View File

@ -31,6 +31,7 @@
#include "nghttp2_hd.h"
#include "nghttp2_helper.h"
#include "nghttp2_extpri.h"
#include "sfparse.h"
static uint8_t downcase(uint8_t c) {
return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
@ -578,713 +579,52 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream,
}
}
/* Generated by genchartbl.py */
static const int SF_KEY_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */,
1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */,
0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */,
0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */,
0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */,
0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_key(const uint8_t *begin, const uint8_t *end) {
const uint8_t *p = begin;
if ((*p < 'a' || 'z' < *p) && *p != '*') {
return -1;
}
for (; p != end && SF_KEY_CHARS[*p]; ++p)
;
return p - begin;
}
static ssize_t sf_parse_integer_or_decimal(nghttp2_sf_value *dest,
const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
int sign = 1;
int64_t value = 0;
int type = NGHTTP2_SF_VALUE_TYPE_INTEGER;
size_t len = 0;
size_t fpos = 0;
size_t i;
if (*p == '-') {
if (++p == end) {
return -1;
}
sign = -1;
}
if (*p < '0' || '9' < *p) {
return -1;
}
for (; p != end; ++p) {
switch (*p) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value *= 10;
value += *p - '0';
if (++len > 15) {
return -1;
}
break;
case '.':
if (type != NGHTTP2_SF_VALUE_TYPE_INTEGER) {
goto fin;
}
if (len > 12) {
return -1;
}
fpos = len;
type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
break;
default:
goto fin;
};
}
fin:
switch (type) {
case NGHTTP2_SF_VALUE_TYPE_INTEGER:
if (dest) {
dest->type = (uint8_t)type;
dest->i = value * sign;
}
return p - begin;
case NGHTTP2_SF_VALUE_TYPE_DECIMAL:
if (fpos == len || len - fpos > 3) {
return -1;
}
if (dest) {
dest->type = (uint8_t)type;
dest->d = (double)value;
for (i = len - fpos; i > 0; --i) {
dest->d /= (double)10;
}
dest->d *= sign;
}
return p - begin;
default:
assert(0);
abort();
}
}
/* Generated by genchartbl.py */
static const int SF_DQUOTE_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */,
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */,
1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */,
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */,
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */,
1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_string(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
if (*p++ != '"') {
return -1;
}
for (; p != end; ++p) {
switch (*p) {
case '\\':
if (++p == end) {
return -1;
}
switch (*p) {
case '"':
case '\\':
break;
default:
return -1;
}
break;
case '"':
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_STRING;
dest->s.base = begin + 1;
dest->s.len = (size_t)(p - dest->s.base);
}
++p;
return p - begin;
default:
if (!SF_DQUOTE_CHARS[*p]) {
return -1;
}
}
}
return -1;
}
/* Generated by genchartbl.py */
static const int SF_TOKEN_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */,
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */,
1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */,
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */,
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */,
0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_token(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') {
return -1;
}
for (; p != end && SF_TOKEN_CHARS[*p]; ++p)
;
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_TOKEN;
dest->s.base = begin;
dest->s.len = (size_t)(p - begin);
}
return p - begin;
}
/* Generated by genchartbl.py */
static const int SF_BYTESEQ_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */,
0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_byteseq(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
if (*p++ != ':') {
return -1;
}
for (; p != end; ++p) {
switch (*p) {
case ':':
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_BYTESEQ;
dest->s.base = begin + 1;
dest->s.len = (size_t)(p - dest->s.base);
}
++p;
return p - begin;
default:
if (!SF_BYTESEQ_CHARS[*p]) {
return -1;
}
}
}
return -1;
}
static ssize_t sf_parse_boolean(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
int b;
if (*p++ != '?') {
return -1;
}
if (p == end) {
return -1;
}
switch (*p++) {
case '0':
b = 0;
break;
case '1':
b = 1;
break;
default:
return -1;
}
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN;
dest->b = b;
}
return p - begin;
}
static ssize_t sf_parse_bare_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
switch (*begin) {
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return sf_parse_integer_or_decimal(dest, begin, end);
case '"':
return sf_parse_string(dest, begin, end);
case '*':
return sf_parse_token(dest, begin, end);
case ':':
return sf_parse_byteseq(dest, begin, end);
case '?':
return sf_parse_boolean(dest, begin, end);
default:
if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) {
return sf_parse_token(dest, begin, end);
}
return -1;
}
}
#define sf_discard_sp_end_err(BEGIN, END, ERR) \
for (;; ++(BEGIN)) { \
if ((BEGIN) == (END)) { \
return (ERR); \
} \
if (*(BEGIN) != ' ') { \
break; \
} \
}
static ssize_t sf_parse_params(const uint8_t *begin, const uint8_t *end) {
const uint8_t *p = begin;
ssize_t slen;
for (; p != end && *p == ';';) {
++p;
sf_discard_sp_end_err(p, end, -1);
slen = sf_parse_key(p, end);
if (slen < 0) {
return -1;
}
p += slen;
if (p == end || *p != '=') {
/* Boolean true */
} else if (++p == end) {
return -1;
} else {
slen = sf_parse_bare_item(NULL, p, end);
if (slen < 0) {
return -1;
}
p += slen;
}
}
return p - begin;
}
static ssize_t sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
ssize_t slen;
slen = sf_parse_bare_item(dest, p, end);
if (slen < 0) {
return -1;
}
p += slen;
slen = sf_parse_params(p, end);
if (slen < 0) {
return -1;
}
p += slen;
return p - begin;
}
ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
return sf_parse_item(dest, begin, end);
}
static ssize_t sf_parse_inner_list(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
ssize_t slen;
if (*p++ != '(') {
return -1;
}
for (;;) {
sf_discard_sp_end_err(p, end, -1);
if (*p == ')') {
++p;
slen = sf_parse_params(p, end);
if (slen < 0) {
return -1;
}
p += slen;
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_INNER_LIST;
}
return p - begin;
}
slen = sf_parse_item(NULL, p, end);
if (slen < 0) {
return -1;
}
p += slen;
if (p == end || (*p != ' ' && *p != ')')) {
return -1;
}
}
}
ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest,
const uint8_t *begin, const uint8_t *end) {
return sf_parse_inner_list(dest, begin, end);
}
static ssize_t sf_parse_item_or_inner_list(nghttp2_sf_value *dest,
const uint8_t *begin,
const uint8_t *end) {
if (*begin == '(') {
return sf_parse_inner_list(dest, begin, end);
}
return sf_parse_item(dest, begin, end);
}
#define sf_discard_ows(BEGIN, END) \
for (;; ++(BEGIN)) { \
if ((BEGIN) == (END)) { \
goto fin; \
} \
if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
break; \
} \
}
#define sf_discard_ows_end_err(BEGIN, END, ERR) \
for (;; ++(BEGIN)) { \
if ((BEGIN) == (END)) { \
return (ERR); \
} \
if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
break; \
} \
}
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
size_t valuelen) {
const uint8_t *p = value, *end = value + valuelen;
ssize_t slen;
nghttp2_sf_value val;
nghttp2_extpri pri = *dest;
const uint8_t *key;
size_t keylen;
sf_parser sfp;
sf_vec key;
sf_value val;
int rv;
for (; p != end && *p == ' '; ++p)
;
for (; p != end;) {
slen = sf_parse_key(p, end);
if (slen < 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
key = p;
keylen = (size_t)slen;
p += slen;
if (p == end || *p != '=') {
/* Boolean true */
val.type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN;
val.b = 1;
slen = sf_parse_params(p, end);
if (slen < 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
} else if (++p == end) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
} else {
slen = sf_parse_item_or_inner_list(&val, p, end);
if (slen < 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
}
p += slen;
if (keylen == 1) {
switch (key[0]) {
case 'i':
if (val.type != NGHTTP2_SF_VALUE_TYPE_BOOLEAN) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
pri.inc = val.b;
break;
case 'u':
if (val.type != NGHTTP2_SF_VALUE_TYPE_INTEGER ||
val.i < NGHTTP2_EXTPRI_URGENCY_HIGH ||
NGHTTP2_EXTPRI_URGENCY_LOW < val.i) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
pri.urgency = (uint32_t)val.i;
sf_parser_init(&sfp, value, valuelen);
for (;;) {
rv = sf_parser_dict(&sfp, &key, &val);
if (rv != 0) {
if (rv == SF_ERR_EOF) {
break;
}
}
sf_discard_ows(p, end);
if (*p++ != ',') {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
sf_discard_ows_end_err(p, end, NGHTTP2_ERR_INVALID_ARGUMENT);
if (key.len != 1) {
continue;
}
switch (key.base[0]) {
case 'i':
if (val.type != SF_TYPE_BOOLEAN) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
pri.inc = val.boolean;
break;
case 'u':
if (val.type != SF_TYPE_INTEGER ||
val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
pri.urgency = (uint32_t)val.integer;
break;
}
}
fin:
*dest = pri;
return 0;

View File

@ -94,54 +94,6 @@ int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n);
void nghttp2_http_record_request_method(nghttp2_stream *stream,
nghttp2_frame *frame);
/*
* RFC 8941 Structured Field Values.
*/
typedef enum nghttp2_sf_value_type {
NGHTTP2_SF_VALUE_TYPE_BOOLEAN,
NGHTTP2_SF_VALUE_TYPE_INTEGER,
NGHTTP2_SF_VALUE_TYPE_DECIMAL,
NGHTTP2_SF_VALUE_TYPE_STRING,
NGHTTP2_SF_VALUE_TYPE_TOKEN,
NGHTTP2_SF_VALUE_TYPE_BYTESEQ,
NGHTTP2_SF_VALUE_TYPE_INNER_LIST,
} nghttp2_sf_value_type;
/*
* nghttp2_sf_value stores Structured Field Values item. For Inner
* List, only type is set to NGHTTP2_SF_VALUE_TYPE_INNER_LIST.
*/
typedef struct nghttp2_sf_value {
uint8_t type;
union {
int b;
int64_t i;
double d;
struct {
const uint8_t *base;
size_t len;
} s;
};
} nghttp2_sf_value;
/*
* nghttp2_sf_parse_item parses the input sequence [|begin|, |end|)
* and stores the parsed an Item in |dest|. It returns the number of
* bytes consumed if it succeeds, or -1. This function is declared
* here for unit tests.
*/
ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end);
/*
* nghttp2_sf_parse_inner_list parses the input sequence [|begin|, |end|)
* and stores the parsed an Inner List in |dest|. It returns the number of
* bytes consumed if it succeeds, or -1. This function is declared
* here for unit tests.
*/
ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest,
const uint8_t *begin, const uint8_t *end);
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
size_t valuelen);

View File

@ -31,21 +31,14 @@
#include "nghttp2_helper.h"
#define NGHTTP2_INITIAL_TABLE_LENBITS 8
#define NGHTTP2_INITIAL_TABLE_LENBITS 4
int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
map->mem = mem;
map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS;
map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS;
map->table =
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket));
if (map->table == NULL) {
return NGHTTP2_ERR_NOMEM;
}
map->tablelen = 0;
map->tablelenbits = 0;
map->table = NULL;
map->size = 0;
return 0;
}
void nghttp2_map_free(nghttp2_map *map) {
@ -78,6 +71,10 @@ int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
uint32_t i;
nghttp2_map_bucket *bkt;
if (map->size == 0) {
return 0;
}
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
@ -223,9 +220,17 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
/* Load factor is 0.75 */
if ((map->size + 1) * 4 > map->tablelen * 3) {
rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
if (rv != 0) {
return rv;
if (map->tablelen) {
rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
if (rv != 0) {
return rv;
}
} else {
rv = map_resize(map, 1 << NGHTTP2_INITIAL_TABLE_LENBITS,
NGHTTP2_INITIAL_TABLE_LENBITS);
if (rv != 0) {
return rv;
}
}
}
@ -239,11 +244,18 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
}
void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
uint32_t h = hash(key);
size_t idx = h2idx(h, map->tablelenbits);
uint32_t h;
size_t idx;
nghttp2_map_bucket *bkt;
size_t d = 0;
if (map->size == 0) {
return NULL;
}
h = hash(key);
idx = h2idx(h, map->tablelenbits);
for (;;) {
bkt = &map->table[idx];
@ -262,11 +274,18 @@ void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
}
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
uint32_t h = hash(key);
size_t idx = h2idx(h, map->tablelenbits), didx;
uint32_t h;
size_t idx, didx;
nghttp2_map_bucket *bkt;
size_t d = 0;
if (map->size == 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
h = hash(key);
idx = h2idx(h, map->tablelenbits);
for (;;) {
bkt = &map->table[idx];
@ -306,6 +325,10 @@ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
}
void nghttp2_map_clear(nghttp2_map *map) {
if (map->tablelen == 0) {
return;
}
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
map->size = 0;
}

View File

@ -54,14 +54,8 @@ typedef struct nghttp2_map {
/*
* Initializes the map |map|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem);
void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem);
/*
* Deallocates any resources allocated for |map|. The stored entries

View File

@ -584,10 +584,6 @@ static int session_new(nghttp2_session **session_ptr,
if (rv != 0) {
goto fail_hd_inflater;
}
rv = nghttp2_map_init(&(*session_ptr)->streams, mem);
if (rv != 0) {
goto fail_map;
}
nbuffer = ((*session_ptr)->max_send_header_block_length +
NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /
@ -605,6 +601,8 @@ static int session_new(nghttp2_session **session_ptr,
goto fail_aob_framebuf;
}
nghttp2_map_init(&(*session_ptr)->streams, mem);
active_outbound_item_reset(&(*session_ptr)->aob, mem);
(*session_ptr)->callbacks = *callbacks;
@ -637,8 +635,6 @@ static int session_new(nghttp2_session **session_ptr,
return 0;
fail_aob_framebuf:
nghttp2_map_free(&(*session_ptr)->streams);
fail_map:
nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
fail_hd_inflater:
nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
@ -5931,7 +5927,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
in += readlen;
if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
return in - first;
return (ssize_t)(in - first);
}
if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
@ -5968,7 +5964,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
in += readlen;
if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
return in - first;
return (ssize_t)(in - first);
}
nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);
@ -6468,7 +6464,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
return in - first;
return (ssize_t)(in - first);
}
switch (iframe->frame.hd.type) {
@ -6772,7 +6768,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
in += hd_proclen;
iframe->payloadleft -= hd_proclen;
return in - first;
return (ssize_t)(in - first);
}
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
@ -6963,7 +6959,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
in += readlen;
if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
return in - first;
return (ssize_t)(in - first);
}
nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);
@ -7021,7 +7017,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
return in - first;
return (ssize_t)(in - first);
}
/* Pad Length field is subject to flow control */
@ -7171,7 +7167,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
in - readlen, (size_t)data_readlen, session->user_data);
if (rv == NGHTTP2_ERR_PAUSE) {
return in - first;
return (ssize_t)(in - first);
}
if (nghttp2_is_fatal(rv)) {
@ -7351,7 +7347,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
assert(in == last);
return in - first;
return (ssize_t)(in - first);
}
int nghttp2_session_recv(nghttp2_session *session) {

1146
lib/sfparse.c Normal file

File diff suppressed because it is too large Load Diff

409
lib/sfparse.h Normal file
View File

@ -0,0 +1,409 @@
/*
* sfparse
*
* Copyright (c) 2023 sfparse contributors
* Copyright (c) 2019 nghttp3 contributors
* Copyright (c) 2015 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SFPARSE_H
#define SFPARSE_H
/* Define WIN32 when build target is Win32 API (borrowed from
libcurl) */
#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
# define WIN32
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_MSC_VER) && (_MSC_VER < 1800)
/* MSVC < 2013 does not have inttypes.h because it is not C99
compliant. See compiler macros and version number in
https://sourceforge.net/p/predef/wiki/Compilers/ */
# include <stdint.h>
#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
# include <inttypes.h>
#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
#include <sys/types.h>
#include <stddef.h>
/**
* @enum
*
* :type:`sf_type` defines value type.
*/
typedef enum sf_type {
/**
* :enum:`SF_TYPE_BOOLEAN` indicates boolean type.
*/
SF_TYPE_BOOLEAN,
/**
* :enum:`SF_TYPE_INTEGER` indicates integer type.
*/
SF_TYPE_INTEGER,
/**
* :enum:`SF_TYPE_DECIMAL` indicates decimal type.
*/
SF_TYPE_DECIMAL,
/**
* :enum:`SF_TYPE_STRING` indicates string type.
*/
SF_TYPE_STRING,
/**
* :enum:`SF_TYPE_TOKEN` indicates token type.
*/
SF_TYPE_TOKEN,
/**
* :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type.
*/
SF_TYPE_BYTESEQ,
/**
* :enum:`SF_TYPE_INNER_LIST` indicates inner list type.
*/
SF_TYPE_INNER_LIST,
/**
* :enum:`SF_TYPE_DATE` indicates date type.
*/
SF_TYPE_DATE
} sf_type;
/**
* @macro
*
* :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has
* occurred, and it is not possible to continue the processing.
*/
#define SF_ERR_PARSE_ERROR -1
/**
* @macro
*
* :macro:`SF_ERR_EOF` indicates that there is nothing left to read.
* The context of this error varies depending on the function that
* returns this error code.
*/
#define SF_ERR_EOF -2
/**
* @struct
*
* :type:`sf_vec` stores sequence of bytes.
*/
typedef struct sf_vec {
/**
* :member:`base` points to the beginning of the sequence of bytes.
*/
uint8_t *base;
/**
* :member:`len` is the number of bytes contained in this sequence.
*/
size_t len;
} sf_vec;
/**
* @macro
*
* :macro:`SF_VALUE_FLAG_NONE` indicates no flag set.
*/
#define SF_VALUE_FLAG_NONE 0x0u
/**
* @macro
*
* :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string
* contains escaped character(s).
*/
#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u
/**
* @struct
*
* :type:`sf_decimal` contains decimal value.
*/
typedef struct sf_decimal {
/**
* :member:`numer` contains numerator of the decimal value.
*/
int64_t numer;
/**
* :member:`denom` contains denominator of the decimal value.
*/
int64_t denom;
} sf_decimal;
/**
* @struct
*
* :type:`sf_value` stores a Structured Field item. For Inner List,
* only type is set to :enum:`sf_type.SF_TYPE_INNER_LIST`. In order
* to read the items contained in an inner list, call
* `sf_parser_inner_list`.
*/
typedef struct sf_value {
/**
* :member:`type` is the type of the value contained in this
* particular object.
*/
sf_type type;
/**
* :member:`flags` is bitwise OR of one or more of
* :macro:`SF_VALUE_FLAG_* <SF_VALUE_FLAG_NONE>`.
*/
uint32_t flags;
/**
* @anonunion_start
*
* @sf_value_value
*/
union {
/**
* :member:`boolean` contains boolean value if :member:`type` ==
* :enum:`sf_type.SF_TYPE_BOOLEAN`. 1 indicates true, and 0
* indicates false.
*/
int boolean;
/**
* :member:`integer` contains integer value if :member:`type` is
* either :enum:`sf_type.SF_TYPE_INTEGER` or
* :enum:`sf_type.SF_TYPE_DATE`.
*/
int64_t integer;
/**
* :member:`decimal` contains decimal value if :member:`type` ==
* :enum:`sf_type.SF_TYPE_DECIMAL`.
*/
sf_decimal decimal;
/**
* :member:`vec` contains sequence of bytes if :member:`type` is
* either :enum:`sf_type.SF_TYPE_STRING`,
* :enum:`sf_type.SF_TYPE_TOKEN`, or
* :enum:`sf_type.SF_TYPE_BYTESEQ`.
*
* For :enum:`sf_type.SF_TYPE_STRING`, this field contains one or
* more escaped characters if :member:`flags` has
* :macro:`SF_VALUE_FLAG_ESCAPED_STRING` set. To unescape the
* string, use `sf_unescape`.
*
* For :enum:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64
* encoded string. To decode this byte string, use
* `sf_base64decode`.
*
* If :member:`vec.len <sf_vec.len>` == 0, :member:`vec.base
* <sf_vec.base>` is guaranteed to be NULL.
*/
sf_vec vec;
/**
* @anonunion_end
*/
};
} sf_value;
/**
* @struct
*
* :type:`sf_parser` is the Structured Field Values parser. Use
* `sf_parser_init` to initialize it.
*/
typedef struct sf_parser {
/* all fields are private */
const uint8_t *pos;
const uint8_t *end;
uint32_t state;
} sf_parser;
/**
* @function
*
* `sf_parser_init` initializes |sfp| with the given buffer pointed by
* |data| of length |datalen|.
*/
void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen);
/**
* @function
*
* `sf_parser_param` reads a parameter. If this function returns 0,
* it stores parameter key and value in |dest_key| and |dest_value|
* respectively, if they are not NULL.
*
* This function does no effort to find duplicated keys. Same key may
* be reported more than once.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all parameters have
* read, and caller can continue to read rest of the values. If it
* returns :macro:`SF_ERR_PARSE_ERROR`, it encountered fatal error
* while parsing field value.
*/
int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value);
/**
* @function
*
* `sf_parser_dict` reads the next dictionary key and value pair. If
* this function returns 0, it stores the key and value in |dest_key|
* and |dest_value| respectively, if they are not NULL.
*
* Caller can optionally read parameters attached to the pair by
* calling `sf_parser_param`.
*
* This function does no effort to find duplicated keys. Same key may
* be reported more than once.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all key and value
* pairs have been read, and there is nothing left to read.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* All values in the dictionary have read.
* :macro:`SF_ERR_PARSE_ERROR`
* It encountered fatal error while parsing field value.
*/
int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value);
/**
* @function
*
* `sf_parser_list` reads the next list item. If this function
* returns 0, it stores the item in |dest| if it is not NULL.
*
* Caller can optionally read parameters attached to the item by
* calling `sf_parser_param`.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all values in the
* list have been read, and there is nothing left to read.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* All values in the list have read.
* :macro:`SF_ERR_PARSE_ERROR`
* It encountered fatal error while parsing field value.
*/
int sf_parser_list(sf_parser *sfp, sf_value *dest);
/**
* @function
*
* `sf_parser_item` reads a single item. If this function returns 0,
* it stores the item in |dest| if it is not NULL.
*
* This function is only used for the field value that consists of a
* single item.
*
* Caller can optionally read parameters attached to the item by
* calling `sf_parser_param`.
*
* Caller should call this function again to make sure that there is
* nothing left to read. If this 2nd function call returns
* :macro:`SF_ERR_EOF`, all data have been processed successfully.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* There is nothing left to read.
* :macro:`SF_ERR_PARSE_ERROR`
* It encountered fatal error while parsing field value.
*/
int sf_parser_item(sf_parser *sfp, sf_value *dest);
/**
* @function
*
* `sf_parser_inner_list` reads the next inner list item. If this
* function returns 0, it stores the item in |dest| if it is not NULL.
*
* Caller can optionally read parameters attached to the item by
* calling `sf_parser_param`.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all values in this
* inner list have been read, and caller can optionally read
* parameters attached to this inner list by calling
* `sf_parser_param`. Then caller can continue to read rest of the
* values.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* All values in the inner list have read.
* :macro:`SF_ERR_PARSE_ERROR`
* It encountered fatal error while parsing field value.
*/
int sf_parser_inner_list(sf_parser *sfp, sf_value *dest);
/**
* @function
*
* `sf_unescape` copies |src| to |dest| by removing escapes (``\``).
* |src| should be the pointer to :member:`sf_value.vec` of type
* :enum:`sf_type.SF_TYPE_STRING` produced by either `sf_parser_dict`,
* `sf_parser_list`, `sf_parser_inner_list`, `sf_parser_item`, or
* `sf_parser_param`, otherwise the behavior is undefined.
*
* :member:`dest->base <sf_vec.base>` must point to the buffer that
* has sufficient space to store the unescaped string.
*
* If there is no escape character in |src|, |*src| is assigned to
* |*dest|. This includes the case that :member:`src->len
* <sf_vec.len>` == 0.
*
* This function sets the length of unescaped string to
* :member:`dest->len <sf_vec.len>`.
*/
void sf_unescape(sf_vec *dest, const sf_vec *src);
/**
* @function
*
* `sf_base64decode` decodes Base64 encoded string |src| and writes
* the result into |dest|. |src| should be the pointer to
* :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ`
* produced by either `sf_parser_dict`, `sf_parser_list`,
* `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`,
* otherwise the behavior is undefined.
*
* :member:`dest->base <sf_vec.base>` must point to the buffer that
* has sufficient space to store the decoded byte string.
*
* If :member:`src->len <sf_vec.len>` == 0, |*src| is assigned to
* |*dest|.
*
* This function sets the length of decoded byte string to
* :member:`dest->len <sf_vec.len>`.
*/
void sf_base64decode(sf_vec *dest, const sf_vec *src);
#ifdef __cplusplus
}
#endif
#endif /* SFPARSE_H */

View File

@ -11,4 +11,13 @@ git submodule update --init
autoreconf -i
./configure --with-mruby && \
make dist-bzip2 && make dist-gzip && make dist-xz || echo "error"
rm -f checksums.txt
VERSION=`echo -n $TAG | sed -E 's|^v([0-9]+\.[0-9]+\.[0-9]+)(-DEV)?$|\1|'`
for f in nghttp2-$VERSION.tar.bz2 nghttp2-$VERSION.tar.gz nghttp2-$VERSION.tar.xz; do
sha256sum $f >> checksums.txt
gpg --armor --detach-sign $f
done
make distclean

View File

@ -15,7 +15,7 @@ include_directories(
${LIBEV_INCLUDE_DIRS}
${LIBNGHTTP3_INCLUDE_DIRS}
${LIBNGTCP2_INCLUDE_DIRS}
${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
${LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
${LIBCARES_INCLUDE_DIRS}
${JANSSON_INCLUDE_DIRS}
@ -31,7 +31,7 @@ link_libraries(
${LIBEV_LIBRARIES}
${LIBNGHTTP3_LIBRARIES}
${LIBNGTCP2_LIBRARIES}
${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}
${LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES}
${OPENSSL_LIBRARIES}
${LIBCARES_LIBRARIES}
${JANSSON_LIBRARIES}

View File

@ -72,6 +72,8 @@
# define O_BINARY (0)
#endif // O_BINARY
using namespace std::chrono_literals;
namespace nghttp2 {
namespace {
@ -194,7 +196,7 @@ void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents);
} // namespace
namespace {
constexpr ev_tstamp FILE_ENTRY_MAX_AGE = 10.;
constexpr auto FILE_ENTRY_MAX_AGE = 10s;
} // namespace
namespace {
@ -202,13 +204,15 @@ constexpr size_t FILE_ENTRY_EVICT_THRES = 2048;
} // namespace
namespace {
bool need_validation_file_entry(const FileEntry *ent, ev_tstamp now) {
bool need_validation_file_entry(
const FileEntry *ent, const std::chrono::steady_clock::time_point &now) {
return ent->last_valid + FILE_ENTRY_MAX_AGE < now;
}
} // namespace
namespace {
bool validate_file_entry(FileEntry *ent, ev_tstamp now) {
bool validate_file_entry(FileEntry *ent,
const std::chrono::steady_clock::time_point &now) {
struct stat stbuf;
int rv;
@ -335,7 +339,7 @@ public:
return nullptr;
}
auto now = ev_now(loop_);
auto now = std::chrono::steady_clock::now();
for (auto it = range.first; it != range.second;) {
auto &ent = (*it).second;
@ -1197,7 +1201,7 @@ bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
// now. We will update it when we get whole request body.
auto path = std::string("echo:") + tempfn;
stream->file_ent =
sessions->cache_fd(path, FileEntry(path, 0, 0, fd, nullptr, 0, true));
sessions->cache_fd(path, FileEntry(path, 0, 0, fd, nullptr, {}, true));
stream->echo_upload = true;
return true;
}
@ -1368,7 +1372,7 @@ void prepare_response(Stream *stream, Http2Handler *hd,
file_ent = sessions->cache_fd(
file_path, FileEntry(file_path, buf.st_size, buf.st_mtime, file,
content_type, ev_now(sessions->get_loop())));
content_type, std::chrono::steady_clock::now()));
}
stream->file_ent = file_ent;
@ -1891,7 +1895,7 @@ class ListenEventHandler {
public:
ListenEventHandler(Sessions *sessions, int fd,
std::shared_ptr<AcceptHandler> acceptor)
: acceptor_(acceptor), sessions_(sessions), fd_(fd) {
: acceptor_(std::move(acceptor)), sessions_(sessions), fd_(fd) {
ev_io_init(&w_, acceptcb, fd, EV_READ);
w_.data = this;
ev_io_start(sessions_->get_loop(), &w_);
@ -1969,7 +1973,7 @@ FileEntry make_status_body(int status, uint16_t port) {
assert(0);
}
return FileEntry(util::utos(status), nwrite, 0, fd, nullptr, 0);
return FileEntry(util::utos(status), nwrite, 0, fd, nullptr, {});
}
} // namespace

View File

@ -92,7 +92,8 @@ class Http2Handler;
struct FileEntry {
FileEntry(std::string path, int64_t length, int64_t mtime, int fd,
const std::string *content_type, ev_tstamp last_valid,
const std::string *content_type,
const std::chrono::steady_clock::time_point &last_valid,
bool stale = false)
: path(std::move(path)),
length(length),
@ -108,7 +109,7 @@ struct FileEntry {
std::multimap<std::string, std::unique_ptr<FileEntry>>::iterator it;
int64_t length;
int64_t mtime;
ev_tstamp last_valid;
std::chrono::steady_clock::time_point last_valid;
const std::string *content_type;
FileEntry *dlnext, *dlprev;
int fd;

View File

@ -20,6 +20,8 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = testdata
EXTRA_DIST = \
CMakeLists.txt \
test.example.com.pem \
@ -43,7 +45,7 @@ AM_CPPFLAGS = \
@LIBXML2_CFLAGS@ \
@LIBEV_CFLAGS@ \
@LIBNGHTTP3_CFLAGS@ \
@LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ \
@LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ \
@LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ \
@LIBNGTCP2_CFLAGS@ \
@OPENSSL_CFLAGS@ \
@ -62,7 +64,7 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
@LIBXML2_LIBS@ \
@LIBEV_LIBS@ \
@LIBNGHTTP3_LIBS@ \
@LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ \
@LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ \
@LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ \
@LIBNGTCP2_LIBS@ \
@OPENSSL_LIBS@ \

View File

@ -480,15 +480,10 @@ std::chrono::steady_clock::time_point get_time() {
ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
size_t inlen) {
int rv;
z_stream zst;
z_stream zst{};
uint8_t temp_out[8_k];
auto temp_outlen = sizeof(temp_out);
zst.next_in = Z_NULL;
zst.zalloc = Z_NULL;
zst.zfree = Z_NULL;
zst.opaque = Z_NULL;
rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9,
Z_DEFAULT_STRATEGY);

View File

@ -51,9 +51,9 @@
#include <openssl/err.h>
#ifdef ENABLE_HTTP3
# ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# include <ngtcp2/ngtcp2_crypto_openssl.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
# include <ngtcp2/ngtcp2_crypto_quictls.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
# ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
# include <ngtcp2/ngtcp2_crypto_boringssl.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
@ -344,11 +344,13 @@ void rps_cb(struct ev_loop *loop, ev_timer *w, int revents) {
return;
}
auto now = ev_now(loop);
auto now = std::chrono::steady_clock::now();
auto d = now - client->rps_duration_started;
auto n = static_cast<size_t>(round(d * config.rps));
auto n = static_cast<size_t>(
round(std::chrono::duration<double>(d).count() * config.rps));
client->rps_req_pending += n;
client->rps_duration_started = now - d + static_cast<double>(n) / config.rps;
client->rps_duration_started +=
util::duration_from(static_cast<double>(n) / config.rps);
if (client->rps_req_pending == 0) {
return;
@ -425,10 +427,10 @@ void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
return;
}
ev_tstamp duration =
auto duration =
config.timings[client->reqidx] - config.timings[client->reqidx - 1];
while (duration < 1e-9) {
while (duration < std::chrono::duration<double>(1e-9)) {
if (client->submit_request() != 0) {
ev_timer_stop(client->worker->loop, w);
client->process_request_failure();
@ -443,7 +445,7 @@ void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
config.timings[client->reqidx] - config.timings[client->reqidx - 1];
}
client->request_timeout_watcher.repeat = duration;
client->request_timeout_watcher.repeat = util::ev_tstamp_from(duration);
ev_timer_again(client->worker->loop, &client->request_timeout_watcher);
}
} // namespace
@ -470,7 +472,6 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
local_addr{},
new_connection_requested(false),
final(false),
rps_duration_started(0),
rps_req_pending(0),
rps_req_inflight(0) {
if (req_todo == 0) { // this means infinite number of requests are to be made
@ -506,7 +507,7 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
quic.tx.data = std::make_unique<uint8_t[]>(64_k);
}
ngtcp2_connection_close_error_default(&quic.last_error);
ngtcp2_ccerr_default(&quic.last_error);
#endif // ENABLE_HTTP3
}
@ -540,6 +541,14 @@ int Client::make_socket(addrinfo *addr) {
return -1;
}
# ifdef UDP_GRO
int val = 1;
if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)) != 0) {
std::cerr << "setsockopt UDP_GRO failed" << std::endl;
return -1;
}
# endif // UDP_GRO
rv = util::bind_any_addr_udp(fd, addr->ai_family);
if (rv != 0) {
close(fd);
@ -1178,7 +1187,7 @@ int Client::connection_made() {
if (config.rps_enabled()) {
rps_watcher.repeat = std::max(0.01, 1. / config.rps);
ev_timer_again(worker->loop, &rps_watcher);
rps_duration_started = ev_now(worker->loop);
rps_duration_started = std::chrono::steady_clock::now();
}
if (config.rps_enabled()) {
@ -1202,9 +1211,9 @@ int Client::connection_made() {
}
} else {
ev_tstamp duration = config.timings[reqidx];
auto duration = config.timings[reqidx];
while (duration < 1e-9) {
while (duration < std::chrono::duration<double>(1e-9)) {
if (submit_request() != 0) {
process_request_failure();
break;
@ -1217,10 +1226,10 @@ int Client::connection_made() {
}
}
if (duration >= 1e-9) {
if (duration >= std::chrono::duration<double>(1e-9)) {
// double check since we may have break due to reqidx wraps
// around back to 0
request_timeout_watcher.repeat = duration;
request_timeout_watcher.repeat = util::ev_tstamp_from(duration);
ev_timer_again(worker->loop, &request_timeout_watcher);
}
}
@ -1971,9 +1980,10 @@ std::vector<std::string> read_uri_from_file(std::istream &infile) {
} // namespace
namespace {
void read_script_from_file(std::istream &infile,
std::vector<ev_tstamp> &timings,
std::vector<std::string> &uris) {
void read_script_from_file(
std::istream &infile,
std::vector<std::chrono::steady_clock::duration> &timings,
std::vector<std::string> &uris) {
std::string script_line;
int line_count = 0;
while (std::getline(infile, script_line)) {
@ -2006,7 +2016,9 @@ void read_script_from_file(std::istream &infile,
exit(EXIT_FAILURE);
}
timings.push_back(v / 1000.0);
timings.emplace_back(
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::duration<double, std::milli>(v)));
uris.push_back(script_line.substr(pos + 1, script_line.size()));
}
}
@ -2915,13 +2927,13 @@ int main(int argc, char **argv) {
if (config.is_quic()) {
#ifdef ENABLE_HTTP3
# ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
if (ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
std::cerr << "ngtcp2_crypto_openssl_configure_client_context failed"
# ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
if (ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
std::cerr << "ngtcp2_crypto_quictls_configure_client_context failed"
<< std::endl;
exit(EXIT_FAILURE);
}
# endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
# ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
if (ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
std::cerr << "ngtcp2_crypto_boringssl_configure_client_context failed"

View File

@ -73,7 +73,7 @@ struct Worker;
struct Config {
std::vector<std::vector<nghttp2_nv>> nva;
std::vector<std::string> h1reqs;
std::vector<ev_tstamp> timings;
std::vector<std::chrono::steady_clock::duration> timings;
nghttp2::Headers custom_headers;
std::string scheme;
std::string host;
@ -342,7 +342,7 @@ struct Client {
ngtcp2_crypto_conn_ref conn_ref;
ev_timer pkt_timer;
ngtcp2_conn *conn;
ngtcp2_connection_close_error last_error;
ngtcp2_ccerr last_error;
bool close_requested;
FILE *qlog_file;
@ -396,7 +396,7 @@ struct Client {
ev_timer rps_watcher;
// The timestamp that starts the period which contributes to the
// next request generation.
ev_tstamp rps_duration_started;
std::chrono::steady_clock::time_point rps_duration_started;
// The number of requests allowed by rps, but limited by stream
// concurrency.
size_t rps_req_pending;
@ -494,8 +494,9 @@ struct Client {
int quic_stream_reset(int64_t stream_id, uint64_t app_error_code);
int quic_stream_stop_sending(int64_t stream_id, uint64_t app_error_code);
int quic_extend_max_local_streams();
int quic_extend_max_stream_data(int64_t stream_id);
int quic_write_client_handshake(ngtcp2_crypto_level level,
int quic_write_client_handshake(ngtcp2_encryption_level level,
const uint8_t *data, size_t datalen);
int quic_pkt_timeout();
void quic_restart_pkt_timer();

View File

@ -224,7 +224,7 @@ int stop_sending(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
} // namespace
int Http3Session::stop_sending(int64_t stream_id, uint64_t app_error_code) {
auto rv = ngtcp2_conn_shutdown_stream_read(client_->quic.conn, stream_id,
auto rv = ngtcp2_conn_shutdown_stream_read(client_->quic.conn, 0, stream_id,
app_error_code);
if (rv != 0) {
std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv)
@ -246,7 +246,7 @@ int reset_stream(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
} // namespace
int Http3Session::reset_stream(int64_t stream_id, uint64_t app_error_code) {
auto rv = ngtcp2_conn_shutdown_stream_write(client_->quic.conn, stream_id,
auto rv = ngtcp2_conn_shutdown_stream_write(client_->quic.conn, 0, stream_id,
app_error_code);
if (rv != 0) {
std::cerr << "ngtcp2_conn_shutdown_stream_write: " << ngtcp2_strerror(rv)
@ -305,7 +305,7 @@ int Http3Session::init_conn() {
assert(conn_ == nullptr);
if (ngtcp2_conn_get_max_local_streams_uni(client_->quic.conn) < 3) {
if (ngtcp2_conn_get_streams_uni_left(client_->quic.conn) < 3) {
return -1;
}
@ -395,7 +395,7 @@ ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id,
if (nconsumed < 0) {
std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
<< std::endl;
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&client_->quic.last_error,
nghttp3_err_infer_quic_app_error_code(nconsumed), nullptr, 0);
return -1;
@ -408,7 +408,7 @@ ssize_t Http3Session::write_stream(int64_t &stream_id, int &fin,
auto sveccnt =
nghttp3_conn_writev_stream(conn_, &stream_id, &fin, vec, veccnt);
if (sveccnt < 0) {
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&client_->quic.last_error,
nghttp3_err_infer_quic_app_error_code(sveccnt), nullptr, 0);
return -1;
@ -420,6 +420,14 @@ void Http3Session::block_stream(int64_t stream_id) {
nghttp3_conn_block_stream(conn_, stream_id);
}
int Http3Session::unblock_stream(int64_t stream_id) {
if (nghttp3_conn_unblock_stream(conn_, stream_id) != 0) {
return -1;
}
return 0;
}
void Http3Session::shutdown_stream_write(int64_t stream_id) {
nghttp3_conn_shutdown_stream_write(conn_, stream_id);
}
@ -427,7 +435,7 @@ void Http3Session::shutdown_stream_write(int64_t stream_id) {
int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen);
if (rv != 0) {
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(rv),
nullptr, 0);
return -1;
@ -438,7 +446,7 @@ int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
int Http3Session::add_ack_offset(int64_t stream_id, size_t datalen) {
auto rv = nghttp3_conn_add_ack_offset(conn_, stream_id, datalen);
if (rv != 0) {
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(rv),
nullptr, 0);
return -1;

View File

@ -64,6 +64,7 @@ public:
ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
size_t veccnt);
void block_stream(int64_t stream_id);
int unblock_stream(int64_t stream_id);
void shutdown_stream_write(int64_t stream_id);
int add_write_offset(int64_t stream_id, size_t ndatalen);
int add_ack_offset(int64_t stream_id, size_t datalen);

View File

@ -28,9 +28,9 @@
#include <iostream>
#ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# include <ngtcp2/ngtcp2_crypto_openssl.h>
#endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
#ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
# include <ngtcp2/ngtcp2_crypto_quictls.h>
#endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
#ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
# include <ngtcp2/ngtcp2_crypto_boringssl.h>
#endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
@ -196,6 +196,28 @@ int Client::quic_extend_max_local_streams() {
return 0;
}
namespace {
int extend_max_stream_data(ngtcp2_conn *conn, int64_t stream_id,
uint64_t max_data, void *user_data,
void *stream_user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_extend_max_stream_data(stream_id) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::quic_extend_max_stream_data(int64_t stream_id) {
auto s = static_cast<Http3Session *>(session.get());
if (s->unblock_stream(stream_id) != 0) {
return -1;
}
return 0;
}
namespace {
int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token,
size_t cidlen, void *user_data) {
@ -238,8 +260,10 @@ int generate_cid(ngtcp2_cid &dest) {
} // namespace
namespace {
ngtcp2_tstamp timestamp(struct ev_loop *loop) {
return ev_now(loop) * NGTCP2_SECONDS;
ngtcp2_tstamp quic_timestamp() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
} // namespace
@ -265,8 +289,9 @@ void rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx) {
} // namespace
namespace {
int recv_rx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level, void *user_data) {
if (level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
int recv_rx_key(ngtcp2_conn *conn, ngtcp2_encryption_level level,
void *user_data) {
if (level != NGTCP2_ENCRYPTION_LEVEL_1RTT) {
return 0;
}
@ -338,7 +363,7 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
h2load::stream_reset,
nullptr, // extend_max_remote_streams_bidi
nullptr, // extend_max_remote_streams_uni
nullptr, // extend_max_stream_data
h2load::extend_max_stream_data,
nullptr, // dcid_status
nullptr, // handshake_confirmed
nullptr, // recv_new_token
@ -369,7 +394,7 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
if (config->verbose) {
settings.log_printf = debug_log_printf;
}
settings.initial_ts = timestamp(worker->loop);
settings.initial_ts = quic_timestamp();
settings.rand_ctx.native_handle = &worker->randgen;
if (!config->qlog_file_base.empty()) {
assert(quic.qlog_file == nullptr);
@ -384,7 +409,7 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
std::cerr << "Failed to open a qlog file: " << path << std::endl;
return -1;
}
settings.qlog.write = qlog_write_cb;
settings.qlog_write = qlog_write_cb;
}
if (config->max_udp_payload_size) {
settings.max_tx_udp_payload_size = config->max_udp_payload_size;
@ -453,7 +478,7 @@ void Client::quic_close_connection() {
auto nwrite = ngtcp2_conn_write_connection_close(
quic.conn, &ps.path, nullptr, buf.data(), buf.size(), &quic.last_error,
timestamp(worker->loop));
quic_timestamp());
if (nwrite <= 0) {
return;
@ -463,7 +488,7 @@ void Client::quic_close_connection() {
ps.path.remote.addrlen, buf.data(), nwrite, 0);
}
int Client::quic_write_client_handshake(ngtcp2_crypto_level level,
int Client::quic_write_client_handshake(ngtcp2_encryption_level level,
const uint8_t *data, size_t datalen) {
int rv;
@ -492,12 +517,11 @@ void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
int Client::quic_pkt_timeout() {
int rv;
auto now = timestamp(worker->loop);
auto now = quic_timestamp();
rv = ngtcp2_conn_handle_expiry(quic.conn, now);
if (rv != 0) {
ngtcp2_connection_close_error_set_transport_error_liberr(&quic.last_error,
rv, nullptr, 0);
ngtcp2_ccerr_set_liberr(&quic.last_error, rv, nullptr, 0);
return -1;
}
@ -506,7 +530,7 @@ int Client::quic_pkt_timeout() {
void Client::quic_restart_pkt_timer() {
auto expiry = ngtcp2_conn_get_expiry(quic.conn);
auto now = timestamp(worker->loop);
auto now = quic_timestamp();
auto t = expiry > now ? static_cast<ev_tstamp>(expiry - now) / NGTCP2_SECONDS
: 1e-9;
quic.pkt_timer.repeat = t;
@ -514,20 +538,40 @@ void Client::quic_restart_pkt_timer() {
}
int Client::read_quic() {
std::array<uint8_t, 65536> buf;
std::array<uint8_t, 65535> buf;
sockaddr_union su;
socklen_t addrlen = sizeof(su);
int rv;
size_t pktcnt = 0;
ngtcp2_pkt_info pi{};
iovec msg_iov;
msg_iov.iov_base = buf.data();
msg_iov.iov_len = buf.size();
msghdr msg{};
msg.msg_name = &su;
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint16_t))];
msg.msg_control = msg_ctrl;
auto ts = quic_timestamp();
for (;;) {
auto nread =
recvfrom(fd, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
msg.msg_namelen = sizeof(su);
msg.msg_controllen = sizeof(msg_ctrl);
auto nread = recvmsg(fd, &msg, 0);
if (nread == -1) {
return 0;
}
auto gso_size = util::msghdr_get_udp_gro(&msg);
if (gso_size == 0) {
gso_size = static_cast<size_t>(nread);
}
assert(quic.conn);
++worker->stats.udp_dgram_recv;
@ -539,30 +583,41 @@ int Client::read_quic() {
},
{
&su.sa,
addrlen,
msg.msg_namelen,
},
};
rv = ngtcp2_conn_read_pkt(quic.conn, &path, &pi, buf.data(), nread,
timestamp(worker->loop));
if (rv != 0) {
std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
auto data = buf.data();
if (!quic.last_error.error_code) {
if (rv == NGTCP2_ERR_CRYPTO) {
ngtcp2_connection_close_error_set_transport_error_tls_alert(
&quic.last_error, ngtcp2_conn_get_tls_alert(quic.conn), nullptr,
0);
} else {
ngtcp2_connection_close_error_set_transport_error_liberr(
&quic.last_error, rv, nullptr, 0);
for (;;) {
auto datalen = std::min(static_cast<size_t>(nread), gso_size);
++pktcnt;
rv = ngtcp2_conn_read_pkt(quic.conn, &path, &pi, data, datalen, ts);
if (rv != 0) {
if (!quic.last_error.error_code) {
if (rv == NGTCP2_ERR_CRYPTO) {
ngtcp2_ccerr_set_tls_alert(&quic.last_error,
ngtcp2_conn_get_tls_alert(quic.conn),
nullptr, 0);
} else {
ngtcp2_ccerr_set_liberr(&quic.last_error, rv, nullptr, 0);
}
}
return -1;
}
return -1;
nread -= datalen;
if (nread == 0) {
break;
}
data += datalen;
}
if (++pktcnt == 100) {
if (pktcnt >= 100) {
break;
}
}
@ -607,6 +662,7 @@ int Client::write_quic() {
ngtcp2_path_storage_zero(&ps);
auto s = static_cast<Http3Session *>(session.get());
auto ts = quic_timestamp();
for (;;) {
int64_t stream_id = -1;
@ -631,8 +687,7 @@ int Client::write_quic() {
auto nwrite = ngtcp2_conn_writev_stream(
quic.conn, &ps.path, nullptr, bufpos, max_udp_payload_size, &ndatalen,
flags, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt,
timestamp(worker->loop));
flags, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, ts);
if (nwrite < 0) {
switch (nwrite) {
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
@ -651,8 +706,7 @@ int Client::write_quic() {
continue;
}
ngtcp2_connection_close_error_set_transport_error_liberr(
&quic.last_error, nwrite, nullptr, 0);
ngtcp2_ccerr_set_liberr(&quic.last_error, nwrite, nullptr, 0);
return -1;
} else if (ndatalen >= 0 && s->add_write_offset(stream_id, ndatalen) != 0) {
return -1;

View File

@ -451,10 +451,9 @@ constexpr llhttp_settings_t htp_hooks = {
namespace {
int submit_request(HttpClient *client, const Headers &headers, Request *req) {
auto path = req->make_reqpath();
auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA);
auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"},
{":path", path},
{":path", req->make_reqpath()},
{":scheme", scheme.str()},
{":authority", client->hostport},
{"accept", "*/*"},

View File

@ -28,16 +28,10 @@
int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr) {
int rv;
*inflater_ptr = malloc(sizeof(nghttp2_gzip));
*inflater_ptr = calloc(1, sizeof(nghttp2_gzip));
if (*inflater_ptr == NULL) {
return -1;
}
(*inflater_ptr)->finished = 0;
(*inflater_ptr)->zst.next_in = Z_NULL;
(*inflater_ptr)->zst.avail_in = 0;
(*inflater_ptr)->zst.zalloc = Z_NULL;
(*inflater_ptr)->zst.zfree = Z_NULL;
(*inflater_ptr)->zst.opaque = Z_NULL;
rv = inflateInit2(&(*inflater_ptr)->zst, 47);
if (rv != Z_OK) {
free(*inflater_ptr);

View File

@ -36,11 +36,7 @@
static size_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
size_t inlen) {
int rv;
z_stream zst;
zst.next_in = Z_NULL;
zst.zalloc = Z_NULL;
zst.zfree = Z_NULL;
zst.opaque = Z_NULL;
z_stream zst = {0};
rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION);
CU_ASSERT(rv == Z_OK);

View File

@ -77,6 +77,10 @@ int main(int argc, char *argv[]) {
shrpx::test_shrpx_tls_cert_lookup_tree_add_ssl_ctx) ||
!CU_add_test(pSuite, "tls_tls_hostname_match",
shrpx::test_shrpx_tls_tls_hostname_match) ||
!CU_add_test(pSuite, "tls_tls_verify_numeric_hostname",
shrpx::test_shrpx_tls_verify_numeric_hostname) ||
!CU_add_test(pSuite, "tls_tls_verify_dns_hostname",
shrpx::test_shrpx_tls_verify_dns_hostname) ||
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
!CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) ||
!CU_add_test(pSuite, "http2_copy_headers_to_nva",

View File

@ -210,31 +210,13 @@ struct WorkerProcess {
)
: loop(loop),
worker_pid(worker_pid),
ipc_fd(ipc_fd),
termination_deadline(0.)
ipc_fd(ipc_fd)
#ifdef ENABLE_HTTP3
,
quic_ipc_fd(quic_ipc_fd),
cid_prefixes(cid_prefixes)
#endif // ENABLE_HTTP3
{
ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
reopen_log_signalev.data = this;
ev_signal_start(loop, &reopen_log_signalev);
ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
exec_binary_signalev.data = this;
ev_signal_start(loop, &exec_binary_signalev);
ev_signal_init(&graceful_shutdown_signalev, signal_cb,
GRACEFUL_SHUTDOWN_SIGNAL);
graceful_shutdown_signalev.data = this;
ev_signal_start(loop, &graceful_shutdown_signalev);
ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
reload_signalev.data = this;
ev_signal_start(loop, &reload_signalev);
ev_child_init(&worker_process_childev, worker_process_child_cb, worker_pid,
0);
worker_process_childev.data = this;
@ -242,8 +224,6 @@ struct WorkerProcess {
}
~WorkerProcess() {
shutdown_signal_watchers();
ev_child_stop(loop, &worker_process_childev);
#ifdef ENABLE_HTTP3
@ -258,22 +238,11 @@ struct WorkerProcess {
}
}
void shutdown_signal_watchers() {
ev_signal_stop(loop, &reopen_log_signalev);
ev_signal_stop(loop, &exec_binary_signalev);
ev_signal_stop(loop, &graceful_shutdown_signalev);
ev_signal_stop(loop, &reload_signalev);
}
ev_signal reopen_log_signalev;
ev_signal exec_binary_signalev;
ev_signal graceful_shutdown_signalev;
ev_signal reload_signalev;
ev_child worker_process_childev;
struct ev_loop *loop;
pid_t worker_pid;
int ipc_fd;
ev_tstamp termination_deadline;
std::chrono::steady_clock::time_point termination_deadline;
#ifdef ENABLE_HTTP3
int quic_ipc_fd;
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
@ -281,7 +250,7 @@ struct WorkerProcess {
};
namespace {
void reload_config(WorkerProcess *wp);
void reload_config();
} // namespace
namespace {
@ -295,21 +264,22 @@ ev_timer worker_process_grace_period_timer;
namespace {
void worker_process_grace_period_timercb(struct ev_loop *loop, ev_timer *w,
int revents) {
auto now = ev_now(loop);
ev_tstamp next_repeat = 0.;
auto now = std::chrono::steady_clock::now();
auto next_repeat = std::chrono::steady_clock::duration::zero();
for (auto it = std::begin(worker_processes);
it != std::end(worker_processes);) {
auto &wp = *it;
if (!(wp->termination_deadline > 0.)) {
if (wp->termination_deadline.time_since_epoch().count() == 0) {
++it;
continue;
}
auto d = wp->termination_deadline - now;
if (d > 0) {
if (!(next_repeat > 0.) || d < next_repeat) {
if (d.count() > 0) {
if (next_repeat == std::chrono::steady_clock::duration::zero() ||
d < next_repeat) {
next_repeat = d;
}
@ -324,8 +294,8 @@ void worker_process_grace_period_timercb(struct ev_loop *loop, ev_timer *w,
it = worker_processes.erase(it);
}
if (next_repeat > 0.) {
w->repeat = next_repeat;
if (next_repeat.count() > 0) {
w->repeat = util::ev_tstamp_from(next_repeat);
ev_timer_again(loop, w);
return;
@ -345,7 +315,8 @@ void worker_process_set_termination_deadline(WorkerProcess *wp,
}
wp->termination_deadline =
ev_now(loop) + config->worker_process_grace_shutdown_period;
std::chrono::steady_clock::now() +
util::duration_from(config->worker_process_grace_shutdown_period);
if (!ev_is_active(&worker_process_grace_period_timer)) {
worker_process_grace_period_timer.repeat =
@ -416,18 +387,6 @@ void worker_process_kill(int signum, struct ev_loop *loop) {
}
} // namespace
namespace {
// Returns the last PID of worker process. Returns -1 if there is no
// worker process at the moment.
int worker_process_last_pid() {
if (worker_processes.empty()) {
return -1;
}
return worker_processes.back()->worker_pid;
}
} // namespace
namespace {
int save_pid() {
std::array<char, STRERROR_BUFSIZE> errbuf;
@ -712,15 +671,12 @@ void reopen_log(WorkerProcess *wp) {
namespace {
void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
auto wp = static_cast<WorkerProcess *>(w->data);
if (wp->worker_pid == -1) {
ev_break(loop);
return;
}
switch (w->signum) {
case REOPEN_LOG_SIGNAL:
reopen_log(wp);
for (auto &wp : worker_processes) {
reopen_log(wp.get());
}
return;
case EXEC_BINARY_SIGNAL:
exec_binary();
@ -730,12 +686,17 @@ void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
for (auto &addr : listenerconf.addrs) {
close(addr.fd);
}
ipc_send(wp, SHRPX_IPC_GRACEFUL_SHUTDOWN);
worker_process_set_termination_deadline(wp, loop);
for (auto &wp : worker_processes) {
ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
worker_process_set_termination_deadline(wp.get(), loop);
}
return;
}
case RELOAD_SIGNAL:
reload_config(wp);
reload_config();
return;
default:
worker_process_kill(w->signum, loop);
@ -751,11 +712,9 @@ void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
log_chld(w->rpid, w->rstatus, "Worker process");
auto pid = wp->worker_pid;
worker_process_remove(wp, loop);
if (worker_process_last_pid() == pid) {
if (worker_processes.empty()) {
ev_break(loop);
}
}
@ -1412,6 +1371,30 @@ int create_ipc_socket(std::array<int, 2> &ipc_fd) {
}
} // namespace
namespace {
int create_worker_process_ready_ipc_socket(std::array<int, 2> &ipc_fd) {
std::array<char, STRERROR_BUFSIZE> errbuf;
int rv;
rv = socketpair(AF_UNIX, SOCK_DGRAM, 0, ipc_fd.data());
if (rv == -1) {
auto error = errno;
LOG(WARN) << "Failed to create socket pair to communicate worker process "
"readiness: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
return -1;
}
for (auto fd : ipc_fd) {
util::make_socket_closeonexec(fd);
}
util::make_socket_nonblocking(ipc_fd[0]);
return 0;
}
} // namespace
#ifdef ENABLE_HTTP3
namespace {
int create_quic_ipc_socket(std::array<int, 2> &quic_ipc_fd) {
@ -1483,6 +1466,130 @@ collect_quic_lingering_worker_processes() {
} // namespace
#endif // ENABLE_HTTP3
namespace {
ev_signal reopen_log_signalev;
ev_signal exec_binary_signalev;
ev_signal graceful_shutdown_signalev;
ev_signal reload_signalev;
} // namespace
namespace {
void start_signal_watchers(struct ev_loop *loop) {
ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
ev_signal_start(loop, &reopen_log_signalev);
ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
ev_signal_start(loop, &exec_binary_signalev);
ev_signal_init(&graceful_shutdown_signalev, signal_cb,
GRACEFUL_SHUTDOWN_SIGNAL);
ev_signal_start(loop, &graceful_shutdown_signalev);
ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
ev_signal_start(loop, &reload_signalev);
}
} // namespace
namespace {
void shutdown_signal_watchers(struct ev_loop *loop) {
ev_signal_stop(loop, &reload_signalev);
ev_signal_stop(loop, &graceful_shutdown_signalev);
ev_signal_stop(loop, &exec_binary_signalev);
ev_signal_stop(loop, &reopen_log_signalev);
}
} // namespace
namespace {
// A pair of connected socket with which a worker process tells main
// process that it is ready for service. A worker process writes its
// PID to worker_process_ready_ipc_fd[1] and main process reads it
// from worker_process_ready_ipc_fd[0].
std::array<int, 2> worker_process_ready_ipc_fd;
} // namespace
namespace {
ev_io worker_process_ready_ipcev;
} // namespace
namespace {
// PID received via NGHTTPX_ORIG_PID environment variable.
pid_t orig_pid = -1;
} // namespace
namespace {
void worker_process_ready_ipc_readcb(struct ev_loop *loop, ev_io *w,
int revents) {
std::array<uint8_t, 8> buf;
ssize_t nread;
while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR)
;
if (nread == -1) {
std::array<char, STRERROR_BUFSIZE> errbuf;
auto error = errno;
LOG(ERROR) << "Failed to read data from worker process ready IPC channel: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
return;
}
if (nread == 0) {
return;
}
if (nread != sizeof(pid_t)) {
LOG(ERROR) << "Read " << nread
<< " bytes from worker process ready IPC channel";
return;
}
pid_t pid;
memcpy(&pid, buf.data(), sizeof(pid));
LOG(NOTICE) << "Worker process pid=" << pid << " is ready";
for (auto &wp : worker_processes) {
// Send graceful shutdown signal to all worker processes prior to
// pid.
if (wp->worker_pid == pid) {
break;
}
LOG(INFO) << "Sending graceful shutdown event to worker process pid="
<< wp->worker_pid;
ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
worker_process_set_termination_deadline(wp.get(), loop);
}
if (orig_pid != -1) {
LOG(NOTICE) << "Send QUIT signal to the original main process to tell "
"that we are ready to serve requests.";
kill(orig_pid, SIGQUIT);
orig_pid = -1;
}
}
} // namespace
namespace {
void start_worker_process_ready_ipc_watcher(struct ev_loop *loop) {
ev_io_init(&worker_process_ready_ipcev, worker_process_ready_ipc_readcb,
worker_process_ready_ipc_fd[0], EV_READ);
ev_io_start(loop, &worker_process_ready_ipcev);
}
} // namespace
namespace {
void shutdown_worker_process_ready_ipc_watcher(struct ev_loop *loop) {
ev_io_stop(loop, &worker_process_ready_ipcev);
}
} // namespace
namespace {
// Creates worker process, and returns PID of worker process. On
// success, file descriptor for IPC (send only) is assigned to
@ -1545,6 +1652,9 @@ pid_t fork_worker_process(
}
if (pid == 0) {
// We are in new process now, update pid for logger.
log_config()->pid = getpid();
ev_loop_fork(EV_DEFAULT);
for (auto &addr : config->conn.listener.addrs) {
@ -1565,6 +1675,13 @@ pid_t fork_worker_process(
}
#endif // ENABLE_HTTP3
close(worker_process_ready_ipc_fd[0]);
shutdown_worker_process_ready_ipc_watcher(EV_DEFAULT);
if (!config->single_process) {
shutdown_signal_watchers(EV_DEFAULT);
}
// Remove all WorkerProcesses to stop any registered watcher on
// default loop.
worker_process_remove_all(EV_DEFAULT);
@ -1595,6 +1712,7 @@ pid_t fork_worker_process(
WorkerProcessConfig wpconf{
.ipc_fd = ipc_fd[0],
.ready_ipc_fd = worker_process_ready_ipc_fd[1],
#ifdef ENABLE_HTTP3
.cid_prefixes = cid_prefixes,
.quic_ipc_fd = quic_ipc_fd[0],
@ -1702,7 +1820,7 @@ int event_loop() {
close_unused_inherited_addr(iaddrs);
}
auto orig_pid = get_orig_pid_from_env();
orig_pid = get_orig_pid_from_env();
#ifdef ENABLE_HTTP3
inherited_quic_lingering_worker_processes =
@ -1724,6 +1842,13 @@ int event_loop() {
}
#endif // ENABLE_HTTP3
if (!config->single_process) {
start_signal_watchers(loop);
}
create_worker_process_ready_ipc_socket(worker_process_ready_ipc_fd);
start_worker_process_ready_ipc_watcher(loop);
auto pid = fork_worker_process(ipc_fd
#ifdef ENABLE_HTTP3
,
@ -1759,19 +1884,19 @@ int event_loop() {
save_pid();
}
// ready to serve requests
shrpx_sd_notifyf(0, "READY=1");
if (orig_pid != -1) {
LOG(NOTICE) << "Send QUIT signal to the original main process to tell "
"that we are ready to serve requests.";
kill(orig_pid, SIGQUIT);
}
ev_run(loop, 0);
ev_timer_stop(loop, &worker_process_grace_period_timer);
shutdown_worker_process_ready_ipc_watcher(loop);
// config is now stale if reload has happened.
if (!get_config()->single_process) {
shutdown_signal_watchers(loop);
}
return 0;
}
} // namespace
@ -3837,7 +3962,7 @@ void close_not_inherited_fd(Config *config,
} // namespace
namespace {
void reload_config(WorkerProcess *wp) {
void reload_config() {
int rv;
LOG(NOTICE) << "Reloading configuration";
@ -3916,13 +4041,6 @@ void reload_config(WorkerProcess *wp) {
close_unused_inherited_addr(iaddrs);
// Send last worker process a graceful shutdown notice
auto &last_wp = worker_processes.back();
ipc_send(last_wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
worker_process_set_termination_deadline(last_wp.get(), loop);
// We no longer use signals for this worker.
last_wp->shutdown_signal_watchers();
worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd
#ifdef ENABLE_HTTP3
,

View File

@ -43,8 +43,8 @@ ConnectBlocker::ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop,
std::function<void()> block_func,
std::function<void()> unblock_func)
: gen_(gen),
block_func_(block_func),
unblock_func_(unblock_func),
block_func_(std::move(block_func)),
unblock_func_(std::move(unblock_func)),
loop_(loop),
fail_count_(0),
offline_(false) {

View File

@ -41,6 +41,7 @@
#include "ssl_compat.h"
using namespace nghttp2;
using namespace std::chrono_literals;
namespace shrpx {
@ -72,9 +73,8 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
data(data),
fd(fd),
tls_dyn_rec_warmup_threshold(tls_dyn_rec_warmup_threshold),
tls_dyn_rec_idle_timeout(tls_dyn_rec_idle_timeout),
tls_dyn_rec_idle_timeout(util::duration_from(tls_dyn_rec_idle_timeout)),
proto(proto),
last_read(0.),
read_timeout(read_timeout) {
ev_io_init(&wev, writecb, fd, EV_WRITE);
@ -89,9 +89,6 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
wt.data = this;
rt.data = this;
// set 0. to double field explicitly just in case
tls.last_write_idle = 0.;
if (ssl) {
set_ssl(ssl);
}
@ -124,7 +121,7 @@ void Connection::disconnect() {
tls.wbuf.reset();
tls.rbuf.reset();
tls.last_write_idle = 0.;
tls.last_write_idle = {};
tls.warmup_writelen = 0;
tls.last_writelen = 0;
tls.last_readlen = 0;
@ -881,9 +878,9 @@ size_t Connection::get_tls_write_limit() {
return std::numeric_limits<ssize_t>::max();
}
auto t = ev_now(loop);
auto t = std::chrono::steady_clock::now();
if (tls.last_write_idle >= 0. &&
if (tls.last_write_idle.time_since_epoch().count() >= 0 &&
t - tls.last_write_idle > tls_dyn_rec_idle_timeout) {
// Time out, use small record size
tls.warmup_writelen = 0;
@ -904,8 +901,8 @@ void Connection::update_tls_warmup_writelen(size_t n) {
}
void Connection::start_tls_write_idle() {
if (tls.last_write_idle < 0.) {
tls.last_write_idle = ev_now(loop);
if (tls.last_write_idle.time_since_epoch().count() < 0) {
tls.last_write_idle = std::chrono::steady_clock::now();
}
}
@ -927,7 +924,7 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
tls.last_writelen = 0;
}
tls.last_write_idle = -1.;
tls.last_write_idle = std::chrono::steady_clock::time_point(-1s);
auto &tlsconf = get_config()->tls;
auto via_bio =
@ -1285,17 +1282,18 @@ void Connection::again_rt(ev_tstamp t) {
read_timeout = t;
rt.repeat = t;
ev_timer_again(loop, &rt);
last_read = ev_now(loop);
last_read = std::chrono::steady_clock::now();
}
void Connection::again_rt() {
rt.repeat = read_timeout;
ev_timer_again(loop, &rt);
last_read = ev_now(loop);
last_read = std::chrono::steady_clock::now();
}
bool Connection::expired_rt() {
auto delta = read_timeout - (ev_now(loop) - last_read);
auto delta = read_timeout - util::ev_tstamp_from(
std::chrono::steady_clock::now() - last_read);
if (delta < 1e-9) {
return true;
}

View File

@ -66,7 +66,7 @@ struct TLSConnection {
SSL_SESSION *cached_session;
MemcachedRequest *cached_session_lookup_req;
tls::TLSSessionCache *client_session_cache;
ev_tstamp last_write_idle;
std::chrono::steady_clock::time_point last_write_idle;
size_t warmup_writelen;
// length passed to SSL_write and SSL_read last time. This is
// required since these functions require the exact same parameters
@ -178,14 +178,14 @@ struct Connection {
void *data;
int fd;
size_t tls_dyn_rec_warmup_threshold;
ev_tstamp tls_dyn_rec_idle_timeout;
std::chrono::steady_clock::duration tls_dyn_rec_idle_timeout;
// Application protocol used over the connection. This field is not
// used in this object at the moment. The rest of the program may
// use this value when it is useful.
Proto proto;
// The point of time when last read is observed. Note: since we use
// |rt| as idle timer, the activity is not limited to read.
ev_tstamp last_read;
std::chrono::steady_clock::time_point last_read;
// Timeout for read timer |rt|.
ev_tstamp read_timeout;
};

View File

@ -107,8 +107,8 @@ struct SerialEvent {
# ifdef HAVE_LIBBPF
struct BPFRef {
bpf_object *obj;
int reuseport_array;
int cid_prefix_map;
bpf_map *reuseport_array;
bpf_map *cid_prefix_map;
};
# endif // HAVE_LIBBPF

View File

@ -71,7 +71,8 @@ ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
switch (status) {
case DNSResolverStatus::ERROR:
case DNSResolverStatus::OK:
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
ent.expiry = std::chrono::steady_clock::now() +
util::duration_from(dnsconf.timeout.cache);
break;
default:
break;
@ -92,7 +93,8 @@ void DNSTracker::update_entry(ResolverEntry &ent,
switch (status) {
case DNSResolverStatus::ERROR:
case DNSResolverStatus::OK:
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
ent.expiry = std::chrono::steady_clock::now() +
util::duration_from(dnsconf.timeout.cache);
break;
default:
break;
@ -175,7 +177,8 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
auto &ent = (*it).second;
if (ent.status != DNSResolverStatus::RUNNING && ent.expiry < ev_now(loop_)) {
if (ent.status != DNSResolverStatus::RUNNING &&
ent.expiry < std::chrono::steady_clock::now()) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "DNS entry found for " << dnsq->host
<< ", but it has been expired";
@ -252,9 +255,8 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
}
void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) {
auto loop = loop_;
ent.resolv->set_complete_cb(
[&ent, loop](DNSResolverStatus status, const Address *result) {
[&ent](DNSResolverStatus status, const Address *result) {
auto &qlist = ent.qlist;
while (!qlist.empty()) {
auto head = qlist.head;
@ -269,7 +271,8 @@ void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) {
ent.resolv.reset();
ent.status = status;
ent.expiry = ev_now(loop) + dnsconf.timeout.cache;
ent.expiry = std::chrono::steady_clock::now() +
util::duration_from(dnsconf.timeout.cache);
if (ent.status == DNSResolverStatus::OK) {
ent.result = *result;
}
@ -306,7 +309,7 @@ void DNSTracker::gc() {
LOG(INFO) << "Starting removing expired DNS cache entries";
}
auto now = ev_now(loop_);
auto now = std::chrono::steady_clock::now();
for (auto it = std::begin(ents_); it != std::end(ents_);) {
auto &ent = (*it).second;
if (ent.expiry >= now) {

View File

@ -28,6 +28,7 @@
#include "shrpx.h"
#include <map>
#include <chrono>
#include "shrpx_dual_dns_resolver.h"
@ -70,7 +71,7 @@ struct ResolverEntry {
// result and its expiry time
Address result;
// time point when cached result expires.
ev_tstamp expiry;
std::chrono::steady_clock::time_point expiry;
};
class DNSTracker {

View File

@ -2018,7 +2018,7 @@ int Http2Session::connected() {
}
int Http2Session::read_clear() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<uint8_t, 16_k> buf;
@ -2040,7 +2040,7 @@ int Http2Session::read_clear() {
}
int Http2Session::write_clear() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<struct iovec, MAX_WR_IOVCNT> iov;
@ -2081,7 +2081,7 @@ int Http2Session::write_clear() {
}
int Http2Session::tls_handshake() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
ERR_clear_error();
@ -2120,7 +2120,7 @@ int Http2Session::tls_handshake() {
}
int Http2Session::read_tls() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<uint8_t, 16_k> buf;
@ -2144,7 +2144,7 @@ int Http2Session::read_tls() {
}
int Http2Session::write_tls() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
ERR_clear_error();

View File

@ -127,7 +127,7 @@ Http3Upstream::Http3Upstream(ClientHandler *handler)
ev_timer_init(&timer_, timeoutcb, 0., 0.);
timer_.data = this;
ngtcp2_connection_close_error_default(&last_error_);
ngtcp2_ccerr_default(&last_error_);
ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 0., 0.);
shutdown_timer_.data = this;
@ -287,7 +287,7 @@ int Http3Upstream::recv_stream_data(uint32_t flags, int64_t stream_id,
if (nconsumed < 0) {
ULOG(ERROR, this) << "nghttp3_conn_read_stream: "
<< nghttp3_strerror(nconsumed);
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&last_error_, nghttp3_err_infer_quic_app_error_code(nconsumed), nullptr,
0);
return -1;
@ -333,7 +333,7 @@ int Http3Upstream::stream_close(int64_t stream_id, uint64_t app_error_code) {
break;
default:
ULOG(ERROR, this) << "nghttp3_conn_close_stream: " << nghttp3_strerror(rv);
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0);
return -1;
}
@ -479,19 +479,42 @@ int Http3Upstream::handshake_completed() {
return -1;
}
auto path = ngtcp2_conn_get_path(conn_);
return send_new_token(&path->remote);
}
namespace {
int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
const ngtcp2_path *old_path,
ngtcp2_path_validation_result res, void *user_data) {
if (res != NGTCP2_PATH_VALIDATION_RESULT_SUCCESS ||
!(flags & NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN)) {
return 0;
}
auto upstream = static_cast<Http3Upstream *>(user_data);
if (upstream->send_new_token(&path->remote) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Http3Upstream::send_new_token(const ngtcp2_addr *remote_addr) {
std::array<uint8_t, NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN + 1> token;
size_t tokenlen;
auto path = ngtcp2_conn_get_path(conn_);
auto worker = handler_->get_worker();
auto conn_handler = worker->get_connection_handler();
auto &qkms = conn_handler->get_quic_keying_materials();
auto &qkm = qkms->keying_materials.front();
if (generate_token(token.data(), tokenlen, path->remote.addr,
path->remote.addrlen, qkm.secret.data(),
if (generate_token(token.data(), tokenlen, remote_addr->addr,
remote_addr->addrlen, qkm.secret.data(),
qkm.secret.size()) != 0) {
return 0;
return -1;
}
assert(tokenlen == NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN);
@ -509,8 +532,9 @@ int Http3Upstream::handshake_completed() {
}
namespace {
int recv_tx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level, void *user_data) {
if (level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
int recv_tx_key(ngtcp2_conn *conn, ngtcp2_encryption_level level,
void *user_data) {
if (level != NGTCP2_ENCRYPTION_LEVEL_1RTT) {
return 0;
}
@ -554,7 +578,7 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
get_new_connection_id,
remove_connection_id,
ngtcp2_crypto_update_key_cb,
nullptr, // path_validation
shrpx::path_validation,
nullptr, // select_preferred_addr
shrpx::stream_reset,
shrpx::extend_max_remote_streams_bidi,
@ -600,8 +624,7 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
auto fd = open_qlog_file(quicconf.upstream.qlog.dir, scid);
if (fd != -1) {
qlog_fd_ = fd;
settings.qlog.odcid = initial_hd.dcid;
settings.qlog.write = shrpx::qlog_write;
settings.qlog_write = shrpx::qlog_write;
}
}
@ -615,6 +638,8 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
settings.rand_ctx.native_handle = &worker->get_randgen();
settings.token = token;
settings.tokenlen = tokenlen;
settings.initial_pkt_num = std::uniform_int_distribution<uint32_t>(
0, std::numeric_limits<int32_t>::max())(worker->get_randgen());
ngtcp2_transport_params params;
ngtcp2_transport_params_default(&params);
@ -629,24 +654,28 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
#ifdef OPENSSL_IS_BORINGSSL
if (quicconf.upstream.early_data) {
ngtcp2_transport_params early_data_params{
.initial_max_stream_data_bidi_local =
params.initial_max_stream_data_bidi_local,
.initial_max_stream_data_bidi_remote =
params.initial_max_stream_data_bidi_remote,
.initial_max_stream_data_uni = params.initial_max_stream_data_uni,
.initial_max_data = params.initial_max_data,
.initial_max_streams_bidi = params.initial_max_streams_bidi,
.initial_max_streams_uni = params.initial_max_streams_uni,
};
ngtcp2_transport_params early_data_params;
ngtcp2_transport_params_default(&early_data_params);
early_data_params.initial_max_stream_data_bidi_local =
params.initial_max_stream_data_bidi_local;
early_data_params.initial_max_stream_data_bidi_remote =
params.initial_max_stream_data_bidi_remote;
early_data_params.initial_max_stream_data_uni =
params.initial_max_stream_data_uni;
early_data_params.initial_max_data = params.initial_max_data;
early_data_params.initial_max_streams_bidi =
params.initial_max_streams_bidi;
early_data_params.initial_max_streams_uni = params.initial_max_streams_uni;
// TODO include HTTP/3 SETTINGS
std::array<uint8_t, 128> quic_early_data_ctx;
auto quic_early_data_ctxlen = ngtcp2_encode_transport_params(
auto quic_early_data_ctxlen = ngtcp2_transport_params_encode(
quic_early_data_ctx.data(), quic_early_data_ctx.size(),
NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, &early_data_params);
&early_data_params);
assert(quic_early_data_ctxlen > 0);
assert(static_cast<size_t>(quic_early_data_ctxlen) <=
@ -669,6 +698,8 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
params.original_dcid = initial_hd.dcid;
}
params.original_dcid_present = 1;
rv = generate_quic_stateless_reset_token(
params.stateless_reset_token, scid, qkm.secret.data(), qkm.secret.size());
if (rv != 0) {
@ -728,10 +759,16 @@ int Http3Upstream::on_write() {
}
}
handler_->get_connection()->wlimit.stopw();
if (write_streams() != 0) {
return -1;
}
if (httpconn_ && nghttp3_conn_is_drained(httpconn_)) {
return -1;
}
reset_timer();
return 0;
@ -767,7 +804,7 @@ int Http3Upstream::write_streams() {
if (sveccnt < 0) {
ULOG(ERROR, this) << "nghttp3_conn_writev_stream: "
<< nghttp3_strerror(sveccnt);
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&last_error_, nghttp3_err_infer_quic_app_error_code(sveccnt),
nullptr, 0);
return handle_error();
@ -802,7 +839,7 @@ int Http3Upstream::write_streams() {
if (rv != 0) {
ULOG(ERROR, this)
<< "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv);
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
0);
return handle_error();
@ -815,8 +852,7 @@ int Http3Upstream::write_streams() {
ULOG(ERROR, this) << "ngtcp2_conn_writev_stream: "
<< ngtcp2_strerror(nwrite);
ngtcp2_connection_close_error_set_transport_error_liberr(
&last_error_, nwrite, nullptr, 0);
ngtcp2_ccerr_set_liberr(&last_error_, nwrite, nullptr, 0);
return handle_error();
} else if (ndatalen >= 0) {
@ -824,7 +860,7 @@ int Http3Upstream::write_streams() {
if (rv != 0) {
ULOG(ERROR, this) << "nghttp3_conn_add_write_offset: "
<< nghttp3_strerror(rv);
ngtcp2_connection_close_error_set_application_error(
ngtcp2_ccerr_set_application_error(
&last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
0);
return handle_error();
@ -845,18 +881,12 @@ int Http3Upstream::write_streams() {
on_send_blocked(faddr, prev_ps.path.remote, prev_ps.path.local,
prev_pi, data, datalen, gso_size);
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
signal_write_upstream_addr(faddr);
return 0;
}
}
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
handler_->get_connection()->wlimit.stopw();
return 0;
}
@ -902,9 +932,9 @@ int Http3Upstream::write_streams() {
if (rv == SHRPX_ERR_SEND_BLOCKED) {
on_send_blocked(faddr, ps.path.remote, ps.path.local, pi, data,
nwrite, 0);
}
signal_write_upstream_addr(faddr);
signal_write_upstream_addr(faddr);
}
}
}
@ -924,12 +954,12 @@ int Http3Upstream::write_streams() {
if (rv == SHRPX_ERR_SEND_BLOCKED) {
on_send_blocked(faddr, ps.path.remote, ps.path.local, pi, data, datalen,
gso_size);
signal_write_upstream_addr(faddr);
}
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
signal_write_upstream_addr(faddr);
return 0;
}
#else // !UDP_SEGMENT
@ -954,8 +984,6 @@ int Http3Upstream::write_streams() {
if (++pktcnt == max_pktcnt) {
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
signal_write_upstream_addr(faddr);
return 0;
}
@ -1472,7 +1500,7 @@ void Http3Upstream::on_handler_delete() {
auto worker = handler_->get_worker();
auto quic_conn_handler = worker->get_quic_connection_handler();
std::vector<ngtcp2_cid> scids(ngtcp2_conn_get_num_scid(conn_) + 1);
std::vector<ngtcp2_cid> scids(ngtcp2_conn_get_scid(conn_, nullptr) + 1);
ngtcp2_conn_get_scid(conn_, scids.data());
scids.back() = hashed_scid_;
@ -1480,23 +1508,26 @@ void Http3Upstream::on_handler_delete() {
quic_conn_handler->remove_connection_id(cid);
}
if (retry_close_ ||
last_error_.type ==
NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE) {
if (retry_close_ || last_error_.type == NGTCP2_CCERR_TYPE_IDLE_CLOSE) {
return;
}
// If this is not idle close, send CONNECTION_CLOSE.
if (!ngtcp2_conn_is_in_closing_period(conn_) &&
!ngtcp2_conn_is_in_draining_period(conn_)) {
if (!ngtcp2_conn_in_closing_period(conn_) &&
!ngtcp2_conn_in_draining_period(conn_)) {
ngtcp2_path_storage ps;
ngtcp2_pkt_info pi;
conn_close_.resize(SHRPX_QUIC_CONN_CLOSE_PKTLEN);
ngtcp2_path_storage_zero(&ps);
ngtcp2_connection_close_error ccerr;
ngtcp2_connection_close_error_default(&ccerr);
ngtcp2_ccerr ccerr;
ngtcp2_ccerr_default(&ccerr);
if (worker->get_graceful_shutdown() &&
!ngtcp2_conn_get_handshake_completed(conn_)) {
ccerr.error_code = NGTCP2_CONNECTION_REFUSED;
}
auto nwrite = ngtcp2_conn_write_connection_close(
conn_, &ps.path, &pi, conn_close_.data(), conn_close_.size(), &ccerr,
@ -1759,6 +1790,13 @@ int Http3Upstream::on_read(const UpstreamAddr *faddr,
auto worker = handler_->get_worker();
auto quic_conn_handler = worker->get_quic_connection_handler();
if (worker->get_graceful_shutdown()) {
ngtcp2_ccerr_set_transport_error(&last_error_,
NGTCP2_CONNECTION_REFUSED, nullptr, 0);
return handle_error();
}
ngtcp2_version_cid vc;
rv =
@ -1767,19 +1805,6 @@ int Http3Upstream::on_read(const UpstreamAddr *faddr,
return -1;
}
if (worker->get_graceful_shutdown()) {
ngtcp2_cid ini_dcid, ini_scid;
ngtcp2_cid_init(&ini_dcid, vc.dcid, vc.dcidlen);
ngtcp2_cid_init(&ini_scid, vc.scid, vc.scidlen);
quic_conn_handler->send_connection_close(
faddr, vc.version, ini_dcid, ini_scid, remote_addr, local_addr,
NGTCP2_CONNECTION_REFUSED, datalen * 3);
return -1;
}
retry_close_ = true;
quic_conn_handler->send_retry(handler_->get_upstream_addr(), vc.version,
@ -1790,7 +1815,7 @@ int Http3Upstream::on_read(const UpstreamAddr *faddr,
}
case NGTCP2_ERR_CRYPTO:
if (!last_error_.error_code) {
ngtcp2_connection_close_error_set_transport_error_tls_alert(
ngtcp2_ccerr_set_tls_alert(
&last_error_, ngtcp2_conn_get_tls_alert(conn_), nullptr, 0);
}
break;
@ -1798,8 +1823,7 @@ int Http3Upstream::on_read(const UpstreamAddr *faddr,
return -1;
default:
if (!last_error_.error_code) {
ngtcp2_connection_close_error_set_transport_error_liberr(
&last_error_, rv, nullptr, 0);
ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0);
}
}
@ -1904,8 +1928,8 @@ void Http3Upstream::signal_write_upstream_addr(const UpstreamAddr *faddr) {
}
int Http3Upstream::handle_error() {
if (ngtcp2_conn_is_in_closing_period(conn_) ||
ngtcp2_conn_is_in_draining_period(conn_)) {
if (ngtcp2_conn_in_closing_period(conn_) ||
ngtcp2_conn_in_draining_period(conn_)) {
return -1;
}
@ -1952,8 +1976,7 @@ int Http3Upstream::handle_expiry() {
} else {
ULOG(ERROR, this) << "ngtcp2_conn_handle_expiry: " << ngtcp2_strerror(rv);
}
ngtcp2_connection_close_error_set_transport_error_liberr(&last_error_, rv,
nullptr, 0);
ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0);
return handle_error();
}
@ -2525,7 +2548,8 @@ int http_stop_sending(nghttp3_conn *conn, int64_t stream_id,
int Http3Upstream::http_stop_sending(int64_t stream_id,
uint64_t app_error_code) {
auto rv = ngtcp2_conn_shutdown_stream_read(conn_, stream_id, app_error_code);
auto rv =
ngtcp2_conn_shutdown_stream_read(conn_, 0, stream_id, app_error_code);
if (ngtcp2_err_is_fatal(rv)) {
ULOG(ERROR, this) << "ngtcp2_conn_shutdown_stream_read: "
<< ngtcp2_strerror(rv);
@ -2551,7 +2575,8 @@ int http_reset_stream(nghttp3_conn *conn, int64_t stream_id,
int Http3Upstream::http_reset_stream(int64_t stream_id,
uint64_t app_error_code) {
auto rv = ngtcp2_conn_shutdown_stream_write(conn_, stream_id, app_error_code);
auto rv =
ngtcp2_conn_shutdown_stream_write(conn_, 0, stream_id, app_error_code);
if (ngtcp2_err_is_fatal(rv)) {
ULOG(ERROR, this) << "ngtcp2_conn_shutdown_stream_write: "
<< ngtcp2_strerror(rv);
@ -2564,7 +2589,7 @@ int Http3Upstream::http_reset_stream(int64_t stream_id,
int Http3Upstream::setup_httpconn() {
int rv;
if (ngtcp2_conn_get_max_local_streams_uni(conn_) < 3) {
if (ngtcp2_conn_get_streams_uni_left(conn_) < 3) {
return -1;
}
@ -2704,7 +2729,7 @@ int Http3Upstream::shutdown_stream(Downstream *downstream,
<< " with app_error_code=" << app_error_code;
}
auto rv = ngtcp2_conn_shutdown_stream(conn_, stream_id, app_error_code);
auto rv = ngtcp2_conn_shutdown_stream(conn_, 0, stream_id, app_error_code);
if (rv != 0) {
ULOG(FATAL, this) << "ngtcp2_conn_shutdown_stream() failed: "
<< ngtcp2_strerror(rv);
@ -2716,8 +2741,8 @@ int Http3Upstream::shutdown_stream(Downstream *downstream,
int Http3Upstream::shutdown_stream_read(int64_t stream_id,
uint64_t app_error_code) {
auto rv =
ngtcp2_conn_shutdown_stream_read(conn_, stream_id, NGHTTP3_H3_NO_ERROR);
auto rv = ngtcp2_conn_shutdown_stream_read(conn_, 0, stream_id,
NGHTTP3_H3_NO_ERROR);
if (ngtcp2_err_is_fatal(rv)) {
ULOG(FATAL, this) << "ngtcp2_conn_shutdown_stream_read: "
<< ngtcp2_strerror(rv);
@ -2783,6 +2808,10 @@ int Http3Upstream::start_graceful_shutdown() {
return 0;
}
if (!httpconn_) {
return -1;
}
rv = nghttp3_conn_submit_shutdown_notice(httpconn_);
if (rv != 0) {
ULOG(FATAL, this) << "nghttp3_conn_submit_shutdown_notice: "

View File

@ -153,6 +153,8 @@ public:
ngtcp2_conn *get_conn() const;
int send_new_token(const ngtcp2_addr *remote_addr);
private:
ClientHandler *handler_;
ev_timer timer_;
@ -161,7 +163,7 @@ private:
int qlog_fd_;
ngtcp2_cid hashed_scid_;
ngtcp2_conn *conn_;
ngtcp2_connection_close_error last_error_;
ngtcp2_ccerr last_error_;
nghttp3_conn *httpconn_;
DownstreamQueue downstream_queue_;
bool retry_close_;

View File

@ -450,7 +450,7 @@ int HttpDownstreamConnection::initiate_connection() {
ev_set_cb(&conn_.rt, timeoutcb);
if (conn_.read_timeout < group_->shared_addr->timeout.read) {
conn_.read_timeout = group_->shared_addr->timeout.read;
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
} else {
conn_.again_rt(group_->shared_addr->timeout.read);
}
@ -875,7 +875,7 @@ void HttpDownstreamConnection::detach_downstream(Downstream *downstream) {
ev_set_cb(&conn_.rt, idle_timeoutcb);
if (conn_.read_timeout < downstreamconf.timeout.idle_read) {
conn_.read_timeout = downstreamconf.timeout.idle_read;
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
} else {
conn_.again_rt(downstreamconf.timeout.idle_read);
}
@ -1231,7 +1231,7 @@ int HttpDownstreamConnection::write_first() {
}
int HttpDownstreamConnection::read_clear() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<uint8_t, 16_k> buf;
int rv;
@ -1270,7 +1270,7 @@ int HttpDownstreamConnection::read_clear() {
}
int HttpDownstreamConnection::write_clear() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
auto upstream = downstream_->get_upstream();
auto input = downstream_->get_request_buf();
@ -1318,7 +1318,7 @@ int HttpDownstreamConnection::write_clear() {
int HttpDownstreamConnection::tls_handshake() {
ERR_clear_error();
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
auto rv = conn_.tls_handshake();
if (rv == SHRPX_ERR_INPROGRESS) {
@ -1360,7 +1360,7 @@ int HttpDownstreamConnection::tls_handshake() {
}
int HttpDownstreamConnection::read_tls() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
ERR_clear_error();
@ -1401,7 +1401,7 @@ int HttpDownstreamConnection::read_tls() {
}
int HttpDownstreamConnection::write_tls() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
ERR_clear_error();

View File

@ -658,6 +658,15 @@ int HttpsUpstream::on_read() {
auto htperr = llhttp_execute(&htp_, reinterpret_cast<const char *>(rb->pos()),
rb->rleft());
if (htperr == HPE_PAUSED_UPGRADE &&
rb->pos() ==
reinterpret_cast<const uint8_t *>(llhttp_get_error_pos(&htp_))) {
llhttp_resume_after_upgrade(&htp_);
htperr = llhttp_execute(&htp_, reinterpret_cast<const char *>(rb->pos()),
rb->rleft());
}
auto nread =
htperr == HPE_OK
? rb->rleft()

View File

@ -377,7 +377,7 @@ int LiveCheck::connected() {
}
int LiveCheck::tls_handshake() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
ERR_clear_error();
@ -446,7 +446,7 @@ int LiveCheck::tls_handshake() {
}
int LiveCheck::read_tls() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<uint8_t, 4_k> buf;
@ -470,7 +470,7 @@ int LiveCheck::read_tls() {
}
int LiveCheck::write_tls() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
ERR_clear_error();
@ -519,7 +519,7 @@ int LiveCheck::write_tls() {
}
int LiveCheck::read_clear() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<uint8_t, 4_k> buf;
@ -541,7 +541,7 @@ int LiveCheck::read_clear() {
}
int LiveCheck::write_clear() {
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
struct iovec iov;

View File

@ -259,7 +259,7 @@ int MemcachedConnection::on_read() { return do_read_(*this); }
int MemcachedConnection::tls_handshake() {
ERR_clear_error();
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
auto rv = conn_.tls_handshake();
if (rv == SHRPX_ERR_INPROGRESS) {
@ -301,7 +301,7 @@ int MemcachedConnection::write_tls() {
return 0;
}
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<struct iovec, MAX_WR_IOVCNT> iov;
std::array<uint8_t, 16_k> buf;
@ -340,7 +340,7 @@ int MemcachedConnection::read_tls() {
return 0;
}
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
for (;;) {
auto nread = conn_.read_tls(recvbuf_.last, recvbuf_.wleft());
@ -368,7 +368,7 @@ int MemcachedConnection::write_clear() {
return 0;
}
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
std::array<struct iovec, MAX_WR_IOVCNT> iov;
@ -396,7 +396,7 @@ int MemcachedConnection::read_clear() {
return 0;
}
conn_.last_read = ev_now(conn_.loop);
conn_.last_read = std::chrono::steady_clock::now();
for (;;) {
auto nread = conn_.read_clear(recvbuf_.last, recvbuf_.wleft());

View File

@ -319,21 +319,6 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
break;
}
case NGTCP2_ERR_RETRY:
if (worker_->get_graceful_shutdown()) {
send_connection_close(faddr, hd.version, hd.dcid, hd.scid, remote_addr,
local_addr, NGTCP2_CONNECTION_REFUSED,
datalen * 3);
return 0;
}
send_retry(faddr, vc.version, vc.dcid, vc.dcidlen, vc.scid, vc.scidlen,
remote_addr, local_addr, datalen * 3);
return 0;
case NGTCP2_ERR_VERSION_NEGOTIATION:
send_version_negotiation(faddr, vc.version, vc.dcid, vc.dcidlen, vc.scid,
vc.scidlen, remote_addr, local_addr);
return 0;
default:
if (!config->single_thread && !(data[0] & 0x80) &&
vc.dcidlen == SHRPX_QUIC_SCIDLEN &&

View File

@ -59,8 +59,9 @@ void QUICListener::on_read() {
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
uint8_t
msg_ctrl[CMSG_SPACE(sizeof(uint8_t)) + CMSG_SPACE(sizeof(in6_pktinfo))];
uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint8_t)) +
CMSG_SPACE(sizeof(in6_pktinfo)) +
CMSG_SPACE(sizeof(uint16_t))];
msg.msg_control = msg_ctrl;
auto quic_conn_handler = worker_->get_quic_connection_handler();
@ -74,11 +75,11 @@ void QUICListener::on_read() {
return;
}
++pktcnt;
Address local_addr{};
if (util::msghdr_get_local_addr(local_addr, &msg, su.storage.ss_family) !=
0) {
++pktcnt;
continue;
}
@ -88,24 +89,44 @@ void QUICListener::on_read() {
.ecn = util::msghdr_get_ecn(&msg, su.storage.ss_family),
};
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "QUIC received packet: local="
<< util::to_numeric_addr(&local_addr)
<< " remote=" << util::to_numeric_addr(&su.sa, msg.msg_namelen)
<< " ecn=" << log::hex << pi.ecn << log::dec << " " << nread
<< " bytes";
auto gso_size = util::msghdr_get_udp_gro(&msg);
if (gso_size == 0) {
gso_size = static_cast<size_t>(nread);
}
if (nread == 0) {
continue;
auto data = buf.data();
for (;;) {
auto datalen = std::min(static_cast<size_t>(nread), gso_size);
++pktcnt;
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "QUIC received packet: local="
<< util::to_numeric_addr(&local_addr) << " remote="
<< util::to_numeric_addr(&su.sa, msg.msg_namelen)
<< " ecn=" << log::hex << pi.ecn << log::dec << " " << datalen
<< " bytes";
}
if (datalen == 0) {
break;
}
Address remote_addr;
remote_addr.su = su;
remote_addr.len = msg.msg_namelen;
quic_conn_handler->handle_packet(faddr_, remote_addr, local_addr, pi,
data, datalen);
nread -= datalen;
if (nread == 0) {
break;
}
data += datalen;
}
Address remote_addr;
remote_addr.su = su;
remote_addr.len = msg.msg_namelen;
quic_conn_handler->handle_packet(faddr_, remote_addr, local_addr, pi,
buf.data(), nread);
}
}

View File

@ -64,9 +64,9 @@
#ifdef ENABLE_HTTP3
# include <ngtcp2/ngtcp2.h>
# include <ngtcp2/ngtcp2_crypto.h>
# ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# include <ngtcp2/ngtcp2_crypto_openssl.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
# include <ngtcp2/ngtcp2_crypto_quictls.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
# ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
# include <ngtcp2/ngtcp2_crypto_boringssl.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
@ -90,6 +90,7 @@
#include "timegm.h"
using namespace nghttp2;
using namespace std::chrono_literals;
namespace shrpx {
@ -383,7 +384,7 @@ int tls_session_client_new_cb(SSL *ssl, SSL_SESSION *session) {
}
try_cache_tls_session(conn->tls.client_session_cache, session,
ev_now(conn->loop));
std::chrono::steady_clock::now());
return 0;
}
@ -1257,12 +1258,12 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file,
SSL_CTX_set_options(ssl_ctx, ssl_opts);
# ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
if (ngtcp2_crypto_openssl_configure_server_context(ssl_ctx) != 0) {
LOG(FATAL) << "ngtcp2_crypto_openssl_configure_server_context failed";
# ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
if (ngtcp2_crypto_quictls_configure_server_context(ssl_ctx) != 0) {
LOG(FATAL) << "ngtcp2_crypto_quictls_configure_server_context failed";
DIE();
}
# endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
# ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
if (ngtcp2_crypto_boringssl_configure_server_context(ssl_ctx) != 0) {
LOG(FATAL) << "ngtcp2_crypto_boringssl_configure_server_context failed";
@ -1813,16 +1814,18 @@ StringRef get_common_name(X509 *cert) {
}
} // namespace
namespace {
int verify_numeric_hostname(X509 *cert, const StringRef &hostname,
const Address *addr) {
const void *saddr;
size_t saddrlen;
switch (addr->su.storage.ss_family) {
case AF_INET:
saddr = &addr->su.in.sin_addr;
saddrlen = sizeof(addr->su.in.sin_addr);
break;
case AF_INET6:
saddr = &addr->su.in6.sin6_addr;
saddrlen = sizeof(addr->su.in6.sin6_addr);
break;
default:
return -1;
@ -1847,7 +1850,7 @@ int verify_numeric_hostname(X509 *cert, const StringRef &hostname,
size_t ip_addrlen = altname->d.iPAddress->length;
ip_found = true;
if (addr->len == ip_addrlen && memcmp(saddr, ip_addr, ip_addrlen) == 0) {
if (saddrlen == ip_addrlen && memcmp(saddr, ip_addr, ip_addrlen) == 0) {
return 0;
}
}
@ -1872,15 +1875,8 @@ int verify_numeric_hostname(X509 *cert, const StringRef &hostname,
return -1;
}
} // namespace
namespace {
int verify_hostname(X509 *cert, const StringRef &hostname,
const Address *addr) {
if (util::numeric_host(hostname.c_str())) {
return verify_numeric_hostname(cert, hostname, addr);
}
int verify_dns_hostname(X509 *cert, const StringRef &hostname) {
auto altnames = static_cast<GENERAL_NAMES *>(
X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
if (altnames) {
@ -1948,6 +1944,16 @@ int verify_hostname(X509 *cert, const StringRef &hostname,
return rv ? 0 : -1;
}
namespace {
int verify_hostname(X509 *cert, const StringRef &hostname,
const Address *addr) {
if (util::numeric_host(hostname.c_str())) {
return verify_numeric_hostname(cert, hostname, addr);
}
return verify_dns_hostname(cert, hostname);
}
} // namespace
int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
@ -2422,8 +2428,8 @@ std::vector<uint8_t> serialize_ssl_session(SSL_SESSION *session) {
} // namespace
void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
ev_tstamp t) {
if (cache->last_updated + 1_min > t) {
const std::chrono::steady_clock::time_point &t) {
if (cache->last_updated + 1min > t) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Client session cache entry is still fresh.";
}
@ -2432,7 +2438,7 @@ void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Update client cache entry "
<< "timestamp = " << t;
<< "timestamp = " << t.time_since_epoch().count();
}
cache->session_data = serialize_ssl_session(session);
@ -2593,13 +2599,6 @@ StringRef get_x509_issuer_name(BlockAllocator &balloc, X509 *x) {
return get_x509_name(balloc, X509_get_issuer_name(x));
}
#ifdef WORDS_BIGENDIAN
# define bswap64(N) (N)
#else /* !WORDS_BIGENDIAN */
# define bswap64(N) \
((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
#endif /* !WORDS_BIGENDIAN */
StringRef get_x509_serial(BlockAllocator &balloc, X509 *x) {
auto sn = X509_get_serialNumber(x);
auto bn = BN_new();

View File

@ -58,7 +58,7 @@ struct TLSSessionCache {
// i2d_SSL_SESSION(3SSL).
std::vector<uint8_t> session_data;
// The last time stamp when this cache entry is created or updated.
ev_tstamp last_updated;
std::chrono::steady_clock::time_point last_updated;
};
// This struct stores the additional information per SSL_CTX. This is
@ -122,6 +122,16 @@ int check_cert(SSL *ssl, const Address *addr, const StringRef &host);
// point to &addr->addr.
int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr);
// Verify |cert| using numeric IP address. |hostname| and |addr|
// should contain the same numeric IP address. This function returns
// 0 if it succeeds, or -1.
int verify_numeric_hostname(X509 *cert, const StringRef &hostname,
const Address *addr);
// Verify |cert| using DNS name hostname. This function returns 0 if
// it succeeds, or -1.
int verify_dns_hostname(X509 *cert, const StringRef &hostname);
struct WildcardRevPrefix {
WildcardRevPrefix(const StringRef &prefix, size_t idx)
: prefix(std::begin(prefix), std::end(prefix)), idx(idx) {}
@ -273,7 +283,7 @@ bool tls_hostname_match(const StringRef &pattern, const StringRef &hostname);
// Depending on the existing cache's time stamp, |session| might not
// be cached.
void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
ev_tstamp t);
const std::chrono::steady_clock::time_point &t);
// Returns cached session associated |addr|. If no cache entry is
// found associated to |addr|, nullptr will be returned.

View File

@ -194,4 +194,146 @@ void test_shrpx_tls_tls_hostname_match(void) {
CU_ASSERT(!tls_hostname_match_wrapper("example.com", "www.example.com"));
}
static X509 *load_cert(const char *path) {
auto f = fopen(path, "r");
auto cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
fclose(f);
return cert;
}
static Address parse_addr(const char *ipaddr) {
addrinfo hints{};
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
addrinfo *res = nullptr;
auto rv = getaddrinfo(ipaddr, "443", &hints, &res);
CU_ASSERT(0 == rv);
CU_ASSERT(nullptr != res);
Address addr;
addr.len = res->ai_addrlen;
memcpy(&addr.su, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
return addr;
}
void test_shrpx_tls_verify_numeric_hostname(void) {
{
// Successful IPv4 address match in SAN
static constexpr char ipaddr[] = "127.0.0.1";
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
auto addr = parse_addr(ipaddr);
auto rv =
tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
CU_ASSERT(0 == rv);
X509_free(cert);
}
{
// Successful IPv6 address match in SAN
static constexpr char ipaddr[] = "::1";
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
auto addr = parse_addr(ipaddr);
auto rv =
tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
CU_ASSERT(0 == rv);
X509_free(cert);
}
{
// Unsuccessful IPv4 address match in SAN
static constexpr char ipaddr[] = "192.168.0.127";
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
auto addr = parse_addr(ipaddr);
auto rv =
tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
CU_ASSERT(-1 == rv);
X509_free(cert);
}
{
// CommonName is not used if SAN is available
static constexpr char ipaddr[] = "192.168.0.1";
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/ipaddr.crt");
auto addr = parse_addr(ipaddr);
auto rv =
tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
CU_ASSERT(-1 == rv);
X509_free(cert);
}
{
// Successful IPv4 address match in CommonName
static constexpr char ipaddr[] = "127.0.0.1";
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/nosan_ip.crt");
auto addr = parse_addr(ipaddr);
auto rv =
tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
CU_ASSERT(0 == rv);
X509_free(cert);
}
}
void test_shrpx_tls_verify_dns_hostname(void) {
{
// Successful exact DNS name match in SAN
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
auto rv = tls::verify_dns_hostname(
cert, StringRef::from_lit("nghttp2.example.com"));
CU_ASSERT(0 == rv);
X509_free(cert);
}
{
// Successful wildcard DNS name match in SAN
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
auto rv = tls::verify_dns_hostname(
cert, StringRef::from_lit("www.nghttp2.example.com"));
CU_ASSERT(0 == rv);
X509_free(cert);
}
{
// CommonName is not used if SAN is available.
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
auto rv = tls::verify_dns_hostname(cert, StringRef::from_lit("localhost"));
CU_ASSERT(-1 == rv);
X509_free(cert);
}
{
// Successful DNS name match in CommonName
auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/nosan.crt");
auto rv = tls::verify_dns_hostname(cert, StringRef::from_lit("localhost"));
CU_ASSERT(0 == rv);
X509_free(cert);
}
}
} // namespace shrpx

View File

@ -34,6 +34,8 @@ namespace shrpx {
void test_shrpx_tls_create_lookup_tree(void);
void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void);
void test_shrpx_tls_tls_hostname_match(void);
void test_shrpx_tls_verify_numeric_hostname(void);
void test_shrpx_tls_verify_dns_hostname(void);
} // namespace shrpx

View File

@ -27,6 +27,7 @@
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <netinet/udp.h>
#include <cstdio>
#include <memory>
@ -892,6 +893,16 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
# endif // defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
}
# ifdef UDP_GRO
if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)) == -1) {
auto error = errno;
LOG(WARN) << "Failed to set UDP_GRO option to listener socket: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
close(fd);
continue;
}
# endif // UDP_GRO
if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
auto error = errno;
LOG(WARN) << "bind() syscall failed: "
@ -919,8 +930,9 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
rv = bpf_object__load(obj);
if (rv != 0) {
auto error = errno;
LOG(FATAL) << "Failed to load bpf object file: "
<< xsi_strerror(-rv, errbuf.data(), errbuf.size());
<< xsi_strerror(error, errbuf.data(), errbuf.size());
close(fd);
return -1;
}
@ -938,9 +950,9 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
ref.obj = obj;
auto reuseport_array =
ref.reuseport_array =
bpf_object__find_map_by_name(obj, "reuseport_array");
if (!reuseport_array) {
if (!ref.reuseport_array) {
auto error = errno;
LOG(FATAL) << "Failed to get reuseport_array: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
@ -948,10 +960,8 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
return -1;
}
ref.reuseport_array = bpf_map__fd(reuseport_array);
auto cid_prefix_map = bpf_object__find_map_by_name(obj, "cid_prefix_map");
if (!cid_prefix_map) {
ref.cid_prefix_map = bpf_object__find_map_by_name(obj, "cid_prefix_map");
if (!ref.cid_prefix_map) {
auto error = errno;
LOG(FATAL) << "Failed to get cid_prefix_map: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
@ -959,8 +969,6 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
return -1;
}
ref.cid_prefix_map = bpf_map__fd(cid_prefix_map);
auto sk_info = bpf_object__find_map_by_name(obj, "sk_info");
if (!sk_info) {
auto error = errno;
@ -973,11 +981,12 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
constexpr uint32_t zero = 0;
uint64_t num_socks = config->num_worker;
rv =
bpf_map_update_elem(bpf_map__fd(sk_info), &zero, &num_socks, BPF_ANY);
rv = bpf_map__update_elem(sk_info, &zero, sizeof(zero), &num_socks,
sizeof(num_socks), BPF_ANY);
if (rv != 0) {
auto error = errno;
LOG(FATAL) << "Failed to update sk_info: "
<< xsi_strerror(-rv, errbuf.data(), errbuf.size());
<< xsi_strerror(error, errbuf.data(), errbuf.size());
close(fd);
return -1;
}
@ -988,20 +997,25 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
auto &qkms = conn_handler_->get_quic_keying_materials();
auto &qkm = qkms->keying_materials.front();
rv = bpf_map_update_elem(bpf_map__fd(sk_info), &key_high_idx,
qkm.cid_encryption_key.data(), BPF_ANY);
rv = bpf_map__update_elem(sk_info, &key_high_idx, sizeof(key_high_idx),
qkm.cid_encryption_key.data(),
qkm.cid_encryption_key.size() / 2, BPF_ANY);
if (rv != 0) {
auto error = errno;
LOG(FATAL) << "Failed to update key_high_idx sk_info: "
<< xsi_strerror(-rv, errbuf.data(), errbuf.size());
<< xsi_strerror(error, errbuf.data(), errbuf.size());
close(fd);
return -1;
}
rv = bpf_map_update_elem(bpf_map__fd(sk_info), &key_low_idx,
qkm.cid_encryption_key.data() + 8, BPF_ANY);
rv = bpf_map__update_elem(sk_info, &key_low_idx, sizeof(key_low_idx),
qkm.cid_encryption_key.data() +
qkm.cid_encryption_key.size() / 2,
qkm.cid_encryption_key.size() / 2, BPF_ANY);
if (rv != 0) {
auto error = errno;
LOG(FATAL) << "Failed to update key_low_idx sk_info: "
<< xsi_strerror(-rv, errbuf.data(), errbuf.size());
<< xsi_strerror(error, errbuf.data(), errbuf.size());
close(fd);
return -1;
}
@ -1021,20 +1035,23 @@ int Worker::create_quic_server_socket(UpstreamAddr &faddr) {
const auto &ref = quic_bpf_refs[faddr.index];
auto sk_index = compute_sk_index();
rv =
bpf_map_update_elem(ref.reuseport_array, &sk_index, &fd, BPF_NOEXIST);
rv = bpf_map__update_elem(ref.reuseport_array, &sk_index,
sizeof(sk_index), &fd, sizeof(fd), BPF_NOEXIST);
if (rv != 0) {
auto error = errno;
LOG(FATAL) << "Failed to update reuseport_array: "
<< xsi_strerror(-rv, errbuf.data(), errbuf.size());
<< xsi_strerror(error, errbuf.data(), errbuf.size());
close(fd);
return -1;
}
rv = bpf_map_update_elem(ref.cid_prefix_map, cid_prefix_.data(),
&sk_index, BPF_NOEXIST);
rv = bpf_map__update_elem(ref.cid_prefix_map, cid_prefix_.data(),
cid_prefix_.size(), &sk_index, sizeof(sk_index),
BPF_NOEXIST);
if (rv != 0) {
auto error = errno;
LOG(FATAL) << "Failed to update cid_prefix_map: "
<< xsi_strerror(-rv, errbuf.data(), errbuf.size());
<< xsi_strerror(error, errbuf.data(), errbuf.size());
close(fd);
return -1;
}

View File

@ -405,6 +405,29 @@ void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
} // namespace
#endif // HAVE_NEVERBLEED
namespace {
int send_ready_event(int ready_ipc_fd) {
std::array<char, STRERROR_BUFSIZE> errbuf;
auto pid = getpid();
ssize_t nwrite;
while ((nwrite = write(ready_ipc_fd, &pid, sizeof(pid))) == -1 &&
errno == EINTR)
;
if (nwrite < 0) {
auto error = errno;
LOG(ERROR) << "Writing PID to ready IPC channel failed: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
return -1;
}
return 0;
}
} // namespace
int worker_process_event_loop(WorkerProcessConfig *wpconf) {
int rv;
std::array<char, STRERROR_BUFSIZE> errbuf;
@ -638,6 +661,10 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
LOG(INFO) << "Entering event loop";
}
if (send_ready_event(wpconf->ready_ipc_fd) != 0) {
return -1;
}
ev_run(loop, 0);
conn_handler->cancel_ocsp_update();

View File

@ -42,6 +42,8 @@ class ConnectionHandler;
struct WorkerProcessConfig {
// IPC socket to read event from main process
int ipc_fd;
// IPC socket to tell that a worker process is ready for service.
int ready_ipc_fd;
// IPv4 or UNIX domain socket, or -1 if not used
int server_fd;
// IPv6 socket, or -1 if not used

27
src/testdata/Makefile.am vendored Normal file
View File

@ -0,0 +1,27 @@
# nghttp2 - HTTP/2 C Library
# Copyright (c) 2023 Tatsuhiro Tsujikawa
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
EXTRA_DIST = \
ipaddr.crt \
nosan.crt \
nosan_ip.crt \
verify_hostname.crt

10
src/testdata/ipaddr.crt vendored Normal file
View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBfDCCASKgAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwsxOTIuMTY4
LjAuMTAeFw0yMzAzMTUxMjQ5MDBaFw0zMzAxMjExMjQ5MDBaMBYxFDASBgNVBAMT
CzE5Mi4xNjguMC4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERDh3hNne6xGM
fOrf7ln5EFnlLpk98qadBx3MKjG5gAfMYHzf/S7v19G608sH1LtabubV+Tvjllon
K56G2Gk0+6NhMF8wDgYDVR0PAQH/BAQDAgeAME0GA1UdEQRGMESCE25naHR0cDIu
ZXhhbXBsZS5jb22CFSoubmdodHRwMi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAA
AAAAAAAAAAAAATAKBggqhkjOPQQDAgNIADBFAiEA3jZzO49MYccR5mYS08qVUCdh
HsEAC8GhRXFwL6zvf2ACIFAJrca2zTU4QRjV6V+LGRHc2ZocE2e7wFTLobblmDfB
-----END CERTIFICATE-----

9
src/testdata/nosan.crt vendored Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN CERTIFICATE-----
MIIBKDCBz6ADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMTCWxvY2FsaG9z
dDAeFw0yMzAzMTUxMjQzMzhaFw0zMzAxMjExMjQzMzhaMBQxEjAQBgNVBAMTCWxv
Y2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIEpWYgtXtcx0uJ2oFPK
RiII93iw5ITMrhMfBXQ0SzCfkUdvCJ0gNW+3isTBu4Jt0URpgP37eGwiJf2wPApq
KpajEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNIADBFAiEA4IYil4G4
cMxaVkcAnMGgiSdn7/qIgdhFB0Vx5AOd+EUCIGubRPhsXAJXvG//cK25mmxi3Wax
r7AgRKuDtWxn2bCO
-----END CERTIFICATE-----

9
src/testdata/nosan_ip.crt vendored Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN CERTIFICATE-----
MIIBJzCBz6ADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMTCTEyNy4wLjAu
MTAeFw0yMzAzMTUxMjQ1MTVaFw0zMzAxMjExMjQ1MTVaMBQxEjAQBgNVBAMTCTEy
Ny4wLjAuMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOXGPfSzXoeD7jszmAQO
qAhak5HQMTmj32Q/xqO9WmCnXRQ+T06701o6q1hjotrC/HdMk9kabsKHc9V7Bk4O
zkGjEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNHADBEAiAI3fKrkNTN
IEo9qI8bd/pZ6on4d9vLcnHtqYhcuWZGTwIgW2zYMwASLUw4H1k/prBtTEEJOahJ
bvFs3oMbJEprQ+g=
-----END CERTIFICATE-----

10
src/testdata/verify_hostname.crt vendored Normal file
View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBeTCCAR6gAwIBAgIBATAKBggqhkjOPQQDAjAUMRIwEAYDVQQDEwlsb2NhbGhv
c3QwHhcNMjMwMzE1MTIzNzU1WhcNMzMwMTIxMTIzNzU1WjAUMRIwEAYDVQQDEwls
b2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATMHcWmb55fi0KHNDwM
cYzVTAOfzJf44AqrqC+Pq2zW/ig8tPZbXf3eA/Vvp07Di+yWmuo3fGatUcY4nsx+
Jd62o2EwXzAOBgNVHQ8BAf8EBAMCB4AwTQYDVR0RBEYwRIITbmdodHRwMi5leGFt
cGxlLmNvbYIVKi5uZ2h0dHAyLmV4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAA
AAAAAAABMAoGCCqGSM49BAMCA0kAMEYCIQDQJFRJ3Ah4cGy7bwpkzVYeTgG+NhDa
55F4dPtJp9dS8wIhALQ9qf379lke1jVHg2t84iZLo3bL23RgICMezEYvqO3K
-----END CERTIFICATE-----

View File

@ -41,6 +41,7 @@
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
#include <netinet/udp.h>
#ifdef _WIN32
# include <ws2tcpip.h>
#else // !_WIN32
@ -1733,6 +1734,22 @@ unsigned int msghdr_get_ecn(msghdr *msg, int family) {
return 0;
}
size_t msghdr_get_udp_gro(msghdr *msg) {
uint16_t gso_size = 0;
# ifdef UDP_GRO
for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size));
break;
}
}
# endif // UDP_GRO
return gso_size;
}
int fd_set_send_ecn(int fd, int family, unsigned int ecn) {
switch (family) {
case AF_INET:

View File

@ -47,6 +47,10 @@
#include <map>
#include <random>
#ifdef HAVE_LIBEV
# include <ev.h>
#endif // HAVE_LIBEV
#include "url-parser/url_parser.h"
#include "template.h"
@ -695,6 +699,17 @@ template <typename Clock, typename Rep> Rep clock_precision() {
return duration.count();
}
#ifdef HAVE_LIBEV
template <typename Duration = std::chrono::steady_clock::duration>
Duration duration_from(ev_tstamp d) {
return std::chrono::duration_cast<Duration>(std::chrono::duration<double>(d));
}
template <typename Duration> ev_tstamp ev_tstamp_from(const Duration &d) {
return std::chrono::duration<double>(d).count();
}
#endif // HAVE_LIBEV
int make_socket_closeonexec(int fd);
int make_socket_nonblocking(int fd);
int make_socket_nodelay(int fd);
@ -944,6 +959,10 @@ int msghdr_get_local_addr(Address &dest, msghdr *msg, int family);
unsigned int msghdr_get_ecn(msghdr *msg, int family);
// msghdr_get_udp_gro returns UDP_GRO value from |msg|. If UDP_GRO is
// not found, or UDP_GRO is not supported, this function returns 0.
size_t msghdr_get_udp_gro(msghdr *msg);
int fd_set_send_ecn(int fd, int family, unsigned int ecn);
#endif // ENABLE_HTTP3

View File

@ -24,11 +24,11 @@
*/
#include "failmalloc_test.h"
#include <CUnit/CUnit.h>
#include <stdio.h>
#include <assert.h>
#include <CUnit/CUnit.h>
#include "nghttp2_session.h"
#include "nghttp2_stream.h"
#include "nghttp2_frame.h"

View File

@ -449,9 +449,6 @@ int main(void) {
!CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc) ||
!CU_add_test(pSuite, "http_parse_priority",
test_nghttp2_http_parse_priority) ||
!CU_add_test(pSuite, "sf_parse_item", test_nghttp2_sf_parse_item) ||
!CU_add_test(pSuite, "sf_parse_inner_list",
test_nghttp2_sf_parse_inner_list) ||
!CU_add_test(pSuite, "extpri_to_uint8", test_nghttp2_extpri_to_uint8)) {
CU_cleanup_registry();
return (int)CU_get_error();

View File

@ -24,6 +24,8 @@
*/
#include "nghttp2_buf_test.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include "nghttp2_buf.h"

View File

@ -25,6 +25,8 @@
*/
#include "nghttp2_extpri_test.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include "nghttp2_extpri.h"

View File

@ -68,7 +68,7 @@ static void check_frame_header(size_t length, uint8_t type, uint8_t flags,
CU_ASSERT(0 == hd->reserved);
}
void test_nghttp2_frame_pack_headers() {
void test_nghttp2_frame_pack_headers(void) {
nghttp2_hd_deflater deflater;
nghttp2_hd_inflater inflater;
nghttp2_headers frame, oframe;
@ -276,7 +276,7 @@ void test_nghttp2_frame_pack_rst_stream(void) {
nghttp2_bufs_free(&bufs);
}
void test_nghttp2_frame_pack_settings() {
void test_nghttp2_frame_pack_settings(void) {
nghttp2_settings frame, oframe;
nghttp2_bufs bufs;
int i;
@ -311,7 +311,7 @@ void test_nghttp2_frame_pack_settings() {
nghttp2_frame_settings_free(&oframe, mem);
}
void test_nghttp2_frame_pack_push_promise() {
void test_nghttp2_frame_pack_push_promise(void) {
nghttp2_hd_deflater deflater;
nghttp2_hd_inflater inflater;
nghttp2_push_promise frame, oframe;
@ -384,7 +384,7 @@ void test_nghttp2_frame_pack_ping(void) {
nghttp2_frame_ping_free(&frame);
}
void test_nghttp2_frame_pack_goaway() {
void test_nghttp2_frame_pack_goaway(void) {
nghttp2_goaway frame, oframe;
nghttp2_bufs bufs;
size_t opaque_data_len = 16;

View File

@ -24,6 +24,8 @@
*/
#include "nghttp2_helper_test.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include "nghttp2_helper.h"

View File

@ -25,6 +25,7 @@
*/
#include "nghttp2_http_test.h"
#include <stdio.h>
#include <assert.h>
#include <CUnit/CUnit.h>
@ -203,453 +204,3 @@ void test_nghttp2_http_parse_priority(void) {
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
}
void test_nghttp2_sf_parse_item(void) {
{
nghttp2_sf_value val;
const uint8_t s[] = "?1";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(1 == val.b);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?1 ";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(1 == val.b);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?1;foo=bar";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(1 == val.b);
}
{
const uint8_t s[] = {'?', '1', ';', 'f', 'o', 'o', '='};
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s)));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?0";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(0 == val.b);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?0 ";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(0 == val.b);
}
{
const uint8_t s[] = "?2";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "?";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "?1";
CU_ASSERT(2 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:";
val.type = 0xff;
CU_ASSERT(46 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(44 == val.s.len);
CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==: ";
val.type = 0xff;
CU_ASSERT(46 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(44 == val.s.len);
CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "::";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(0 == val.s.len);
}
{
const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = ":";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = ":@:";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = ":foo:";
CU_ASSERT(5 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] =
":abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=:";
val.type = 0xff;
CU_ASSERT(67 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(65 == val.s.len);
CU_ASSERT(
0 ==
memcmp(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=",
val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "foo123/456";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
CU_ASSERT(10 == val.s.len);
CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "foo123/456 ";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
CU_ASSERT(10 == val.s.len);
CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "*";
val.type = 0xff;
CU_ASSERT(1 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
CU_ASSERT(1 == val.s.len);
CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
}
{
const uint8_t s[] = "*";
CU_ASSERT(1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"hello world\"";
val.type = 0xff;
CU_ASSERT(13 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(11 == val.s.len);
CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"hello world\" ";
val.type = 0xff;
CU_ASSERT(13 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(11 == val.s.len);
CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"foo\\\"\\\\\"";
val.type = 0xff;
CU_ASSERT(9 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(7 == val.s.len);
CU_ASSERT(0 == memcmp("foo\\\"\\\\", val.s.base, val.s.len));
}
{
const uint8_t s[] = "\"foo\\x\"";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"foo";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"\x7f\"";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"\x1f\"";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"foo\"";
CU_ASSERT(5 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "4.5";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(3 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "4.5 ";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(3 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "-4.5";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(4 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(-4.5 - val.d) < 1e-9);
}
{
const uint8_t s[] = "4.5";
CU_ASSERT(3 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "123456789012.123";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(16 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(123456789012.123 - val.d) < 1e-9);
}
{
const uint8_t s[] = "1123456789012.123";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "123456789012.1234";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "1.";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "123456789012345";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(15 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INTEGER == val.type);
CU_ASSERT(123456789012345 == val.i);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "1 ";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(1 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INTEGER == val.type);
CU_ASSERT(1 == val.i);
}
{
const uint8_t s[] = "1";
CU_ASSERT(1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "1234567890123456";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"foo\";a; b=\"bar\";c=1.3;d=9;e=baz;f=:aaa:";
val.type = 0xff;
CU_ASSERT(41 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(0 == memcmp("foo", val.s.base, val.s.len));
}
{
const uint8_t s[] = "\"foo\";a; b=\"bar";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "foo;";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
}
void test_nghttp2_sf_parse_inner_list(void) {
{
nghttp2_sf_value val;
const uint8_t s[] = "()";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "( )";
val.type = 0xff;
CU_ASSERT(7 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "(a)";
val.type = 0xff;
CU_ASSERT(3 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "(a b)";
val.type = 0xff;
CU_ASSERT(5 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "( a b )";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "( a;foo=bar)";
val.type = 0xff;
CU_ASSERT(12 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
const uint8_t s[] = "(";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "(a";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "(a ";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "(a;b";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
}

View File

@ -31,7 +31,5 @@
#endif /* HAVE_CONFIG_H */
void test_nghttp2_http_parse_priority(void);
void test_nghttp2_sf_parse_item(void);
void test_nghttp2_sf_parse_inner_list(void);
#endif /* NGHTTP2_HTTP_TEST_H */

View File

@ -25,6 +25,8 @@
*/
#include "nghttp2_map_test.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include "nghttp2_map.h"

View File

@ -24,6 +24,7 @@
*/
#include "nghttp2_npn_test.h"
#include <stdio.h>
#include <string.h>
#include <CUnit/CUnit.h>

View File

@ -24,6 +24,8 @@
*/
#include "nghttp2_pq_test.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include "nghttp2_pq.h"

View File

@ -24,6 +24,8 @@
*/
#include "nghttp2_queue_test.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include "nghttp2_queue.h"

View File

@ -572,8 +572,7 @@ static ssize_t defer_data_source_read_callback(nghttp2_session *session,
}
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
nghttp2_error_code error_code,
void *user_data) {
uint32_t error_code, void *user_data) {
my_user_data *my_data = (my_user_data *)user_data;
(void)session;
(void)stream_id;

View File

@ -24,6 +24,8 @@
*/
#include "nghttp2_stream_test.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include "nghttp2_stream.h"

View File

@ -24,6 +24,7 @@
*/
#include "nghttp2_test_helper.h"
#include <stdio.h>
#include <assert.h>
#include <CUnit/CUnit.h>

View File

@ -58,7 +58,7 @@ if(ENABLE_THIRD_PARTY)
"MRUBY_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/build_config.rb"
"BUILD_DIR=${MRUBY_BUILD_DIR}"
"INSTALL_DIR=${MRUBY_BUILD_DIR}/install/bin"
"CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}"
"MRUBY_CC=${CMAKE_C_COMPILER}" "MRUBY_CXX=${CMAKE_CXX_COMPILER}"
"${CMAKE_CURRENT_SOURCE_DIR}/mruby/minirake"
-f "${CMAKE_CURRENT_SOURCE_DIR}/mruby/Rakefile"
${_byproducts}

View File

@ -100,6 +100,7 @@ EXTRA_DIST += \
mruby/lib/mruby/lockfile.rb \
mruby/lib/mruby/build.rb \
mruby/lib/mruby/core_ext.rb \
mruby/lib/mruby/doc.rb \
mruby/lib/mruby/gem.rb \
mruby/lib/mruby/presym.rb \
mruby/lib/mruby/source.rb \
@ -107,6 +108,10 @@ EXTRA_DIST += \
mruby/examples/mrbgems/ruby_extension_example/README.md \
mruby/examples/mrbgems/ruby_extension_example/test/example.rb \
mruby/examples/mrbgems/ruby_extension_example/mrblib/example.rb \
mruby/examples/mrbgems/cdata_extension_example/mrbgem.rake \
mruby/examples/mrbgems/cdata_extension_example/README.md \
mruby/examples/mrbgems/cdata_extension_example/test/example.c \
mruby/examples/mrbgems/cdata_extension_example/src/example.c \
mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake \
mruby/examples/mrbgems/c_and_ruby_extension_example/README.md \
mruby/examples/mrbgems/c_and_ruby_extension_example/test/example.rb \
@ -117,6 +122,9 @@ EXTRA_DIST += \
mruby/examples/mrbgems/c_extension_example/test/example.rb \
mruby/examples/mrbgems/c_extension_example/test/example.c \
mruby/examples/mrbgems/c_extension_example/src/example.c \
mruby/examples/mrbgems/mruby-YOUR-bigint/TODO-HINT.md \
mruby/examples/mrbgems/mruby-YOUR-bigint/core/bigint.c \
mruby/examples/mrbgems/mruby-YOUR-bigint/mrbgem.rake \
mruby/test/bintest.rb \
mruby/test/t/enumerable.rb \
mruby/test/t/comparable.rb \
@ -162,8 +170,10 @@ EXTRA_DIST += \
mruby/test/t/standarderror.rb \
mruby/test/t/superclass.rb \
mruby/test/assert.rb \
mruby/super-linter.report/.keep \
mruby/TODO.md \
mruby/Doxyfile \
mruby/mrblib/00kernel.rb \
mruby/mrblib/range.rb \
mruby/mrblib/kernel.rb \
mruby/mrblib/hash.rb \
@ -186,12 +196,15 @@ EXTRA_DIST += \
mruby/src/load.c \
mruby/src/codedump.c \
mruby/src/error.c \
mruby/src/readfloat.c \
mruby/src/state.c \
mruby/src/string.c \
mruby/src/numeric.c \
mruby/src/readnum.c \
mruby/src/enum.c \
mruby/src/print.c \
mruby/src/readint.c \
mruby/src/numops.c \
mruby/src/hash.c \
mruby/src/version.c \
mruby/src/backtrace.c \
@ -210,7 +223,6 @@ EXTRA_DIST += \
mruby/src/compar.c \
mruby/src/value_array.h \
mruby/src/pool.c \
mruby/src/readflt.c \
mruby/mruby-source.gemspec \
mruby/mrbgems/mruby-eval/mrbgem.rake \
mruby/mrbgems/mruby-eval/test/eval.rb \
@ -223,6 +235,12 @@ EXTRA_DIST += \
mruby/mrbgems/mruby-string-ext/mrblib/string.rb \
mruby/mrbgems/mruby-string-ext/src/string.c \
mruby/mrbgems/default-no-stdio.gembox \
mruby/mrbgems/mruby-set/mrbgem.rake \
mruby/mrbgems/mruby-set/README.md \
mruby/mrbgems/mruby-set/test/set.rb \
mruby/mrbgems/mruby-set/mrblib/set.rb \
mruby/mrbgems/mruby-set/LICENSE \
mruby/mrbgems/mruby-set/mruby-set.gem \
mruby/mrbgems/mruby-method/mrbgem.rake \
mruby/mrbgems/mruby-method/README.md \
mruby/mrbgems/mruby-method/test/method.rb \
@ -249,6 +267,12 @@ EXTRA_DIST += \
mruby/mrbgems/mruby-struct/test/struct.rb \
mruby/mrbgems/mruby-struct/mrblib/struct.rb \
mruby/mrbgems/mruby-struct/src/struct.c \
mruby/mrbgems/mruby-bigint/core/bigint.c \
mruby/mrbgems/mruby-bigint/core/bigint.h \
mruby/mrbgems/mruby-bigint/mrbgem.rake \
mruby/mrbgems/mruby-bigint/README.md \
mruby/mrbgems/mruby-bigint/test/bigint.rb \
mruby/mrbgems/mruby-bigint/README-fgmp.md \
mruby/mrbgems/mruby-symbol-ext/mrbgem.rake \
mruby/mrbgems/mruby-symbol-ext/test/symbol.rb \
mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb \
@ -283,6 +307,16 @@ EXTRA_DIST += \
mruby/mrbgems/mruby-proc-ext/test/proc.c \
mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb \
mruby/mrbgems/mruby-proc-ext/src/proc.c \
mruby/mrbgems/mruby-data/mrbgem.rake \
mruby/mrbgems/mruby-data/test/data.rb \
mruby/mrbgems/mruby-data/src/data.c \
mruby/mrbgems/mruby-dir/mrbgem.rake \
mruby/mrbgems/mruby-dir/README.md \
mruby/mrbgems/mruby-dir/test/dir.rb \
mruby/mrbgems/mruby-dir/test/dirtest.c \
mruby/mrbgems/mruby-dir/mrblib/dir.rb \
mruby/mrbgems/mruby-dir/src/Win/dirent.c \
mruby/mrbgems/mruby-dir/src/dir.c \
mruby/mrbgems/mruby-object-ext/mrbgem.rake \
mruby/mrbgems/mruby-object-ext/test/nil.rb \
mruby/mrbgems/mruby-object-ext/test/object.rb \
@ -292,6 +326,7 @@ EXTRA_DIST += \
mruby/mrbgems/mruby-kernel-ext/test/kernel.rb \
mruby/mrbgems/mruby-kernel-ext/src/kernel.c \
mruby/mrbgems/mruby-class-ext/mrbgem.rake \
mruby/mrbgems/mruby-class-ext/test/class.rb \
mruby/mrbgems/mruby-class-ext/test/module.rb \
mruby/mrbgems/mruby-class-ext/mrblib/module.rb \
mruby/mrbgems/mruby-class-ext/src/class.c \
@ -425,12 +460,21 @@ EXTRA_DIST += \
mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c \
mruby/mrbgems/mruby-bin-strip/mrbgem.rake \
mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb \
mruby/mrbgems/mruby-errno/mrbgem.rake \
mruby/mrbgems/mruby-errno/README.md \
mruby/mrbgems/mruby-errno/test/errno.rb \
mruby/mrbgems/mruby-errno/mrblib/errno.rb \
mruby/mrbgems/mruby-errno/src/gen.rb \
mruby/mrbgems/mruby-errno/src/known_errors_def.cstub \
mruby/mrbgems/mruby-errno/src/known_errors.def \
mruby/mrbgems/mruby-errno/src/errno.c \
mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake \
mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb \
mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb \
mruby/mrbgems/mruby-math/mrbgem.rake \
mruby/mrbgems/mruby-math/test/math.rb \
mruby/mrbgems/mruby-math/src/math.c \
mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/stub.c \
mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c \
mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake \
mruby/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb \
@ -451,6 +495,7 @@ EXTRA_DIST += \
mruby/doc/internal/opcode.md \
mruby/doc/internal/boxing.md \
mruby/doc/limitations.md \
mruby/doc/mruby3.2.md \
mruby/doc/guides/compile.md \
mruby/doc/guides/mrbgems.md \
mruby/doc/guides/debugger.md \
@ -484,6 +529,7 @@ EXTRA_DIST += \
mruby/include/mruby/boxing_nan.h \
mruby/include/mruby/value.h \
mruby/include/mruby/endian.h \
mruby/include/mruby/internal.h \
mruby/include/mruby/dump.h \
mruby/include/mruby/version.h \
mruby/include/mruby/khash.h \
@ -528,10 +574,9 @@ mruby:
MRUBY_CONFIG="${abs_builddir}/mruby/build/build_config.rb" \
BUILD_DIR="${abs_builddir}/mruby/build" \
INSTALL_DIR="${abs_builddir}/mruby/build/install/bin" \
CC="${CC}" CXX="$(firstword $(CXX))" LD="${LD}" \
CFLAGS="${CPPFLAGS} ${CFLAGS}" \
CXXFLAGS="$(wordlist 2, $(words $(CXX)), $(CXX)) ${CPPFLAGS} ${CXXFLAGS}" \
LDFLAGS="${LDFLAGS}" \
MRUBY_CC="${CC}" MRUBY_CXX="$(firstword $(CXX))" MRUBY_LD="${LD}" \
MRUBY_AR="${AR}" \
HOST="${host}" BUILD="${build}" \
"${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile"
all-local: mruby
@ -540,7 +585,7 @@ clean-local:
[ ! -f "${abs_builddir}/mruby/build/build_config.rb" ] || \
MRUBY_CONFIG="${abs_builddir}/mruby/build/build_config.rb" \
BUILD_DIR="${abs_builddir}/mruby/build" \
CC="${CC}" \
MRUBY_CC="${CC}" \
"${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile" clean
endif # HAVE_MRUBY

View File

@ -1,6 +1,16 @@
MRuby::Build.new do |conf|
toolchain :clang if ENV['CC'].include? "clang"
toolchain :gcc if ENV['CC'].include? "gcc"
def config(conf)
toolchain :clang if ENV['MRUBY_CC'].include? "clang"
toolchain :gcc if ENV['MRUBY_CC'].include? "gcc"
conf.cc.command = ENV['MRUBY_CC']
conf.cxx.command = ENV['MRUBY_CXX']
if ENV['MRUBY_LD']
conf.linker.command = ENV['MRUBY_LD']
end
if ENV['MRUBY_AR']
conf.archiver.command = ENV['MRUBY_AR']
end
# C++ project needs this. Without this, mruby exception does not
# properly destroy C++ object allocated on stack.
@ -12,3 +22,13 @@ MRuby::Build.new do |conf|
conf.gembox 'default'
conf.gem :core => 'mruby-eval'
end
if ENV['BUILD'] == ENV['HOST'] then
MRuby::Build.new do |conf|
config(conf)
end
else
MRuby::CrossBuild.new(ENV['HOST']) do |conf|
config(conf)
end
end

View File

@ -61,33 +61,41 @@ checks could be performed to get even stricter verification of the llhttp.
## Usage
```C
#include "stdio.h"
#include "llhttp.h"
#include "string.h"
llhttp_t parser;
llhttp_settings_t settings;
int handle_on_message_complete(llhttp_t* parser) {
fprintf(stdout, "Message completed!\n");
return 0;
}
/* Initialize user callbacks and settings */
llhttp_settings_init(&settings);
int main() {
llhttp_t parser;
llhttp_settings_t settings;
/* Set user callback */
settings.on_message_complete = handle_on_message_complete;
/*Initialize user callbacks and settings */
llhttp_settings_init(&settings);
/* Initialize the parser in HTTP_BOTH mode, meaning that it will select between
* HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first
* input.
*/
llhttp_init(&parser, HTTP_BOTH, &settings);
/*Set user callback */
settings.on_message_complete = handle_on_message_complete;
/* Parse request! */
const char* request = "GET / HTTP/1.1\r\n\r\n";
int request_len = strlen(request);
/*Initialize the parser in HTTP_BOTH mode, meaning that it will select between
*HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first
*input.
*/
llhttp_init(&parser, HTTP_BOTH, &settings);
enum llhttp_errno err = llhttp_execute(&parser, request, request_len);
if (err == HPE_OK) {
/* Successfully parsed! */
} else {
fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err),
parser.reason);
/*Parse request! */
const char* request = "GET / HTTP/1.1\r\n\r\n";
int request_len = strlen(request);
enum llhttp_errno err = llhttp_execute(&parser, request, request_len);
if (err == HPE_OK) {
fprintf(stdout, "Successfully parsed!\n");
} else {
fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason);
}
}
```
For more information on API usage, please refer to [src/native/api.h](https://github.com/nodejs/llhttp/blob/main/src/native/api.h).
@ -345,17 +353,34 @@ make
### Using with CMake
If you want to use this library in a CMake project you can use the snippet below.
If you want to use this library in a CMake project as a shared library, you can use the snippet below.
```
FetchContent_Declare(llhttp
URL "https://github.com/nodejs/llhttp/archive/refs/tags/v6.0.5.tar.gz") # Using version 6.0.5
URL "https://github.com/nodejs/llhttp/archive/refs/tags/release/v8.1.0.tar.gz")
FetchContent_MakeAvailable(llhttp)
target_link_libraries(${EXAMPLE_PROJECT_NAME} ${PROJECT_LIBRARIES} llhttp ${PROJECT_NAME})
# Link with the llhttp_shared target
target_link_libraries(${EXAMPLE_PROJECT_NAME} ${PROJECT_LIBRARIES} llhttp_shared ${PROJECT_NAME})
```
If you want to use this library in a CMake project as a static library, you can set some cache variables first.
```
FetchContent_Declare(llhttp
URL "https://github.com/nodejs/llhttp/archive/refs/tags/release/v8.1.0.tar.gz")
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "")
set(BUILD_STATIC_LIBS ON CACHE INTERNAL "")
FetchContent_MakeAvailable(llhttp)
# Link with the llhttp_static target
target_link_libraries(${EXAMPLE_PROJECT_NAME} ${PROJECT_LIBRARIES} llhttp_static ${PROJECT_NAME})
```
_Note that using the git repo directly (e.g., via a git repo url and tag) will not work with FetchContent_Declare because [CMakeLists.txt](./CMakeLists.txt) requires string replacements (e.g., `_RELEASE_`) before it will build._
## Building on Windows
### Installation

View File

@ -3,7 +3,7 @@
#define LLHTTP_VERSION_MAJOR 8
#define LLHTTP_VERSION_MINOR 1
#define LLHTTP_VERSION_PATCH 0
#define LLHTTP_VERSION_PATCH 1
#ifndef LLHTTP_STRICT_MODE
# define LLHTTP_STRICT_MODE 0

View File

@ -1,4 +1,11 @@
{
'variables': {
'llhttp_sources': [
'src/llhttp.c',
'src/api.c',
'src/http.c',
]
},
'targets': [
{
'target_name': 'llhttp',
@ -7,7 +14,9 @@
'direct_dependent_settings': {
'include_dirs': [ 'include' ],
},
'sources': [ 'src/llhttp.c', 'src/api.c', 'src/http.c' ],
'sources': [
'<@(llhttp_sources)',
],
},
]
}

File diff suppressed because it is too large Load Diff

2
third-party/mruby vendored

@ -1 +1 @@
Subproject commit 1bec3392300e99ea037a8e18aa9694c83390cea3
Subproject commit 87260e7bb1a9edfb2ce9b41549c4142129061ca5

@ -1 +1 @@
Subproject commit f31bf05f792b0c41de8f774c68d278df2067aaf8
Subproject commit ab9ac5ecb30188683cf29b45c0e66ca2c94394f9