mirror of
https://gitee.com/openharmony/third_party_nghttp2
synced 2024-11-26 17:40:25 +00:00
commit
e0e3425635
302
.github/workflows/build.yml
vendored
302
.github/workflows/build.yml
vendored
@ -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
|
||||
|
1
.github/workflows/fuzz.yml
vendored
1
.github/workflows/fuzz.yml
vendored
@ -1,5 +1,6 @@
|
||||
name: CIFuzz
|
||||
on: [pull_request]
|
||||
permissions: read-all
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -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}')
|
||||
|
@ -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
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
"Name" : "nghttp2",
|
||||
"License" : "The MIT License",
|
||||
"License File" : "COPYING",
|
||||
"Version Number" : "1.52.0",
|
||||
"Version Number" : "1.55.0",
|
||||
"Owner" : "zhuwenchao@huawei.com",
|
||||
"Upstream URL" : "https://nghttp2.org",
|
||||
"Description" : "nghttp2 is an implementation of HTTP/2 and its header compression algorithm HPACK in C."
|
||||
|
18
README.rst
18
README.rst
@ -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 ..
|
||||
|
@ -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)
|
43
cmake/FindLibngtcp2_crypto_quictls.cmake
Normal file
43
cmake/FindLibngtcp2_crypto_quictls.cmake
Normal 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)
|
@ -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
|
||||
|
41
configure.ac
41
configure.ac
@ -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}')
|
||||
|
12
doc/h2load.1
12
doc/h2load.1
@ -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
|
||||
|
@ -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__':
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
338
doc/nghttpx.1
338
doc/nghttpx.1
@ -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
|
||||
|
@ -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
26
go.mod
@ -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
48
go.sum
@ -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=
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
|
@ -50,6 +50,7 @@ nghttp2_lib_sources = [
|
||||
"nghttp2_stream.c",
|
||||
"nghttp2_submit.c",
|
||||
"nghttp2_version.c",
|
||||
"sfparse.c",
|
||||
]
|
||||
|
||||
config("nghttp2_lib_config") {
|
||||
|
@ -25,6 +25,7 @@ set(NGHTTP2_SOURCES
|
||||
nghttp2_rcbuf.c
|
||||
nghttp2_extpri.c
|
||||
nghttp2_debug.c
|
||||
sfparse.c
|
||||
)
|
||||
|
||||
set(NGHTTP2_RES "")
|
||||
|
@ -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 \
|
||||
|
@ -188,6 +188,7 @@
|
||||
nghttp2_option_set_no_clo;
|
||||
nghttp2_option_set_no_htt;
|
||||
nghttp2_option_set_no_rec;
|
||||
nghttp2_option_set_no_auto_window_update;
|
||||
nghttp2_option_set_peer_m;
|
||||
nghttp2_option_set_user_r;
|
||||
nghttp2_outbound_item_fre;
|
||||
@ -254,6 +255,7 @@
|
||||
nghttp2_session_get_hd_in;
|
||||
nghttp2_session_get_last_;
|
||||
nghttp2_session_get_local;
|
||||
nghttp2_session_get_local_window_size;
|
||||
nghttp2_session_get_next_;
|
||||
nghttp2_session_get_outbo;
|
||||
nghttp2_session_get_remot;
|
||||
@ -355,16 +357,23 @@
|
||||
nghttp2_session_callbacks_new;
|
||||
nghttp2_session_callbacks_set_send_callback;
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback;
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback;
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback;
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback;
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback;
|
||||
nghttp2_session_callbacks_set_on_header_callback;
|
||||
nghttp2_session_callbacks_set_error_callback;
|
||||
nghttp2_session_client_new;
|
||||
nghttp2_session_client_new2;
|
||||
nghttp2_session_callbacks_del;
|
||||
nghttp2_session_set_local_window_size;
|
||||
nghttp2_session_get_stream_user_data;
|
||||
nghttp2_session_get_stream_remote_window_size;
|
||||
nghttp2_session_get_stream_effective_recv_data_length;
|
||||
nghttp2_session_get_stream_effective_local_window_size;
|
||||
nghttp2_session_get_stream_local_window_size;
|
||||
nghttp2_session_get_remote_settings;
|
||||
nghttp2_session_get_remote_window_size;
|
||||
local:
|
||||
*;
|
||||
};
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
1146
lib/sfparse.c
Normal file
File diff suppressed because it is too large
Load Diff
409
lib/sfparse.h
Normal file
409
lib/sfparse.h
Normal 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 */
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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@ \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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", "*/*"},
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
284
src/shrpx.cc
284
src/shrpx.cc
@ -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
|
||||
,
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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(¶ms);
|
||||
@ -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: "
|
||||
|
@ -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_;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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 &&
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
27
src/testdata/Makefile.am
vendored
Normal 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
10
src/testdata/ipaddr.crt
vendored
Normal 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
9
src/testdata/nosan.crt
vendored
Normal 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
9
src/testdata/nosan_ip.crt
vendored
Normal 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
10
src/testdata/verify_hostname.crt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBeTCCAR6gAwIBAgIBATAKBggqhkjOPQQDAjAUMRIwEAYDVQQDEwlsb2NhbGhv
|
||||
c3QwHhcNMjMwMzE1MTIzNzU1WhcNMzMwMTIxMTIzNzU1WjAUMRIwEAYDVQQDEwls
|
||||
b2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATMHcWmb55fi0KHNDwM
|
||||
cYzVTAOfzJf44AqrqC+Pq2zW/ig8tPZbXf3eA/Vvp07Di+yWmuo3fGatUcY4nsx+
|
||||
Jd62o2EwXzAOBgNVHQ8BAf8EBAMCB4AwTQYDVR0RBEYwRIITbmdodHRwMi5leGFt
|
||||
cGxlLmNvbYIVKi5uZ2h0dHAyLmV4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAA
|
||||
AAAAAAABMAoGCCqGSM49BAMCA0kAMEYCIQDQJFRJ3Ah4cGy7bwpkzVYeTgG+NhDa
|
||||
55F4dPtJp9dS8wIhALQ9qf379lke1jVHg2t84iZLo3bL23RgICMezEYvqO3K
|
||||
-----END CERTIFICATE-----
|
17
src/util.cc
17
src/util.cc
@ -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:
|
||||
|
19
src/util.h
19
src/util.h
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
#include "nghttp2_buf_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_buf.h"
|
||||
|
@ -25,6 +25,8 @@
|
||||
*/
|
||||
#include "nghttp2_extpri_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_extpri.h"
|
||||
|
@ -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;
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
#include "nghttp2_helper_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -25,6 +25,8 @@
|
||||
*/
|
||||
#include "nghttp2_map_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_map.h"
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
#include "nghttp2_npn_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
#include "nghttp2_pq_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_pq.h"
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
#include "nghttp2_queue_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_queue.h"
|
||||
|
@ -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;
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
#include "nghttp2_stream_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_stream.h"
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
#include "nghttp2_test_helper.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
2
third-party/CMakeLists.txt
vendored
2
third-party/CMakeLists.txt
vendored
@ -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}
|
||||
|
57
third-party/Makefile.am
vendored
57
third-party/Makefile.am
vendored
@ -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
|
||||
|
26
third-party/build_config.rb
vendored
26
third-party/build_config.rb
vendored
@ -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
|
||||
|
71
third-party/llhttp/README.md
vendored
71
third-party/llhttp/README.md
vendored
@ -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
|
||||
|
2
third-party/llhttp/include/llhttp.h
vendored
2
third-party/llhttp/include/llhttp.h
vendored
@ -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
|
||||
|
11
third-party/llhttp/llhttp.gyp
vendored
11
third-party/llhttp/llhttp.gyp
vendored
@ -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)',
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
1140
third-party/llhttp/src/llhttp.c
vendored
1140
third-party/llhttp/src/llhttp.c
vendored
File diff suppressed because it is too large
Load Diff
2
third-party/mruby
vendored
2
third-party/mruby
vendored
@ -1 +1 @@
|
||||
Subproject commit 1bec3392300e99ea037a8e18aa9694c83390cea3
|
||||
Subproject commit 87260e7bb1a9edfb2ce9b41549c4142129061ca5
|
2
third-party/neverbleed
vendored
2
third-party/neverbleed
vendored
@ -1 +1 @@
|
||||
Subproject commit f31bf05f792b0c41de8f774c68d278df2067aaf8
|
||||
Subproject commit ab9ac5ecb30188683cf29b45c0e66ca2c94394f9
|
Loading…
Reference in New Issue
Block a user