mirror of
https://github.com/nghttp2/nghttp2.git
synced 2026-03-24 23:16:15 +08:00
Compare commits
198 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af36d716b9 | ||
|
|
6b75efe257 | ||
|
|
7310f0583c | ||
|
|
58ef096c2f | ||
|
|
27198cfbd9 | ||
|
|
b4cccf03d8 | ||
|
|
b4a5bc1830 | ||
|
|
4d0a6e4d00 | ||
|
|
a74e8d61fa | ||
|
|
31d6973b2f | ||
|
|
e82b41d6b8 | ||
|
|
f4139aa4cf | ||
|
|
b17a692a78 | ||
|
|
da755b2f05 | ||
|
|
6f55c84a13 | ||
|
|
d4b813b862 | ||
|
|
99065afe91 | ||
|
|
4f7f90eaa1 | ||
|
|
d0ad6a2f90 | ||
|
|
4aff44b6b9 | ||
|
|
2d9ce2eab1 | ||
|
|
dc1ac54552 | ||
|
|
35383a4f0b | ||
|
|
52f895500b | ||
|
|
5c947628d5 | ||
|
|
483ea05120 | ||
|
|
745b025e07 | ||
|
|
2d2c666df0 | ||
|
|
864ba96694 | ||
|
|
310c239817 | ||
|
|
caed460cd7 | ||
|
|
92ad06a703 | ||
|
|
a86693f273 | ||
|
|
41d3be9070 | ||
|
|
3469972630 | ||
|
|
285cf1c884 | ||
|
|
68016b9982 | ||
|
|
9727c13113 | ||
|
|
19898e103b | ||
|
|
461c467ab2 | ||
|
|
74c131b865 | ||
|
|
f6431d721b | ||
|
|
9e2252a146 | ||
|
|
a79b59bd85 | ||
|
|
74dafcdc4e | ||
|
|
f12a7e7ce3 | ||
|
|
579d55dae2 | ||
|
|
b367158b7b | ||
|
|
2ce0dfaf00 | ||
|
|
5604194a3b | ||
|
|
cf2d5b93e2 | ||
|
|
af2f0f5c33 | ||
|
|
db091f096d | ||
|
|
4f3eaff3c4 | ||
|
|
b92c98e8a5 | ||
|
|
21b1ae3ed5 | ||
|
|
5907198a7c | ||
|
|
8b06168dbf | ||
|
|
d2a4e4cf89 | ||
|
|
c9971a6f29 | ||
|
|
577650a30f | ||
|
|
71e73659d7 | ||
|
|
f7e49bc9dd | ||
|
|
8cb8c3baa8 | ||
|
|
9f309fdae6 | ||
|
|
fce8e889c9 | ||
|
|
478a1e5608 | ||
|
|
aa1fcda83f | ||
|
|
726dce78cd | ||
|
|
47e1c42600 | ||
|
|
16ba46cbe1 | ||
|
|
53c04fdbcb | ||
|
|
7b1a2343cb | ||
|
|
d8e226271d | ||
|
|
ae4ee62134 | ||
|
|
bcc0b9cfc5 | ||
|
|
e4f55dc528 | ||
|
|
4db13a7d4d | ||
|
|
24e72ca9d9 | ||
|
|
378ce7c0b0 | ||
|
|
aeeadcad7b | ||
|
|
34409d4e62 | ||
|
|
d1fbcfacde | ||
|
|
14d2145299 | ||
|
|
e7e0fb7b9f | ||
|
|
fbded45cd1 | ||
|
|
95321ef9b3 | ||
|
|
883f9370e4 | ||
|
|
6813f88cac | ||
|
|
4f78e96df2 | ||
|
|
6168c8be53 | ||
|
|
1b3c183521 | ||
|
|
4eda36bb11 | ||
|
|
9b4393df30 | ||
|
|
ad65ce60f3 | ||
|
|
435b6d4505 | ||
|
|
2fc46fa64c | ||
|
|
4170fd8c31 | ||
|
|
cd3c01267d | ||
|
|
545ee4ff83 | ||
|
|
619e861ddf | ||
|
|
68f77a3475 | ||
|
|
c14f38b84f | ||
|
|
f5a9a72c2e | ||
|
|
dcb6842fb6 | ||
|
|
e99404a6a6 | ||
|
|
2a25753088 | ||
|
|
e93993af5d | ||
|
|
a0b9f89677 | ||
|
|
d45577eeea | ||
|
|
34e45c6c66 | ||
|
|
507e6c8f36 | ||
|
|
b320a57c78 | ||
|
|
f8f777b674 | ||
|
|
b35e136d0b | ||
|
|
13b25a40ac | ||
|
|
c4e990bc32 | ||
|
|
42b7d162e7 | ||
|
|
5653439558 | ||
|
|
05febf8a3a | ||
|
|
1d6f58461a | ||
|
|
e94a42d22a | ||
|
|
85cf340e27 | ||
|
|
1595540dc7 | ||
|
|
7fa2531bd8 | ||
|
|
d50fc14deb | ||
|
|
ae9dedbc8b | ||
|
|
f1468b9f7a | ||
|
|
d3d90be75d | ||
|
|
8e8b057d68 | ||
|
|
3e78214051 | ||
|
|
76bbcad48c | ||
|
|
a107cc8c76 | ||
|
|
c51d154977 | ||
|
|
f3cc363b59 | ||
|
|
dea60b982c | ||
|
|
9d75e0048e | ||
|
|
a19239863d | ||
|
|
73b773710d | ||
|
|
d3f0a6d9ee | ||
|
|
a581d84d99 | ||
|
|
8a8c319c72 | ||
|
|
c186b00b5c | ||
|
|
c322eec789 | ||
|
|
0c570c823d | ||
|
|
d6f85b11ce | ||
|
|
ba1747b97c | ||
|
|
6c79b9afde | ||
|
|
18f2edf50a | ||
|
|
1c98cc6673 | ||
|
|
ba9b332627 | ||
|
|
279cee2af7 | ||
|
|
05d77b6d30 | ||
|
|
15a13d5ac4 | ||
|
|
bf3cc82a4b | ||
|
|
b91f598282 | ||
|
|
fa4a274a18 | ||
|
|
fc73d69ffb | ||
|
|
b4d6889fb6 | ||
|
|
73ae764211 | ||
|
|
1c6d26488b | ||
|
|
b485a87b57 | ||
|
|
b0f79f18bb | ||
|
|
14c02f6bc3 | ||
|
|
3a79f38503 | ||
|
|
800023a8e9 | ||
|
|
a957322040 | ||
|
|
a7fa441f0a | ||
|
|
61d3a68415 | ||
|
|
476a5f805e | ||
|
|
81b74b4e42 | ||
|
|
61cb0095b3 | ||
|
|
e4454672f0 | ||
|
|
e15a5517c7 | ||
|
|
9b0044d051 | ||
|
|
e9e5e15bbf | ||
|
|
2c7ef6442d | ||
|
|
d3ecf78031 | ||
|
|
d01db47215 | ||
|
|
8a760d0726 | ||
|
|
73bfe4bf21 | ||
|
|
6e5e9bceca | ||
|
|
0476f0efbc | ||
|
|
ca23a490c3 | ||
|
|
ee2a4b625b | ||
|
|
cec4bf08a2 | ||
|
|
0b67049243 | ||
|
|
ebf4b7eaee | ||
|
|
0bf5b764fa | ||
|
|
081eb29e9f | ||
|
|
ca81d89fe1 | ||
|
|
450ed6afce | ||
|
|
e72f4af5de | ||
|
|
3fa6a6349c | ||
|
|
6c0fd9400d | ||
|
|
de81da7621 | ||
|
|
8593b1f46c | ||
|
|
0e9d325dee |
6
.github/workflows/android.yml
vendored
6
.github/workflows/android.yml
vendored
@@ -15,10 +15,10 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
file: Dockerfile.android
|
||||
|
||||
76
.github/workflows/build.yml
vendored
76
.github/workflows/build.yml
vendored
@@ -4,49 +4,53 @@ on: [push, pull_request]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
env:
|
||||
LIBBPF_VERSION: v1.6.2
|
||||
OPENSSL1_VERSION: 1_1_1w+quic
|
||||
OPENSSL3_VERSION: 3.6.0
|
||||
BORINGSSL_VERSION: db1a8456167249f95b854a1cd24c6b553d0f1567
|
||||
AWSLC_VERSION: v1.62.0
|
||||
NGHTTP3_VERSION: v1.12.0
|
||||
NGTCP2_VERSION: v1.17.0
|
||||
WOLFSSL_VERSION: v5.8.2-stable
|
||||
BORINGSSL_VERSION: 52975ff6ea9fb076e53025b82f2e80a23b027a5c
|
||||
AWSLC_VERSION: v1.66.2
|
||||
NGHTTP3_VERSION: v1.15.0
|
||||
NGTCP2_VERSION: v1.20.0
|
||||
WOLFSSL_VERSION: v5.8.4-stable
|
||||
|
||||
jobs:
|
||||
build-cache:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-24.04, macos-14, macos-15]
|
||||
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-14, macos-15]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Restore libbpf cache
|
||||
id: cache-libbpf
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
if: runner.os == 'Linux'
|
||||
with:
|
||||
path: libbpf/build
|
||||
key: ${{ matrix.os }}-libbpf-${{ env.LIBBPF_VERSION }}
|
||||
- name: Restore OpenSSL v1.1.1 cache
|
||||
id: cache-openssl1
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: openssl1/build
|
||||
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL1_VERSION }}
|
||||
- name: Restore OpenSSL v3.x cache
|
||||
id: cache-openssl3
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: openssl3/build
|
||||
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL3_VERSION }}
|
||||
- name: Restore BoringSSL cache
|
||||
id: cache-boringssl
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
boringssl/build/libcrypto.a
|
||||
@@ -55,7 +59,7 @@ jobs:
|
||||
key: ${{ matrix.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
|
||||
- name: Restore aws-lc cache
|
||||
id: cache-awslc
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
aws-lc/build/crypto/libcrypto.a
|
||||
@@ -64,25 +68,25 @@ jobs:
|
||||
key: ${{ matrix.os }}-awslc-${{ env.AWSLC_VERSION }}
|
||||
- name: Restore wolfSSL cache
|
||||
id: cache-wolfssl
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: wolfssl/build
|
||||
key: ${{ matrix.os }}-wolfssl-${{ env.WOLFSSL_VERSION }}
|
||||
- name: Restore nghttp3 cache
|
||||
id: cache-nghttp3
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: nghttp3/build
|
||||
key: ${{ matrix.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
|
||||
- name: Restore ngtcp2 + quictls/openssl v1.1.1 cache
|
||||
id: cache-ngtcp2-openssl1
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ngtcp2-openssl1/build
|
||||
key: ${{ matrix.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
|
||||
- name: Restore ngtcp2 + quictls/openssl v3.x cache
|
||||
id: cache-ngtcp2-openssl3
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ngtcp2-openssl3/build
|
||||
key: ${{ matrix.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL3_VERSION }}
|
||||
@@ -257,12 +261,22 @@ jobs:
|
||||
buildtool: distcheck
|
||||
http3: http3
|
||||
openssl: awslc
|
||||
- os: ubuntu-24.04-arm
|
||||
compiler: clang
|
||||
buildtool: autotools
|
||||
http3: http3
|
||||
openssl: wolfssl
|
||||
- os: ubuntu-24.04-arm
|
||||
compiler: gcc
|
||||
buildtool: autotools
|
||||
http3: http3
|
||||
openssl: wolfssl
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Linux setup
|
||||
@@ -330,7 +344,7 @@ jobs:
|
||||
echo 'CC=gcc' >> $GITHUB_ENV
|
||||
echo 'CXX=g++' >> $GITHUB_ENV
|
||||
- name: Restore libbpf cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
|
||||
with:
|
||||
path: libbpf/build
|
||||
@@ -355,21 +369,21 @@ jobs:
|
||||
echo 'LIBEV_CFLAGS='"$LIBEV_CFLAGS" >> $GITHUB_ENV
|
||||
echo 'LIBEV_LIBS='"$LIBEV_LIBS" >> $GITHUB_ENV
|
||||
- name: Restore quictls/openssl v1.1.1 cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.openssl == 'openssl1'
|
||||
with:
|
||||
path: openssl1/build
|
||||
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL1_VERSION }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Restore openssl/openssl v3.x cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.openssl == 'openssl3'
|
||||
with:
|
||||
path: openssl3/build
|
||||
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL3_VERSION }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Restore BoringSSL cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.openssl == 'boringssl'
|
||||
with:
|
||||
path: |
|
||||
@@ -379,7 +393,7 @@ jobs:
|
||||
key: ${{ matrix.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Restore aws-lc cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.openssl == 'awslc'
|
||||
with:
|
||||
path: |
|
||||
@@ -417,7 +431,7 @@ jobs:
|
||||
echo 'BORINGSSL_LIBS='"$OPENSSL_LIBS" >> $GITHUB_ENV
|
||||
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
|
||||
- name: Restore wolfSSL cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.openssl == 'wolfssl'
|
||||
with:
|
||||
path: wolfssl/build
|
||||
@@ -432,21 +446,21 @@ jobs:
|
||||
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
|
||||
echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
|
||||
- name: Restore nghttp3 cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.http3 == 'http3'
|
||||
with:
|
||||
path: nghttp3/build
|
||||
key: ${{ matrix.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Restore ngtcp2 + quictls/openssl v1.1.1 cache + BoringSSL
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.http3 == 'http3' && (matrix.openssl == 'openssl1' || matrix.openssl == 'boringssl' || matrix.openssl == 'wolfssl')
|
||||
with:
|
||||
path: ngtcp2-openssl1/build
|
||||
key: ${{ matrix.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Restore ngtcp2 + quictls/openssl v3.x cache + aws-lc
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
if: matrix.http3 == 'http3' && (matrix.openssl == 'openssl3' || matrix.openssl == 'awslc')
|
||||
with:
|
||||
path: ngtcp2-openssl3/build
|
||||
@@ -527,7 +541,7 @@ jobs:
|
||||
- uses: actions/setup-go@v6
|
||||
if: matrix.buildtool != 'distcheck'
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: Integration test
|
||||
# Integration tests for nghttpx; autotools erases build
|
||||
# artifacts.
|
||||
@@ -549,7 +563,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Prepare for i386
|
||||
@@ -597,10 +611,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: microsoft/setup-msbuild@v2
|
||||
- uses: microsoft/setup-msbuild@v3
|
||||
- name: Configure cmake
|
||||
run: cmake -B build -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_GENERATOR_PLATFORM=${{ matrix.platform }} -DVCPKG_TARGET_TRIPLET=${{ matrix.arch}}-windows -DBUILD_STATIC_LIBS=ON -DBUILD_TESTING=ON
|
||||
- name: Build nghttp2
|
||||
@@ -622,7 +636,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
6
.github/workflows/docker.yaml
vendored
6
.github/workflows/docker.yaml
vendored
@@ -14,11 +14,11 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: docker
|
||||
build-args: NGHTTP2_BRANCH=${{ github.ref_name }}
|
||||
|
||||
2
.github/workflows/fuzz.yml
vendored
2
.github/workflows/fuzz.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
if: failure()
|
||||
with:
|
||||
name: artifacts
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
# XXX using 1.8.90 instead of 1.9.0-DEV
|
||||
project(nghttp2 VERSION 1.68.1 LANGUAGES C)
|
||||
project(nghttp2 VERSION 1.68.90 LANGUAGES C)
|
||||
|
||||
# See versioning rule:
|
||||
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
|
||||
@@ -126,7 +126,7 @@ following libraries are required:
|
||||
<https://github.com/quictls/openssl/tree/OpenSSL_1_1_1w+quic>`_; or
|
||||
wolfSSL; or LibreSSL (does not support 0RTT); or aws-lc; or
|
||||
`BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit
|
||||
db1a8456167249f95b854a1cd24c6b553d0f1567); or OpenSSL >= 3.5.0
|
||||
52975ff6ea9fb076e53025b82f2e80a23b027a5c); or OpenSSL >= 3.5.0
|
||||
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ >= 1.16.0
|
||||
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 1.12.0
|
||||
|
||||
@@ -340,7 +340,7 @@ Build aws-lc:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b v1.62.0 https://github.com/aws/aws-lc
|
||||
$ git clone --depth 1 -b v1.66.2 https://github.com/aws/aws-lc
|
||||
$ cd aws-lc
|
||||
$ cmake -B build -DDISABLE_GO=ON --install-prefix=$PWD/opt
|
||||
$ make -j$(nproc) -C build
|
||||
@@ -351,7 +351,7 @@ Build nghttp3:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b v1.12.0 https://github.com/ngtcp2/nghttp3
|
||||
$ git clone --depth 1 -b v1.15.0 https://github.com/ngtcp2/nghttp3
|
||||
$ cd nghttp3
|
||||
$ git submodule update --init --depth 1
|
||||
$ autoreconf -i
|
||||
@@ -364,7 +364,7 @@ Build ngtcp2:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b v1.17.0 https://github.com/ngtcp2/ngtcp2
|
||||
$ git clone --depth 1 -b v1.20.0 https://github.com/ngtcp2/ngtcp2
|
||||
$ cd ngtcp2
|
||||
$ git submodule update --init --depth 1
|
||||
$ autoreconf -i
|
||||
|
||||
@@ -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.68.1], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.69.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
@@ -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" "Mar 18, 2026" "1.68.1" "nghttp2"
|
||||
.TH "H2LOAD" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.SH SYNOPSIS
|
||||
|
||||
149
doc/h2load.h2r
149
doc/h2load.h2r
@@ -3,107 +3,96 @@
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
REQUEST METRICS
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
requests
|
||||
total
|
||||
The number of requests h2load was instructed to make.
|
||||
The total number of requests h2load was instructed to make.
|
||||
started
|
||||
The number of requests h2load has started.
|
||||
The number of requests initiated by the tool.
|
||||
done
|
||||
The number of requests completed.
|
||||
The number of requests that reached completion.
|
||||
succeeded
|
||||
The number of requests completed successfully. Only HTTP status
|
||||
code 2xx or 3xx are considered as success.
|
||||
Requests resulting in an HTTP 2xx or 3xx status code.
|
||||
failed
|
||||
The number of requests failed, including HTTP level failures
|
||||
(non-successful HTTP status code).
|
||||
The total number of failed requests. This includes both
|
||||
``errored`` requests and requests that completed with a
|
||||
non-2xx/3xx status code.
|
||||
errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
This is the subset of the number reported in ``failed`` and most
|
||||
likely the network level failures or stream was reset by
|
||||
RST_STREAM.
|
||||
A subset of ``failed`` where the requests failed due to
|
||||
network-level issues (e.g., TCP resets, ``RST_STREAM``) rather
|
||||
than HTTP status codes.
|
||||
timeout
|
||||
The number of requests whose connection timed out before they were
|
||||
completed. This is the subset of the number reported in
|
||||
``errored``.
|
||||
A subset of ``errored`` where the connection timed out before
|
||||
completion.
|
||||
|
||||
status codes
|
||||
The number of status code h2load received.
|
||||
The specific count of received HTTP status codes categorized by
|
||||
class (2xx, 3xx, 4xx, 5xx).
|
||||
|
||||
TRAFFIC METRICS
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
traffic
|
||||
total
|
||||
The number of bytes received from the server "on the wire". If
|
||||
requests were made via TLS, this value is the number of decrypted
|
||||
bytes.
|
||||
Total application data bytes received "on the wire" (decrypted if
|
||||
using TLS).
|
||||
headers
|
||||
The number of response header bytes from the server without
|
||||
decompression. The ``space savings`` shows efficiency of header
|
||||
compression. Let ``decompressed(headers)`` to the number of bytes
|
||||
used for header fields after decompression. The ``space savings``
|
||||
is calculated by (1 - ``headers`` / ``decompressed(headers)``) *
|
||||
100. For HTTP/1.1, this is usually 0.00%, since it does not have
|
||||
header compression. For HTTP/2, it shows some insightful numbers.
|
||||
Total bytes used for response headers (pre-decompression).
|
||||
|
||||
space savings
|
||||
Header compression efficiency, calculated as:
|
||||
|
||||
(1 - headers / decompressed_headers) * 100
|
||||
|
||||
where ``headers`` is the compressed size and
|
||||
``decompressed_headers`` is the size after decompression.
|
||||
data
|
||||
The number of response body bytes received from the server.
|
||||
Total bytes received in response bodies.
|
||||
|
||||
time for request
|
||||
min
|
||||
The minimum time taken for request and response.
|
||||
max
|
||||
The maximum time taken for request and response.
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
PERFORMANCE STATISTICS
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server including TLS
|
||||
handshake.
|
||||
max
|
||||
The maximum time taken to connect to a server including TLS
|
||||
handshake.
|
||||
mean
|
||||
The mean time taken to connect to a server including TLS
|
||||
handshake.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
Metric Definitions
|
||||
request
|
||||
The duration from sending the first byte of a request to receiving
|
||||
the last byte of the response.
|
||||
connect
|
||||
The time taken to establish a connection, including TLS
|
||||
handshakes.
|
||||
TTFB
|
||||
The duration until the first byte of application data is received
|
||||
from the server (decrypted if using TLS).
|
||||
req/s
|
||||
The requests per second measured individually across all clients.
|
||||
min RTT
|
||||
The minimum RTT (QUIC).
|
||||
smoothed RTT
|
||||
The smoothed RTT (QUIC).
|
||||
packets sent
|
||||
The number of packets sent (QUIC).
|
||||
packets recv
|
||||
The number of packets received (QUIC).
|
||||
packets lost
|
||||
The number of packets declared lost (QUIC).
|
||||
GRO packets
|
||||
The number of packets received in a single recvmsg call (QUIC).
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
Distribution Fields
|
||||
min / max
|
||||
The absolute minimum and maximum values recorded.
|
||||
median
|
||||
The 50th percentile value.
|
||||
p95 / p99
|
||||
The 95th and 99th percentiles, indicating tail performance.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
The arithmetic average of all samples.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
The standard deviation (measure of data dispersion).
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
req/s
|
||||
min
|
||||
The minimum request per second among all clients.
|
||||
max
|
||||
The maximum request per second among all clients.
|
||||
mean
|
||||
The mean request per second among all clients.
|
||||
sd
|
||||
The standard deviation of request per second among all clients.
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
The percentage of successful samples falling within one standard
|
||||
deviation of the mean (mean +/- sd).
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
@@ -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" "Mar 18, 2026" "1.68.1" "nghttp2"
|
||||
.TH "NGHTTP" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 client
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -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" "Mar 18, 2026" "1.68.1" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 server
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -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" "Mar 18, 2026" "1.68.1" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 proxy
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -243,6 +243,10 @@ exists and serves incoming requests.
|
||||
If you want to just reload configuration file without executing new
|
||||
binary, send SIGHUP to nghttpx main process.
|
||||
|
||||
For TCP connections, nghttpx does moderate effort not to lose a
|
||||
connection during this process. To make it more robust, consider to
|
||||
enable ``net.ipv4.tcp_migrate_req``.
|
||||
|
||||
Re-opening log files
|
||||
--------------------
|
||||
|
||||
|
||||
@@ -2,75 +2,83 @@ FROM debian:12 as build
|
||||
|
||||
ARG NGHTTP2_BRANCH=master
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
git clang-19 make binutils autoconf automake autotools-dev libtool \
|
||||
pkg-config cmake cmake-data \
|
||||
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
|
||||
libelf-dev libbrotli-dev
|
||||
RUN <<EOF
|
||||
set -e
|
||||
|
||||
RUN git clone --recursive --shallow-submodules --depth 1 -b v1.62.0 https://github.com/aws/aws-lc && \
|
||||
cd aws-lc && \
|
||||
export CC=clang-19 CXX=clang++-19 && \
|
||||
cmake -B build -DDISABLE_GO=ON && \
|
||||
make -j$(nproc) -C build && \
|
||||
cmake --install build && \
|
||||
cd .. && \
|
||||
rm -rf aws-lc
|
||||
apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
git clang-19 make binutils autoconf automake autotools-dev libtool \
|
||||
pkg-config cmake cmake-data \
|
||||
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
|
||||
libelf-dev libbrotli-dev
|
||||
|
||||
RUN git clone --recursive --shallow-submodules --depth 1 -b v1.12.0 https://github.com/ngtcp2/nghttp3 && \
|
||||
cd nghttp3 && \
|
||||
autoreconf -i && \
|
||||
./configure --disable-dependency-tracking --enable-lib-only \
|
||||
CC=clang-19 CXX=clang++-19 && \
|
||||
make -j$(nproc) && \
|
||||
make install-strip && \
|
||||
cd .. && \
|
||||
rm -rf nghttp3
|
||||
git clone --recursive --shallow-submodules --depth 1 -b v1.66.2 \
|
||||
https://github.com/aws/aws-lc
|
||||
cd aws-lc
|
||||
export CC=clang-19 CXX=clang++-19
|
||||
cmake -B build -DDISABLE_GO=ON
|
||||
make -j$(nproc) -C build
|
||||
cmake --install build
|
||||
cd ..
|
||||
rm -rf aws-lc
|
||||
|
||||
RUN git clone --recursive --shallow-submodules --depth 1 -b v1.17.0 https://github.com/ngtcp2/ngtcp2 && \
|
||||
cd ngtcp2 && \
|
||||
autoreconf -i && \
|
||||
./configure --disable-dependency-tracking --enable-lib-only \
|
||||
--with-boringssl \
|
||||
CC=clang-19 CXX=clang++-19 \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
BORINGSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
|
||||
make -j$(nproc) && \
|
||||
make install-strip && \
|
||||
cd .. && \
|
||||
rm -rf ngtcp2
|
||||
git clone --recursive --shallow-submodules --depth 1 -b v1.15.0 \
|
||||
https://github.com/ngtcp2/nghttp3
|
||||
cd nghttp3
|
||||
autoreconf -i
|
||||
./configure --disable-dependency-tracking --enable-lib-only \
|
||||
CC=clang-19 CXX=clang++-19
|
||||
make -j$(nproc)
|
||||
make install-strip
|
||||
cd ..
|
||||
rm -rf nghttp3
|
||||
|
||||
RUN git clone --depth 1 -b v1.6.2 https://github.com/libbpf/libbpf && \
|
||||
cd libbpf && \
|
||||
CC=clang-19 PREFIX=/usr/local make -C src install && \
|
||||
cd .. && \
|
||||
rm -rf libbpf
|
||||
git clone --recursive --shallow-submodules --depth 1 -b v1.20.0 \
|
||||
https://github.com/ngtcp2/ngtcp2
|
||||
cd ngtcp2
|
||||
autoreconf -i
|
||||
./configure --disable-dependency-tracking --enable-lib-only \
|
||||
--with-boringssl \
|
||||
CC=clang-19 CXX=clang++-19 \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
BORINGSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig"
|
||||
make -j$(nproc)
|
||||
make install-strip
|
||||
cd ..
|
||||
rm -rf ngtcp2
|
||||
|
||||
RUN git clone --recursive --shallow-submodules --depth 1 -b $NGHTTP2_BRANCH https://github.com/nghttp2/nghttp2 && \
|
||||
cd nghttp2 && \
|
||||
autoreconf -i && \
|
||||
./configure --disable-dependency-tracking --disable-examples \
|
||||
--disable-hpack-tools \
|
||||
--with-mruby \
|
||||
--enable-http3 --with-libbpf \
|
||||
--with-libbrotlienc --with-libbrotlidec \
|
||||
CC=clang-19 CXX=clang++-19 \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
|
||||
LIBEV_LIBS="-l:libev.a" \
|
||||
JEMALLOC_LIBS="-l:libjemalloc.a" \
|
||||
LIBCARES_LIBS="-l:libcares.a" \
|
||||
ZLIB_LIBS="-l:libz.a" \
|
||||
LIBBPF_LIBS="-L/usr/local/lib64 -l:libbpf.a -l:libelf.a" \
|
||||
LIBBROTLIENC_LIBS="-l:libbrotlienc.a -l:libbrotlicommon.a" \
|
||||
LIBBROTLIDEC_LIBS="-l:libbrotlidec.a -l:libbrotlicommon.a" \
|
||||
LDFLAGS="-static-libgcc -static-libstdc++" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
|
||||
make -j$(nproc) install-strip && \
|
||||
cd .. && \
|
||||
rm -rf nghttp2
|
||||
git clone --depth 1 -b v1.6.2 https://github.com/libbpf/libbpf
|
||||
cd libbpf
|
||||
CC=clang-19 PREFIX=/usr/local make -C src install
|
||||
cd ..
|
||||
rm -rf libbpf
|
||||
|
||||
git clone --recursive --shallow-submodules --depth 1 -b $NGHTTP2_BRANCH \
|
||||
https://github.com/nghttp2/nghttp2
|
||||
cd nghttp2
|
||||
autoreconf -i
|
||||
./configure --disable-dependency-tracking --disable-examples \
|
||||
--disable-hpack-tools \
|
||||
--with-mruby \
|
||||
--enable-http3 --with-libbpf \
|
||||
--with-libbrotlienc --with-libbrotlidec \
|
||||
CC=clang-19 CXX=clang++-19 \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
|
||||
LIBEV_LIBS="-l:libev.a" \
|
||||
JEMALLOC_LIBS="-l:libjemalloc.a" \
|
||||
LIBCARES_LIBS="-l:libcares.a" \
|
||||
ZLIB_LIBS="-l:libz.a" \
|
||||
LIBBPF_LIBS="-L/usr/local/lib64 -l:libbpf.a -l:libelf.a" \
|
||||
LIBBROTLIENC_LIBS="-l:libbrotlienc.a -l:libbrotlicommon.a" \
|
||||
LIBBROTLIDEC_LIBS="-l:libbrotlidec.a -l:libbrotlicommon.a" \
|
||||
LDFLAGS="-static-libgcc -static-libstdc++" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig"
|
||||
make -j$(nproc) install-strip
|
||||
cd ..
|
||||
rm -rf nghttp2
|
||||
EOF
|
||||
|
||||
FROM gcr.io/distroless/base-nossl-debian12
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ enum {''')
|
||||
|
||||
def gen_index_header(tokens, prefix, comp_fun, return_type, fail_value):
|
||||
print('''\
|
||||
{} lookup_token(const std::string_view &name) {{
|
||||
{} lookup_token(std::string_view name) {{
|
||||
switch (name.size()) {{'''.format(return_type))
|
||||
b = build_header(tokens)
|
||||
for size in sorted(b.keys()):
|
||||
|
||||
17
go.mod
17
go.mod
@@ -1,20 +1,17 @@
|
||||
module github.com/nghttp2/nghttp2
|
||||
|
||||
go 1.24.0
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
|
||||
github.com/quic-go/quic-go v0.55.0
|
||||
github.com/quic-go/quic-go v0.59.0
|
||||
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20240121064059-46ccb0a462a8
|
||||
golang.org/x/net v0.46.0
|
||||
golang.org/x/net v0.52.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
)
|
||||
|
||||
36
go.sum
36
go.sum
@@ -2,33 +2,25 @@ github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8b
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
||||
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20240121064059-46ccb0a462a8 h1:zKJxuRe+a0O34V81GAZWOrotuU6mveT30QLjJ7OPMMg=
|
||||
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20240121064059-46ccb0a462a8/go.mod h1:gTqc3Q4boc+cKRlSFywTYdX9t6VGRcsThlNIWwaL3Dc=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -926,6 +926,228 @@ func TestH1H1CONNECTMethod(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1OPTIONSServerWide tests that server-wide OPTIONS request.
|
||||
func TestH1H1OPTIONS(t *testing.T) {
|
||||
st := newServerTester(t, options{})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "OPTIONS * HTTP/1.1\r\nTest-Case: TestH1H1OPTIONSServerWide\r\nHost: 127.0.0.1\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)
|
||||
}
|
||||
|
||||
if _, err := io.ReadAll(resp.Body); err != nil {
|
||||
t.Fatalf("Error io.ReadAll() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1OPTIONSProxyServerWide tests that server-wide OPTIONS
|
||||
// request in proxy request.
|
||||
func TestH1H1OPTIONSProxyServerWide(t *testing.T) {
|
||||
st := newServerTester(t, options{})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "OPTIONS http://127.0.0.1 HTTP/1.1\r\nTest-Case: TestH1H1OPTIONSProxyServerWide\r\nHost: 127.0.0.1\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)
|
||||
}
|
||||
|
||||
if _, err := io.ReadAll(resp.Body); err != nil {
|
||||
t.Fatalf("Error io.ReadAll() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1BadMethodAsterisk tests that "*" in HTTP request other than
|
||||
// OPTIONS request.
|
||||
func TestH1H1BadMethodAsterisk(t *testing.T) {
|
||||
st := newServerTester(t, options{})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET * HTTP/1.1\r\nTest-Case: TestH1H1BadMethodAsterisk\r\nHost: 127.0.0.1\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.StatusBadRequest; 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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1AsteriskPrefix tests that a path starting with "*" in HTTP
|
||||
// request.
|
||||
func TestH1H1AsteriskPrefix(t *testing.T) {
|
||||
st := newServerTester(t, options{})
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "OPTIONS *foo HTTP/1.1\r\nTest-Case: TestH1H1AsteriskPrefix\r\nHost: 127.0.0.1\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.StatusBadRequest; 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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2OPTIONSServerWide tests that server-wide OPTIONS request.
|
||||
func TestH1H2OPTIONS(t *testing.T) {
|
||||
opts := options{
|
||||
args: []string{"--http2-bridge"},
|
||||
}
|
||||
|
||||
st := newServerTester(t, opts)
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "OPTIONS * HTTP/1.1\r\nTest-Case: TestH1H2OPTIONSServerWide\r\nHost: 127.0.0.1\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)
|
||||
}
|
||||
|
||||
if _, err := io.ReadAll(resp.Body); err != nil {
|
||||
t.Fatalf("Error io.ReadAll() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2OPTIONSProxyServerWide tests that server-wide OPTIONS
|
||||
// request in proxy request.
|
||||
func TestH1H2OPTIONSProxyServerWide(t *testing.T) {
|
||||
opts := options{
|
||||
args: []string{"--http2-bridge"},
|
||||
}
|
||||
|
||||
st := newServerTester(t, opts)
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "OPTIONS http://127.0.0.1 HTTP/1.1\r\nTest-Case: TestH1H2OPTIONSProxyServerWide\r\nHost: 127.0.0.1\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)
|
||||
}
|
||||
|
||||
if _, err := io.ReadAll(resp.Body); err != nil {
|
||||
t.Fatalf("Error io.ReadAll() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2BadMethodAsterisk tests that "*" in HTTP request other than
|
||||
// OPTIONS request.
|
||||
func TestH1H2BadMethodAsterisk(t *testing.T) {
|
||||
opts := options{
|
||||
args: []string{"--http2-bridge"},
|
||||
}
|
||||
|
||||
st := newServerTester(t, opts)
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET * HTTP/1.1\r\nTest-Case: TestH1H2BadMethodAsterisk\r\nHost: 127.0.0.1\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.StatusBadRequest; 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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2AsteriskPrefix tests that a path starting with "*" in HTTP
|
||||
// request.
|
||||
func TestH1H2AsteriskPrefix(t *testing.T) {
|
||||
opts := options{
|
||||
args: []string{"--http2-bridge"},
|
||||
}
|
||||
|
||||
st := newServerTester(t, opts)
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "OPTIONS *foo HTTP/1.1\r\nTest-Case: TestH1H2AsteriskPrefix\r\nHost: 127.0.0.1\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.StatusBadRequest; 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)
|
||||
}
|
||||
}
|
||||
|
||||
// // TestH1H2ConnectFailure tests that server handles the situation that
|
||||
// // connection attempt to HTTP/2 backend failed.
|
||||
// func TestH1H2ConnectFailure(t *testing.T) {
|
||||
@@ -1748,3 +1970,33 @@ func TestH1H1RequestHTTP10TransferEncoding(t *testing.T) {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2BadHost tests that invalid character in host is treated as
|
||||
// bad request.
|
||||
func TestH1H2BadHost(t *testing.T) {
|
||||
opts := options{
|
||||
args: []string{"--http2-bridge"},
|
||||
}
|
||||
|
||||
st := newServerTester(t, opts)
|
||||
defer st.Close()
|
||||
|
||||
if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2BadHost\r\nHost: 127.0.0.1\x8c\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.StatusBadRequest; 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,14 +323,14 @@ func (st *serverTester) Close() {
|
||||
close(done)
|
||||
}()
|
||||
|
||||
if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil {
|
||||
if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil && !errors.Is(err, os.ErrProcessDone) {
|
||||
st.t.Errorf("Error st.cmd.Process.Signal() = %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(10 * time.Second):
|
||||
if err := st.cmd.Process.Kill(); err != nil {
|
||||
if err := st.cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) {
|
||||
st.t.Errorf("Error st.cmd.Process.Kill() = %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ set(NGHTTP2_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
set(NGHTTP2_VERSION_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(NGHTTP2_PROJECT_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
|
||||
set(NGHTTP2_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
|
||||
set(NGHTTP2_CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
|
||||
set(NGHTTP2_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
set(NGHTTP2_NAMESPACE "${PROJECT_NAME}::")
|
||||
set(NGHTTP2_VERSION ${PROJECT_VERSION})
|
||||
|
||||
|
||||
@@ -3252,7 +3252,7 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option,
|
||||
* regenerated per second. When a suspicious activity is detected,
|
||||
* some amount of tokens are consumed. If there is no token
|
||||
* available, GOAWAY is sent to tear down the connection. |burst| and
|
||||
* |rate| default to 1000 and 33 respectively.
|
||||
* |rate| default to 10000 and 330 respectively.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option,
|
||||
uint64_t burst,
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
static int select_alpn(const unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
const char *key, unsigned int keylen) {
|
||||
@@ -41,7 +43,7 @@ static int select_alpn(const unsigned char **out, unsigned char *outlen,
|
||||
}
|
||||
|
||||
#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1"
|
||||
#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1)
|
||||
#define NGHTTP2_HTTP_1_1_ALPN_LEN nghttp2_strlen_lit(NGHTTP2_HTTP_1_1_ALPN)
|
||||
|
||||
int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen) {
|
||||
|
||||
@@ -34,10 +34,7 @@
|
||||
#include "nghttp2_buf.h"
|
||||
|
||||
#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1)
|
||||
#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1)
|
||||
|
||||
/* The number of bytes of frame header. */
|
||||
#define NGHTTP2_FRAME_HDLEN 9
|
||||
|
||||
@@ -35,9 +35,10 @@
|
||||
/* Make scalar initialization form of nghttp2_hd_entry */
|
||||
#define MAKE_STATIC_ENT(N, V, T, H) \
|
||||
{ \
|
||||
{NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
|
||||
{NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \
|
||||
{(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \
|
||||
{NULL, NULL, (uint8_t *)(N), nghttp2_strlen_lit((N)), -1}, \
|
||||
{NULL, NULL, (uint8_t *)(V), nghttp2_strlen_lit((V)), -1}, \
|
||||
{(uint8_t *)(N), (uint8_t *)(V), nghttp2_strlen_lit((N)), \
|
||||
nghttp2_strlen_lit((V)), 0}, \
|
||||
T, \
|
||||
H, \
|
||||
}
|
||||
|
||||
@@ -104,15 +104,15 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
|
||||
}
|
||||
|
||||
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
|
||||
ctx->fstate = NGHTTP2_HUFF_ACCEPTED;
|
||||
ctx->fstate = 0;
|
||||
ctx->flags = NGHTTP2_HUFF_ACCEPTED;
|
||||
}
|
||||
|
||||
nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_buf *buf, const uint8_t *src,
|
||||
size_t srclen, int final) {
|
||||
const uint8_t *end = src + srclen;
|
||||
nghttp2_huff_decode node = {ctx->fstate, 0};
|
||||
const nghttp2_huff_decode *t = &node;
|
||||
nghttp2_huff_decode t = {ctx->fstate, ctx->flags, 0};
|
||||
uint8_t c;
|
||||
|
||||
/* We use the decoding algorithm described in
|
||||
@@ -121,20 +121,21 @@ nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
- https://github.com/nghttp2/nghttp2/files/15141264/Prefix.pdf */
|
||||
for (; src != end;) {
|
||||
c = *src++;
|
||||
t = &huff_decode_table[t->fstate & 0x1ff][c >> 4];
|
||||
if (t->fstate & NGHTTP2_HUFF_SYM) {
|
||||
*buf->last++ = t->sym;
|
||||
t = huff_decode_table[t.fstate][c >> 4];
|
||||
if (t.flags & NGHTTP2_HUFF_SYM) {
|
||||
*buf->last++ = t.sym;
|
||||
}
|
||||
|
||||
t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf];
|
||||
if (t->fstate & NGHTTP2_HUFF_SYM) {
|
||||
*buf->last++ = t->sym;
|
||||
t = huff_decode_table[t.fstate][c & 0xf];
|
||||
if (t.flags & NGHTTP2_HUFF_SYM) {
|
||||
*buf->last++ = t.sym;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->fstate = t->fstate;
|
||||
ctx->fstate = t.fstate;
|
||||
ctx->flags = t.flags;
|
||||
|
||||
if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) {
|
||||
if (final && !(ctx->flags & NGHTTP2_HUFF_ACCEPTED)) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
typedef enum {
|
||||
/* FSA accepts this state as the end of huffman encoding
|
||||
sequence. */
|
||||
NGHTTP2_HUFF_ACCEPTED = 1 << 14,
|
||||
NGHTTP2_HUFF_ACCEPTED = 1,
|
||||
/* This state emits symbol */
|
||||
NGHTTP2_HUFF_SYM = 1 << 15,
|
||||
NGHTTP2_HUFF_SYM = 1 << 1,
|
||||
} nghttp2_huff_decode_flag;
|
||||
|
||||
typedef struct {
|
||||
@@ -48,6 +48,7 @@ typedef struct {
|
||||
a special node and it is a terminal state that means decoding
|
||||
failed. */
|
||||
uint16_t fstate;
|
||||
uint8_t flags;
|
||||
/* symbol if NGHTTP2_HUFF_SYM flag set */
|
||||
uint8_t sym;
|
||||
} nghttp2_huff_decode;
|
||||
@@ -57,6 +58,7 @@ typedef nghttp2_huff_decode huff_decode_table_type[16];
|
||||
typedef struct {
|
||||
/* fstate is the current huffman decoding state. */
|
||||
uint16_t fstate;
|
||||
uint8_t flags;
|
||||
} nghttp2_hd_huff_decode_context;
|
||||
|
||||
typedef struct {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -61,7 +61,15 @@ nghttp2_min_def(uint32, uint32_t)
|
||||
nghttp2_min_def(uint64, uint64_t)
|
||||
nghttp2_min_def(size, size_t)
|
||||
|
||||
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
|
||||
/*
|
||||
* nghttp2_strlen_lit returns the length of string literal |S|. This
|
||||
* macro assumes |S| is NULL-terminated string literal. It must not
|
||||
* be used with pointers.
|
||||
*/
|
||||
#define nghttp2_strlen_lit(S) (sizeof(S) - 1)
|
||||
|
||||
#define lstreq(A, B, N) \
|
||||
(nghttp2_strlen_lit((A)) == (N) && memcmp((A), (B), (N)) == 0)
|
||||
|
||||
#define nghttp2_struct_of(ptr, type, member) \
|
||||
((type *)(void *)((char *)(ptr) - offsetof(type, member)))
|
||||
|
||||
@@ -49,7 +49,8 @@ static int memieq(const void *a, const void *b, size_t n) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
|
||||
#define lstrieq(A, B, N) \
|
||||
(nghttp2_strlen_lit((A)) == (N) && memieq((A), (B), (N)))
|
||||
|
||||
static int64_t parse_uint(const uint8_t *s, size_t len) {
|
||||
int64_t n = 0;
|
||||
|
||||
@@ -39,7 +39,6 @@ typedef int (*nghttp2_less)(const void *lhs, const void *rhs);
|
||||
/* Internal error code. They must be in the range [-499, -100],
|
||||
inclusive. */
|
||||
typedef enum {
|
||||
NGHTTP2_ERR_CREDENTIAL_PENDING = -101,
|
||||
NGHTTP2_ERR_IGN_HEADER_BLOCK = -103,
|
||||
NGHTTP2_ERR_IGN_PAYLOAD = -104,
|
||||
/*
|
||||
|
||||
@@ -33,12 +33,11 @@
|
||||
|
||||
#define NGHTTP2_INITIAL_HASHBITS 4
|
||||
|
||||
void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem) {
|
||||
map->mem = mem;
|
||||
map->hashbits = 0;
|
||||
map->table = NULL;
|
||||
map->seed = seed;
|
||||
map->size = 0;
|
||||
void nghttp2_map_init(nghttp2_map *map, uint64_t seed, nghttp2_mem *mem) {
|
||||
*map = (nghttp2_map){
|
||||
.mem = mem,
|
||||
.seed = seed,
|
||||
};
|
||||
}
|
||||
|
||||
void nghttp2_map_free(nghttp2_map *map) {
|
||||
@@ -46,30 +45,27 @@ void nghttp2_map_free(nghttp2_map *map) {
|
||||
return;
|
||||
}
|
||||
|
||||
nghttp2_mem_free(map->mem, map->table);
|
||||
nghttp2_mem_free(map->mem, map->keys);
|
||||
}
|
||||
|
||||
int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr) {
|
||||
int rv;
|
||||
size_t i;
|
||||
nghttp2_map_bucket *bkt;
|
||||
size_t tablelen;
|
||||
|
||||
if (map->size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tablelen = 1u << map->hashbits;
|
||||
tablelen = (size_t)1 << map->hashbits;
|
||||
|
||||
for (i = 0; i < tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
if (map->psl[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rv = func(bkt->data, ptr);
|
||||
rv = func(map->data[i], ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -78,175 +74,230 @@ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t map_hash(const nghttp2_map *map, nghttp2_map_key_type key) {
|
||||
/* hasher from
|
||||
https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs
|
||||
We do not perform finalization here because we use top bits
|
||||
anyway. */
|
||||
uint32_t h = ((uint32_t)key + map->seed) * 0x93d765dd;
|
||||
return (size_t)((h * 2654435769u) >> (32 - map->hashbits));
|
||||
}
|
||||
/* Hasher from
|
||||
https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs
|
||||
to maximize the output's sensitivity to all input bits. */
|
||||
#define NGHTTP2_MAP_HASHER 0xf1357aea2e62a9c5ull
|
||||
/* 64-bit Fibonacci hashing constant, Golden Ratio constant, to get
|
||||
the high bits with the good distribution. */
|
||||
#define NGHTTP2_MAP_FIBO 0x9e3779b97f4a7c15ull
|
||||
|
||||
static void map_bucket_swap(nghttp2_map_bucket *a, nghttp2_map_bucket *b) {
|
||||
nghttp2_map_bucket c = *a;
|
||||
static size_t map_index(const nghttp2_map *map, nghttp2_map_key_type key32) {
|
||||
uint64_t key = (uint64_t)key32;
|
||||
|
||||
*a = *b;
|
||||
*b = c;
|
||||
key += map->seed;
|
||||
key *= NGHTTP2_MAP_HASHER;
|
||||
return (size_t)((key * NGHTTP2_MAP_FIBO) >> (64 - map->hashbits));
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
void nghttp2_map_print_distance(const nghttp2_map *map) {
|
||||
size_t i;
|
||||
size_t idx;
|
||||
nghttp2_map_bucket *bkt;
|
||||
size_t tablelen;
|
||||
|
||||
if (map->size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tablelen = 1u << map->hashbits;
|
||||
tablelen = (size_t)1 << map->hashbits;
|
||||
|
||||
for (i = 0; i < tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
if (map->psl[i] == 0) {
|
||||
fprintf(stderr, "@%zu <EMPTY>\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
idx = map_hash(map, bkt->key);
|
||||
fprintf(stderr, "@%zu hash=%zu key=%d base=%zu distance=%u\n", i,
|
||||
map_hash(map, bkt->key), bkt->key, idx, bkt->psl);
|
||||
idx = map_index(map, map->keys[i]);
|
||||
fprintf(stderr, "@%zu key=%d base=%zu distance=%u\n", i, map->keys[i], idx,
|
||||
map->psl[i] - 1);
|
||||
}
|
||||
}
|
||||
#endif /* !defined(WIN32) */
|
||||
|
||||
static int map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
|
||||
size_t idx = map_hash(map, key);
|
||||
nghttp2_map_bucket b = {
|
||||
.key = key,
|
||||
.data = data,
|
||||
};
|
||||
nghttp2_map_bucket *bkt;
|
||||
size_t mask = (1u << map->hashbits) - 1;
|
||||
static void map_set_entry(nghttp2_map *map, size_t idx,
|
||||
nghttp2_map_key_type key, void *data, size_t psl) {
|
||||
map->keys[idx] = key;
|
||||
map->data[idx] = data;
|
||||
map->psl[idx] = (uint8_t)psl;
|
||||
}
|
||||
|
||||
#define NGHTTP2_SWAP(TYPE, A, B) \
|
||||
do { \
|
||||
TYPE t = (TYPE) * (A); \
|
||||
\
|
||||
*(A) = *(B); \
|
||||
*(B) = t; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* map_insert inserts |key| and |data| to |map|, and returns the index
|
||||
* where the pair is stored if it succeeds. Otherwise, it returns one
|
||||
* of the following negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The another data associated to |key| is already present.
|
||||
*/
|
||||
static nghttp2_ssize map_insert(nghttp2_map *map, nghttp2_map_key_type key,
|
||||
void *data) {
|
||||
size_t idx = map_index(map, key);
|
||||
size_t mask = ((size_t)1 << map->hashbits) - 1;
|
||||
size_t psl = 1;
|
||||
size_t kpsl;
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
kpsl = map->psl[idx];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
*bkt = b;
|
||||
if (kpsl == 0) {
|
||||
map_set_entry(map, idx, key, data, psl);
|
||||
++map->size;
|
||||
return 0;
|
||||
|
||||
return (nghttp2_ssize)idx;
|
||||
}
|
||||
|
||||
if (b.psl > bkt->psl) {
|
||||
map_bucket_swap(bkt, &b);
|
||||
} else if (bkt->key == key) {
|
||||
/* TODO This check is just a waste after first swap or if this
|
||||
function is called from map_resize. That said, there is no
|
||||
difference with or without this conditional in performance
|
||||
wise. */
|
||||
if (psl > kpsl) {
|
||||
NGHTTP2_SWAP(nghttp2_map_key_type, &key, &map->keys[idx]);
|
||||
NGHTTP2_SWAP(void *, &data, &map->data[idx]);
|
||||
NGHTTP2_SWAP(uint8_t, &psl, &map->psl[idx]);
|
||||
} else if (map->keys[idx] == key) {
|
||||
/* This check ensures that no duplicate keys are inserted. But
|
||||
it is just a waste after first swap or if this function is
|
||||
called from map_resize. That said, there is no difference
|
||||
with or without this conditional in performance wise. */
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
++b.psl;
|
||||
++psl;
|
||||
idx = (idx + 1) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* NGHTTP2_MAP_MAX_HASHBITS is the maximum number of bits used for
|
||||
hash table. The theoretical limit of the maximum number of keys
|
||||
that can be stored is 1 << NGHTTP2_MAP_MAX_HASHBITS. */
|
||||
#define NGHTTP2_MAP_MAX_HASHBITS (sizeof(size_t) * 8 - 1)
|
||||
|
||||
static int map_resize(nghttp2_map *map, size_t new_hashbits) {
|
||||
size_t i;
|
||||
nghttp2_map_bucket *bkt;
|
||||
size_t tablelen;
|
||||
int rv;
|
||||
nghttp2_ssize idx;
|
||||
nghttp2_map new_map = {
|
||||
.table = nghttp2_mem_calloc(map->mem, 1u << new_hashbits,
|
||||
sizeof(nghttp2_map_bucket)),
|
||||
.mem = map->mem,
|
||||
.seed = map->seed,
|
||||
.hashbits = new_hashbits,
|
||||
};
|
||||
(void)rv;
|
||||
void *buf;
|
||||
(void)idx;
|
||||
|
||||
if (new_map.table == NULL) {
|
||||
if (new_hashbits > NGHTTP2_MAP_MAX_HASHBITS) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
tablelen = (size_t)1 << new_hashbits;
|
||||
|
||||
buf = nghttp2_mem_calloc(map->mem, tablelen,
|
||||
sizeof(nghttp2_map_key_type) + sizeof(void *) +
|
||||
sizeof(uint8_t));
|
||||
if (buf == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
new_map.keys = buf;
|
||||
new_map.data =
|
||||
(void *)((uint8_t *)new_map.keys + tablelen * sizeof(nghttp2_map_key_type));
|
||||
new_map.psl = (uint8_t *)new_map.data + tablelen * sizeof(void *);
|
||||
|
||||
if (map->size) {
|
||||
tablelen = 1u << map->hashbits;
|
||||
tablelen = (size_t)1 << map->hashbits;
|
||||
|
||||
for (i = 0; i < tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
if (bkt->data == NULL) {
|
||||
if (map->psl[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rv = map_insert(&new_map, bkt->key, bkt->data);
|
||||
idx = map_insert(&new_map, map->keys[i], map->data[i]);
|
||||
|
||||
assert(0 == rv);
|
||||
/* map_insert must not fail because all keys are unique during
|
||||
resize. */
|
||||
assert(idx >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_mem_free(map->mem, map->table);
|
||||
map->table = new_map.table;
|
||||
nghttp2_mem_free(map->mem, map->keys);
|
||||
map->keys = new_map.keys;
|
||||
map->data = new_map.data;
|
||||
map->psl = new_map.psl;
|
||||
map->hashbits = new_hashbits;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NGHTTP2_MAX_PSL_RESIZE_THRESH is the maximum psl threshold. If
|
||||
reached, resize the table. */
|
||||
#define NGHTTP2_MAX_PSL_RESIZE_THRESH 128
|
||||
|
||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
|
||||
int rv;
|
||||
size_t tablelen;
|
||||
nghttp2_ssize idx;
|
||||
|
||||
assert(data);
|
||||
|
||||
/* Load factor is 7/8 */
|
||||
/* Under the very initial condition, that is map->size == 0 and
|
||||
map->hashbits == 0, 8 > 7 still holds nicely. */
|
||||
if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) {
|
||||
if (map->hashbits) {
|
||||
rv = map_resize(map, map->hashbits + 1);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
rv = map_resize(map, NGHTTP2_INITIAL_HASHBITS);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
/* tablelen is incorrect if map->hashbits == 0 which leads to
|
||||
tablelen = 1, but it is only used to check the load factor, and
|
||||
it works in this special case. */
|
||||
tablelen = (size_t)1 << map->hashbits;
|
||||
|
||||
/* Load factor is 7 / 8. Because tablelen is power of 2, (tablelen
|
||||
- (tablelen >> 3)) computes tablelen * 7 / 8. */
|
||||
if (map->size + 1 >= (tablelen - (tablelen >> 3))) {
|
||||
rv = map_resize(map, map->hashbits ? map->hashbits + 1
|
||||
: NGHTTP2_INITIAL_HASHBITS);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
idx = map_insert(map, key, data);
|
||||
if (idx < 0) {
|
||||
return (int)idx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = map_insert(map, key, data);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
idx = map_insert(map, key, data);
|
||||
if (idx < 0) {
|
||||
return (int)idx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Resize if psl reaches really large value which is almost
|
||||
improbable, but just in case. */
|
||||
if (map->psl[idx] - 1 < NGHTTP2_MAX_PSL_RESIZE_THRESH) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return map_resize(map, map->hashbits + 1);
|
||||
}
|
||||
|
||||
void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) {
|
||||
size_t idx;
|
||||
nghttp2_map_bucket *bkt;
|
||||
size_t psl = 0;
|
||||
size_t psl = 1;
|
||||
size_t mask;
|
||||
|
||||
if (map->size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idx = map_hash(map, key);
|
||||
mask = (1u << map->hashbits) - 1;
|
||||
idx = map_index(map, key);
|
||||
mask = ((size_t)1 << map->hashbits) - 1;
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->data == NULL || psl > bkt->psl) {
|
||||
if (psl > map->psl[idx]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bkt->key == key) {
|
||||
return bkt->data;
|
||||
if (map->keys[idx] == key) {
|
||||
return map->data[idx];
|
||||
}
|
||||
|
||||
++psl;
|
||||
@@ -256,38 +307,36 @@ void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) {
|
||||
|
||||
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
|
||||
size_t idx;
|
||||
nghttp2_map_bucket *b, *bkt;
|
||||
size_t psl = 0;
|
||||
size_t dest;
|
||||
size_t psl = 1, kpsl;
|
||||
size_t mask;
|
||||
|
||||
if (map->size == 0) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
idx = map_hash(map, key);
|
||||
mask = (1u << map->hashbits) - 1;
|
||||
idx = map_index(map, key);
|
||||
mask = ((size_t)1 << map->hashbits) - 1;
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->data == NULL || psl > bkt->psl) {
|
||||
if (psl > map->psl[idx]) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (bkt->key == key) {
|
||||
b = bkt;
|
||||
if (map->keys[idx] == key) {
|
||||
dest = idx;
|
||||
idx = (idx + 1) & mask;
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
if (bkt->data == NULL || bkt->psl == 0) {
|
||||
b->data = NULL;
|
||||
kpsl = map->psl[idx];
|
||||
if (kpsl <= 1) {
|
||||
map->psl[dest] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
--bkt->psl;
|
||||
*b = *bkt;
|
||||
b = bkt;
|
||||
map_set_entry(map, dest, map->keys[idx], map->data[idx], kpsl - 1);
|
||||
|
||||
dest = idx;
|
||||
|
||||
idx = (idx + 1) & mask;
|
||||
}
|
||||
@@ -307,7 +356,7 @@ void nghttp2_map_clear(nghttp2_map *map) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(map->table, 0, sizeof(*map->table) * (1u << map->hashbits));
|
||||
memset(map->psl, 0, sizeof(*map->psl) * ((size_t)1 << map->hashbits));
|
||||
map->size = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,16 +38,15 @@
|
||||
|
||||
typedef int32_t nghttp2_map_key_type;
|
||||
|
||||
typedef struct nghttp2_map_bucket {
|
||||
uint32_t psl;
|
||||
nghttp2_map_key_type key;
|
||||
void *data;
|
||||
} nghttp2_map_bucket;
|
||||
|
||||
typedef struct nghttp2_map {
|
||||
nghttp2_map_bucket *table;
|
||||
nghttp2_map_key_type *keys;
|
||||
void **data;
|
||||
/* psl is the Probe Sequence Length. 0 has special meaning that the
|
||||
element is not stored at i-th position if psl[i] == 0. Because
|
||||
of this, the actual psl value is psl[i] - 1 if psl[i] > 0. */
|
||||
uint8_t *psl;
|
||||
nghttp2_mem *mem;
|
||||
uint32_t seed;
|
||||
uint64_t seed;
|
||||
size_t size;
|
||||
size_t hashbits;
|
||||
} nghttp2_map;
|
||||
@@ -55,7 +54,7 @@ typedef struct nghttp2_map {
|
||||
/*
|
||||
* nghttp2_map_init initializes the map |map|.
|
||||
*/
|
||||
void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem);
|
||||
void nghttp2_map_init(nghttp2_map *map, uint64_t seed, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp2_map_free deallocates any resources allocated for |map|.
|
||||
|
||||
@@ -53,6 +53,18 @@ nghttp2_data_provider_wrap_v2(nghttp2_data_provider_wrap *dpw,
|
||||
return dpw;
|
||||
}
|
||||
|
||||
int nghttp2_data_provider_wrap_contains_read_callback(
|
||||
const nghttp2_data_provider_wrap *dpw) {
|
||||
switch (dpw->version) {
|
||||
case NGHTTP2_DATA_PROVIDER_V1:
|
||||
return dpw->data_prd.v1.read_callback != NULL;
|
||||
case NGHTTP2_DATA_PROVIDER_V2:
|
||||
return dpw->data_prd.v2.read_callback != NULL;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_outbound_item_init(nghttp2_outbound_item *item) {
|
||||
item->cycle = 0;
|
||||
item->qnext = NULL;
|
||||
|
||||
@@ -39,10 +39,6 @@
|
||||
typedef struct nghttp2_data_provider_wrap {
|
||||
int version;
|
||||
union {
|
||||
struct {
|
||||
nghttp2_data_source source;
|
||||
void *read_callback;
|
||||
};
|
||||
nghttp2_data_provider v1;
|
||||
nghttp2_data_provider2 v2;
|
||||
} data_prd;
|
||||
@@ -56,6 +52,11 @@ nghttp2_data_provider_wrap *
|
||||
nghttp2_data_provider_wrap_v2(nghttp2_data_provider_wrap *dpw,
|
||||
const nghttp2_data_provider2 *data_prd);
|
||||
|
||||
/* nghttp2_data_provider_wrap_contains_read_callback returns nonzero
|
||||
if |dpw| contains read_callback in either version. */
|
||||
int nghttp2_data_provider_wrap_contains_read_callback(
|
||||
const nghttp2_data_provider_wrap *dpw);
|
||||
|
||||
/* struct used for HEADERS and PUSH_PROMISE frame */
|
||||
typedef struct {
|
||||
nghttp2_data_provider_wrap dpw;
|
||||
@@ -110,6 +111,12 @@ typedef struct {
|
||||
uint8_t flags;
|
||||
} nghttp2_goaway_aux_data;
|
||||
|
||||
typedef struct {
|
||||
/* nonzero if RST_STREAM should be sent even if stream is not
|
||||
found. */
|
||||
uint8_t continue_without_stream;
|
||||
} nghttp2_rst_stream_aux_data;
|
||||
|
||||
/* struct used for extension frame */
|
||||
typedef struct {
|
||||
/* nonzero if this extension frame is serialized by library
|
||||
@@ -122,6 +129,7 @@ typedef union {
|
||||
nghttp2_data_aux_data data;
|
||||
nghttp2_headers_aux_data headers;
|
||||
nghttp2_goaway_aux_data goaway;
|
||||
nghttp2_rst_stream_aux_data rst_stream;
|
||||
nghttp2_ext_aux_data ext;
|
||||
} nghttp2_aux_data;
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ static int session_new(nghttp2_session **session_ptr,
|
||||
size_t max_deflate_dynamic_table_size =
|
||||
NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
|
||||
size_t i;
|
||||
uint32_t map_seed;
|
||||
uint64_t map_seed;
|
||||
|
||||
if (mem == NULL) {
|
||||
mem = nghttp2_mem_default();
|
||||
@@ -1051,7 +1051,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
||||
nghttp2_outbound_queue_push(&session->ob_syn, item);
|
||||
item->queued = 1;
|
||||
return 0;
|
||||
;
|
||||
}
|
||||
|
||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||
@@ -1189,6 +1188,10 @@ int nghttp2_session_add_rst_stream_continue(nghttp2_session *session,
|
||||
frame = &item->frame;
|
||||
|
||||
nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);
|
||||
|
||||
item->aux_data.rst_stream.continue_without_stream =
|
||||
(uint8_t)(continue_without_stream != 0);
|
||||
|
||||
rv = nghttp2_session_add_item(session, item);
|
||||
if (rv != 0) {
|
||||
nghttp2_frame_rst_stream_free(&frame->rst_stream);
|
||||
@@ -2141,6 +2144,12 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
if (session_is_closing(session)) {
|
||||
return NGHTTP2_ERR_SESSION_CLOSING;
|
||||
}
|
||||
|
||||
if (!item->aux_data.rst_stream.continue_without_stream &&
|
||||
!nghttp2_session_get_stream(session, frame->rst_stream.hd.stream_id)) {
|
||||
return NGHTTP2_ERR_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream);
|
||||
return 0;
|
||||
case NGHTTP2_SETTINGS: {
|
||||
@@ -2584,7 +2593,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
|
||||
}
|
||||
/* We assume aux_data is a pointer to nghttp2_headers_aux_data */
|
||||
aux_data = &item->aux_data.headers;
|
||||
if (aux_data->dpw.data_prd.read_callback) {
|
||||
if (nghttp2_data_provider_wrap_contains_read_callback(&aux_data->dpw)) {
|
||||
/* nghttp2_submit_data_shared() makes a copy of
|
||||
aux_data->dpw */
|
||||
rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM,
|
||||
@@ -2615,7 +2624,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
|
||||
}
|
||||
/* We assume aux_data is a pointer to nghttp2_headers_aux_data */
|
||||
aux_data = &item->aux_data.headers;
|
||||
if (aux_data->dpw.data_prd.read_callback) {
|
||||
if (nghttp2_data_provider_wrap_contains_read_callback(&aux_data->dpw)) {
|
||||
rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM,
|
||||
frame->hd.stream_id, &aux_data->dpw);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
@@ -2795,7 +2804,10 @@ static int session_call_send_data(nghttp2_session *session,
|
||||
aux_data = &item->aux_data.data;
|
||||
|
||||
rv = session->callbacks.send_data_callback(session, frame, buf->pos, length,
|
||||
&aux_data->dpw.data_prd.source,
|
||||
/* This is fine because
|
||||
of Common Initial
|
||||
Sequence rule. */
|
||||
&aux_data->dpw.data_prd.v2.source,
|
||||
session->user_data);
|
||||
|
||||
switch (rv) {
|
||||
@@ -2853,8 +2865,12 @@ static nghttp2_ssize nghttp2_session_mem_send_internal(nghttp2_session *session,
|
||||
nghttp2_frame *frame = &item->frame;
|
||||
/* The library is responsible for the transmission of
|
||||
WINDOW_UPDATE frame, so we don't call error callback for
|
||||
it. */
|
||||
it. As for RST_STREAM, if it is not sent due to missing
|
||||
stream, we also do not call error callback because it may
|
||||
cause a lot of noises.*/
|
||||
if (frame->hd.type != NGHTTP2_WINDOW_UPDATE &&
|
||||
(frame->hd.type != NGHTTP2_RST_STREAM ||
|
||||
rv != NGHTTP2_ERR_STREAM_CLOSED) &&
|
||||
session->callbacks.on_frame_not_send_callback(
|
||||
session, frame, rv, session->user_data) != 0) {
|
||||
nghttp2_outbound_item_free(item, mem);
|
||||
@@ -5477,15 +5493,6 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session,
|
||||
DEBUGF("recv: DATA not allowed stream_id=%d\n",
|
||||
iframe->frame.hd.stream_id);
|
||||
|
||||
rv = session_update_glitch_ratelim(session);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (iframe->state == NGHTTP2_IB_IGN_ALL) {
|
||||
return (nghttp2_ssize)inlen;
|
||||
}
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_DATA;
|
||||
break;
|
||||
}
|
||||
@@ -7402,13 +7409,13 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
case NGHTTP2_DATA_PROVIDER_V1:
|
||||
payloadlen = (nghttp2_ssize)aux_data->dpw.data_prd.v1.read_callback(
|
||||
session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
|
||||
&aux_data->dpw.data_prd.source, session->user_data);
|
||||
&aux_data->dpw.data_prd.v1.source, session->user_data);
|
||||
|
||||
break;
|
||||
case NGHTTP2_DATA_PROVIDER_V2:
|
||||
payloadlen = aux_data->dpw.data_prd.v2.read_callback(
|
||||
session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
|
||||
&aux_data->dpw.data_prd.source, session->user_data);
|
||||
&aux_data->dpw.data_prd.v2.source, session->user_data);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -107,8 +107,8 @@ typedef struct {
|
||||
#define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33
|
||||
|
||||
/* The default values for glitch rate limiter. */
|
||||
#define NGHTTP2_DEFAULT_GLITCH_BURST 1000
|
||||
#define NGHTTP2_DEFAULT_GLITCH_RATE 33
|
||||
#define NGHTTP2_DEFAULT_GLITCH_BURST 10000
|
||||
#define NGHTTP2_DEFAULT_GLITCH_RATE 330
|
||||
|
||||
/* The default max number of CONTINUATION frames following an incoming
|
||||
HEADER frame. */
|
||||
|
||||
@@ -57,7 +57,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
if (dpw != NULL && dpw->data_prd.read_callback != NULL) {
|
||||
if (dpw != NULL && nghttp2_data_provider_wrap_contains_read_callback(dpw)) {
|
||||
item->aux_data.headers.dpw = *dpw;
|
||||
}
|
||||
|
||||
@@ -649,7 +649,7 @@ fail_item_malloc:
|
||||
|
||||
static uint8_t set_request_flags(const nghttp2_data_provider_wrap *dpw) {
|
||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||
if (dpw == NULL || dpw->data_prd.read_callback == NULL) {
|
||||
if (dpw == NULL || !nghttp2_data_provider_wrap_contains_read_callback(dpw)) {
|
||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
|
||||
@@ -700,7 +700,7 @@ int32_t nghttp2_submit_request2(nghttp2_session *session,
|
||||
|
||||
static uint8_t set_response_flags(const nghttp2_data_provider_wrap *dpw) {
|
||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||
if (dpw == NULL || dpw->data_prd.read_callback == NULL) {
|
||||
if (dpw == NULL || !nghttp2_data_provider_wrap_contains_read_callback(dpw)) {
|
||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
return flags;
|
||||
|
||||
27
mkhufftbl.py
27
mkhufftbl.py
@@ -356,8 +356,8 @@ def _build_transition_table(ctx, node):
|
||||
def huffman_tree_build_transition_table(ctx):
|
||||
_build_transition_table(ctx, ctx.root)
|
||||
|
||||
NGHTTP2_HUFF_ACCEPTED = 1 << 14
|
||||
NGHTTP2_HUFF_SYM = 1 << 15
|
||||
NGHTTP2_HUFF_ACCEPTED = 1
|
||||
NGHTTP2_HUFF_SYM = 1 << 1
|
||||
|
||||
def _print_transition_table(node):
|
||||
if node.term is not None:
|
||||
@@ -381,7 +381,7 @@ def _print_transition_table(node):
|
||||
flags |= NGHTTP2_HUFF_ACCEPTED
|
||||
elif nd.accept:
|
||||
flags |= NGHTTP2_HUFF_ACCEPTED
|
||||
print(' {{0x{:02x}, {}}},'.format(id | flags, out))
|
||||
print(' {{0x{:02x}, {}, {}}},'.format(id, flags, out))
|
||||
print('},')
|
||||
_print_transition_table(node.left)
|
||||
_print_transition_table(node.right)
|
||||
@@ -390,22 +390,10 @@ def huffman_tree_print_transition_table(ctx):
|
||||
_print_transition_table(ctx.root)
|
||||
print('/* 256 */')
|
||||
print('{')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
print(' {0x100, 0},')
|
||||
|
||||
for _ in range(16):
|
||||
print(' {0x100, 0, 0},')
|
||||
|
||||
print('},')
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -458,6 +446,7 @@ enum {{
|
||||
print('''\
|
||||
typedef struct {
|
||||
uint16_t fstate;
|
||||
uint8_t flags;
|
||||
uint8_t sym;
|
||||
} nghttp2_huff_decode;
|
||||
''')
|
||||
|
||||
@@ -56,6 +56,7 @@ if(ENABLE_APP)
|
||||
set(HELPER_OBJECTS
|
||||
util.cc
|
||||
http2.cc timegm.c app_helper.cc nghttp2_gzip.c
|
||||
network.cc
|
||||
)
|
||||
|
||||
# nghttp client
|
||||
@@ -82,6 +83,7 @@ if(ENABLE_APP)
|
||||
http2.cc h2load.cc
|
||||
timegm.c
|
||||
tls.cc
|
||||
network.cc
|
||||
h2load_http2_session.cc
|
||||
h2load_http1_session.cc
|
||||
)
|
||||
@@ -97,6 +99,7 @@ if(ENABLE_APP)
|
||||
util.cc http2.cc timegm.c
|
||||
app_helper.cc
|
||||
tls.cc
|
||||
network.cc
|
||||
shrpx_config.cc
|
||||
shrpx_accept_handler.cc
|
||||
shrpx_connection_handler.cc
|
||||
@@ -190,6 +193,7 @@ if(ENABLE_APP)
|
||||
memchunk_test.cc
|
||||
template_test.cc
|
||||
base64_test.cc
|
||||
network_test.cc
|
||||
${CMAKE_SOURCE_DIR}/tests/munit/munit.c
|
||||
)
|
||||
if(ENABLE_HTTP3)
|
||||
@@ -249,6 +253,8 @@ if(ENABLE_HPACK_TOOLS)
|
||||
comp_helper.c
|
||||
util.cc
|
||||
timegm.c
|
||||
tls.cc
|
||||
network.cc
|
||||
)
|
||||
add_executable(inflatehd ${inflatehd_SOURCES})
|
||||
add_executable(deflatehd ${deflatehd_SOURCES})
|
||||
|
||||
@@ -39,7 +39,7 @@ HtmlParser::HtmlParser(const std::string &base_uri)
|
||||
HtmlParser::~HtmlParser() { htmlFreeParserCtxt(parser_ctx_); }
|
||||
|
||||
namespace {
|
||||
std::string_view get_attr(const xmlChar **attrs, const std::string_view &name) {
|
||||
std::string_view get_attr(const xmlChar **attrs, std::string_view name) {
|
||||
if (attrs == nullptr) {
|
||||
return ""sv;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ std::string_view get_attr(const xmlChar **attrs, const std::string_view &name) {
|
||||
|
||||
namespace {
|
||||
ResourceType
|
||||
get_resource_type_for_preload_as(const std::string_view &attribute_value) {
|
||||
get_resource_type_for_preload_as(std::string_view attribute_value) {
|
||||
if (util::strieq("image"sv, attribute_value)) {
|
||||
return REQ_IMG;
|
||||
} else if (util::strieq("style"sv, attribute_value)) {
|
||||
@@ -69,7 +69,7 @@ get_resource_type_for_preload_as(const std::string_view &attribute_value) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void add_link(ParserData *parser_data, const std::string_view &uri,
|
||||
void add_link(ParserData *parser_data, std::string_view uri,
|
||||
ResourceType res_type) {
|
||||
auto u = xmlBuildURI(
|
||||
reinterpret_cast<const xmlChar *>(uri.data()),
|
||||
@@ -175,27 +175,30 @@ xmlSAXHandler saxHandler = {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
int HtmlParser::parse_chunk(const char *chunk, size_t size, int fin) {
|
||||
int HtmlParser::parse_chunk(std::span<const uint8_t> chunk, int fin) {
|
||||
if (!parser_ctx_) {
|
||||
parser_ctx_ = htmlCreatePushParserCtxt(
|
||||
&saxHandler, &parser_data_, chunk, static_cast<int>(size),
|
||||
base_uri_.c_str(), XML_CHAR_ENCODING_NONE);
|
||||
&saxHandler, &parser_data_, reinterpret_cast<const char *>(chunk.data()),
|
||||
static_cast<int>(chunk.size()), base_uri_.c_str(),
|
||||
XML_CHAR_ENCODING_NONE);
|
||||
if (!parser_ctx_) {
|
||||
return -1;
|
||||
} else {
|
||||
if (fin) {
|
||||
return parse_chunk_internal(nullptr, 0, fin);
|
||||
return parse_chunk_internal({}, fin);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return parse_chunk_internal(chunk, size, fin);
|
||||
return parse_chunk_internal(chunk, fin);
|
||||
}
|
||||
}
|
||||
|
||||
int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) {
|
||||
int rv = htmlParseChunk(parser_ctx_, chunk, static_cast<int>(size), fin);
|
||||
int HtmlParser::parse_chunk_internal(std::span<const uint8_t> chunk, int fin) {
|
||||
int rv =
|
||||
htmlParseChunk(parser_ctx_, reinterpret_cast<const char *>(chunk.data()),
|
||||
static_cast<int>(chunk.size()), fin);
|
||||
if (rv == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <span>
|
||||
|
||||
#ifdef HAVE_LIBXML2
|
||||
# include <libxml/HTMLparser.h>
|
||||
@@ -58,12 +60,12 @@ class HtmlParser {
|
||||
public:
|
||||
HtmlParser(const std::string &base_uri);
|
||||
~HtmlParser();
|
||||
int parse_chunk(const char *chunk, size_t size, int fin);
|
||||
int parse_chunk(std::span<const uint8_t> chunk, int fin);
|
||||
const std::vector<std::pair<std::string, ResourceType>> &get_links() const;
|
||||
void clear_links();
|
||||
|
||||
private:
|
||||
int parse_chunk_internal(const char *chunk, size_t size, int fin);
|
||||
int parse_chunk_internal(std::span<const uint8_t> chunk, int fin);
|
||||
|
||||
std::string base_uri_;
|
||||
htmlParserCtxtPtr parser_ctx_;
|
||||
@@ -75,7 +77,7 @@ private:
|
||||
class HtmlParser {
|
||||
public:
|
||||
HtmlParser(const std::string &base_uri) {}
|
||||
int parse_chunk(const char *chunk, size_t size, int fin) { return 0; }
|
||||
int parse_chunk(std::span<const uint8_t> chunk, int fin) { return 0; }
|
||||
const std::vector<std::pair<std::string, ResourceType>> &get_links() const {
|
||||
return links_;
|
||||
}
|
||||
|
||||
@@ -545,8 +545,6 @@ Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl,
|
||||
session_(nullptr),
|
||||
sessions_(sessions),
|
||||
ssl_(ssl),
|
||||
data_pending_(nullptr),
|
||||
data_pendinglen_(0),
|
||||
fd_(fd) {
|
||||
ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.);
|
||||
ev_io_init(&wev_, writecb, fd, EV_WRITE);
|
||||
@@ -599,17 +597,14 @@ void Http2Handler::start_settings_timer() {
|
||||
}
|
||||
|
||||
int Http2Handler::fill_wb() {
|
||||
if (data_pending_) {
|
||||
auto n = std::min(wb_.wleft(), data_pendinglen_);
|
||||
wb_.write(data_pending_, n);
|
||||
if (n < data_pendinglen_) {
|
||||
data_pending_ += n;
|
||||
data_pendinglen_ -= n;
|
||||
if (!data_pending_.empty()) {
|
||||
auto n = wb_.write(data_pending_);
|
||||
if (n < data_pending_.size()) {
|
||||
data_pending_ = data_pending_.subspan(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data_pending_ = nullptr;
|
||||
data_pendinglen_ = 0;
|
||||
data_pending_ = {};
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
@@ -624,10 +619,10 @@ int Http2Handler::fill_wb() {
|
||||
if (datalen == 0) {
|
||||
break;
|
||||
}
|
||||
auto n = wb_.write(data, as_unsigned(datalen));
|
||||
if (n < static_cast<decltype(n)>(datalen)) {
|
||||
data_pending_ = data + n;
|
||||
data_pendinglen_ = as_unsigned(datalen) - n;
|
||||
auto chunk = std::span{data, as_unsigned(datalen)};
|
||||
auto n = wb_.write(chunk);
|
||||
if (n < chunk.size()) {
|
||||
data_pending_ = chunk.subspan(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -924,9 +919,8 @@ int Http2Handler::verify_alpn_result() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Http2Handler::submit_file_response(const std::string_view &status,
|
||||
Stream *stream, time_t last_modified,
|
||||
off_t file_length,
|
||||
int Http2Handler::submit_file_response(std::string_view status, Stream *stream,
|
||||
time_t last_modified, off_t file_length,
|
||||
const std::string *content_type,
|
||||
nghttp2_data_provider2 *data_prd) {
|
||||
std::string last_modified_str;
|
||||
@@ -962,8 +956,8 @@ int Http2Handler::submit_file_response(const std::string_view &status,
|
||||
nvlen, data_prd);
|
||||
}
|
||||
|
||||
int Http2Handler::submit_response(const std::string_view &status,
|
||||
int32_t stream_id, const HeaderRefs &headers,
|
||||
int Http2Handler::submit_response(std::string_view status, int32_t stream_id,
|
||||
const HeaderRefs &headers,
|
||||
nghttp2_data_provider2 *data_prd) {
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(4 + headers.size());
|
||||
@@ -987,8 +981,7 @@ int Http2Handler::submit_response(const std::string_view &status,
|
||||
return r;
|
||||
}
|
||||
|
||||
int Http2Handler::submit_response(const std::string_view &status,
|
||||
int32_t stream_id,
|
||||
int Http2Handler::submit_response(std::string_view status, int32_t stream_id,
|
||||
nghttp2_data_provider2 *data_prd) {
|
||||
auto nva = std::to_array({
|
||||
http2::make_field(":status"sv, status),
|
||||
@@ -1017,7 +1010,7 @@ int Http2Handler::submit_non_final_response(const std::string &status,
|
||||
}
|
||||
|
||||
int Http2Handler::submit_push_promise(Stream *stream,
|
||||
const std::string_view &push_path) {
|
||||
std::string_view push_path) {
|
||||
auto authority = stream->header.authority;
|
||||
|
||||
if (authority.empty()) {
|
||||
@@ -1213,7 +1206,7 @@ bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
|
||||
|
||||
namespace {
|
||||
void prepare_redirect_response(Stream *stream, Http2Handler *hd,
|
||||
const std::string_view &path, int status) {
|
||||
std::string_view path, int status) {
|
||||
auto scheme = stream->header.scheme;
|
||||
|
||||
auto authority = stream->header.authority;
|
||||
@@ -1906,7 +1899,9 @@ public:
|
||||
ev_io_start(sessions_->get_loop(), &w_);
|
||||
}
|
||||
void accept_connection() {
|
||||
for (;;) {
|
||||
constexpr size_t max_num_accept = 10;
|
||||
|
||||
for (size_t i = 0; i < max_num_accept; ++i) {
|
||||
#ifdef HAVE_ACCEPT4
|
||||
auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK);
|
||||
#else // !defined(HAVE_ACCEPT4)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
|
||||
#include "ssl_compat.h"
|
||||
|
||||
@@ -178,21 +179,21 @@ public:
|
||||
int connection_made();
|
||||
int verify_alpn_result();
|
||||
|
||||
int submit_file_response(const std::string_view &status, Stream *stream,
|
||||
int submit_file_response(std::string_view status, Stream *stream,
|
||||
time_t last_modified, off_t file_length,
|
||||
const std::string *content_type,
|
||||
nghttp2_data_provider2 *data_prd);
|
||||
|
||||
int submit_response(const std::string_view &status, int32_t stream_id,
|
||||
int submit_response(std::string_view status, int32_t stream_id,
|
||||
nghttp2_data_provider2 *data_prd);
|
||||
|
||||
int submit_response(const std::string_view &status, int32_t stream_id,
|
||||
int submit_response(std::string_view status, int32_t stream_id,
|
||||
const HeaderRefs &headers,
|
||||
nghttp2_data_provider2 *data_prd);
|
||||
|
||||
int submit_non_final_response(const std::string &status, int32_t stream_id);
|
||||
|
||||
int submit_push_promise(Stream *stream, const std::string_view &push_path);
|
||||
int submit_push_promise(Stream *stream, std::string_view push_path);
|
||||
|
||||
int submit_rst_stream(Stream *stream, uint32_t error_code);
|
||||
|
||||
@@ -230,8 +231,7 @@ private:
|
||||
nghttp2_session *session_;
|
||||
Sessions *sessions_;
|
||||
SSL *ssl_;
|
||||
const uint8_t *data_pending_;
|
||||
size_t data_pendinglen_;
|
||||
std::span<const uint8_t> data_pending_;
|
||||
int fd_;
|
||||
};
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ if ENABLE_APP
|
||||
bin_PROGRAMS += nghttp nghttpd nghttpx
|
||||
|
||||
HELPER_OBJECTS = util.cc \
|
||||
http2.cc timegm.c app_helper.cc nghttp2_gzip.c
|
||||
http2.cc timegm.c app_helper.cc nghttp2_gzip.c network.cc
|
||||
HELPER_HFILES = util.h \
|
||||
http2.h timegm.h app_helper.h nghttp2_config.h \
|
||||
nghttp2_gzip.h network.h
|
||||
@@ -120,7 +120,8 @@ h2load_SOURCES = util.cc util.h \
|
||||
tls.cc tls.h ssl_compat.h \
|
||||
h2load_session.h \
|
||||
h2load_http2_session.cc h2load_http2_session.h \
|
||||
h2load_http1_session.cc h2load_http1_session.h
|
||||
h2load_http1_session.cc h2load_http1_session.h \
|
||||
network.cc network.h
|
||||
|
||||
if ENABLE_HTTP3
|
||||
h2load_SOURCES += \
|
||||
@@ -132,6 +133,7 @@ NGHTTPX_SRCS = \
|
||||
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
||||
app_helper.cc app_helper.h \
|
||||
tls.cc tls.h ssl_compat.h \
|
||||
network.cc network.h \
|
||||
shrpx_config.cc shrpx_config.h \
|
||||
shrpx_error.h \
|
||||
shrpx_accept_handler.cc shrpx_accept_handler.h \
|
||||
@@ -229,6 +231,7 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
|
||||
memchunk_test.cc memchunk_test.h \
|
||||
template_test.cc template_test.h \
|
||||
base64_test.cc base64_test.h \
|
||||
network_test.cc network_test.h \
|
||||
$(top_srcdir)/tests/munit/munit.c $(top_srcdir)/tests/munit/munit.h \
|
||||
$(top_srcdir)/tests/munit/munitxx.h
|
||||
if ENABLE_HTTP3
|
||||
@@ -262,7 +265,9 @@ bin_PROGRAMS += inflatehd deflatehd
|
||||
HPACK_TOOLS_COMMON_SRCS = \
|
||||
comp_helper.c comp_helper.h \
|
||||
util.cc util.h \
|
||||
timegm.c timegm.h
|
||||
timegm.c timegm.h \
|
||||
tls.cc tls.h \
|
||||
network.cc network.h
|
||||
|
||||
inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ struct BlockAllocator {
|
||||
|
||||
// Makes a copy of a range [|first|, |last|). The resulting string
|
||||
// will be NULL-terminated.
|
||||
template <std::input_iterator I>
|
||||
template <std::forward_iterator I>
|
||||
std::string_view make_string_ref(BlockAllocator &alloc, I first, I last) {
|
||||
auto dst = static_cast<char *>(
|
||||
alloc.alloc(static_cast<size_t>(std::ranges::distance(first, last) + 1)));
|
||||
@@ -210,7 +210,7 @@ std::string_view make_string_ref(BlockAllocator &alloc, I first, I last) {
|
||||
|
||||
// Makes a copy of |r| as std::string_view. The resulting string will be
|
||||
// NULL-terminated.
|
||||
template <std::ranges::input_range R>
|
||||
template <std::ranges::forward_range R>
|
||||
requires(!std::is_array_v<std::remove_cvref_t<R>>)
|
||||
std::string_view make_string_ref(BlockAllocator &alloc, R &&r) {
|
||||
return make_string_ref(alloc, std::ranges::begin(r), std::ranges::end(r));
|
||||
@@ -223,7 +223,7 @@ constexpr size_t concat_string_ref_count(size_t acc) { return acc; }
|
||||
// private function used in concat_string_ref. This function counts
|
||||
// the sum of length of given arguments. The calculated length is
|
||||
// accumulated, and passed to the next function.
|
||||
template <std::ranges::input_range R, std::ranges::input_range... Args>
|
||||
template <std::ranges::sized_range R, std::ranges::sized_range... Args>
|
||||
requires(!std::is_array_v<std::remove_cvref_t<R>>)
|
||||
constexpr size_t concat_string_ref_count(size_t acc, R &&r, Args &&...args) {
|
||||
return concat_string_ref_count(acc + std::ranges::size(r), args...);
|
||||
@@ -246,7 +246,7 @@ uint8_t *concat_string_ref_copy(uint8_t *p, R &&r, Args &&...args) {
|
||||
|
||||
// Returns the string which is the concatenation of |args| in the
|
||||
// given order. The resulting string will be NULL-terminated.
|
||||
template <std::ranges::input_range... Args>
|
||||
template <std::ranges::sized_range... Args>
|
||||
std::string_view concat_string_ref(BlockAllocator &alloc, Args &&...args) {
|
||||
auto len = concat_string_ref_count(0, args...);
|
||||
auto dst = static_cast<uint8_t *>(alloc.alloc(len + 1));
|
||||
@@ -262,9 +262,9 @@ std::string_view concat_string_ref(BlockAllocator &alloc, Args &&...args) {
|
||||
// obtained from alloc.alloc() or alloc.realloc(), and attempts to use
|
||||
// unused memory region by using alloc.realloc(). If value is empty,
|
||||
// then just call concat_string_ref().
|
||||
template <std::ranges::input_range... Args>
|
||||
template <std::ranges::sized_range... Args>
|
||||
std::string_view realloc_concat_string_ref(BlockAllocator &alloc,
|
||||
const std::string_view &value,
|
||||
std::string_view value,
|
||||
Args &&...args) {
|
||||
if (value.empty()) {
|
||||
return concat_string_ref(alloc, std::forward<Args>(args)...);
|
||||
|
||||
10
src/base64.h
10
src/base64.h
@@ -46,7 +46,7 @@ inline constexpr char B64_CHARS[] = {
|
||||
|
||||
constexpr size_t encode_length(size_t n) { return (n + 2) / 3 * 4; }
|
||||
|
||||
template <std::input_iterator I, std::weakly_incrementable O>
|
||||
template <std::forward_iterator I, std::weakly_incrementable O>
|
||||
requires(std::indirectly_writable<O, char>)
|
||||
constexpr O encode(I first, I last, O result) {
|
||||
using result_type = std::iter_value_t<O>;
|
||||
@@ -96,14 +96,14 @@ constexpr O encode(I first, I last, O result) {
|
||||
return p;
|
||||
}
|
||||
|
||||
template <std::ranges::input_range R, std::weakly_incrementable O>
|
||||
template <std::ranges::forward_range R, std::weakly_incrementable O>
|
||||
requires(std::indirectly_writable<O, char> &&
|
||||
!std::is_array_v<std::remove_cvref_t<R>>)
|
||||
constexpr O encode(R &&r, O result) {
|
||||
return encode(std::ranges::begin(r), std::ranges::end(r), std::move(result));
|
||||
}
|
||||
|
||||
template <std::ranges::input_range R>
|
||||
template <std::ranges::sized_range R>
|
||||
requires(!std::is_array_v<std::remove_cvref_t<R>>)
|
||||
constexpr std::string encode(R &&r) {
|
||||
std::string res;
|
||||
@@ -135,7 +135,7 @@ inline constexpr int B64_INDEX_TABLE[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1};
|
||||
|
||||
template <std::input_iterator I, std::weakly_incrementable O>
|
||||
template <std::forward_iterator I, std::weakly_incrementable O>
|
||||
requires(std::indirectly_writable<O, uint8_t>)
|
||||
constexpr O decode(I first, I last, O result) {
|
||||
using result_type = std::iter_value_t<O>;
|
||||
@@ -177,7 +177,7 @@ constexpr O decode(I first, I last, O result) {
|
||||
return p;
|
||||
}
|
||||
|
||||
template <std::ranges::input_range R>
|
||||
template <std::ranges::sized_range R>
|
||||
requires(!std::is_array_v<std::remove_cvref_t<R>>)
|
||||
std::span<const uint8_t> decode(BlockAllocator &balloc, R &&r) {
|
||||
auto len = std::ranges::size(r);
|
||||
|
||||
14
src/buffer.h
14
src/buffer.h
@@ -41,13 +41,12 @@ template <size_t N> struct Buffer {
|
||||
constexpr size_t wleft() const noexcept {
|
||||
return as_unsigned(&buf[N] - last);
|
||||
}
|
||||
// Writes up to min(wleft(), |count|) bytes from buffer pointed by
|
||||
// |src|. Returns number of bytes written.
|
||||
constexpr size_t write(const void *src, size_t count) {
|
||||
count = std::min(count, wleft());
|
||||
auto p = static_cast<const uint8_t *>(src);
|
||||
last = std::ranges::copy_n(p, as_signed(count), last).out;
|
||||
return count;
|
||||
// Writes up to min(wleft(), |src|.size()) bytes from |src|.
|
||||
// Returns number of bytes written.
|
||||
constexpr size_t write(std::span<const uint8_t> src) {
|
||||
src = src.first(std::min(src.size(), wleft()));
|
||||
last = std::ranges::copy(src, last).out;
|
||||
return src.size();
|
||||
}
|
||||
constexpr size_t write(size_t count) {
|
||||
count = std::min(count, wleft());
|
||||
@@ -70,6 +69,7 @@ template <size_t N> struct Buffer {
|
||||
constexpr uint8_t *begin() noexcept { return buf; }
|
||||
constexpr uint8_t &operator[](size_t n) { return buf[n]; }
|
||||
constexpr const uint8_t &operator[](size_t n) const { return buf[n]; }
|
||||
constexpr std::span<uint8_t> wbuffer() { return {last, wleft()}; }
|
||||
uint8_t buf[N];
|
||||
uint8_t *pos, *last;
|
||||
};
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace {
|
||||
@@ -52,7 +54,7 @@ void test_buffer_write(void) {
|
||||
assert_size(0, ==, b.rleft());
|
||||
assert_size(16, ==, b.wleft());
|
||||
|
||||
b.write("012", 3);
|
||||
b.write(as_uint8_span(std::span{"012"sv}));
|
||||
|
||||
assert_size(3, ==, b.rleft());
|
||||
assert_size(13, ==, b.wleft());
|
||||
@@ -64,7 +66,7 @@ void test_buffer_write(void) {
|
||||
assert_size(13, ==, b.wleft());
|
||||
assert_ptrdiff(3, ==, b.pos - std::ranges::begin(b.buf));
|
||||
|
||||
auto n = b.write("0123456789ABCDEF", 16);
|
||||
auto n = b.write(as_uint8_span(std::span{"0123456789ABCDEF"sv}));
|
||||
|
||||
assert_size(13, ==, n);
|
||||
|
||||
|
||||
848
src/h2load.cc
848
src/h2load.cc
File diff suppressed because it is too large
Load Diff
78
src/h2load.h
78
src/h2load.h
@@ -151,6 +151,14 @@ struct Config {
|
||||
// sni is the value sent in TLS SNI, overriding DNS name of the
|
||||
// remote host.
|
||||
std::string sni;
|
||||
// Plot histogram.
|
||||
bool histogram{};
|
||||
// Path to TLS session file.
|
||||
std::string tls_session_file;
|
||||
// TLS session read from file.
|
||||
SSL_SESSION *tls_session{};
|
||||
// Path to file to write the measurement results.
|
||||
std::string output_file;
|
||||
|
||||
Config();
|
||||
~Config();
|
||||
@@ -197,24 +205,56 @@ struct ClientStat {
|
||||
std::chrono::steady_clock::time_point connect_time;
|
||||
// time to first byte (TTFB)
|
||||
std::chrono::steady_clock::time_point ttfb;
|
||||
|
||||
// The minimum RTT (QUIC)
|
||||
std::chrono::nanoseconds min_rtt;
|
||||
// The smoothed RTT (QUIC)
|
||||
std::chrono::nanoseconds smoothed_rtt;
|
||||
// The number of packets sent (QUIC)
|
||||
uint64_t pkt_sent;
|
||||
// The number of packets received (QUIC)
|
||||
uint64_t pkt_recv;
|
||||
// The number of packets declared lost (QUIC)
|
||||
uint64_t pkt_lost;
|
||||
};
|
||||
|
||||
struct SDStat {
|
||||
// min, max, mean and sd (standard deviation)
|
||||
double min, max, mean, sd;
|
||||
struct GROStat {
|
||||
// The number of packets received in a single recvmsg (QUIC)
|
||||
size_t num_pkts;
|
||||
};
|
||||
|
||||
template <typename T> struct SDStat {
|
||||
// min, max, median, p95, and p99
|
||||
T min, max, median, p95, p99;
|
||||
// mean and sd (standard deviation)
|
||||
double mean, sd;
|
||||
// percentage of samples inside mean -/+ sd
|
||||
double within_sd;
|
||||
// sampled data
|
||||
std::vector<T> samples;
|
||||
};
|
||||
|
||||
struct SDStats {
|
||||
// time for request
|
||||
SDStat request;
|
||||
SDStat<double> request;
|
||||
// time for connect
|
||||
SDStat connect;
|
||||
SDStat<double> connect;
|
||||
// time to first byte (TTFB)
|
||||
SDStat ttfb;
|
||||
SDStat<double> ttfb;
|
||||
// request per second for each client
|
||||
SDStat rps;
|
||||
SDStat<double> rps;
|
||||
// minimum RTT (QUIC)
|
||||
SDStat<double> min_rtt;
|
||||
// smoothed RTT (QUIC)
|
||||
SDStat<double> smoothed_rtt;
|
||||
// the number of packets sent (QUIC)
|
||||
SDStat<uint64_t> pkt_sent;
|
||||
// the number of packets received (QUIC)
|
||||
SDStat<uint64_t> pkt_recv;
|
||||
// the number of packets declared lost (QUIC)
|
||||
SDStat<uint64_t> pkt_lost;
|
||||
// the number of packets received in a single recvmsg call (QUIC)
|
||||
SDStat<uint64_t> gro_pkts;
|
||||
};
|
||||
|
||||
struct Stats {
|
||||
@@ -256,6 +296,8 @@ struct Stats {
|
||||
std::vector<RequestStat> req_stats;
|
||||
// The statistics per client
|
||||
std::vector<ClientStat> client_stats;
|
||||
// The statistics about GRO, sampled across all clients.
|
||||
std::vector<GROStat> gro_stats;
|
||||
// The number of UDP datagrams received.
|
||||
size_t udp_dgram_recv;
|
||||
// The number of UDP datagrams sent.
|
||||
@@ -289,6 +331,7 @@ struct Worker {
|
||||
Stats stats;
|
||||
Sampling request_times_smp;
|
||||
Sampling client_smp;
|
||||
Sampling gro_smp;
|
||||
struct ev_loop *loop;
|
||||
SSL_CTX *ssl_ctx;
|
||||
Config *config;
|
||||
@@ -296,6 +339,8 @@ struct Worker {
|
||||
uint32_t id;
|
||||
bool tls_info_report_done;
|
||||
bool app_info_report_done;
|
||||
bool tls_session_store_done{};
|
||||
SSL_SESSION *tls_session{};
|
||||
size_t nconns_made;
|
||||
// number of clients this worker handles
|
||||
size_t nclients;
|
||||
@@ -313,7 +358,7 @@ struct Worker {
|
||||
// worker
|
||||
Phase current_phase;
|
||||
// We need to keep track of the clients in order to stop them when needed
|
||||
std::vector<Client *> clients;
|
||||
std::unordered_map<uint32_t, Client *> clients;
|
||||
// This is only active when there is not a bounded number of requests
|
||||
// specified
|
||||
ev_timer duration_watcher;
|
||||
@@ -326,12 +371,14 @@ struct Worker {
|
||||
void run();
|
||||
void sample_req_stat(RequestStat *req_stat);
|
||||
void sample_client_stat(ClientStat *cstat);
|
||||
void sample_gro_stat(const GROStat &gro_stat);
|
||||
void report_progress();
|
||||
void report_rate_progress();
|
||||
// This function calls the destructors of all the clients.
|
||||
void stop_all_clients();
|
||||
// This function frees a client from the list of clients for this Worker.
|
||||
void free_client(Client *);
|
||||
void write_tls_session(const std::string &path);
|
||||
};
|
||||
|
||||
struct Stream {
|
||||
@@ -442,9 +489,10 @@ struct Client {
|
||||
void process_abandoned_streams();
|
||||
void report_tls_info();
|
||||
void report_app_info();
|
||||
void terminate_session();
|
||||
int terminate_session();
|
||||
// Asks client to create new connection, instead of just fail.
|
||||
void try_new_connection();
|
||||
uint32_t get_id() const;
|
||||
|
||||
int do_read();
|
||||
int do_write();
|
||||
@@ -457,14 +505,14 @@ struct Client {
|
||||
int read_tls();
|
||||
int write_tls();
|
||||
|
||||
int on_read(const uint8_t *data, size_t len);
|
||||
int on_read(std::span<const uint8_t> data);
|
||||
int on_write();
|
||||
|
||||
int connection_made();
|
||||
|
||||
void on_request(int64_t stream_id);
|
||||
void on_header(int64_t stream_id, const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen);
|
||||
void on_header(int64_t stream_id, std::span<const uint8_t> name,
|
||||
std::span<const uint8_t> value);
|
||||
void on_status_code(int64_t stream_id, uint16_t status);
|
||||
// |success| == true means that the request/response was exchanged
|
||||
// |successfully, but it does not mean response carried successful
|
||||
@@ -494,7 +542,7 @@ struct Client {
|
||||
int read_quic();
|
||||
int write_quic();
|
||||
ngtcp2_ssize write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi,
|
||||
uint8_t *dest, size_t destlen, ngtcp2_tstamp ts);
|
||||
std::span<uint8_t> dest, ngtcp2_tstamp ts);
|
||||
std::span<const uint8_t> write_udp(const sockaddr *addr, socklen_t addrlen,
|
||||
std::span<const uint8_t> data,
|
||||
size_t gso_size);
|
||||
@@ -507,7 +555,7 @@ struct Client {
|
||||
|
||||
int quic_handshake_completed();
|
||||
int quic_recv_stream_data(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen);
|
||||
std::span<const uint8_t> data);
|
||||
int quic_acked_stream_data_offset(int64_t stream_id, size_t datalen);
|
||||
int quic_stream_close(int64_t stream_id, uint64_t app_error_code);
|
||||
int quic_stream_reset(int64_t stream_id, uint64_t app_error_code);
|
||||
@@ -515,8 +563,6 @@ struct Client {
|
||||
int quic_extend_max_local_streams();
|
||||
int quic_extend_max_stream_data(int64_t stream_id);
|
||||
|
||||
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();
|
||||
void quic_write_qlog(const void *data, size_t datalen);
|
||||
|
||||
@@ -190,24 +190,22 @@ int Http1Session::submit_request() {
|
||||
if (config->data_fd == -1 || config->data_length == 0) {
|
||||
// increment for next request
|
||||
stream_req_counter_ += 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return on_write();
|
||||
}
|
||||
|
||||
int Http1Session::on_read(const uint8_t *data, size_t len) {
|
||||
auto htperr =
|
||||
llhttp_execute(&htp_, reinterpret_cast<const char *>(data), len);
|
||||
int Http1Session::on_read(std::span<const uint8_t> data) {
|
||||
auto htperr = llhttp_execute(
|
||||
&htp_, reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto nread = htperr == HPE_OK
|
||||
? len
|
||||
? data.size()
|
||||
: static_cast<size_t>(reinterpret_cast<const uint8_t *>(
|
||||
llhttp_get_error_pos(&htp_)) -
|
||||
data);
|
||||
data.data());
|
||||
|
||||
if (client_->worker->config->verbose) {
|
||||
std::cout.write(reinterpret_cast<const char *>(data),
|
||||
std::cout.write(reinterpret_cast<const char *>(data.data()),
|
||||
static_cast<std::streamsize>(nread));
|
||||
}
|
||||
|
||||
|
||||
@@ -38,13 +38,13 @@ struct Client;
|
||||
class Http1Session : public Session {
|
||||
public:
|
||||
Http1Session(Client *client);
|
||||
virtual ~Http1Session();
|
||||
virtual void on_connect();
|
||||
virtual int submit_request();
|
||||
virtual int on_read(const uint8_t *data, size_t len);
|
||||
virtual int on_write();
|
||||
virtual void terminate();
|
||||
virtual size_t max_concurrent_streams();
|
||||
~Http1Session() override;
|
||||
void on_connect() override;
|
||||
int submit_request() override;
|
||||
int on_read(std::span<const uint8_t> data) override;
|
||||
int on_write() override;
|
||||
void terminate() override;
|
||||
size_t max_concurrent_streams() override;
|
||||
Client *get_client();
|
||||
int32_t stream_req_counter_;
|
||||
int32_t stream_resp_counter_;
|
||||
|
||||
@@ -50,7 +50,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
if (frame->hd.type != NGHTTP2_HEADERS) {
|
||||
return 0;
|
||||
}
|
||||
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
|
||||
client->on_header(frame->hd.stream_id, {name, namelen}, {value, valuelen});
|
||||
client->worker->stats.bytes_head_decomp += namelen + valuelen;
|
||||
|
||||
if (client->worker->config->verbose) {
|
||||
@@ -186,7 +186,8 @@ void Http2Session::on_connect() {
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks);
|
||||
auto callbacks_deleter =
|
||||
defer([callbacks] { nghttp2_session_callbacks_del(callbacks); });
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
@@ -278,13 +279,13 @@ int Http2Session::submit_request() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http2Session::on_read(const uint8_t *data, size_t len) {
|
||||
auto rv = nghttp2_session_mem_recv2(session_, data, len);
|
||||
int Http2Session::on_read(std::span<const uint8_t> data) {
|
||||
auto rv = nghttp2_session_mem_recv2(session_, data.data(), data.size());
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(static_cast<size_t>(rv) == len);
|
||||
assert(static_cast<size_t>(rv) == data.size());
|
||||
|
||||
if (nghttp2_session_want_read(session_) == 0 &&
|
||||
nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
|
||||
|
||||
@@ -36,13 +36,13 @@ struct Client;
|
||||
class Http2Session : public Session {
|
||||
public:
|
||||
Http2Session(Client *client);
|
||||
virtual ~Http2Session();
|
||||
virtual void on_connect();
|
||||
virtual int submit_request();
|
||||
virtual int on_read(const uint8_t *data, size_t len);
|
||||
virtual int on_write();
|
||||
virtual void terminate();
|
||||
virtual size_t max_concurrent_streams();
|
||||
~Http2Session() override;
|
||||
void on_connect() override;
|
||||
int submit_request() override;
|
||||
int on_read(std::span<const uint8_t> data) override;
|
||||
int on_write() override;
|
||||
void terminate() override;
|
||||
size_t max_concurrent_streams() override;
|
||||
|
||||
private:
|
||||
Client *client_;
|
||||
|
||||
@@ -127,7 +127,7 @@ int64_t Http3Session::submit_request_internal() {
|
||||
return stream_id;
|
||||
}
|
||||
|
||||
int Http3Session::on_read(const uint8_t *data, size_t len) { return -1; }
|
||||
int Http3Session::on_read(std::span<const uint8_t> data) { return -1; }
|
||||
|
||||
int Http3Session::on_write() { return -1; }
|
||||
|
||||
@@ -178,16 +178,15 @@ namespace {
|
||||
int recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data,
|
||||
size_t datalen, void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
s->recv_data(stream_id, data, datalen);
|
||||
s->recv_data(stream_id, {data, datalen});
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::recv_data(int64_t stream_id, const uint8_t *data,
|
||||
size_t datalen) {
|
||||
void Http3Session::recv_data(int64_t stream_id, std::span<const uint8_t> data) {
|
||||
client_->record_ttfb();
|
||||
client_->worker->stats.bytes_body += datalen;
|
||||
consume(stream_id, datalen);
|
||||
client_->worker->stats.bytes_body += data.size();
|
||||
consume(stream_id, data.size());
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -228,15 +227,15 @@ int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
auto k = nghttp3_rcbuf_get_buf(name);
|
||||
auto v = nghttp3_rcbuf_get_buf(value);
|
||||
s->recv_header(stream_id, &k, &v);
|
||||
s->recv_header(stream_id, {k.base, k.len}, {v.base, v.len});
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::recv_header(int64_t stream_id, const nghttp3_vec *name,
|
||||
const nghttp3_vec *value) {
|
||||
client_->on_header(stream_id, name->base, name->len, value->base, value->len);
|
||||
client_->worker->stats.bytes_head_decomp += name->len + value->len;
|
||||
void Http3Session::recv_header(int64_t stream_id, std::span<const uint8_t> name,
|
||||
std::span<const uint8_t> value) {
|
||||
client_->on_header(stream_id, name, value);
|
||||
client_->worker->stats.bytes_head_decomp += name.size() + value.size();
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -423,10 +422,11 @@ int Http3Session::init_conn() {
|
||||
}
|
||||
|
||||
ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
auto nconsumed = nghttp3_conn_read_stream2(
|
||||
conn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN,
|
||||
ngtcp2_conn_get_timestamp(client_->quic.conn));
|
||||
std::span<const uint8_t> data) {
|
||||
auto nconsumed =
|
||||
nghttp3_conn_read_stream2(conn_, stream_id, data.data(), data.size(),
|
||||
flags & NGTCP2_STREAM_DATA_FLAG_FIN,
|
||||
ngtcp2_conn_get_timestamp(client_->quic.conn));
|
||||
if (nconsumed < 0) {
|
||||
std::cerr << "nghttp3_conn_read_stream2: "
|
||||
<< nghttp3_strerror(static_cast<int>(nconsumed)) << std::endl;
|
||||
|
||||
@@ -36,22 +36,22 @@ struct Client;
|
||||
class Http3Session : public Session {
|
||||
public:
|
||||
Http3Session(Client *client);
|
||||
virtual ~Http3Session();
|
||||
virtual void on_connect();
|
||||
virtual int submit_request();
|
||||
virtual int on_read(const uint8_t *data, size_t len);
|
||||
virtual int on_write();
|
||||
virtual void terminate();
|
||||
virtual size_t max_concurrent_streams();
|
||||
~Http3Session() override;
|
||||
void on_connect() override;
|
||||
int submit_request() override;
|
||||
int on_read(std::span<const uint8_t> data) override;
|
||||
int on_write() override;
|
||||
void terminate() override;
|
||||
size_t max_concurrent_streams() override;
|
||||
|
||||
int init_conn();
|
||||
int stream_close(int64_t stream_id, uint64_t app_error_code);
|
||||
int end_stream(int64_t stream_id);
|
||||
void recv_data(int64_t stream_id, const uint8_t *data, size_t datalen);
|
||||
void recv_data(int64_t stream_id, std::span<const uint8_t> data);
|
||||
void consume(int64_t stream_id, size_t nconsumed);
|
||||
void begin_headers(int64_t stream_id);
|
||||
void recv_header(int64_t stream_id, const nghttp3_vec *name,
|
||||
const nghttp3_vec *value);
|
||||
void recv_header(int64_t stream_id, std::span<const uint8_t> name,
|
||||
std::span<const uint8_t> value);
|
||||
int stop_sending(int64_t stream_id, uint64_t app_error_code);
|
||||
int reset_stream(int64_t stream_id, uint64_t app_error_code);
|
||||
|
||||
@@ -60,8 +60,8 @@ public:
|
||||
int extend_max_local_streams();
|
||||
int64_t submit_request_internal();
|
||||
|
||||
ssize_t read_stream(uint32_t flags, int64_t stream_id, const uint8_t *data,
|
||||
size_t datalen);
|
||||
ssize_t read_stream(uint32_t flags, int64_t stream_id,
|
||||
std::span<const uint8_t> data);
|
||||
ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
|
||||
size_t veccnt);
|
||||
void block_stream(int64_t stream_id);
|
||||
|
||||
@@ -71,7 +71,7 @@ int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
||||
uint64_t offset, const uint8_t *data, size_t datalen,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_recv_stream_data(flags, stream_id, data, datalen) != 0) {
|
||||
if (c->quic_recv_stream_data(flags, stream_id, {data, datalen}) != 0) {
|
||||
// TODO Better to do this gracefully rather than
|
||||
// NGTCP2_ERR_CALLBACK_FAILURE. Perhaps, call
|
||||
// ngtcp2_conn_write_application_close() ?
|
||||
@@ -82,13 +82,13 @@ int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
||||
} // namespace
|
||||
|
||||
int Client::quic_recv_stream_data(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
std::span<const uint8_t> data) {
|
||||
if (worker->current_phase == Phase::MAIN_DURATION) {
|
||||
worker->stats.bytes_total += datalen;
|
||||
worker->stats.bytes_total += data.size();
|
||||
}
|
||||
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
auto nconsumed = s->read_stream(flags, stream_id, data, datalen);
|
||||
auto nconsumed = s->read_stream(flags, stream_id, data);
|
||||
if (nconsumed == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -344,6 +344,8 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
const sockaddr *remote_addr, socklen_t remote_addrlen) {
|
||||
int rv;
|
||||
|
||||
auto config = worker->config;
|
||||
|
||||
if (!ssl) {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
|
||||
@@ -368,6 +370,14 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
#else // !OPENSSL_3_5_0_API
|
||||
SSL_set_quic_use_legacy_codepoint(ssl, 0);
|
||||
#endif // !OPENSSL_3_5_0_API
|
||||
|
||||
if (config->tls_session && !SSL_set_session(ssl, config->tls_session)) {
|
||||
std::cerr << "Could not set TLS session" << std::endl;
|
||||
}
|
||||
|
||||
if (!config->tls_session_file.empty()) {
|
||||
SSL_set_ex_data(ssl, 1, worker);
|
||||
}
|
||||
}
|
||||
|
||||
auto callbacks = ngtcp2_callbacks{
|
||||
@@ -402,8 +412,6 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto config = worker->config;
|
||||
|
||||
ngtcp2_settings settings;
|
||||
ngtcp2_settings_default(&settings);
|
||||
if (config->verbose) {
|
||||
@@ -479,6 +487,18 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
}
|
||||
|
||||
void Client::quic_free() {
|
||||
if (quic.conn) {
|
||||
ngtcp2_conn_info ci;
|
||||
|
||||
ngtcp2_conn_get_conn_info(quic.conn, &ci);
|
||||
|
||||
cstat.min_rtt = std::chrono::nanoseconds(ci.min_rtt);
|
||||
cstat.smoothed_rtt = std::chrono::nanoseconds(ci.smoothed_rtt);
|
||||
cstat.pkt_sent = ci.pkt_sent;
|
||||
cstat.pkt_recv = ci.pkt_recv;
|
||||
cstat.pkt_lost = ci.pkt_lost;
|
||||
}
|
||||
|
||||
#if OPENSSL_3_5_0_API
|
||||
ngtcp2_crypto_ossl_ctx_del(quic.ossl_ctx);
|
||||
#endif // OPENSSL_3_5_0_API
|
||||
@@ -512,22 +532,6 @@ void Client::quic_close_connection() {
|
||||
as_unsigned(nwrite));
|
||||
}
|
||||
|
||||
int Client::quic_write_client_handshake(ngtcp2_encryption_level level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
int rv;
|
||||
|
||||
assert(level < 2);
|
||||
|
||||
rv = ngtcp2_conn_submit_crypto_data(quic.conn, level, data, datalen);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_submit_crypto_data: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto c = static_cast<Client *>(w->data);
|
||||
|
||||
@@ -565,7 +569,7 @@ void Client::quic_restart_pkt_timer() {
|
||||
|
||||
int Client::read_quic() {
|
||||
std::array<uint8_t, 64_k> buf;
|
||||
sockaddr_union su;
|
||||
sockaddr_storage ss;
|
||||
int rv;
|
||||
size_t pktcnt = 0;
|
||||
ngtcp2_pkt_info pi;
|
||||
@@ -578,7 +582,7 @@ int Client::read_quic() {
|
||||
uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
msghdr msg{
|
||||
.msg_name = &su,
|
||||
.msg_name = &ss,
|
||||
.msg_iov = &msg_iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = msg_ctrl,
|
||||
@@ -587,7 +591,7 @@ int Client::read_quic() {
|
||||
auto ts = quic_timestamp();
|
||||
|
||||
for (;;) {
|
||||
msg.msg_namelen = sizeof(su);
|
||||
msg.msg_namelen = sizeof(ss);
|
||||
msg.msg_controllen = sizeof(msg_ctrl);
|
||||
|
||||
auto nread = recvmsg(fd, &msg, 0);
|
||||
@@ -602,21 +606,24 @@ int Client::read_quic() {
|
||||
|
||||
assert(quic.conn);
|
||||
|
||||
size_t num_pkts;
|
||||
|
||||
if (gso_size) {
|
||||
worker->stats.udp_dgram_recv +=
|
||||
(as_unsigned(nread) + gso_size - 1) / gso_size;
|
||||
num_pkts = (as_unsigned(nread) + gso_size - 1) / gso_size;
|
||||
} else {
|
||||
++worker->stats.udp_dgram_recv;
|
||||
num_pkts = 1;
|
||||
}
|
||||
|
||||
worker->stats.udp_dgram_recv += num_pkts;
|
||||
worker->sample_gro_stat(GROStat{
|
||||
.num_pkts = num_pkts,
|
||||
});
|
||||
|
||||
auto path = ngtcp2_path{
|
||||
{
|
||||
&local_addr.su.sa,
|
||||
local_addr.len,
|
||||
},
|
||||
{
|
||||
&su.sa,
|
||||
msg.msg_namelen,
|
||||
.local{as_ngtcp2_addr(local_addr)},
|
||||
.remote{
|
||||
.addr = reinterpret_cast<sockaddr *>(&ss),
|
||||
.addrlen = msg.msg_namelen,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -664,13 +671,12 @@ ngtcp2_ssize write_pkt(ngtcp2_conn *conn, ngtcp2_path *path,
|
||||
ngtcp2_tstamp ts, void *user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
|
||||
return c->write_quic_pkt(path, pi, dest, destlen, ts);
|
||||
return c->write_quic_pkt(path, pi, {dest, destlen}, ts);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ngtcp2_ssize Client::write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi,
|
||||
uint8_t *dest, size_t destlen,
|
||||
ngtcp2_tstamp ts) {
|
||||
std::span<uint8_t> dest, ngtcp2_tstamp ts) {
|
||||
std::array<nghttp3_vec, 16> vec;
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
|
||||
@@ -697,8 +703,8 @@ ngtcp2_ssize Client::write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi,
|
||||
}
|
||||
|
||||
auto nwrite = ngtcp2_conn_writev_stream(
|
||||
quic.conn, path, nullptr, dest, destlen, &ndatalen, flags, stream_id,
|
||||
reinterpret_cast<const ngtcp2_vec *>(v), vcnt, ts);
|
||||
quic.conn, path, nullptr, dest.data(), dest.size(), &ndatalen, flags,
|
||||
stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, ts);
|
||||
if (nwrite < 0) {
|
||||
switch (nwrite) {
|
||||
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
|
||||
@@ -794,9 +800,7 @@ void Client::on_send_blocked(const ngtcp2_addr &remote_addr,
|
||||
|
||||
auto &p = quic.tx.blocked;
|
||||
|
||||
memcpy(&p.remote_addr.su, remote_addr.addr, remote_addr.addrlen);
|
||||
|
||||
p.remote_addr.len = remote_addr.addrlen;
|
||||
p.remote_addr.set(remote_addr.addr);
|
||||
p.data = data;
|
||||
p.gso_size = gso_size;
|
||||
|
||||
@@ -808,8 +812,8 @@ int Client::send_blocked_packet() {
|
||||
|
||||
auto &p = quic.tx.blocked;
|
||||
|
||||
auto rest =
|
||||
write_udp(&p.remote_addr.su.sa, p.remote_addr.len, p.data, p.gso_size);
|
||||
auto rest = write_udp(p.remote_addr.as_sockaddr(), p.remote_addr.size(),
|
||||
p.data, p.gso_size);
|
||||
if (!rest.empty()) {
|
||||
p.data = rest;
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
virtual int submit_request() = 0;
|
||||
// Called when incoming bytes are available. The subclass has to
|
||||
// return the number of bytes read.
|
||||
virtual int on_read(const uint8_t *data, size_t len) = 0;
|
||||
virtual int on_read(std::span<const uint8_t> data) = 0;
|
||||
// Called when write is available. Returns 0 on success, otherwise
|
||||
// return -1.
|
||||
virtual int on_write() = 0;
|
||||
|
||||
81
src/http2.cc
81
src/http2.cc
@@ -253,7 +253,7 @@ std::string_view stringify_status(BlockAllocator &balloc,
|
||||
struct Capitalizer {
|
||||
template <std::weakly_incrementable O>
|
||||
requires(std::indirectly_writable<O, char>)
|
||||
constexpr O operator()(const std::string_view &s, O result) noexcept {
|
||||
constexpr O operator()(std::string_view s, O result) noexcept {
|
||||
using result_type = std::iter_value_t<O>;
|
||||
|
||||
*result++ = static_cast<result_type>(util::upcase(s[0]));
|
||||
@@ -271,7 +271,7 @@ struct Capitalizer {
|
||||
};
|
||||
|
||||
namespace {
|
||||
void capitalize_long(DefaultMemchunks *buf, const std::string_view &s) {
|
||||
void capitalize_long(DefaultMemchunks *buf, std::string_view s) {
|
||||
buf->append(util::upcase(s[0]));
|
||||
|
||||
auto it = std::ranges::begin(s) + 1;
|
||||
@@ -294,7 +294,7 @@ void capitalize_long(DefaultMemchunks *buf, const std::string_view &s) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void capitalize(DefaultMemchunks *buf, const std::string_view &s) {
|
||||
void capitalize(DefaultMemchunks *buf, std::string_view s) {
|
||||
assert(!s.empty());
|
||||
|
||||
constexpr size_t max_namelen = 32;
|
||||
@@ -307,21 +307,20 @@ void capitalize(DefaultMemchunks *buf, const std::string_view &s) {
|
||||
buf->append(s.size(), std::bind_front(Capitalizer{}, s));
|
||||
}
|
||||
|
||||
Headers::value_type to_header(const std::string_view &name,
|
||||
const std::string_view &value, bool no_index,
|
||||
int32_t token) {
|
||||
Headers::value_type to_header(std::string_view name, std::string_view value,
|
||||
bool no_index, int32_t token) {
|
||||
return Header(std::string{std::ranges::begin(name), std::ranges::end(name)},
|
||||
std::string{std::ranges::begin(value), std::ranges::end(value)},
|
||||
no_index, token);
|
||||
}
|
||||
|
||||
void add_header(Headers &nva, const std::string_view &name,
|
||||
const std::string_view &value, bool no_index, int32_t token) {
|
||||
void add_header(Headers &nva, std::string_view name, std::string_view value,
|
||||
bool no_index, int32_t token) {
|
||||
nva.push_back(to_header(name, value, no_index, token));
|
||||
}
|
||||
|
||||
const Headers::value_type *get_header(const Headers &nva,
|
||||
const std::string_view &name) {
|
||||
std::string_view name) {
|
||||
const Headers::value_type *res = nullptr;
|
||||
for (auto &nv : nva) {
|
||||
if (nv.name == name) {
|
||||
@@ -585,11 +584,11 @@ void erase_header(HeaderRef *hd) {
|
||||
}
|
||||
|
||||
std::string_view rewrite_location_uri(BlockAllocator &balloc,
|
||||
const std::string_view &uri,
|
||||
std::string_view uri,
|
||||
const urlparse_url &u,
|
||||
const std::string_view &match_host,
|
||||
const std::string_view &request_authority,
|
||||
const std::string_view &upstream_scheme) {
|
||||
std::string_view match_host,
|
||||
std::string_view request_authority,
|
||||
std::string_view upstream_scheme) {
|
||||
// We just rewrite scheme and authority.
|
||||
if ((u.field_set & (1 << URLPARSE_HOST)) == 0) {
|
||||
return ""sv;
|
||||
@@ -649,7 +648,7 @@ std::string_view rewrite_location_uri(BlockAllocator &balloc,
|
||||
return as_string_view(std::ranges::begin(iov), p);
|
||||
}
|
||||
|
||||
int parse_http_status_code(const std::string_view &src) {
|
||||
int parse_http_status_code(std::string_view src) {
|
||||
if (src.size() != 3) {
|
||||
return -1;
|
||||
}
|
||||
@@ -672,7 +671,7 @@ int parse_http_status_code(const std::string_view &src) {
|
||||
|
||||
// This function was generated by genheaderfunc.py. Inspired by h2o
|
||||
// header lookup. https://github.com/h2o/h2o
|
||||
int lookup_token(const std::string_view &name) {
|
||||
int lookup_token(std::string_view name) {
|
||||
switch (name.size()) {
|
||||
case 2:
|
||||
switch (name[1]) {
|
||||
@@ -1025,8 +1024,7 @@ namespace {
|
||||
// Returns true if link-param does not match pattern |pat| of length
|
||||
// |patlen| or it has empty value (""). |pat| should be parmname
|
||||
// followed by "=".
|
||||
bool check_link_param_empty(const std::string_view &s,
|
||||
const std::string_view &pat) {
|
||||
bool check_link_param_empty(std::string_view s, std::string_view pat) {
|
||||
return s.size() < pat.size() ||
|
||||
!std::ranges::equal(s.substr(0, pat.size()), pat, util::CaseCmp()) ||
|
||||
(s.size() >= pat.size() + 2 &&
|
||||
@@ -1039,8 +1037,7 @@ bool check_link_param_empty(const std::string_view &s,
|
||||
namespace {
|
||||
// Returns true if link-param consists of only parmname, and it
|
||||
// matches string [pat, pat + patlen).
|
||||
bool check_link_param_without_value(const std::string_view &s,
|
||||
const std::string_view &pat) {
|
||||
bool check_link_param_without_value(std::string_view s, std::string_view pat) {
|
||||
if (s.size() < pat.size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -1284,7 +1281,7 @@ almost_done:
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::vector<LinkHeader> parse_link_header(const std::string_view &src) {
|
||||
std::vector<LinkHeader> parse_link_header(std::string_view src) {
|
||||
std::vector<LinkHeader> res;
|
||||
for (auto first = std::ranges::begin(src); first != std::ranges::end(src);) {
|
||||
auto rv = parse_next_link_header_once(first, std::ranges::end(src));
|
||||
@@ -1297,10 +1294,8 @@ std::vector<LinkHeader> parse_link_header(const std::string_view &src) {
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string path_join(const std::string_view &base_path,
|
||||
const std::string_view &base_query,
|
||||
const std::string_view &rel_path,
|
||||
const std::string_view &rel_query) {
|
||||
std::string path_join(std::string_view base_path, std::string_view base_query,
|
||||
std::string_view rel_path, std::string_view rel_query) {
|
||||
BlockAllocator balloc(1024, 1024);
|
||||
|
||||
return std::string{
|
||||
@@ -1321,7 +1316,7 @@ bool expect_response_body(int method_token, uint32_t status_code) {
|
||||
}
|
||||
|
||||
// This function was generated by genmethodfunc.py.
|
||||
int lookup_method_token(const std::string_view &name) {
|
||||
int lookup_method_token(std::string_view name) {
|
||||
switch (name.size()) {
|
||||
case 3:
|
||||
switch (name[2]) {
|
||||
@@ -1523,7 +1518,7 @@ std::string_view to_method_string(int method_token) {
|
||||
llhttp_method_name(static_cast<llhttp_method>(method_token))};
|
||||
}
|
||||
|
||||
std::string_view get_pure_path_component(const std::string_view &uri) {
|
||||
std::string_view get_pure_path_component(std::string_view uri) {
|
||||
int rv;
|
||||
|
||||
urlparse_url u;
|
||||
@@ -1542,9 +1537,8 @@ std::string_view get_pure_path_component(const std::string_view &uri) {
|
||||
|
||||
int construct_push_component(BlockAllocator &balloc, std::string_view &scheme,
|
||||
std::string_view &authority,
|
||||
std::string_view &path,
|
||||
const std::string_view &base,
|
||||
const std::string_view &uri) {
|
||||
std::string_view &path, std::string_view base,
|
||||
std::string_view uri) {
|
||||
int rv;
|
||||
std::string_view rel, relq;
|
||||
|
||||
@@ -1648,11 +1642,10 @@ template <typename InputIt> InputIt eat_dir(InputIt first, InputIt last) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string_view path_join(BlockAllocator &balloc,
|
||||
const std::string_view &base_path,
|
||||
const std::string_view &base_query,
|
||||
const std::string_view &rel_path,
|
||||
const std::string_view &rel_query) {
|
||||
std::string_view path_join(BlockAllocator &balloc, std::string_view base_path,
|
||||
std::string_view base_query,
|
||||
std::string_view rel_path,
|
||||
std::string_view rel_query) {
|
||||
auto res =
|
||||
make_byte_ref(balloc, std::max(static_cast<size_t>(1), base_path.size()) +
|
||||
rel_path.size() + 1 +
|
||||
@@ -1741,9 +1734,8 @@ std::string_view path_join(BlockAllocator &balloc,
|
||||
return as_string_view(std::ranges::begin(res), p);
|
||||
}
|
||||
|
||||
std::string_view normalize_path(BlockAllocator &balloc,
|
||||
const std::string_view &path,
|
||||
const std::string_view &query) {
|
||||
std::string_view normalize_path(BlockAllocator &balloc, std::string_view path,
|
||||
std::string_view query) {
|
||||
// First, decode %XX for unreserved characters, then do
|
||||
// http2::path_join
|
||||
|
||||
@@ -1790,8 +1782,8 @@ std::string_view normalize_path(BlockAllocator &balloc,
|
||||
}
|
||||
|
||||
std::string_view normalize_path_colon(BlockAllocator &balloc,
|
||||
const std::string_view &path,
|
||||
const std::string_view &query) {
|
||||
std::string_view path,
|
||||
std::string_view query) {
|
||||
// First, decode %XX for unreserved characters and ':', then do
|
||||
// http2::path_join
|
||||
|
||||
@@ -1837,15 +1829,14 @@ std::string_view normalize_path_colon(BlockAllocator &balloc,
|
||||
as_string_view(std::ranges::begin(result), p), query);
|
||||
}
|
||||
|
||||
std::string normalize_path(const std::string_view &path,
|
||||
const std::string_view &query) {
|
||||
std::string normalize_path(std::string_view path, std::string_view query) {
|
||||
BlockAllocator balloc(1024, 1024);
|
||||
|
||||
return std::string{normalize_path(balloc, path, query)};
|
||||
}
|
||||
|
||||
std::string_view rewrite_clean_path(BlockAllocator &balloc,
|
||||
const std::string_view &src) {
|
||||
std::string_view src) {
|
||||
if (src.empty() || src[0] != '/') {
|
||||
return src;
|
||||
}
|
||||
@@ -1861,7 +1852,7 @@ std::string_view rewrite_clean_path(BlockAllocator &balloc,
|
||||
std::string_view{query, fragment});
|
||||
}
|
||||
|
||||
bool contains_trailers(const std::string_view &s) {
|
||||
bool contains_trailers(std::string_view s) {
|
||||
constexpr auto trailers = "trailers"sv;
|
||||
|
||||
for (auto p = std::ranges::begin(s), end = std::ranges::end(s);; ++p) {
|
||||
@@ -1888,7 +1879,7 @@ bool contains_trailers(const std::string_view &s) {
|
||||
}
|
||||
|
||||
std::string_view make_websocket_accept_token(uint8_t *dest,
|
||||
const std::string_view &key) {
|
||||
std::string_view key) {
|
||||
static constexpr auto magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"sv;
|
||||
std::array<char, base64::encode_length(16) + magic.size()> s;
|
||||
auto p = std::ranges::copy(key, std::ranges::begin(s)).out;
|
||||
@@ -1906,7 +1897,7 @@ bool legacy_http1(int major, int minor) {
|
||||
return major <= 0 || (major == 1 && minor == 0);
|
||||
}
|
||||
|
||||
bool check_transfer_encoding(const std::string_view &s) {
|
||||
bool check_transfer_encoding(std::string_view s) {
|
||||
if (s.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
86
src/http2.h
86
src/http2.h
@@ -70,7 +70,7 @@ struct Header {
|
||||
};
|
||||
|
||||
struct HeaderRef {
|
||||
HeaderRef(const std::string_view &name, const std::string_view &value,
|
||||
HeaderRef(std::string_view name, std::string_view value,
|
||||
bool no_index = false, int32_t token = -1)
|
||||
: name(name), value(value), token(token), no_index(no_index) {}
|
||||
|
||||
@@ -103,30 +103,29 @@ std::string_view get_reason_phrase(unsigned int status_code);
|
||||
std::string_view stringify_status(BlockAllocator &balloc,
|
||||
unsigned int status_code);
|
||||
|
||||
void capitalize(DefaultMemchunks *buf, const std::string_view &s);
|
||||
void capitalize(DefaultMemchunks *buf, std::string_view s);
|
||||
|
||||
Headers::value_type to_header(const std::string_view &name,
|
||||
const std::string_view &value, bool no_index,
|
||||
int32_t token);
|
||||
Headers::value_type to_header(std::string_view name, std::string_view value,
|
||||
bool no_index, int32_t token);
|
||||
|
||||
// Add name/value pairs to |nva|. If |no_index| is true, this
|
||||
// name/value pair won't be indexed when it is forwarded to the next
|
||||
// hop.
|
||||
void add_header(Headers &nva, const std::string_view &name,
|
||||
const std::string_view &value, bool no_index, int32_t token);
|
||||
void add_header(Headers &nva, std::string_view name, std::string_view value,
|
||||
bool no_index, int32_t token);
|
||||
|
||||
// Returns pointer to the entry in |nva| which has name |name|. If
|
||||
// more than one entries which have the name |name|, last occurrence
|
||||
// in |nva| is returned. If no such entry exist, returns nullptr.
|
||||
const Headers::value_type *get_header(const Headers &nva,
|
||||
const std::string_view &name);
|
||||
std::string_view name);
|
||||
|
||||
// Returns true if the value of |nv| is not empty.
|
||||
bool non_empty_value(const HeaderRefs::value_type *nv);
|
||||
|
||||
// Create nghttp2_nv from |name|, |value| and |flags|.
|
||||
inline nghttp2_nv make_field_flags(const std::string_view &name,
|
||||
const std::string_view &value,
|
||||
inline nghttp2_nv make_field_flags(std::string_view name,
|
||||
std::string_view value,
|
||||
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
|
||||
auto ns = as_uint8_span(std::span{name});
|
||||
auto vs = as_uint8_span(std::span{value});
|
||||
@@ -137,8 +136,7 @@ inline nghttp2_nv make_field_flags(const std::string_view &name,
|
||||
|
||||
// Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2
|
||||
// library does not copy them.
|
||||
inline nghttp2_nv make_field(const std::string_view &name,
|
||||
const std::string_view &value,
|
||||
inline nghttp2_nv make_field(std::string_view name, std::string_view value,
|
||||
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
|
||||
return make_field_flags(name, value,
|
||||
static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME |
|
||||
@@ -149,8 +147,7 @@ inline nghttp2_nv make_field(const std::string_view &name,
|
||||
// Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2
|
||||
// library copies |value| unless |flags| includes
|
||||
// NGHTTP2_NV_FLAG_NO_COPY_VALUE.
|
||||
inline nghttp2_nv make_field_v(const std::string_view &name,
|
||||
const std::string_view &value,
|
||||
inline nghttp2_nv make_field_v(std::string_view name, std::string_view value,
|
||||
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
|
||||
return make_field_flags(
|
||||
name, value, static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME | flags));
|
||||
@@ -159,8 +156,7 @@ inline nghttp2_nv make_field_v(const std::string_view &name,
|
||||
// Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2
|
||||
// library copies |name| and |value| unless |flags| includes
|
||||
// NGHTTP2_NV_FLAG_NO_COPY_NAME or NGHTTP2_NV_FLAG_NO_COPY_VALUE.
|
||||
inline nghttp2_nv make_field_nv(const std::string_view &name,
|
||||
const std::string_view &value,
|
||||
inline nghttp2_nv make_field_nv(std::string_view name, std::string_view value,
|
||||
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
|
||||
return make_field_flags(name, value, flags);
|
||||
}
|
||||
@@ -260,14 +256,14 @@ void erase_header(HeaderRef *hd);
|
||||
// location URI is not subject to the rewrite, this function returns
|
||||
// empty string.
|
||||
std::string_view rewrite_location_uri(BlockAllocator &balloc,
|
||||
const std::string_view &uri,
|
||||
std::string_view uri,
|
||||
const urlparse_url &u,
|
||||
const std::string_view &match_host,
|
||||
const std::string_view &request_authority,
|
||||
const std::string_view &upstream_scheme);
|
||||
std::string_view match_host,
|
||||
std::string_view request_authority,
|
||||
std::string_view upstream_scheme);
|
||||
|
||||
// Returns parsed HTTP status code. Returns -1 on failure.
|
||||
int parse_http_status_code(const std::string_view &src);
|
||||
int parse_http_status_code(std::string_view src);
|
||||
|
||||
// Header fields to be indexed, except HD_MAXIDX which is convenient
|
||||
// member to get maximum value.
|
||||
@@ -320,7 +316,7 @@ using HeaderIndex = std::array<int16_t, HD_MAXIDX>;
|
||||
// Looks up header token for header name |name|. Only headers we are
|
||||
// interested in are tokenized. If header name cannot be tokenized,
|
||||
// returns -1.
|
||||
int lookup_token(const std::string_view &name);
|
||||
int lookup_token(std::string_view name);
|
||||
|
||||
// Initializes |hdidx|, header index. The |hdidx| must point to the
|
||||
// array containing at least HD_MAXIDX elements.
|
||||
@@ -337,23 +333,20 @@ struct LinkHeader {
|
||||
// URI-reference found after searching all input, returned uri field
|
||||
// is empty. This imply that empty URI-reference is ignored during
|
||||
// parsing.
|
||||
std::vector<LinkHeader> parse_link_header(const std::string_view &src);
|
||||
std::vector<LinkHeader> parse_link_header(std::string_view src);
|
||||
|
||||
// Constructs path by combining base path |base_path| with another
|
||||
// path |rel_path|. The base path and another path can have optional
|
||||
// query component. This function assumes |base_path| is normalized.
|
||||
// In other words, it does not contain ".." or "." path components
|
||||
// and starts with "/" if it is not empty.
|
||||
std::string path_join(const std::string_view &base,
|
||||
const std::string_view &base_query,
|
||||
const std::string_view &rel_path,
|
||||
const std::string_view &rel_query);
|
||||
std::string path_join(std::string_view base, std::string_view base_query,
|
||||
std::string_view rel_path, std::string_view rel_query);
|
||||
|
||||
std::string_view path_join(BlockAllocator &balloc,
|
||||
const std::string_view &base_path,
|
||||
const std::string_view &base_query,
|
||||
const std::string_view &rel_path,
|
||||
const std::string_view &rel_query);
|
||||
std::string_view path_join(BlockAllocator &balloc, std::string_view base_path,
|
||||
std::string_view base_query,
|
||||
std::string_view rel_path,
|
||||
std::string_view rel_query);
|
||||
|
||||
// true if response has body, taking into account the request method
|
||||
// and status code.
|
||||
@@ -366,7 +359,7 @@ bool expect_response_body(uint32_t status_code);
|
||||
// Looks up method token for method name |name|. Only methods defined
|
||||
// in llhttp.h (llhttp_method) are tokenized. If method name cannot
|
||||
// be tokenized, returns -1.
|
||||
int lookup_method_token(const std::string_view &name);
|
||||
int lookup_method_token(std::string_view name);
|
||||
|
||||
// Returns string representation of |method_token|. This is wrapper
|
||||
// around llhttp_method_name from llhttp. If |method_token| is
|
||||
@@ -374,27 +367,25 @@ int lookup_method_token(const std::string_view &name);
|
||||
// be NULL-terminated.
|
||||
std::string_view to_method_string(int method_token);
|
||||
|
||||
std::string_view normalize_path(BlockAllocator &balloc,
|
||||
const std::string_view &path,
|
||||
const std::string_view &query);
|
||||
std::string_view normalize_path(BlockAllocator &balloc, std::string_view path,
|
||||
std::string_view query);
|
||||
|
||||
// normalize_path_colon is like normalize_path, but it additionally
|
||||
// does percent-decoding %3A in order to workaround the issue that ':'
|
||||
// cannot be included in backend pattern.
|
||||
std::string_view normalize_path_colon(BlockAllocator &balloc,
|
||||
const std::string_view &path,
|
||||
const std::string_view &query);
|
||||
std::string_view path,
|
||||
std::string_view query);
|
||||
|
||||
std::string normalize_path(const std::string_view &path,
|
||||
const std::string_view &query);
|
||||
std::string normalize_path(std::string_view path, std::string_view query);
|
||||
|
||||
std::string_view rewrite_clean_path(BlockAllocator &balloc,
|
||||
const std::string_view &src);
|
||||
std::string_view src);
|
||||
|
||||
// Returns path component of |uri|. The returned path does not
|
||||
// include query component. This function returns empty string if it
|
||||
// fails.
|
||||
std::string_view get_pure_path_component(const std::string_view &uri);
|
||||
std::string_view get_pure_path_component(std::string_view uri);
|
||||
|
||||
// Deduces scheme, authority and path from given |uri|, and stores
|
||||
// them in |scheme|, |authority|, and |path| respectively. If |uri|
|
||||
@@ -403,19 +394,18 @@ std::string_view get_pure_path_component(const std::string_view &uri);
|
||||
// succeeds, or -1.
|
||||
int construct_push_component(BlockAllocator &balloc, std::string_view &scheme,
|
||||
std::string_view &authority,
|
||||
std::string_view &path,
|
||||
const std::string_view &base,
|
||||
const std::string_view &uri);
|
||||
std::string_view &path, std::string_view base,
|
||||
std::string_view uri);
|
||||
|
||||
// Returns true if te header field value |s| contains "trailers".
|
||||
bool contains_trailers(const std::string_view &s);
|
||||
bool contains_trailers(std::string_view s);
|
||||
|
||||
// Creates Sec-WebSocket-Accept value for |key|. The capacity of
|
||||
// buffer pointed by |dest| must have at least 24 bytes (base64
|
||||
// encoded length of 16 bytes data). It returns empty string in case
|
||||
// of error.
|
||||
std::string_view make_websocket_accept_token(uint8_t *dest,
|
||||
const std::string_view &key);
|
||||
std::string_view key);
|
||||
|
||||
// Returns true if HTTP version represents pre-HTTP/1.1 (e.g.,
|
||||
// HTTP/0.9 or HTTP/1.0).
|
||||
@@ -424,7 +414,7 @@ bool legacy_http1(int major, int minor);
|
||||
// Returns true if transfer-encoding field value |s| conforms RFC
|
||||
// strictly. This function does not allow empty value, BWS, and empty
|
||||
// list elements.
|
||||
bool check_transfer_encoding(const std::string_view &s);
|
||||
bool check_transfer_encoding(std::string_view s);
|
||||
|
||||
// Encodes |extpri| in the wire format.
|
||||
std::string encode_extpri(const nghttp2_extpri &extpri);
|
||||
|
||||
@@ -41,8 +41,8 @@ namespace nghttp2 {
|
||||
namespace http3 {
|
||||
|
||||
// Create nghttp3_nv from |name|, |value| and |flags|.
|
||||
inline nghttp3_nv make_field_flags(const std::string_view &name,
|
||||
const std::string_view &value,
|
||||
inline nghttp3_nv make_field_flags(std::string_view name,
|
||||
std::string_view value,
|
||||
uint8_t flags = NGHTTP3_NV_FLAG_NONE) {
|
||||
auto ns = as_uint8_span(std::span{name});
|
||||
auto vs = as_uint8_span(std::span{value});
|
||||
@@ -53,8 +53,7 @@ inline nghttp3_nv make_field_flags(const std::string_view &name,
|
||||
|
||||
// Creates nghttp3_nv from |name|, |value| and |flags|. nghttp3
|
||||
// library does not copy them.
|
||||
inline nghttp3_nv make_field(const std::string_view &name,
|
||||
const std::string_view &value,
|
||||
inline nghttp3_nv make_field(std::string_view name, std::string_view value,
|
||||
uint8_t flags = NGHTTP3_NV_FLAG_NONE) {
|
||||
return make_field_flags(name, value,
|
||||
static_cast<uint8_t>(NGHTTP3_NV_FLAG_NO_COPY_NAME |
|
||||
|
||||
@@ -172,7 +172,7 @@ template <typename Memchunk> struct Memchunks {
|
||||
*tail->last++ = as_unsigned(c);
|
||||
++len;
|
||||
}
|
||||
template <std::input_iterator I> void append(I first, I last) {
|
||||
template <std::forward_iterator I> void append(I first, I last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
@@ -181,14 +181,16 @@ template <typename Memchunk> struct Memchunks {
|
||||
head = tail = pool->get();
|
||||
}
|
||||
|
||||
auto inlen = static_cast<size_t>(std::ranges::distance(first, last));
|
||||
|
||||
for (;;) {
|
||||
auto n = std::min(static_cast<size_t>(std::ranges::distance(first, last)),
|
||||
tail->left());
|
||||
auto n = std::min(inlen, tail->left());
|
||||
auto iores = std::ranges::copy_n(first, as_signed(n), tail->last);
|
||||
first = iores.in;
|
||||
tail->last = iores.out;
|
||||
len += n;
|
||||
if (first == last) {
|
||||
inlen -= n;
|
||||
if (inlen == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -202,7 +204,7 @@ template <typename Memchunk> struct Memchunks {
|
||||
auto s = static_cast<const uint8_t *>(src);
|
||||
append(s, s + count);
|
||||
}
|
||||
template <std::ranges::input_range R>
|
||||
template <std::ranges::forward_range R>
|
||||
requires(!std::is_array_v<std::remove_cvref_t<R>>)
|
||||
void append(R &&r) {
|
||||
append(std::ranges::begin(r), std::ranges::end(r));
|
||||
@@ -238,26 +240,25 @@ template <typename Memchunk> struct Memchunks {
|
||||
}
|
||||
return len;
|
||||
}
|
||||
size_t remove(void *dest, size_t count) {
|
||||
size_t remove(std::span<uint8_t> dest) {
|
||||
assert(mark == nullptr);
|
||||
|
||||
if (!tail || count == 0) {
|
||||
if (!tail || dest.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto first = static_cast<uint8_t *>(dest);
|
||||
auto last = first + count;
|
||||
|
||||
auto destlen = dest.size();
|
||||
auto m = head;
|
||||
|
||||
while (m) {
|
||||
auto next = m->next;
|
||||
auto n = std::min(static_cast<size_t>(last - first), m->len());
|
||||
auto n = std::min(dest.size(), m->len());
|
||||
|
||||
assert(m->len());
|
||||
auto iores = std::ranges::copy_n(m->pos, as_signed(n), first);
|
||||
m->pos = iores.in;
|
||||
first = iores.out;
|
||||
|
||||
m->pos =
|
||||
std::ranges::copy_n(m->pos, as_signed(n), std::ranges::begin(dest)).in;
|
||||
dest = dest.subspan(n);
|
||||
len -= n;
|
||||
if (m->len() > 0) {
|
||||
break;
|
||||
@@ -270,7 +271,7 @@ template <typename Memchunk> struct Memchunks {
|
||||
tail = nullptr;
|
||||
}
|
||||
|
||||
return as_unsigned(first - static_cast<uint8_t *>(dest));
|
||||
return destlen - dest.size();
|
||||
}
|
||||
size_t remove(Memchunks &dest, size_t count) {
|
||||
assert(mark == nullptr);
|
||||
@@ -431,6 +432,13 @@ template <typename Memchunk> struct Memchunks {
|
||||
}
|
||||
size_t rleft() const { return len; }
|
||||
size_t rleft_mark() const { return len - mark_offset; }
|
||||
std::span<const uint8_t> peek() const {
|
||||
if (!head) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {head->pos, head->len()};
|
||||
}
|
||||
void reset() {
|
||||
for (auto m = head; m;) {
|
||||
auto next = m->next;
|
||||
@@ -560,6 +568,9 @@ template <typename Memchunk> struct MemchunkBuffer {
|
||||
uint8_t *begin() { return std::ranges::begin(chunk->buf); }
|
||||
uint8_t &operator[](size_t n) { return chunk->buf[n]; }
|
||||
const uint8_t &operator[](size_t n) const { return chunk->buf[n]; }
|
||||
// Returns the readable chunk of data.
|
||||
std::span<const uint8_t> peek() const { return {chunk->pos, chunk->len()}; }
|
||||
std::span<uint8_t> wbuffer() { return {chunk->last, chunk->left()}; }
|
||||
|
||||
Pool<Memchunk> *pool;
|
||||
Memchunk *chunk;
|
||||
|
||||
@@ -40,11 +40,14 @@ const MunitTest tests[]{
|
||||
munit_void_test(test_pool_recycle),
|
||||
munit_void_test(test_memchunks_append),
|
||||
munit_void_test(test_memchunks_drain),
|
||||
munit_void_test(test_memchunks_remove),
|
||||
munit_void_test(test_memchunks_riovec),
|
||||
munit_void_test(test_memchunks_peek),
|
||||
munit_void_test(test_memchunks_recycle),
|
||||
munit_void_test(test_memchunks_reset),
|
||||
munit_void_test(test_memchunks_reserve),
|
||||
munit_void_test(test_memchunkbuffer_drain_reset),
|
||||
munit_void_test(test_memchunkbuffer_peek),
|
||||
munit_test_end(),
|
||||
};
|
||||
} // namespace
|
||||
@@ -128,19 +131,19 @@ void test_memchunks_append(void) {
|
||||
assert_size(15, ==, m->left());
|
||||
assert_size(17, ==, chunks.rleft());
|
||||
|
||||
char buf[16];
|
||||
std::array<uint8_t, 16> buf;
|
||||
size_t nread;
|
||||
|
||||
nread = chunks.remove(buf, 8);
|
||||
nread = chunks.remove(std::span{buf}.first(8));
|
||||
|
||||
assert_size(8, ==, nread);
|
||||
assert_memory_equal(nread, "01234567", buf);
|
||||
assert_memory_equal(nread, "01234567", buf.data());
|
||||
assert_size(9, ==, chunks.rleft());
|
||||
|
||||
nread = chunks.remove(buf, sizeof(buf));
|
||||
nread = chunks.remove(buf);
|
||||
|
||||
assert_size(9, ==, nread);
|
||||
assert_memory_equal(nread, "89abcdef@", buf);
|
||||
assert_memory_equal(nread, "89abcdef@", buf.data());
|
||||
assert_size(0, ==, chunks.rleft());
|
||||
assert_null(chunks.head);
|
||||
assert_null(chunks.tail);
|
||||
@@ -159,12 +162,31 @@ void test_memchunks_drain(void) {
|
||||
|
||||
assert_size(3, ==, nread);
|
||||
|
||||
char buf[16];
|
||||
std::array<uint8_t, 16> buf;
|
||||
|
||||
nread = chunks.remove(buf, sizeof(buf));
|
||||
nread = chunks.remove(buf);
|
||||
|
||||
assert_size(7, ==, nread);
|
||||
assert_memory_equal(nread, "3456789", buf);
|
||||
assert_memory_equal(nread, "3456789", buf.data());
|
||||
}
|
||||
|
||||
void test_memchunks_remove(void) {
|
||||
MemchunkPool16 pool;
|
||||
Memchunks16 chunks(&pool);
|
||||
|
||||
chunks.append("0123456789"sv);
|
||||
|
||||
std::array<uint8_t, 16> buf;
|
||||
|
||||
auto nread = chunks.remove(std::span{buf}.first(1));
|
||||
|
||||
assert_size(1, ==, nread);
|
||||
assert_memory_equal(nread, "0", buf.data());
|
||||
|
||||
nread = chunks.remove(buf);
|
||||
|
||||
assert_size(9, ==, nread);
|
||||
assert_memory_equal(nread, "123456789", buf.data());
|
||||
}
|
||||
|
||||
void test_memchunks_riovec(void) {
|
||||
@@ -200,6 +222,24 @@ void test_memchunks_riovec(void) {
|
||||
assert_size(m->len(), ==, iov[0].iov_len);
|
||||
}
|
||||
|
||||
void test_memchunks_peek(void) {
|
||||
MemchunkPool16 pool;
|
||||
Memchunks16 chunks(&pool);
|
||||
|
||||
assert_true(chunks.peek().empty());
|
||||
|
||||
std::array<char, 3 * 16> buf{};
|
||||
|
||||
chunks.append(buf.data(), buf.size());
|
||||
|
||||
auto data = chunks.peek();
|
||||
|
||||
auto m = chunks.head;
|
||||
|
||||
assert_ptr_equal(m->buf.data(), data.data());
|
||||
assert_size(m->len(), ==, data.size());
|
||||
}
|
||||
|
||||
void test_memchunks_recycle(void) {
|
||||
MemchunkPool16 pool;
|
||||
{
|
||||
@@ -298,4 +338,16 @@ void test_memchunkbuffer_drain_reset(void) {
|
||||
assert_true(buf.begin() + buf.rleft() == buf.chunk->last);
|
||||
}
|
||||
|
||||
void test_memchunkbuffer_peek(void) {
|
||||
MemchunkPool16 pool;
|
||||
MemchunkBuffer16 buf(&pool);
|
||||
|
||||
buf.ensure_chunk();
|
||||
auto data = "0123456789"sv;
|
||||
std::ranges::copy(data, buf.begin());
|
||||
buf.write(data.size());
|
||||
|
||||
assert_stdsv_equal(data, as_string_view(buf.peek()));
|
||||
}
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -40,11 +40,14 @@ extern const MunitSuite memchunk_suite;
|
||||
munit_void_test_decl(test_pool_recycle)
|
||||
munit_void_test_decl(test_memchunks_append)
|
||||
munit_void_test_decl(test_memchunks_drain)
|
||||
munit_void_test_decl(test_memchunks_remove)
|
||||
munit_void_test_decl(test_memchunks_riovec)
|
||||
munit_void_test_decl(test_memchunks_peek)
|
||||
munit_void_test_decl(test_memchunks_recycle)
|
||||
munit_void_test_decl(test_memchunks_reset)
|
||||
munit_void_test_decl(test_memchunks_reserve)
|
||||
munit_void_test_decl(test_memchunkbuffer_drain_reset)
|
||||
munit_void_test_decl(test_memchunkbuffer_peek)
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
|
||||
164
src/network.cc
Normal file
164
src/network.cc
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2025 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.
|
||||
*/
|
||||
#include "network.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
const sockaddr *as_sockaddr(const Sockaddr &skaddr) {
|
||||
return std::visit(
|
||||
[](auto &&arg) {
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
|
||||
std::monostate>) {
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
return reinterpret_cast<const sockaddr *>(&arg);
|
||||
},
|
||||
skaddr);
|
||||
}
|
||||
|
||||
sockaddr *as_sockaddr(Sockaddr &skaddr) {
|
||||
return std::visit(
|
||||
[](auto &&arg) {
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
|
||||
std::monostate>) {
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
return reinterpret_cast<sockaddr *>(&arg);
|
||||
},
|
||||
skaddr);
|
||||
}
|
||||
|
||||
int sockaddr_family(const Sockaddr &skaddr) {
|
||||
return as_sockaddr(skaddr)->sa_family;
|
||||
}
|
||||
|
||||
uint16_t sockaddr_port(const Sockaddr &skaddr) {
|
||||
return std::visit(
|
||||
[](auto &&arg) -> uint16_t {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, sockaddr_in>) {
|
||||
return ntohs(arg.sin_port);
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, sockaddr_in6>) {
|
||||
return ntohs(arg.sin6_port);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// The existing codebase expects this.
|
||||
if constexpr (std::is_same_v<T, sockaddr_un>) {
|
||||
return 0;
|
||||
}
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
assert(0);
|
||||
abort();
|
||||
},
|
||||
skaddr);
|
||||
}
|
||||
|
||||
void sockaddr_port(Sockaddr &skaddr, uint16_t port) {
|
||||
std::visit(
|
||||
[port](auto &&arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, sockaddr_in>) {
|
||||
arg.sin_port = htons(port);
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, sockaddr_in6>) {
|
||||
arg.sin6_port = htons(port);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// The existing codebase expects this.
|
||||
if constexpr (std::is_same_v<T, sockaddr_un>) {
|
||||
return;
|
||||
}
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
assert(0);
|
||||
abort();
|
||||
},
|
||||
skaddr);
|
||||
}
|
||||
|
||||
void sockaddr_set(Sockaddr &skaddr, const sockaddr *sa) {
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
skaddr.emplace<sockaddr_in>(*reinterpret_cast<const sockaddr_in *>(sa));
|
||||
return;
|
||||
case AF_INET6:
|
||||
skaddr.emplace<sockaddr_in6>(*reinterpret_cast<const sockaddr_in6 *>(sa));
|
||||
return;
|
||||
#ifndef _WIN32
|
||||
case AF_UNIX:
|
||||
skaddr.emplace<sockaddr_un>(*reinterpret_cast<const sockaddr_un *>(sa));
|
||||
return;
|
||||
#endif // !defined(_WIN32)
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
socklen_t sockaddr_size(const Sockaddr &skaddr) {
|
||||
return std::visit(
|
||||
[](auto &&arg) { return static_cast<socklen_t>(sizeof(arg)); }, skaddr);
|
||||
}
|
||||
|
||||
bool sockaddr_empty(const Sockaddr &skaddr) {
|
||||
return std::holds_alternative<std::monostate>(skaddr);
|
||||
}
|
||||
|
||||
const sockaddr *Address::as_sockaddr() const {
|
||||
return nghttp2::as_sockaddr(skaddr);
|
||||
}
|
||||
|
||||
sockaddr *Address::as_sockaddr() { return nghttp2::as_sockaddr(skaddr); }
|
||||
|
||||
int Address::family() const { return sockaddr_family(skaddr); }
|
||||
|
||||
uint16_t Address::port() const { return sockaddr_port(skaddr); }
|
||||
|
||||
void Address::port(uint16_t port) { sockaddr_port(skaddr, port); }
|
||||
|
||||
void Address::set(const sockaddr *sa) { sockaddr_set(skaddr, sa); }
|
||||
|
||||
socklen_t Address::size() const { return sockaddr_size(skaddr); }
|
||||
|
||||
bool Address::empty() const { return sockaddr_empty(skaddr); }
|
||||
|
||||
} // namespace nghttp2
|
||||
@@ -45,23 +45,92 @@
|
||||
# include <arpa/inet.h>
|
||||
#endif // defined(HAVE_ARPA_INET_H)
|
||||
|
||||
#include <variant>
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include <ngtcp2/ngtcp2.h>
|
||||
#endif // defined(ENABLE_HTTP3)
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
union sockaddr_union {
|
||||
sockaddr_storage storage;
|
||||
sockaddr sa;
|
||||
sockaddr_in6 in6;
|
||||
sockaddr_in in;
|
||||
using Sockaddr = std::variant<std::monostate, sockaddr_in, sockaddr_in6
|
||||
#ifndef _WIN32
|
||||
sockaddr_un un;
|
||||
,
|
||||
sockaddr_un
|
||||
#endif // !defined(_WIN32)
|
||||
};
|
||||
>;
|
||||
|
||||
// as_sockaddr returns the pointer to the stored address casted to
|
||||
// const sockaddr *.
|
||||
[[nodiscard]] const sockaddr *as_sockaddr(const Sockaddr &skaddr);
|
||||
[[nodiscard]] sockaddr *as_sockaddr(Sockaddr &skaddr);
|
||||
|
||||
// sockaddr_family returns the address family.
|
||||
[[nodiscard]] int sockaddr_family(const Sockaddr &skaddr);
|
||||
|
||||
// sockaddr_port returns the port.
|
||||
[[nodiscard]] uint16_t sockaddr_port(const Sockaddr &skaddr);
|
||||
|
||||
// sockaddr_port sets |port| to |skaddr|.
|
||||
void sockaddr_port(Sockaddr &skaddr, uint16_t port);
|
||||
|
||||
// sockaddr_set stores |sa| to |skaddr|. The address family is
|
||||
// determined by |sa|->sa_family, and |sa| must point to the memory
|
||||
// that contains valid object which is either sockaddr_in,
|
||||
// sockaddr_in6, or sockaddr_un.
|
||||
void sockaddr_set(Sockaddr &skaddr, const sockaddr *sa);
|
||||
|
||||
// sockaddr_size returns the size of the stored address. If no
|
||||
// meaningful address is set, the return value is implementation
|
||||
// dependent.
|
||||
[[nodiscard]] socklen_t sockaddr_size(const Sockaddr &skaddr);
|
||||
|
||||
// sockaddr_empty returns true if |skaddr| does not contain any
|
||||
// meaningful address.
|
||||
[[nodiscard]] bool sockaddr_empty(const Sockaddr &skaddr);
|
||||
|
||||
struct Address {
|
||||
socklen_t len;
|
||||
union sockaddr_union su;
|
||||
// as_sockaddr returns the pointer to the stored address casted to
|
||||
// const sockaddr *.
|
||||
[[nodiscard]] const sockaddr *as_sockaddr() const;
|
||||
[[nodiscard]] sockaddr *as_sockaddr();
|
||||
// family returns the address family.
|
||||
[[nodiscard]] int family() const;
|
||||
// port returns the port.
|
||||
[[nodiscard]] uint16_t port() const;
|
||||
// port sets |port| to this address.
|
||||
void port(uint16_t port);
|
||||
// set stores |sa| to this address. The address family is
|
||||
// determined by |sa|->sa_family, and |sa| must point to the memory
|
||||
// that contains valid object which is either sockaddr_in,
|
||||
// sockaddr_in6, or sockaddr_un.
|
||||
void set(const sockaddr *sa);
|
||||
// size returns the size of the stored address. If no meaningful
|
||||
// address is set, the return value is implementation dependent.
|
||||
[[nodiscard]] socklen_t size() const;
|
||||
// empty returns true if this address does not contain any
|
||||
// meaningful address.
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
Sockaddr skaddr;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
[[nodiscard]] inline ngtcp2_addr as_ngtcp2_addr(const Address &addr) {
|
||||
return {
|
||||
.addr = const_cast<sockaddr *>(addr.as_sockaddr()),
|
||||
.addrlen = addr.size(),
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline ngtcp2_addr as_ngtcp2_addr(Address &addr) {
|
||||
return {
|
||||
.addr = addr.as_sockaddr(),
|
||||
.addrlen = addr.size(),
|
||||
};
|
||||
}
|
||||
#endif // defined(ENABLE_HTTP3)
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // !defined(NETWORK_H)
|
||||
|
||||
164
src/network_test.cc
Normal file
164
src/network_test.cc
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2025 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.
|
||||
*/
|
||||
#include "network_test.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "munitxx.h"
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "network.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace {
|
||||
const MunitTest tests[]{
|
||||
munit_void_test(test_network_address),
|
||||
munit_test_end(),
|
||||
};
|
||||
} // namespace
|
||||
|
||||
const MunitSuite network_suite{
|
||||
"/network", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE,
|
||||
};
|
||||
|
||||
namespace {
|
||||
Address parse_addr(const char *ipaddr, const char *port) {
|
||||
addrinfo hints{
|
||||
.ai_flags = AI_NUMERICHOST
|
||||
#ifdef AI_NUMERICSERV
|
||||
| AI_NUMERICSERV
|
||||
#endif // defined(AI_NUMERICSERV)
|
||||
,
|
||||
.ai_family = AF_UNSPEC,
|
||||
};
|
||||
|
||||
addrinfo *res = nullptr;
|
||||
|
||||
auto rv = getaddrinfo(ipaddr, port, &hints, &res);
|
||||
|
||||
assert_int(0, ==, rv);
|
||||
assert_not_null(res);
|
||||
|
||||
Address addr;
|
||||
addr.set(res->ai_addr);
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return addr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void test_network_address(void) {
|
||||
// Not set
|
||||
{
|
||||
Address addr;
|
||||
|
||||
assert_true(addr.empty());
|
||||
}
|
||||
|
||||
// IPv4
|
||||
{
|
||||
constexpr auto ipaddr = "10.1.0.100";
|
||||
|
||||
auto addr = parse_addr(ipaddr, "443");
|
||||
|
||||
assert_ptr_equal(&std::get<sockaddr_in>(addr.skaddr), addr.as_sockaddr());
|
||||
assert_size(sizeof(sockaddr_in), ==, addr.size());
|
||||
assert_false(addr.empty());
|
||||
assert_int(AF_INET, ==, addr.family());
|
||||
assert_uint16(443, ==, addr.port());
|
||||
|
||||
addr.port(8443);
|
||||
|
||||
assert_uint16(8443, ==, addr.port());
|
||||
|
||||
in_addr r;
|
||||
|
||||
assert_int(1, ==, inet_pton(AF_INET, ipaddr, &r));
|
||||
|
||||
const auto &inaddr = std::get<sockaddr_in>(addr.skaddr);
|
||||
|
||||
assert_memory_equal(sizeof(in_addr), &r, &inaddr.sin_addr);
|
||||
}
|
||||
|
||||
// IPv6
|
||||
{
|
||||
constexpr auto ipaddr = "2001:db8::1";
|
||||
|
||||
auto addr = parse_addr(ipaddr, "443");
|
||||
|
||||
assert_ptr_equal(&std::get<sockaddr_in6>(addr.skaddr), addr.as_sockaddr());
|
||||
assert_size(sizeof(sockaddr_in6), ==, addr.size());
|
||||
assert_false(addr.empty());
|
||||
assert_int(AF_INET6, ==, addr.family());
|
||||
assert_uint16(443, ==, addr.port());
|
||||
|
||||
addr.port(8443);
|
||||
|
||||
assert_uint16(8443, ==, addr.port());
|
||||
|
||||
in6_addr r;
|
||||
|
||||
assert_int(1, ==, inet_pton(AF_INET6, ipaddr, &r));
|
||||
|
||||
const auto &inaddr = std::get<sockaddr_in6>(addr.skaddr);
|
||||
|
||||
assert_memory_equal(sizeof(in6_addr), &r, &inaddr.sin6_addr);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// UNIX
|
||||
{
|
||||
constexpr char path[] = "/unix.sock";
|
||||
|
||||
Address addr;
|
||||
|
||||
auto &unaddr = addr.skaddr.emplace<sockaddr_un>();
|
||||
unaddr.sun_family = AF_UNIX;
|
||||
memcpy(unaddr.sun_path, path, sizeof(path));
|
||||
|
||||
assert_ptr_equal(&std::get<sockaddr_un>(addr.skaddr), addr.as_sockaddr());
|
||||
assert_size(sizeof(sockaddr_un), ==, addr.size());
|
||||
assert_false(addr.empty());
|
||||
assert_int(AF_UNIX, ==, addr.family());
|
||||
assert_uint16(0, ==, addr.port());
|
||||
|
||||
addr.port(8443);
|
||||
|
||||
assert_uint16(0, ==, addr.port());
|
||||
}
|
||||
#endif // !defined(_WIN32)
|
||||
}
|
||||
|
||||
} // namespace nghttp2
|
||||
44
src/network_test.h
Normal file
44
src/network_test.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2025 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 NETWORK_TEST_H
|
||||
#define NETWORK_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif // defined(HAVE_CONFIG_H)
|
||||
|
||||
#define MUNIT_ENABLE_ASSERT_ALIASES
|
||||
|
||||
#include "munit.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
extern const MunitSuite network_suite;
|
||||
|
||||
munit_void_test_decl(test_network_address)
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // !defined(NETWORK_TEST_H)
|
||||
@@ -210,12 +210,11 @@ void Request::init_html_parser() {
|
||||
html_parser = std::make_unique<HtmlParser>(base_uri);
|
||||
}
|
||||
|
||||
int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
|
||||
int Request::update_html_parser(std::span<const uint8_t> data, int fin) {
|
||||
if (!html_parser) {
|
||||
return 0;
|
||||
}
|
||||
return html_parser->parse_chunk(reinterpret_cast<const char *>(data), len,
|
||||
fin);
|
||||
return html_parser->parse_chunk(data, fin);
|
||||
}
|
||||
|
||||
std::string Request::make_reqpath() const {
|
||||
@@ -234,7 +233,7 @@ std::string Request::make_reqpath() const {
|
||||
namespace {
|
||||
// Perform special handling |host| if it is IPv6 literal and includes
|
||||
// zone ID per RFC 6874.
|
||||
std::string decode_host(const std::string_view &host) {
|
||||
std::string decode_host(std::string_view host) {
|
||||
auto zone_start = std::ranges::find(host, '%');
|
||||
if (zone_start == std::ranges::end(host) ||
|
||||
!util::ipv6_numeric_addr(
|
||||
@@ -727,7 +726,8 @@ void HttpClient::disconnect() {
|
||||
int HttpClient::read_clear() {
|
||||
ev_timer_again(loop, &rt);
|
||||
|
||||
std::array<uint8_t, 8_k> buf;
|
||||
std::array<uint8_t, 8_k> rawbuf;
|
||||
auto buf = std::span<uint8_t>{rawbuf};
|
||||
|
||||
for (;;) {
|
||||
ssize_t nread;
|
||||
@@ -744,7 +744,7 @@ int HttpClient::read_clear() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (on_readfn(*this, buf.data(), as_unsigned(nread)) != 0) {
|
||||
if (on_readfn(*this, buf.first(as_unsigned(nread))) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -988,19 +988,19 @@ int HttpClient::on_upgrade_connect() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
|
||||
int HttpClient::on_upgrade_read(std::span<const uint8_t> data) {
|
||||
int rv;
|
||||
|
||||
auto htperr =
|
||||
llhttp_execute(htp.get(), reinterpret_cast<const char *>(data), len);
|
||||
auto htperr = llhttp_execute(
|
||||
htp.get(), reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto nread = htperr == HPE_OK
|
||||
? len
|
||||
? data.size()
|
||||
: static_cast<size_t>(reinterpret_cast<const uint8_t *>(
|
||||
llhttp_get_error_pos(htp.get())) -
|
||||
data);
|
||||
data.data());
|
||||
|
||||
if (config.verbose) {
|
||||
std::cout.write(reinterpret_cast<const char *>(data),
|
||||
std::cout.write(reinterpret_cast<const char *>(data.data()),
|
||||
static_cast<std::streamsize>(nread));
|
||||
}
|
||||
|
||||
@@ -1040,7 +1040,7 @@ int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
|
||||
|
||||
// Read remaining data in the buffer because it is not notified
|
||||
// callback anymore.
|
||||
rv = on_readfn(*this, data + nread, len - nread);
|
||||
rv = on_readfn(*this, data.subspan(nread));
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -1145,19 +1145,19 @@ int HttpClient::connection_made() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpClient::on_read(const uint8_t *data, size_t len) {
|
||||
int HttpClient::on_read(std::span<const uint8_t> data) {
|
||||
if (config.hexdump) {
|
||||
util::hexdump(stdout, data, len);
|
||||
util::hexdump(stdout, data.data(), data.size());
|
||||
}
|
||||
|
||||
auto rv = nghttp2_session_mem_recv2(session, data, len);
|
||||
auto rv = nghttp2_session_mem_recv2(session, data.data(), data.size());
|
||||
if (rv < 0) {
|
||||
std::cerr << "[ERROR] nghttp2_session_mem_recv2() returned error: "
|
||||
<< nghttp2_strerror(static_cast<int>(rv)) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(static_cast<size_t>(rv) == len);
|
||||
assert(static_cast<size_t>(rv) == data.size());
|
||||
|
||||
if (nghttp2_session_want_read(session) == 0 &&
|
||||
nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
|
||||
@@ -1247,9 +1247,11 @@ int HttpClient::read_tls() {
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
std::array<uint8_t, 8_k> buf;
|
||||
std::array<uint8_t, 8_k> rawbuf;
|
||||
auto buf = std::span<uint8_t>{rawbuf};
|
||||
|
||||
for (;;) {
|
||||
auto rv = SSL_read(ssl, buf.data(), buf.size());
|
||||
auto rv = SSL_read(ssl, buf.data(), static_cast<int>(buf.size()));
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(ssl, rv);
|
||||
@@ -1264,7 +1266,7 @@ int HttpClient::read_tls() {
|
||||
}
|
||||
}
|
||||
|
||||
if (on_readfn(*this, buf.data(), static_cast<size_t>(rv)) != 0) {
|
||||
if (on_readfn(*this, buf.first(static_cast<size_t>(rv))) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1569,12 +1571,12 @@ void HttpClient::output_har(FILE *outfile) {
|
||||
#endif // defined(HAVE_JANSSON)
|
||||
|
||||
namespace {
|
||||
void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
|
||||
size_t len, int fin) {
|
||||
void update_html_parser(HttpClient *client, Request *req,
|
||||
std::span<const uint8_t> data, int fin) {
|
||||
if (!req->html_parser) {
|
||||
return;
|
||||
}
|
||||
req->update_html_parser(data, len, fin);
|
||||
req->update_html_parser(data, fin);
|
||||
|
||||
auto scheme = req->get_real_scheme();
|
||||
auto host = req->get_real_host();
|
||||
@@ -1638,39 +1640,44 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
req->response_len += len;
|
||||
|
||||
auto chunk = std::span{data, len};
|
||||
|
||||
if (req->inflater) {
|
||||
while (len > 0) {
|
||||
const size_t MAX_OUTLEN = 4_k;
|
||||
std::array<uint8_t, MAX_OUTLEN> out;
|
||||
size_t outlen = MAX_OUTLEN;
|
||||
size_t tlen = len;
|
||||
int rv =
|
||||
nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen);
|
||||
constexpr size_t MAX_OUTLEN = 4_k;
|
||||
std::array<uint8_t, MAX_OUTLEN> rawout;
|
||||
|
||||
while (!chunk.empty()) {
|
||||
auto out = std::span<uint8_t>{rawout};
|
||||
size_t outlen = out.size();
|
||||
auto tlen = chunk.size();
|
||||
int rv = nghttp2_gzip_inflate(req->inflater, out.data(), &outlen,
|
||||
chunk.data(), &tlen);
|
||||
if (rv != 0) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
out = out.first(outlen);
|
||||
|
||||
if (!config.null_out) {
|
||||
std::cout.write(reinterpret_cast<const char *>(out.data()),
|
||||
static_cast<std::streamsize>(outlen));
|
||||
static_cast<std::streamsize>(out.size()));
|
||||
}
|
||||
|
||||
update_html_parser(client, req, out.data(), outlen, 0);
|
||||
data += tlen;
|
||||
len -= tlen;
|
||||
update_html_parser(client, req, out, 0);
|
||||
chunk = chunk.subspan(tlen);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!config.null_out) {
|
||||
std::cout.write(reinterpret_cast<const char *>(data),
|
||||
static_cast<std::streamsize>(len));
|
||||
std::cout.write(reinterpret_cast<const char *>(chunk.data()),
|
||||
static_cast<std::streamsize>(chunk.size()));
|
||||
}
|
||||
|
||||
update_html_parser(client, req, data, len, 0);
|
||||
update_html_parser(client, req, chunk, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2071,7 +2078,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
req->continue_timer->stop();
|
||||
}
|
||||
|
||||
update_html_parser(client, req, nullptr, 0, 1);
|
||||
update_html_parser(client, req, {}, 1);
|
||||
++client->complete;
|
||||
|
||||
if (client->all_requests_processed()) {
|
||||
@@ -2381,7 +2388,8 @@ int run(char **uris, int n) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks);
|
||||
auto cbsdel =
|
||||
defer([callbacks] { nghttp2_session_callbacks_del(callbacks); });
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
@@ -150,7 +150,7 @@ struct Request {
|
||||
void init_inflater();
|
||||
|
||||
void init_html_parser();
|
||||
int update_html_parser(const uint8_t *data, size_t len, int fin);
|
||||
int update_html_parser(std::span<const uint8_t> data, int fin);
|
||||
|
||||
std::string make_reqpath() const;
|
||||
|
||||
@@ -239,8 +239,8 @@ struct HttpClient {
|
||||
int do_write();
|
||||
|
||||
int on_upgrade_connect();
|
||||
int on_upgrade_read(const uint8_t *data, size_t len);
|
||||
int on_read(const uint8_t *data, size_t len);
|
||||
int on_upgrade_read(std::span<const uint8_t> data);
|
||||
int on_read(std::span<const uint8_t> data);
|
||||
int on_write();
|
||||
|
||||
int connection_made();
|
||||
@@ -281,7 +281,7 @@ struct HttpClient {
|
||||
ev_timer rt;
|
||||
ev_timer settings_timer;
|
||||
std::function<int(HttpClient &)> readfn, writefn;
|
||||
std::function<int(HttpClient &, const uint8_t *, size_t)> on_readfn;
|
||||
std::function<int(HttpClient &, std::span<const uint8_t>)> on_readfn;
|
||||
std::function<int(HttpClient &)> on_writefn;
|
||||
nghttp2_session *session;
|
||||
const nghttp2_session_callbacks *callbacks;
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "tls.h"
|
||||
#include "shrpx_router_test.h"
|
||||
#include "shrpx_log.h"
|
||||
#include "network_test.h"
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include "siphash_test.h"
|
||||
#endif // defined(ENABLE_HTTP3)
|
||||
@@ -66,6 +67,7 @@ int main(int argc, char *argv[]) {
|
||||
memchunk_suite,
|
||||
template_suite,
|
||||
base64_suite,
|
||||
network_suite,
|
||||
#ifdef ENABLE_HTTP3
|
||||
siphash_suite,
|
||||
#endif // defined(ENABLE_HTTP3)
|
||||
|
||||
16
src/shrpx.cc
16
src/shrpx.cc
@@ -739,22 +739,24 @@ int create_unix_domain_server_socket(
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockaddr_union addr;
|
||||
addr.un.sun_family = AF_UNIX;
|
||||
if (faddr.host.size() + 1 > sizeof(addr.un.sun_path)) {
|
||||
Address addr;
|
||||
auto &unaddr = addr.skaddr.emplace<sockaddr_un>();
|
||||
unaddr.sun_family = AF_UNIX;
|
||||
if (faddr.host.size() + 1 > sizeof(unaddr.sun_path)) {
|
||||
LOG(FATAL) << "UNIX domain socket path " << faddr.host << " is too long > "
|
||||
<< sizeof(addr.un.sun_path);
|
||||
<< sizeof(unaddr.sun_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
// copy path including terminal NULL
|
||||
std::ranges::copy_n(faddr.host.data(), as_signed(faddr.host.size() + 1),
|
||||
addr.un.sun_path);
|
||||
unaddr.sun_path);
|
||||
|
||||
// unlink (remove) already existing UNIX domain socket path
|
||||
unlink(faddr.host.data());
|
||||
|
||||
if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) {
|
||||
if (bind(fd, reinterpret_cast<const sockaddr *>(&unaddr), sizeof(unaddr)) !=
|
||||
0) {
|
||||
auto error = errno;
|
||||
LOG(FATAL) << "Failed to bind UNIX domain socket: "
|
||||
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
||||
@@ -1643,7 +1645,7 @@ void fill_default_config(Config *config) {
|
||||
memcachedconf.family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
ticketconf.cipher = EVP_aes_128_cbc();
|
||||
ticketconf.cipher = nghttp2::tls::aes_128_cbc();
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -43,7 +43,14 @@ namespace shrpx {
|
||||
namespace {
|
||||
void acceptcb(struct ev_loop *loop, ev_io *w, int revent) {
|
||||
auto h = static_cast<AcceptHandler *>(w->data);
|
||||
h->accept_connection();
|
||||
|
||||
constexpr size_t max_num_accept = 10;
|
||||
|
||||
for (size_t i = 0; i < max_num_accept; ++i) {
|
||||
if (h->accept_connection() != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -59,20 +66,24 @@ AcceptHandler::~AcceptHandler() {
|
||||
close(faddr_->fd);
|
||||
}
|
||||
|
||||
void AcceptHandler::accept_connection() {
|
||||
sockaddr_union sockaddr;
|
||||
socklen_t addrlen = sizeof(sockaddr);
|
||||
int AcceptHandler::accept_connection() {
|
||||
sockaddr_storage ss;
|
||||
socklen_t addrlen = sizeof(ss);
|
||||
int cfd;
|
||||
|
||||
while ((
|
||||
#ifdef HAVE_ACCEPT4
|
||||
auto cfd =
|
||||
accept4(faddr_->fd, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
cfd = accept4(faddr_->fd, reinterpret_cast<sockaddr *>(&ss),
|
||||
&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC)
|
||||
#else // !defined(HAVE_ACCEPT4)
|
||||
auto cfd = accept(faddr_->fd, &sockaddr.sa, &addrlen);
|
||||
cfd = accept(faddr_->fd, reinterpret_cast<sockaddr *>(&ss), &addrlen)
|
||||
#endif // !defined(HAVE_ACCEPT4)
|
||||
) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (cfd == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case ENETDOWN:
|
||||
case EPROTO:
|
||||
case ENOPROTOOPT:
|
||||
@@ -83,15 +94,15 @@ void AcceptHandler::accept_connection() {
|
||||
case EHOSTUNREACH:
|
||||
case EOPNOTSUPP:
|
||||
case ENETUNREACH:
|
||||
return;
|
||||
return -1;
|
||||
case EMFILE:
|
||||
case ENFILE:
|
||||
LOG(WARN) << "acceptor: running out file descriptor; disable acceptor "
|
||||
"temporarily";
|
||||
worker_->sleep_listener(get_config()->conn.listener.timeout.sleep);
|
||||
return;
|
||||
return -1;
|
||||
default:
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +111,52 @@ void AcceptHandler::accept_connection() {
|
||||
util::make_socket_closeonexec(cfd);
|
||||
#endif // !defined(HAVE_ACCEPT4)
|
||||
|
||||
worker_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
|
||||
worker_->handle_connection(cfd, reinterpret_cast<const sockaddr *>(&ss),
|
||||
addrlen, faddr_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AcceptHandler::drain_connection() {
|
||||
sockaddr_storage ss;
|
||||
socklen_t addrlen = sizeof(ss);
|
||||
int cfd;
|
||||
|
||||
for (;;) {
|
||||
while ((
|
||||
#ifdef HAVE_ACCEPT4
|
||||
cfd = accept4(faddr_->fd, reinterpret_cast<sockaddr *>(&ss),
|
||||
&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC)
|
||||
#else // !defined(HAVE_ACCEPT4)
|
||||
cfd =
|
||||
accept(faddr_->fd, reinterpret_cast<sockaddr *>(&ss), &addrlen)
|
||||
#endif // !defined(HAVE_ACCEPT4)
|
||||
) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (cfd == -1) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EWOULDBLOCK:
|
||||
#endif // EAGAIN != EWOULDBLOCK
|
||||
case EMFILE:
|
||||
case ENFILE:
|
||||
return;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_ACCEPT4
|
||||
util::make_socket_nonblocking(cfd);
|
||||
util::make_socket_closeonexec(cfd);
|
||||
#endif // !defined(HAVE_ACCEPT4)
|
||||
|
||||
worker_->handle_connection(cfd, reinterpret_cast<const sockaddr *>(&ss),
|
||||
addrlen, faddr_);
|
||||
}
|
||||
}
|
||||
|
||||
void AcceptHandler::enable() { ev_io_start(worker_->get_loop(), &wev_); }
|
||||
|
||||
@@ -38,7 +38,8 @@ class AcceptHandler {
|
||||
public:
|
||||
AcceptHandler(Worker *worker, const UpstreamAddr *faddr);
|
||||
~AcceptHandler();
|
||||
void accept_connection();
|
||||
int accept_connection();
|
||||
void drain_connection();
|
||||
void enable();
|
||||
void disable();
|
||||
int get_fd() const;
|
||||
|
||||
@@ -91,7 +91,7 @@ void APIDownstreamConnection::detach_downstream(Downstream *downstream) {
|
||||
|
||||
int APIDownstreamConnection::send_reply(unsigned int http_status,
|
||||
APIStatusCode api_status,
|
||||
const std::string_view &data) {
|
||||
std::string_view data) {
|
||||
shutdown_read_ = true;
|
||||
|
||||
auto upstream = downstream_->get_upstream();
|
||||
@@ -150,7 +150,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status,
|
||||
break;
|
||||
}
|
||||
|
||||
if (upstream->send_reply(downstream_, buf.data(), buf.size()) != 0) {
|
||||
if (upstream->send_reply(downstream_, buf) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status,
|
||||
}
|
||||
|
||||
namespace {
|
||||
const APIEndpoint *lookup_api(const std::string_view &path) {
|
||||
const APIEndpoint *lookup_api(std::string_view path) {
|
||||
switch (path.size()) {
|
||||
case 26:
|
||||
switch (path[25]) {
|
||||
@@ -292,8 +292,8 @@ int APIDownstreamConnection::error_method_not_allowed() {
|
||||
return send_reply(405, APIStatusCode::FAILURE);
|
||||
}
|
||||
|
||||
int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
||||
size_t datalen) {
|
||||
int APIDownstreamConnection::push_upload_data_chunk(
|
||||
std::span<const uint8_t> data) {
|
||||
if (shutdown_read_ || !api_->require_body) {
|
||||
return 0;
|
||||
}
|
||||
@@ -308,14 +308,20 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
||||
}
|
||||
|
||||
ssize_t nwrite;
|
||||
while ((nwrite = write(fd_, data, datalen)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (nwrite == -1) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Could not write API request body: errno=" << error;
|
||||
send_reply(500, APIStatusCode::FAILURE);
|
||||
|
||||
return 0;
|
||||
for (; !data.empty();) {
|
||||
while ((nwrite = write(fd_, data.data(), data.size())) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (nwrite == -1) {
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Could not write API request body: errno=" << error;
|
||||
send_reply(500, APIStatusCode::FAILURE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = data.subspan(as_unsigned(nwrite));
|
||||
}
|
||||
|
||||
// We don't have to call Upstream::resume_read() here, because
|
||||
@@ -349,7 +355,9 @@ int APIDownstreamConnection::handle_backendconfig() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto unmapper = defer(munmap, rp, req.recv_body_length);
|
||||
auto unmapper = defer([rp, size = req.recv_body_length] {
|
||||
munmap(rp, static_cast<size_t>(size));
|
||||
});
|
||||
|
||||
Config new_config{};
|
||||
new_config.conn.downstream = std::make_shared<DownstreamConfig>();
|
||||
|
||||
@@ -67,32 +67,32 @@ struct APIEndpoint {
|
||||
class APIDownstreamConnection : public DownstreamConnection {
|
||||
public:
|
||||
APIDownstreamConnection(Worker *worker);
|
||||
virtual ~APIDownstreamConnection();
|
||||
virtual int attach_downstream(Downstream *downstream);
|
||||
virtual void detach_downstream(Downstream *downstream);
|
||||
~APIDownstreamConnection() override;
|
||||
int attach_downstream(Downstream *downstream) override;
|
||||
void detach_downstream(Downstream *downstream) override;
|
||||
|
||||
virtual int push_request_headers();
|
||||
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
|
||||
virtual int end_upload_data();
|
||||
int push_request_headers() override;
|
||||
int push_upload_data_chunk(std::span<const uint8_t> data) override;
|
||||
int end_upload_data() override;
|
||||
|
||||
virtual void pause_read(IOCtrlReason reason);
|
||||
virtual int resume_read(IOCtrlReason reason, size_t consumed);
|
||||
virtual void force_resume_read();
|
||||
void pause_read(IOCtrlReason reason) override;
|
||||
int resume_read(IOCtrlReason reason, size_t consumed) override;
|
||||
void force_resume_read() override;
|
||||
|
||||
virtual int on_read();
|
||||
virtual int on_write();
|
||||
int on_read() override;
|
||||
int on_write() override;
|
||||
|
||||
virtual void on_upstream_change(Upstream *upstream);
|
||||
void on_upstream_change(Upstream *upstream) override;
|
||||
|
||||
// true if this object is poolable.
|
||||
virtual bool poolable() const;
|
||||
bool poolable() const override;
|
||||
|
||||
virtual const std::shared_ptr<DownstreamAddrGroup> &
|
||||
get_downstream_addr_group() const;
|
||||
virtual DownstreamAddr *get_addr() const;
|
||||
const std::shared_ptr<DownstreamAddrGroup> &
|
||||
get_downstream_addr_group() const override;
|
||||
DownstreamAddr *get_addr() const override;
|
||||
|
||||
int send_reply(unsigned int http_status, APIStatusCode api_status,
|
||||
const std::string_view &data = ""sv);
|
||||
std::string_view data = ""sv);
|
||||
int error_method_not_allowed();
|
||||
|
||||
// Handles backendconfig API request.
|
||||
|
||||
@@ -133,7 +133,7 @@ int ClientHandler::read_clear() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nread = conn_.read_clear(rb_.last(), rb_.wleft());
|
||||
auto nread = conn_.read_clear(rb_.wbuffer());
|
||||
|
||||
if (nread == 0) {
|
||||
if (rb_.rleft() == 0) {
|
||||
@@ -187,7 +187,7 @@ int ClientHandler::proxy_protocol_peek_clear() {
|
||||
|
||||
assert(rb_.rleft() == 0);
|
||||
|
||||
auto nread = conn_.peek_clear(rb_.last(), rb_.wleft());
|
||||
auto nread = conn_.peek_clear(rb_.wbuffer());
|
||||
if (nread < 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -263,7 +263,7 @@ int ClientHandler::read_tls() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nread = conn_.read_tls(rb_.last(), rb_.wleft());
|
||||
auto nread = conn_.read_tls(rb_.wbuffer());
|
||||
|
||||
if (nread == 0) {
|
||||
if (rb_.rleft() == 0) {
|
||||
@@ -282,26 +282,19 @@ int ClientHandler::read_tls() {
|
||||
}
|
||||
|
||||
int ClientHandler::write_tls() {
|
||||
struct iovec iov;
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
if (on_write() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto iovcnt = upstream_->response_riovec(&iov, 1);
|
||||
if (iovcnt == 0) {
|
||||
conn_.start_tls_write_idle();
|
||||
|
||||
conn_.wlimit.stopw();
|
||||
ev_timer_stop(conn_.loop, &conn_.wt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
|
||||
if (on_write() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto data = upstream_->response_peek();
|
||||
if (data.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto nwrite = conn_.write_tls(data);
|
||||
if (nwrite < 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -311,12 +304,14 @@ int ClientHandler::write_tls() {
|
||||
}
|
||||
|
||||
upstream_->response_drain(as_unsigned(nwrite));
|
||||
|
||||
iovcnt = upstream_->response_riovec(&iov, 1);
|
||||
if (iovcnt == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
conn_.start_tls_write_idle();
|
||||
|
||||
conn_.wlimit.stopw();
|
||||
ev_timer_stop(conn_.loop, &conn_.wt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
@@ -446,8 +441,8 @@ int ClientHandler::upstream_http1_connhd_read() {
|
||||
}
|
||||
|
||||
ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
const std::string_view &ipaddr,
|
||||
const std::string_view &port, int family,
|
||||
std::string_view ipaddr, std::string_view port,
|
||||
int family,
|
||||
const UpstreamAddr *faddr)
|
||||
: // We use balloc_ for TLS session ID (64), ipaddr (IPv6) (39),
|
||||
// port (5), forwarded-for (IPv6) (41), alpn (5), proxyproto
|
||||
@@ -520,8 +515,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
}
|
||||
}
|
||||
|
||||
void ClientHandler::init_forwarded_for(int family,
|
||||
const std::string_view &ipaddr) {
|
||||
void ClientHandler::init_forwarded_for(int family, std::string_view ipaddr) {
|
||||
if (family == AF_INET6) {
|
||||
// 2 for '[' and ']'
|
||||
auto len = 2 + ipaddr.size();
|
||||
@@ -707,7 +701,7 @@ int ClientHandler::on_read() {
|
||||
}
|
||||
int ClientHandler::on_write() { return on_write_(*this); }
|
||||
|
||||
const std::string_view &ClientHandler::get_ipaddr() const { return ipaddr_; }
|
||||
std::string_view ClientHandler::get_ipaddr() const { return ipaddr_; }
|
||||
|
||||
bool ClientHandler::get_should_close_after_write() const {
|
||||
return should_close_after_write_;
|
||||
@@ -739,7 +733,7 @@ void ClientHandler::pool_downstream_connection(
|
||||
|
||||
namespace {
|
||||
// Computes 32bits hash for session affinity for IP address |ip|.
|
||||
uint32_t compute_affinity_from_ip(const std::string_view &ip) {
|
||||
uint32_t compute_affinity_from_ip(std::string_view ip) {
|
||||
int rv;
|
||||
std::array<uint8_t, 32> buf;
|
||||
|
||||
@@ -809,9 +803,8 @@ Http2Session *ClientHandler::get_http2_session(
|
||||
return session;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ClientHandler::get_affinity_cookie(Downstream *downstream,
|
||||
const std::string_view &cookie_name) {
|
||||
uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
|
||||
std::string_view cookie_name) {
|
||||
auto h = downstream->find_affinity_cookie(cookie_name);
|
||||
if (h) {
|
||||
return h;
|
||||
@@ -1264,13 +1257,13 @@ ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
|
||||
}
|
||||
|
||||
if (*p == '0') {
|
||||
if (p + 1 != last && util::is_digit(as_signed(*(p + 1)))) {
|
||||
if (p + 1 != last && util::is_digit(static_cast<char>(*(p + 1)))) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (; p != last && util::is_digit(as_signed(*p)); ++p) {
|
||||
for (; p != last && util::is_digit(static_cast<char>(*p)); ++p) {
|
||||
port *= 10;
|
||||
port += *p - '0';
|
||||
|
||||
@@ -1295,7 +1288,7 @@ int ClientHandler::on_proxy_protocol_finish() {
|
||||
|
||||
rb_.reset();
|
||||
|
||||
if (conn_.read_nolim_clear(rb_.pos(), len) < 0) {
|
||||
if (conn_.read_nolim_clear({rb_.pos(), len}) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1693,7 +1686,7 @@ const UpstreamAddr *ClientHandler::get_upstream_addr() const { return faddr_; }
|
||||
|
||||
Connection *ClientHandler::get_connection() { return &conn_; }
|
||||
|
||||
void ClientHandler::set_tls_sni(const std::string_view &sni) {
|
||||
void ClientHandler::set_tls_sni(std::string_view sni) {
|
||||
sni_ = make_string_ref(balloc_, sni);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,9 +67,8 @@ class Http3Upstream;
|
||||
|
||||
class ClientHandler {
|
||||
public:
|
||||
ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
const std::string_view &ipaddr, const std::string_view &port,
|
||||
int family, const UpstreamAddr *faddr);
|
||||
ClientHandler(Worker *worker, int fd, SSL *ssl, std::string_view ipaddr,
|
||||
std::string_view port, int family, const UpstreamAddr *faddr);
|
||||
~ClientHandler();
|
||||
|
||||
int noop();
|
||||
@@ -107,7 +106,7 @@ public:
|
||||
void reset_upstream_write_timeout(ev_tstamp t);
|
||||
|
||||
int validate_next_proto();
|
||||
const std::string_view &get_ipaddr() const;
|
||||
std::string_view get_ipaddr() const;
|
||||
bool get_should_close_after_write() const;
|
||||
void set_should_close_after_write(bool f);
|
||||
Upstream *get_upstream();
|
||||
@@ -143,7 +142,7 @@ public:
|
||||
Worker *get_worker() const;
|
||||
|
||||
// Initializes forwarded_for_.
|
||||
void init_forwarded_for(int family, const std::string_view &ipaddr);
|
||||
void init_forwarded_for(int family, std::string_view ipaddr);
|
||||
|
||||
using ReadBuf = DefaultMemchunkBuffer;
|
||||
|
||||
@@ -179,7 +178,7 @@ public:
|
||||
// Returns an affinity cookie value for |downstream|. |cookie_name|
|
||||
// is used to inspect cookie header field in request header fields.
|
||||
uint32_t get_affinity_cookie(Downstream *downstream,
|
||||
const std::string_view &cookie_name);
|
||||
std::string_view cookie_name);
|
||||
|
||||
DownstreamAddr *get_downstream_addr_strict_affinity(
|
||||
int &err, const std::shared_ptr<SharedDownstreamAddr> &shared_addr,
|
||||
@@ -194,7 +193,7 @@ public:
|
||||
|
||||
// Stores |sni| which is TLS SNI extension value client sent in this
|
||||
// connection.
|
||||
void set_tls_sni(const std::string_view &sni);
|
||||
void set_tls_sni(std::string_view sni);
|
||||
// Returns TLS SNI extension value client sent in this connection.
|
||||
std::string_view get_tls_sni() const;
|
||||
|
||||
|
||||
@@ -131,8 +131,8 @@ struct HostPort {
|
||||
|
||||
namespace {
|
||||
std::optional<HostPort> split_host_port(BlockAllocator &balloc,
|
||||
const std::string_view &hostport,
|
||||
const std::string_view &opt) {
|
||||
std::string_view hostport,
|
||||
std::string_view opt) {
|
||||
// host and port in |hostport| is separated by single ','.
|
||||
auto sep = std::ranges::find(hostport, ',');
|
||||
if (sep == std::ranges::end(hostport)) {
|
||||
@@ -160,7 +160,7 @@ std::optional<HostPort> split_host_port(BlockAllocator &balloc,
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
bool is_secure(const std::string_view &filename) {
|
||||
bool is_secure(std::string_view filename) {
|
||||
struct stat buf;
|
||||
int rv = stat(filename.data(), &buf);
|
||||
if (rv == 0) {
|
||||
@@ -182,7 +182,7 @@ read_tls_ticket_key_file(const std::vector<std::string_view> &files,
|
||||
keys.resize(files.size());
|
||||
auto enc_keylen = static_cast<size_t>(EVP_CIPHER_key_length(cipher));
|
||||
auto hmac_keylen = static_cast<size_t>(EVP_MD_size(hmac));
|
||||
if (cipher == EVP_aes_128_cbc()) {
|
||||
if (cipher == nghttp2::tls::aes_128_cbc()) {
|
||||
// backward compatibility, as a legacy of using same file format
|
||||
// with nginx and apache.
|
||||
hmac_keylen = 16;
|
||||
@@ -251,7 +251,7 @@ read_tls_ticket_key_file(const std::vector<std::string_view> &files,
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
std::shared_ptr<QUICKeyingMaterials>
|
||||
read_quic_secret_file(const std::string_view &path) {
|
||||
read_quic_secret_file(std::string_view path) {
|
||||
constexpr size_t expectedlen =
|
||||
SHRPX_QUIC_SECRET_RESERVEDLEN + SHRPX_QUIC_SECRETLEN + SHRPX_QUIC_SALTLEN;
|
||||
|
||||
@@ -352,8 +352,8 @@ FILE *open_file_for_write(const char *filename) {
|
||||
|
||||
namespace {
|
||||
// Read passwd from |filename|
|
||||
std::string read_passwd_from_file(const std::string_view &opt,
|
||||
const std::string_view &filename) {
|
||||
std::string read_passwd_from_file(std::string_view opt,
|
||||
std::string_view filename) {
|
||||
std::string line;
|
||||
|
||||
if (!is_secure(filename)) {
|
||||
@@ -374,7 +374,7 @@ std::string read_passwd_from_file(const std::string_view &opt,
|
||||
} // namespace
|
||||
|
||||
HeaderRefs::value_type parse_header(BlockAllocator &balloc,
|
||||
const std::string_view &optarg) {
|
||||
std::string_view optarg) {
|
||||
auto colon = std::ranges::find(optarg, ':');
|
||||
|
||||
if (colon == std::ranges::end(optarg) ||
|
||||
@@ -408,8 +408,7 @@ HeaderRefs::value_type parse_header(BlockAllocator &balloc,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int parse_uint(T *dest, const std::string_view &opt,
|
||||
const std::string_view &optarg) {
|
||||
int parse_uint(T *dest, std::string_view opt, std::string_view optarg) {
|
||||
auto val = util::parse_uint(optarg);
|
||||
if (!val) {
|
||||
LOG(ERROR) << opt << ": bad value. Specify an integer >= 0.";
|
||||
@@ -423,8 +422,8 @@ int parse_uint(T *dest, const std::string_view &opt,
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
int parse_uint_with_unit(T *dest, const std::string_view &opt,
|
||||
const std::string_view &optarg) {
|
||||
int parse_uint_with_unit(T *dest, std::string_view opt,
|
||||
std::string_view optarg) {
|
||||
auto n = util::parse_uint_with_unit(optarg);
|
||||
if (!n) {
|
||||
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
|
||||
@@ -448,8 +447,8 @@ int parse_uint_with_unit(T *dest, const std::string_view &opt,
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int parse_altsvc(AltSvc &altsvc, const std::string_view &opt,
|
||||
const std::string_view &optarg) {
|
||||
int parse_altsvc(AltSvc &altsvc, std::string_view opt,
|
||||
std::string_view optarg) {
|
||||
// PROTOID, PORT, HOST, ORIGIN, PARAMS.
|
||||
auto tokens = util::split_str(optarg, ',', 5);
|
||||
|
||||
@@ -500,7 +499,7 @@ int parse_altsvc(AltSvc &altsvc, const std::string_view &opt,
|
||||
|
||||
namespace {
|
||||
// generated by gennghttpxfun.py
|
||||
LogFragmentType log_var_lookup_token(const std::string_view &name) {
|
||||
LogFragmentType log_var_lookup_token(std::string_view name) {
|
||||
switch (name.size()) {
|
||||
case 3:
|
||||
switch (name[2]) {
|
||||
@@ -721,7 +720,7 @@ bool var_token(char c) {
|
||||
} // namespace
|
||||
|
||||
std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
|
||||
const std::string_view &optarg) {
|
||||
std::string_view optarg) {
|
||||
auto literal_start = std::ranges::begin(optarg);
|
||||
auto p = literal_start;
|
||||
auto eop = std::ranges::end(optarg);
|
||||
@@ -817,8 +816,8 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
|
||||
}
|
||||
|
||||
namespace {
|
||||
int parse_address_family(int *dest, const std::string_view &opt,
|
||||
const std::string_view &optarg) {
|
||||
int parse_address_family(int *dest, std::string_view opt,
|
||||
std::string_view optarg) {
|
||||
if (util::strieq("auto"sv, optarg)) {
|
||||
*dest = AF_UNSPEC;
|
||||
return 0;
|
||||
@@ -838,8 +837,8 @@ int parse_address_family(int *dest, const std::string_view &opt,
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int parse_duration(ev_tstamp *dest, const std::string_view &opt,
|
||||
const std::string_view &optarg) {
|
||||
int parse_duration(ev_tstamp *dest, std::string_view opt,
|
||||
std::string_view optarg) {
|
||||
auto t = util::parse_duration_with_unit(optarg);
|
||||
if (!t) {
|
||||
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
|
||||
@@ -853,8 +852,8 @@ int parse_duration(ev_tstamp *dest, const std::string_view &opt,
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int parse_tls_proto_version(int &dest, const std::string_view &opt,
|
||||
const std::string_view &optarg) {
|
||||
int parse_tls_proto_version(int &dest, std::string_view opt,
|
||||
std::string_view optarg) {
|
||||
auto v = tls::proto_version_from_string(optarg);
|
||||
if (v == -1) {
|
||||
LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
|
||||
@@ -876,8 +875,8 @@ namespace {
|
||||
// and stores parsed results into |out|. This function returns 0 if
|
||||
// it succeeds, or -1.
|
||||
int parse_memcached_connection_params(MemcachedConnectionParams &out,
|
||||
const std::string_view &src_params,
|
||||
const std::string_view &opt) {
|
||||
std::string_view src_params,
|
||||
std::string_view opt) {
|
||||
auto last = std::ranges::end(src_params);
|
||||
for (auto first = std::ranges::begin(src_params); first != last;) {
|
||||
auto end = std::ranges::find(first, last, ';');
|
||||
@@ -915,8 +914,7 @@ namespace {
|
||||
// Parses upstream configuration parameter |src_params|, and stores
|
||||
// parsed results into |out|. This function returns 0 if it succeeds,
|
||||
// or -1.
|
||||
int parse_upstream_params(UpstreamParams &out,
|
||||
const std::string_view &src_params) {
|
||||
int parse_upstream_params(UpstreamParams &out, std::string_view src_params) {
|
||||
auto last = std::ranges::end(src_params);
|
||||
for (auto first = std::ranges::begin(src_params); first != last;) {
|
||||
auto end = std::ranges::find(first, last, ';');
|
||||
@@ -990,9 +988,8 @@ namespace {
|
||||
// Parses |value| of parameter named |name| as duration. This
|
||||
// function returns 0 if it succeeds and the parsed value is assigned
|
||||
// to |dest|, or -1.
|
||||
int parse_downstream_param_duration(ev_tstamp &dest,
|
||||
const std::string_view &name,
|
||||
const std::string_view &value) {
|
||||
int parse_downstream_param_duration(ev_tstamp &dest, std::string_view name,
|
||||
std::string_view value) {
|
||||
auto t = util::parse_duration_with_unit(value);
|
||||
if (!t) {
|
||||
LOG(ERROR) << "backend: " << name << ": bad value: '" << value << "'";
|
||||
@@ -1008,7 +1005,7 @@ namespace {
|
||||
// parsed results into |out|. This function returns 0 if it succeeds,
|
||||
// or -1.
|
||||
int parse_downstream_params(DownstreamParams &out,
|
||||
const std::string_view &src_params) {
|
||||
std::string_view src_params) {
|
||||
auto last = std::ranges::end(src_params);
|
||||
for (auto first = std::ranges::begin(src_params); first != last;) {
|
||||
auto end = std::ranges::find(first, last, ';');
|
||||
@@ -1202,7 +1199,7 @@ namespace {
|
||||
int parse_mapping(
|
||||
Config *config, DownstreamAddrConfig &addr,
|
||||
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer,
|
||||
const std::string_view &src_pattern, const std::string_view &src_params) {
|
||||
std::string_view src_pattern, std::string_view src_params) {
|
||||
// This returns at least 1 element (it could be empty string). We
|
||||
// will append '/' to all patterns, so it becomes catch-all pattern.
|
||||
auto mapping = util::split_str(src_pattern, ':');
|
||||
@@ -1378,7 +1375,7 @@ int parse_mapping(
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
ForwardedNode parse_forwarded_node_type(const std::string_view &optarg) {
|
||||
ForwardedNode parse_forwarded_node_type(std::string_view optarg) {
|
||||
if (util::strieq("obfuscated"sv, optarg)) {
|
||||
return ForwardedNode::OBFUSCATED;
|
||||
}
|
||||
@@ -1403,9 +1400,8 @@ ForwardedNode parse_forwarded_node_type(const std::string_view &optarg) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int parse_error_page(std::vector<ErrorPage> &error_pages,
|
||||
const std::string_view &opt,
|
||||
const std::string_view &optarg) {
|
||||
int parse_error_page(std::vector<ErrorPage> &error_pages, std::string_view opt,
|
||||
std::string_view optarg) {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
|
||||
auto eq = std::ranges::find(optarg, '=');
|
||||
@@ -1441,7 +1437,7 @@ int parse_error_page(std::vector<ErrorPage> &error_pages,
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto fd_closer = defer(close, fd);
|
||||
auto fd_closer = defer([fd] { close(fd); });
|
||||
|
||||
std::array<uint8_t, 4096> buf;
|
||||
for (;;) {
|
||||
@@ -1477,8 +1473,7 @@ struct SubcertParams {
|
||||
namespace {
|
||||
// Parses subcert parameter |src_params|, and stores parsed results
|
||||
// into |out|. This function returns 0 if it succeeds, or -1.
|
||||
int parse_subcert_params(SubcertParams &out,
|
||||
const std::string_view &src_params) {
|
||||
int parse_subcert_params(SubcertParams &out, std::string_view src_params) {
|
||||
auto last = std::ranges::end(src_params);
|
||||
for (auto first = std::ranges::begin(src_params); first != last;) {
|
||||
auto end = std::ranges::find(first, last, ';');
|
||||
@@ -1519,9 +1514,8 @@ int parse_subcert_params(SubcertParams &out,
|
||||
namespace {
|
||||
// Reads *.sct files from directory denoted by |dir_path|. |dir_path|
|
||||
// must be NULL-terminated string.
|
||||
int read_tls_sct_from_dir(std::vector<uint8_t> &dst,
|
||||
const std::string_view &opt,
|
||||
const std::string_view &dir_path) {
|
||||
int read_tls_sct_from_dir(std::vector<uint8_t> &dst, std::string_view opt,
|
||||
std::string_view dir_path) {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
|
||||
auto dir = opendir(dir_path.data());
|
||||
@@ -1532,7 +1526,7 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst,
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto closer = defer(closedir, dir);
|
||||
auto closer = defer([dir] { closedir(dir); });
|
||||
|
||||
// 2 bytes total length field
|
||||
auto len_idx = dst.size();
|
||||
@@ -1574,7 +1568,7 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst,
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto closer = defer(close, fd);
|
||||
auto closer = defer([fd] { close(fd); });
|
||||
|
||||
// 2 bytes length field for this SCT.
|
||||
auto len_idx = dst.size();
|
||||
@@ -1638,7 +1632,7 @@ namespace {
|
||||
// Reads PSK secrets from path, and parses each line. The result is
|
||||
// directly stored into config->tls.psk_secrets. This function
|
||||
// returns 0 if it succeeds, or -1.
|
||||
int parse_psk_secrets(Config *config, const std::string_view &path) {
|
||||
int parse_psk_secrets(Config *config, std::string_view path) {
|
||||
auto &tlsconf = config->tls;
|
||||
|
||||
std::ifstream f(path.data(), std::ios::binary);
|
||||
@@ -1704,7 +1698,7 @@ namespace {
|
||||
// Reads PSK secrets from path, and parses each line. The result is
|
||||
// directly stored into config->tls.client.psk. This function returns
|
||||
// 0 if it succeeds, or -1.
|
||||
int parse_client_psk_secrets(Config *config, const std::string_view &path) {
|
||||
int parse_client_psk_secrets(Config *config, std::string_view path) {
|
||||
auto &tlsconf = config->tls;
|
||||
|
||||
std::ifstream f(path.data(), std::ios::binary);
|
||||
@@ -1762,7 +1756,7 @@ int parse_client_psk_secrets(Config *config, const std::string_view &path) {
|
||||
#endif // !defined(OPENSSL_NO_PSK)
|
||||
|
||||
// generated by gennghttpxfun.py
|
||||
int option_lookup_token(const std::string_view &name) {
|
||||
int option_lookup_token(std::string_view name) {
|
||||
switch (name.size()) {
|
||||
case 4:
|
||||
switch (name[3]) {
|
||||
@@ -2838,7 +2832,7 @@ int option_lookup_token(const std::string_view &name) {
|
||||
}
|
||||
|
||||
int parse_config(
|
||||
Config *config, const std::string_view &opt, const std::string_view &optarg,
|
||||
Config *config, std::string_view opt, std::string_view optarg,
|
||||
std::unordered_set<std::string_view> &included_set,
|
||||
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer) {
|
||||
auto optid = option_lookup_token(opt);
|
||||
@@ -2847,8 +2841,7 @@ int parse_config(
|
||||
}
|
||||
|
||||
int parse_config(
|
||||
Config *config, int optid, const std::string_view &opt,
|
||||
const std::string_view &optarg,
|
||||
Config *config, int optid, std::string_view opt, std::string_view optarg,
|
||||
std::unordered_set<std::string_view> &included_set,
|
||||
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer) {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
@@ -3615,9 +3608,9 @@ int parse_config(
|
||||
}
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER:
|
||||
if (util::strieq("aes-128-cbc"sv, optarg)) {
|
||||
config->tls.ticket.cipher = EVP_aes_128_cbc();
|
||||
config->tls.ticket.cipher = nghttp2::tls::aes_128_cbc();
|
||||
} else if (util::strieq("aes-256-cbc"sv, optarg)) {
|
||||
config->tls.ticket.cipher = EVP_aes_256_cbc();
|
||||
config->tls.ticket.cipher = nghttp2::tls::aes_256_cbc();
|
||||
} else {
|
||||
LOG(ERROR) << opt
|
||||
<< ": unsupported cipher for ticket encryption: " << optarg;
|
||||
@@ -4327,7 +4320,7 @@ std::string_view str_syslog_facility(int facility) {
|
||||
}
|
||||
}
|
||||
|
||||
int int_syslog_facility(const std::string_view &strfacility) {
|
||||
int int_syslog_facility(std::string_view strfacility) {
|
||||
if (util::strieq("auth"sv, strfacility)) {
|
||||
return LOG_AUTH;
|
||||
}
|
||||
@@ -4444,7 +4437,7 @@ namespace {
|
||||
// and do lower bound search in the array. The returned index is the
|
||||
// backend to use.
|
||||
int compute_affinity_hash(std::vector<AffinityHash> &res, size_t idx,
|
||||
const std::string_view &s) {
|
||||
std::string_view s) {
|
||||
int rv;
|
||||
std::array<uint8_t, 32> buf;
|
||||
|
||||
@@ -4657,10 +4650,11 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
|
||||
auto path = addr.host.data();
|
||||
auto pathlen = addr.host.size();
|
||||
auto &unaddr = addr.addr.skaddr.emplace<sockaddr_un>();
|
||||
|
||||
if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) {
|
||||
if (pathlen + 1 > sizeof(unaddr.sun_path)) {
|
||||
LOG(FATAL) << "UNIX domain socket path " << path << " is too long > "
|
||||
<< sizeof(addr.addr.su.un.sun_path);
|
||||
<< sizeof(unaddr.sun_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4669,11 +4663,9 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
<< " for backend connection";
|
||||
}
|
||||
|
||||
addr.addr.su.un.sun_family = AF_UNIX;
|
||||
unaddr.sun_family = AF_UNIX;
|
||||
// copy path including terminal NULL
|
||||
std::ranges::copy_n(path, as_signed(pathlen + 1),
|
||||
addr.addr.su.un.sun_path);
|
||||
addr.addr.len = sizeof(addr.addr.su.un);
|
||||
std::ranges::copy_n(path, as_signed(pathlen + 1), unaddr.sun_path);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -4723,8 +4715,9 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
key = addr.hostport;
|
||||
}
|
||||
} else {
|
||||
key = std::string_view{reinterpret_cast<char *>(&addr.addr.su),
|
||||
addr.addr.len};
|
||||
key = std::string_view{
|
||||
reinterpret_cast<const char *>(addr.addr.as_sockaddr()),
|
||||
addr.addr.size()};
|
||||
}
|
||||
rv = compute_affinity_hash(g.affinity_hash, idx, key);
|
||||
if (rv != 0) {
|
||||
@@ -4788,7 +4781,7 @@ int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto res_d = defer(freeaddrinfo, res);
|
||||
auto res_d = defer([res] { freeaddrinfo(res); });
|
||||
|
||||
std::array<char, NI_MAXHOST> host;
|
||||
rv = getnameinfo(res->ai_addr, res->ai_addrlen, host.data(), host.size(),
|
||||
@@ -4805,8 +4798,7 @@ int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
|
||||
<< " succeeded: " << host.data();
|
||||
}
|
||||
|
||||
memcpy(&addr->su, res->ai_addr, res->ai_addrlen);
|
||||
addr->len = res->ai_addrlen;
|
||||
addr->set(res->ai_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ struct UpstreamAddr {
|
||||
std::string_view hostport;
|
||||
// Binary representation of this address. Only filled if quic is
|
||||
// true.
|
||||
sockaddr_union sockaddr;
|
||||
Address sockaddr;
|
||||
// frontend port. 0 if |host_unix| is true.
|
||||
uint16_t port;
|
||||
// For TCP socket, this is either AF_INET or AF_INET6. For UNIX
|
||||
@@ -555,7 +555,7 @@ struct AffinityHash {
|
||||
};
|
||||
|
||||
struct DownstreamAddrGroupConfig {
|
||||
DownstreamAddrGroupConfig(const std::string_view &pattern)
|
||||
DownstreamAddrGroupConfig(std::string_view pattern)
|
||||
: pattern(pattern),
|
||||
affinity{SessionAffinity::NONE},
|
||||
redirect_if_not_tls(false),
|
||||
@@ -920,7 +920,7 @@ struct RateLimitConfig {
|
||||
// field. router includes all path patterns sharing the same wildcard
|
||||
// host.
|
||||
struct WildcardPattern {
|
||||
WildcardPattern(const std::string_view &host) : host(host) {}
|
||||
WildcardPattern(std::string_view host) : host(host) {}
|
||||
|
||||
// This might not be NULL terminated. Currently it is only used for
|
||||
// comparison.
|
||||
@@ -1343,7 +1343,7 @@ enum {
|
||||
};
|
||||
|
||||
// Looks up token for given option name |name|.
|
||||
int option_lookup_token(const std::string_view &name);
|
||||
int option_lookup_token(std::string_view name);
|
||||
|
||||
// Parses option name |opt| and value |optarg|. The results are
|
||||
// stored into the object pointed by |config|. This function returns 0
|
||||
@@ -1354,15 +1354,14 @@ int option_lookup_token(const std::string_view &name);
|
||||
// It is introduced to speed up loading configuration file with lots
|
||||
// of backends.
|
||||
int parse_config(
|
||||
Config *config, const std::string_view &opt, const std::string_view &optarg,
|
||||
Config *config, std::string_view opt, std::string_view optarg,
|
||||
std::unordered_set<std::string_view> &included_set,
|
||||
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer);
|
||||
|
||||
// Similar to parse_config() above, but additional |optid| which
|
||||
// should be the return value of option_lookup_token(opt).
|
||||
int parse_config(
|
||||
Config *config, int optid, const std::string_view &opt,
|
||||
const std::string_view &optarg,
|
||||
Config *config, int optid, std::string_view opt, std::string_view optarg,
|
||||
std::unordered_set<std::string_view> &included_set,
|
||||
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer);
|
||||
|
||||
@@ -1379,16 +1378,16 @@ int load_config(
|
||||
// is allowed at the start of the NAME, but NAME == ":" is not
|
||||
// allowed. This function returns pair of NAME and VALUE.
|
||||
HeaderRefs::value_type parse_header(BlockAllocator &balloc,
|
||||
const std::string_view &optarg);
|
||||
std::string_view optarg);
|
||||
|
||||
std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
|
||||
const std::string_view &optarg);
|
||||
std::string_view optarg);
|
||||
|
||||
// Returns string for syslog |facility|.
|
||||
std::string_view str_syslog_facility(int facility);
|
||||
|
||||
// Returns integer value of syslog |facility| string.
|
||||
int int_syslog_facility(const std::string_view &strfacility);
|
||||
int int_syslog_facility(std::string_view strfacility);
|
||||
|
||||
FILE *open_file_for_write(const char *filename);
|
||||
|
||||
@@ -1402,7 +1401,7 @@ read_tls_ticket_key_file(const std::vector<std::string_view> &files,
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
std::shared_ptr<QUICKeyingMaterials>
|
||||
read_quic_secret_file(const std::string_view &path);
|
||||
read_quic_secret_file(std::string_view path);
|
||||
#endif // defined(ENABLE_HTTP3)
|
||||
|
||||
// Returns string representation of |proto|.
|
||||
|
||||
@@ -189,9 +189,9 @@ void test_shrpx_config_read_tls_ticket_key_file(void) {
|
||||
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
auto ticket_keys =
|
||||
read_tls_ticket_key_file({std::string_view{file1}, std::string_view{file2}},
|
||||
EVP_aes_128_cbc(), EVP_sha256());
|
||||
auto ticket_keys = read_tls_ticket_key_file(
|
||||
{std::string_view{file1}, std::string_view{file2}},
|
||||
nghttp2::tls::aes_128_cbc(), nghttp2::tls::sha256());
|
||||
unlink(file1);
|
||||
unlink(file2);
|
||||
assert_not_null(ticket_keys.get());
|
||||
@@ -233,9 +233,9 @@ void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) {
|
||||
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
auto ticket_keys =
|
||||
read_tls_ticket_key_file({std::string_view{file1}, std::string_view{file2}},
|
||||
EVP_aes_256_cbc(), EVP_sha256());
|
||||
auto ticket_keys = read_tls_ticket_key_file(
|
||||
{std::string_view{file1}, std::string_view{file2}},
|
||||
nghttp2::tls::aes_256_cbc(), nghttp2::tls::sha256());
|
||||
unlink(file1);
|
||||
unlink(file2);
|
||||
assert_not_null(ticket_keys.get());
|
||||
|
||||
@@ -482,7 +482,7 @@ void Connection::start_tls_write_idle() {
|
||||
}
|
||||
}
|
||||
|
||||
nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
|
||||
nghttp2_ssize Connection::write_tls(std::span<const uint8_t> data) {
|
||||
// SSL_write requires the same arguments (buf pointer and its
|
||||
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
|
||||
// get_write_limit() may return smaller length than previously
|
||||
@@ -490,13 +490,13 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
|
||||
// this, we keep last length passed to SSL_write to
|
||||
// tls.last_writelen if SSL_write indicated I/O blocking.
|
||||
if (tls.last_writelen == 0) {
|
||||
len = std::min(len, wlimit.avail());
|
||||
len = std::min(len, get_tls_write_limit());
|
||||
if (len == 0) {
|
||||
data = data.first(
|
||||
std::ranges::min({data.size(), wlimit.avail(), get_tls_write_limit()}));
|
||||
if (data.empty()) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
len = tls.last_writelen;
|
||||
data = data.first(tls.last_writelen);
|
||||
tls.last_writelen = 0;
|
||||
}
|
||||
|
||||
@@ -507,17 +507,17 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
|
||||
#ifdef NGHTTP2_GENUINE_OPENSSL
|
||||
int rv;
|
||||
if (SSL_is_init_finished(tls.ssl)) {
|
||||
rv = SSL_write(tls.ssl, data, static_cast<int>(len));
|
||||
rv = SSL_write(tls.ssl, data.data(), static_cast<int>(data.size()));
|
||||
} else {
|
||||
size_t nwrite;
|
||||
rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
|
||||
rv = SSL_write_early_data(tls.ssl, data.data(), data.size(), &nwrite);
|
||||
// Use the same semantics with SSL_write.
|
||||
if (rv == 1) {
|
||||
rv = static_cast<int>(nwrite);
|
||||
}
|
||||
}
|
||||
#else // !defined(NGHTTP2_GENUINE_OPENSSL)
|
||||
auto rv = SSL_write(tls.ssl, data, static_cast<int>(len));
|
||||
auto rv = SSL_write(tls.ssl, data.data(), static_cast<int>(data.size()));
|
||||
#endif // !defined(NGHTTP2_GENUINE_OPENSSL)
|
||||
|
||||
if (rv <= 0) {
|
||||
@@ -529,7 +529,7 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
tls.last_writelen = len;
|
||||
tls.last_writelen = data.size();
|
||||
wlimit.startw();
|
||||
ev_timer_again(loop, &wt);
|
||||
|
||||
@@ -559,13 +559,13 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nghttp2_ssize Connection::read_tls(void *data, size_t len) {
|
||||
nghttp2_ssize Connection::read_tls(std::span<uint8_t> data) {
|
||||
ERR_clear_error();
|
||||
|
||||
#if defined(NGHTTP2_GENUINE_OPENSSL) || \
|
||||
defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL)
|
||||
if (tls.earlybuf.rleft()) {
|
||||
return as_signed(tls.earlybuf.remove(data, len));
|
||||
return as_signed(tls.earlybuf.remove(data));
|
||||
}
|
||||
#endif // defined(NGHTTP2_GENUINE_OPENSSL) ||
|
||||
// defined(NGHTTP2_OPENSSL_IS_BORINGSSL) ||
|
||||
@@ -579,12 +579,12 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
|
||||
// to SSL_read to tls_last_readlen_ if SSL_read indicated I/O
|
||||
// blocking.
|
||||
if (tls.last_readlen == 0) {
|
||||
len = std::min(len, rlimit.avail());
|
||||
if (len == 0) {
|
||||
data = data.first(std::min(data.size(), rlimit.avail()));
|
||||
if (data.empty()) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
len = tls.last_readlen;
|
||||
data = data.first(tls.last_readlen);
|
||||
tls.last_readlen = 0;
|
||||
}
|
||||
|
||||
@@ -592,12 +592,12 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
|
||||
if (!tls.early_data_finish) {
|
||||
// TLSv1.3 handshake is still going on.
|
||||
size_t nread;
|
||||
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
|
||||
auto rv = SSL_read_early_data(tls.ssl, data.data(), data.size(), &nread);
|
||||
if (rv == SSL_READ_EARLY_DATA_ERROR) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
tls.last_readlen = len;
|
||||
tls.last_readlen = data.size();
|
||||
return 0;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -636,12 +636,12 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
|
||||
if (!tls.early_data_finish) {
|
||||
// TLSv1.3 handshake is still going on.
|
||||
size_t nread = 0;
|
||||
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
|
||||
auto rv = SSL_read_early_data(tls.ssl, data.data(), data.size(), &nread);
|
||||
if (rv < 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
tls.last_readlen = len;
|
||||
tls.last_readlen = data.size();
|
||||
return 0;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -677,13 +677,13 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
|
||||
#endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) &&
|
||||
// defined(WOLFSSL_EARLY_DATA)
|
||||
|
||||
auto rv = SSL_read(tls.ssl, data, static_cast<int>(len));
|
||||
auto rv = SSL_read(tls.ssl, data.data(), static_cast<int>(data.size()));
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
tls.last_readlen = len;
|
||||
tls.last_readlen = data.size();
|
||||
return 0;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
@@ -710,14 +710,14 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nghttp2_ssize Connection::write_clear(const void *data, size_t len) {
|
||||
len = std::min(len, wlimit.avail());
|
||||
if (len == 0) {
|
||||
nghttp2_ssize Connection::write_clear(std::span<const uint8_t> data) {
|
||||
data = data.first(std::min(data.size(), wlimit.avail()));
|
||||
if (data.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nwrite;
|
||||
while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR)
|
||||
while ((nwrite = write(fd, data.data(), data.size())) == -1 && errno == EINTR)
|
||||
;
|
||||
if (nwrite == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
@@ -764,14 +764,14 @@ nghttp2_ssize Connection::writev_clear(struct iovec *iov, int iovcnt) {
|
||||
return nwrite;
|
||||
}
|
||||
|
||||
nghttp2_ssize Connection::read_clear(void *data, size_t len) {
|
||||
len = std::min(len, rlimit.avail());
|
||||
if (len == 0) {
|
||||
nghttp2_ssize Connection::read_clear(std::span<uint8_t> data) {
|
||||
data = data.first(std::min(data.size(), rlimit.avail()));
|
||||
if (data.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nread;
|
||||
while ((nread = read(fd, data, len)) == -1 && errno == EINTR)
|
||||
while ((nread = read(fd, data.data(), data.size())) == -1 && errno == EINTR)
|
||||
;
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
@@ -789,9 +789,9 @@ nghttp2_ssize Connection::read_clear(void *data, size_t len) {
|
||||
return nread;
|
||||
}
|
||||
|
||||
nghttp2_ssize Connection::read_nolim_clear(void *data, size_t len) {
|
||||
nghttp2_ssize Connection::read_nolim_clear(std::span<uint8_t> data) {
|
||||
ssize_t nread;
|
||||
while ((nread = read(fd, data, len)) == -1 && errno == EINTR)
|
||||
while ((nread = read(fd, data.data(), data.size())) == -1 && errno == EINTR)
|
||||
;
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
@@ -807,9 +807,10 @@ nghttp2_ssize Connection::read_nolim_clear(void *data, size_t len) {
|
||||
return nread;
|
||||
}
|
||||
|
||||
nghttp2_ssize Connection::peek_clear(void *data, size_t len) {
|
||||
nghttp2_ssize Connection::peek_clear(std::span<uint8_t> data) {
|
||||
ssize_t nread;
|
||||
while ((nread = recv(fd, data, len, MSG_PEEK)) == -1 && errno == EINTR)
|
||||
while ((nread = recv(fd, data.data(), data.size(), MSG_PEEK)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
|
||||
@@ -121,8 +121,8 @@ struct Connection {
|
||||
// underlying connection blocks), return 0. SHRPX_ERR_EOF is
|
||||
// returned in case of EOF and no data was read. Otherwise
|
||||
// SHRPX_ERR_NETWORK is return in case of error.
|
||||
nghttp2_ssize write_tls(const void *data, size_t len);
|
||||
nghttp2_ssize read_tls(void *data, size_t len);
|
||||
nghttp2_ssize write_tls(std::span<const uint8_t> data);
|
||||
nghttp2_ssize read_tls(std::span<uint8_t> data);
|
||||
|
||||
size_t get_tls_write_limit();
|
||||
// Updates the number of bytes written in warm up period.
|
||||
@@ -131,13 +131,13 @@ struct Connection {
|
||||
// determine fallback to short record size mode.
|
||||
void start_tls_write_idle();
|
||||
|
||||
nghttp2_ssize write_clear(const void *data, size_t len);
|
||||
nghttp2_ssize write_clear(std::span<const uint8_t> data);
|
||||
nghttp2_ssize writev_clear(struct iovec *iov, int iovcnt);
|
||||
nghttp2_ssize read_clear(void *data, size_t len);
|
||||
nghttp2_ssize read_clear(std::span<uint8_t> data);
|
||||
// Read at most |len| bytes of data from socket without rate limit.
|
||||
nghttp2_ssize read_nolim_clear(void *data, size_t len);
|
||||
nghttp2_ssize read_nolim_clear(std::span<uint8_t> data);
|
||||
// Peek at most |len| bytes of data from socket without rate limit.
|
||||
nghttp2_ssize peek_clear(void *data, size_t len);
|
||||
nghttp2_ssize peek_clear(std::span<uint8_t> data);
|
||||
|
||||
void handle_tls_pending_read();
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ void ConnectionHandler::graceful_shutdown_worker() {
|
||||
#ifndef NOTHREADS
|
||||
ev_async_start(loop_, &thread_join_asyncev_);
|
||||
|
||||
thread_join_fut_ = std::async(std::launch::async, [this]() {
|
||||
thread_join_fut_ = std::async(std::launch::async, [this] {
|
||||
(void)reopen_log_files(get_config()->logging);
|
||||
join_worker();
|
||||
ev_async_send(get_loop(), &thread_join_asyncev_);
|
||||
@@ -677,19 +677,23 @@ int ConnectionHandler::forward_quic_packet_to_lingering_worker_process(
|
||||
std::array<uint8_t, 512> header;
|
||||
|
||||
assert(header.size() >= 1 + 1 + 1 + 1 + sizeof(sockaddr_storage) * 2);
|
||||
assert(remote_addr.len > 0);
|
||||
assert(local_addr.len > 0);
|
||||
assert(!remote_addr.empty());
|
||||
assert(!local_addr.empty());
|
||||
|
||||
auto p = header.data();
|
||||
|
||||
*p++ = static_cast<uint8_t>(QUICIPCType::DGRAM_FORWARD);
|
||||
*p++ = static_cast<uint8_t>(remote_addr.len - 1);
|
||||
p = std::ranges::copy_n(reinterpret_cast<const uint8_t *>(&remote_addr.su),
|
||||
as_signed(remote_addr.len), p)
|
||||
auto remote_addrlen = remote_addr.size();
|
||||
*p++ = static_cast<uint8_t>(remote_addrlen - 1);
|
||||
p = std::ranges::copy_n(
|
||||
reinterpret_cast<const uint8_t *>(remote_addr.as_sockaddr()),
|
||||
as_signed(remote_addrlen), p)
|
||||
.out;
|
||||
*p++ = static_cast<uint8_t>(local_addr.len - 1);
|
||||
p = std::ranges::copy_n(reinterpret_cast<const uint8_t *>(&local_addr.su),
|
||||
as_signed(local_addr.len), p)
|
||||
auto local_addrlen = local_addr.size();
|
||||
*p++ = static_cast<uint8_t>(local_addrlen - 1);
|
||||
p = std::ranges::copy_n(
|
||||
reinterpret_cast<const uint8_t *>(local_addr.as_sockaddr()),
|
||||
as_signed(local_addrlen), p)
|
||||
.out;
|
||||
*p++ = pi.ecn;
|
||||
|
||||
@@ -790,8 +794,9 @@ int ConnectionHandler::quic_ipc_read() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkt->remote_addr.len = remote_addrlen;
|
||||
memcpy(&pkt->remote_addr.su, p, remote_addrlen);
|
||||
sockaddr_storage ss;
|
||||
memcpy(&ss, p, remote_addrlen);
|
||||
pkt->remote_addr.set(reinterpret_cast<const sockaddr *>(&ss));
|
||||
|
||||
p += remote_addrlen;
|
||||
|
||||
@@ -810,8 +815,8 @@ int ConnectionHandler::quic_ipc_read() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkt->local_addr.len = local_addrlen;
|
||||
memcpy(&pkt->local_addr.su, p, local_addrlen);
|
||||
memcpy(&ss, p, local_addrlen);
|
||||
pkt->local_addr.set(reinterpret_cast<const sockaddr *>(&ss));
|
||||
|
||||
p += local_addrlen;
|
||||
|
||||
@@ -859,9 +864,10 @@ int ConnectionHandler::quic_ipc_read() {
|
||||
|
||||
ConnectionID decrypted_dcid;
|
||||
|
||||
if (decrypt_quic_connection_id(decrypted_dcid,
|
||||
vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET,
|
||||
qkm.cid_decryption_ctx) != 0) {
|
||||
if (decrypt_quic_connection_id(
|
||||
decrypted_dcid,
|
||||
std::span{vc.dcid, vc.dcidlen}.subspan(SHRPX_QUIC_CID_WORKER_ID_OFFSET),
|
||||
qkm.cid_decryption_ctx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ DNSResolver::~DNSResolver() {
|
||||
ev_timer_stop(loop_, &timer_);
|
||||
}
|
||||
|
||||
int DNSResolver::resolve(const std::string_view &name, int family) {
|
||||
int DNSResolver::resolve(std::string_view name, int family) {
|
||||
if (status_ != DNSResolverStatus::IDLE) {
|
||||
return -1;
|
||||
}
|
||||
@@ -238,7 +238,7 @@ DNSResolverStatus DNSResolver::get_status(Address *result) const {
|
||||
}
|
||||
|
||||
if (result) {
|
||||
memcpy(result, &result_, sizeof(result_));
|
||||
*result = result_;
|
||||
}
|
||||
|
||||
return status_;
|
||||
@@ -311,24 +311,32 @@ void DNSResolver::on_result(int status, ares_addrinfo *ai) {
|
||||
|
||||
for (; ap; ap = ap->ai_next) {
|
||||
switch (ap->ai_family) {
|
||||
case AF_INET:
|
||||
case AF_INET: {
|
||||
status_ = DNSResolverStatus::OK;
|
||||
result_.len = sizeof(result_.su.in);
|
||||
|
||||
assert(sizeof(result_.su.in) == ap->ai_addrlen);
|
||||
sockaddr_in sa;
|
||||
|
||||
memcpy(&result_.su.in, ap->ai_addr, sizeof(result_.su.in));
|
||||
assert(sizeof(sa) == ap->ai_addrlen);
|
||||
|
||||
memcpy(&sa, ap->ai_addr, sizeof(sa));
|
||||
|
||||
result_.skaddr.emplace<sockaddr_in>(sa);
|
||||
|
||||
break;
|
||||
case AF_INET6:
|
||||
}
|
||||
case AF_INET6: {
|
||||
status_ = DNSResolverStatus::OK;
|
||||
result_.len = sizeof(result_.su.in6);
|
||||
|
||||
assert(sizeof(result_.su.in6) == ap->ai_addrlen);
|
||||
sockaddr_in6 sa;
|
||||
|
||||
memcpy(&result_.su.in6, ap->ai_addr, sizeof(result_.su.in6));
|
||||
assert(sizeof(sa) == ap->ai_addrlen);
|
||||
|
||||
memcpy(&sa, ap->ai_addr, sizeof(sa));
|
||||
|
||||
result_.skaddr.emplace<sockaddr_in6>(sa);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
@@ -348,7 +356,7 @@ void DNSResolver::on_result(int status, ares_addrinfo *ai) {
|
||||
if (status_ == DNSResolverStatus::OK) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
|
||||
<< util::numeric_name(&result_.su.sa, result_.len);
|
||||
<< util::numeric_name(result_.as_sockaddr(), result_.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
~DNSResolver();
|
||||
|
||||
// Starts resolving hostname |name|.
|
||||
int resolve(const std::string_view &name, int family);
|
||||
int resolve(std::string_view name, int family);
|
||||
// Returns status. If status_ is DNSResolverStatus::SUCCESS &&
|
||||
// |result| is not nullptr, |*result| is filled.
|
||||
DNSResolverStatus get_status(Address *result) const;
|
||||
|
||||
@@ -148,7 +148,7 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
|
||||
case DNSResolverStatus::OK:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
|
||||
<< util::numeric_name(&result->su.sa, result->len);
|
||||
<< util::numeric_name(result->as_sockaddr(), result->size());
|
||||
}
|
||||
|
||||
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
|
||||
@@ -210,7 +210,7 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
|
||||
case DNSResolverStatus::OK:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
|
||||
<< util::numeric_name(&result->su.sa, result->len);
|
||||
<< util::numeric_name(result->as_sockaddr(), result->size());
|
||||
}
|
||||
|
||||
update_entry(ent, nullptr, DNSResolverStatus::OK, result);
|
||||
@@ -242,10 +242,11 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
|
||||
case DNSResolverStatus::OK:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
|
||||
<< util::numeric_name(&ent.result.su.sa, ent.result.len);
|
||||
<< util::numeric_name(ent.result.as_sockaddr(),
|
||||
ent.result.size());
|
||||
}
|
||||
if (result) {
|
||||
memcpy(result, &ent.result, sizeof(*result));
|
||||
*result = ent.result;
|
||||
}
|
||||
return DNSResolverStatus::OK;
|
||||
default:
|
||||
|
||||
@@ -321,7 +321,7 @@ void Downstream::force_resume_read() {
|
||||
namespace {
|
||||
const HeaderRefs::value_type *
|
||||
search_header_linear_backwards(const HeaderRefs &headers,
|
||||
const std::string_view &name) {
|
||||
std::string_view name) {
|
||||
for (auto it = headers.rbegin(); it != headers.rend(); ++it) {
|
||||
auto &kv = *it;
|
||||
if (kv.name == name) {
|
||||
@@ -374,7 +374,7 @@ std::string_view Downstream::assemble_request_cookie() {
|
||||
return as_string_view(std::ranges::begin(iov), p);
|
||||
}
|
||||
|
||||
uint32_t Downstream::find_affinity_cookie(const std::string_view &name) {
|
||||
uint32_t Downstream::find_affinity_cookie(std::string_view name) {
|
||||
for (auto &kv : req_.fs.headers()) {
|
||||
if (kv.token != http2::HD_COOKIE) {
|
||||
continue;
|
||||
@@ -467,8 +467,8 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void add_header(size_t &sum, HeaderRefs &headers, const std::string_view &name,
|
||||
const std::string_view &value, bool no_index, int32_t token) {
|
||||
void add_header(size_t &sum, HeaderRefs &headers, std::string_view name,
|
||||
std::string_view value, bool no_index, int32_t token) {
|
||||
sum += name.size() + value.size();
|
||||
headers.emplace_back(name, value, no_index, token);
|
||||
}
|
||||
@@ -476,7 +476,7 @@ void add_header(size_t &sum, HeaderRefs &headers, const std::string_view &name,
|
||||
|
||||
namespace {
|
||||
std::string_view alloc_header_name(BlockAllocator &balloc,
|
||||
const std::string_view &name) {
|
||||
std::string_view name) {
|
||||
auto iov = make_byte_ref(balloc, name.size() + 1);
|
||||
auto p = util::tolower(name, std::ranges::begin(iov));
|
||||
*p = '\0';
|
||||
@@ -487,7 +487,7 @@ std::string_view alloc_header_name(BlockAllocator &balloc,
|
||||
|
||||
namespace {
|
||||
void append_last_header_key(BlockAllocator &balloc, bool &key_prev, size_t &sum,
|
||||
HeaderRefs &headers, const std::string_view &data) {
|
||||
HeaderRefs &headers, std::string_view data) {
|
||||
assert(key_prev);
|
||||
sum += data.size();
|
||||
auto &item = headers.back();
|
||||
@@ -502,7 +502,7 @@ void append_last_header_key(BlockAllocator &balloc, bool &key_prev, size_t &sum,
|
||||
namespace {
|
||||
void append_last_header_value(BlockAllocator &balloc, bool &key_prev,
|
||||
size_t &sum, HeaderRefs &headers,
|
||||
const std::string_view &data) {
|
||||
std::string_view data) {
|
||||
key_prev = false;
|
||||
sum += data.size();
|
||||
auto &item = headers.back();
|
||||
@@ -550,30 +550,28 @@ HeaderRefs::value_type *FieldStore::header(int32_t token) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const HeaderRefs::value_type *
|
||||
FieldStore::header(const std::string_view &name) const {
|
||||
const HeaderRefs::value_type *FieldStore::header(std::string_view name) const {
|
||||
return search_header_linear_backwards(headers_, name);
|
||||
}
|
||||
|
||||
void FieldStore::add_header_token(const std::string_view &name,
|
||||
const std::string_view &value, bool no_index,
|
||||
int32_t token) {
|
||||
void FieldStore::add_header_token(std::string_view name, std::string_view value,
|
||||
bool no_index, int32_t token) {
|
||||
shrpx::add_header(buffer_size_, headers_, name, value, no_index, token);
|
||||
}
|
||||
|
||||
void FieldStore::alloc_add_header_name(const std::string_view &name) {
|
||||
void FieldStore::alloc_add_header_name(std::string_view name) {
|
||||
auto name_ref = alloc_header_name(balloc_, name);
|
||||
auto token = http2::lookup_token(name_ref);
|
||||
add_header_token(name_ref, ""sv, false, token);
|
||||
header_key_prev_ = true;
|
||||
}
|
||||
|
||||
void FieldStore::append_last_header_key(const std::string_view &data) {
|
||||
void FieldStore::append_last_header_key(std::string_view data) {
|
||||
shrpx::append_last_header_key(balloc_, header_key_prev_, buffer_size_,
|
||||
headers_, data);
|
||||
}
|
||||
|
||||
void FieldStore::append_last_header_value(const std::string_view &data) {
|
||||
void FieldStore::append_last_header_value(std::string_view data) {
|
||||
shrpx::append_last_header_value(balloc_, header_key_prev_, buffer_size_,
|
||||
headers_, data);
|
||||
}
|
||||
@@ -583,27 +581,27 @@ void FieldStore::clear_headers() {
|
||||
header_key_prev_ = false;
|
||||
}
|
||||
|
||||
void FieldStore::add_trailer_token(const std::string_view &name,
|
||||
const std::string_view &value, bool no_index,
|
||||
void FieldStore::add_trailer_token(std::string_view name,
|
||||
std::string_view value, bool no_index,
|
||||
int32_t token) {
|
||||
// Header size limit should be applied to all header and trailer
|
||||
// fields combined.
|
||||
shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token);
|
||||
}
|
||||
|
||||
void FieldStore::alloc_add_trailer_name(const std::string_view &name) {
|
||||
void FieldStore::alloc_add_trailer_name(std::string_view name) {
|
||||
auto name_ref = alloc_header_name(balloc_, name);
|
||||
auto token = http2::lookup_token(name_ref);
|
||||
add_trailer_token(name_ref, ""sv, false, token);
|
||||
trailer_key_prev_ = true;
|
||||
}
|
||||
|
||||
void FieldStore::append_last_trailer_key(const std::string_view &data) {
|
||||
void FieldStore::append_last_trailer_key(std::string_view data) {
|
||||
shrpx::append_last_header_key(balloc_, trailer_key_prev_, buffer_size_,
|
||||
trailers_, data);
|
||||
}
|
||||
|
||||
void FieldStore::append_last_trailer_value(const std::string_view &data) {
|
||||
void FieldStore::append_last_trailer_value(std::string_view data) {
|
||||
shrpx::append_last_header_value(balloc_, trailer_key_prev_, buffer_size_,
|
||||
trailers_, data);
|
||||
}
|
||||
@@ -684,12 +682,12 @@ int Downstream::push_request_headers() {
|
||||
return dconn_->push_request_headers();
|
||||
}
|
||||
|
||||
int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
|
||||
req_.recv_body_length += datalen;
|
||||
int Downstream::push_upload_data_chunk(std::span<const uint8_t> data) {
|
||||
req_.recv_body_length += data.size();
|
||||
|
||||
if (!dconn_ && !request_header_sent_) {
|
||||
blocked_request_buf_.append(data, datalen);
|
||||
req_.unconsumed_body_length += datalen;
|
||||
blocked_request_buf_.append(data);
|
||||
req_.unconsumed_body_length += data.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -699,11 +697,11 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
|
||||
DLOG(INFO, this) << "dconn_ is NULL";
|
||||
return -1;
|
||||
}
|
||||
if (dconn_->push_upload_data_chunk(data, datalen) != 0) {
|
||||
if (dconn_->push_upload_data_chunk(data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
req_.unconsumed_body_length += datalen;
|
||||
req_.unconsumed_body_length += data.size();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -721,7 +719,7 @@ int Downstream::end_upload_data() {
|
||||
}
|
||||
|
||||
void Downstream::rewrite_location_response_header(
|
||||
const std::string_view &upstream_scheme) {
|
||||
std::string_view upstream_scheme) {
|
||||
auto hd = resp_.fs.header(http2::HD_LOCATION);
|
||||
if (!hd) {
|
||||
return;
|
||||
@@ -1104,7 +1102,7 @@ void Downstream::add_retry() { ++num_retry_; }
|
||||
|
||||
bool Downstream::no_more_retry() const { return num_retry_ > 50; }
|
||||
|
||||
void Downstream::set_request_downstream_host(const std::string_view &host) {
|
||||
void Downstream::set_request_downstream_host(std::string_view host) {
|
||||
request_downstream_host_ = host;
|
||||
}
|
||||
|
||||
@@ -1210,7 +1208,7 @@ void Downstream::set_blocked_request_data_eof(bool f) {
|
||||
blocked_request_data_eof_ = f;
|
||||
}
|
||||
|
||||
void Downstream::set_ws_key(const std::string_view &key) { ws_key_ = key; }
|
||||
void Downstream::set_ws_key(std::string_view key) { ws_key_ = key; }
|
||||
|
||||
bool Downstream::get_expect_100_continue() const {
|
||||
return expect_100_continue_;
|
||||
|
||||
@@ -89,19 +89,18 @@ public:
|
||||
HeaderRefs::value_type *header(int32_t token);
|
||||
// Returns pointer to the header field with the name |name|. If no
|
||||
// such header is found, returns nullptr.
|
||||
const HeaderRefs::value_type *header(const std::string_view &name) const;
|
||||
const HeaderRefs::value_type *header(std::string_view name) const;
|
||||
|
||||
void add_header_token(const std::string_view &name,
|
||||
const std::string_view &value, bool no_index,
|
||||
int32_t token);
|
||||
void add_header_token(std::string_view name, std::string_view value,
|
||||
bool no_index, int32_t token);
|
||||
|
||||
// Adds header field name |name|. First, the copy of header field
|
||||
// name pointed by name.c_str() of length name.size() is made, and
|
||||
// stored.
|
||||
void alloc_add_header_name(const std::string_view &name);
|
||||
void alloc_add_header_name(std::string_view name);
|
||||
|
||||
void append_last_header_key(const std::string_view &data);
|
||||
void append_last_header_value(const std::string_view &data);
|
||||
void append_last_header_key(std::string_view data);
|
||||
void append_last_header_value(std::string_view data);
|
||||
|
||||
bool header_key_prev() const { return header_key_prev_; }
|
||||
|
||||
@@ -112,17 +111,16 @@ public:
|
||||
// Empties headers.
|
||||
void clear_headers();
|
||||
|
||||
void add_trailer_token(const std::string_view &name,
|
||||
const std::string_view &value, bool no_index,
|
||||
int32_t token);
|
||||
void add_trailer_token(std::string_view name, std::string_view value,
|
||||
bool no_index, int32_t token);
|
||||
|
||||
// Adds trailer field name |name|. First, the copy of trailer field
|
||||
// name pointed by name.c_str() of length name.size() is made, and
|
||||
// stored.
|
||||
void alloc_add_trailer_name(const std::string_view &name);
|
||||
void alloc_add_trailer_name(std::string_view name);
|
||||
|
||||
void append_last_trailer_key(const std::string_view &data);
|
||||
void append_last_trailer_value(const std::string_view &data);
|
||||
void append_last_trailer_key(std::string_view data);
|
||||
void append_last_trailer_value(std::string_view data);
|
||||
|
||||
bool trailer_key_prev() const { return trailer_key_prev_; }
|
||||
|
||||
@@ -253,9 +251,8 @@ struct Response {
|
||||
|
||||
// returns true if a resource denoted by scheme, authority, and path
|
||||
// has already been pushed.
|
||||
bool is_resource_pushed(const std::string_view &scheme,
|
||||
const std::string_view &authority,
|
||||
const std::string_view &path) const {
|
||||
bool is_resource_pushed(std::string_view scheme, std::string_view authority,
|
||||
std::string_view path) const {
|
||||
if (!pushed_resources) {
|
||||
return false;
|
||||
}
|
||||
@@ -266,9 +263,8 @@ struct Response {
|
||||
|
||||
// remember that a resource denoted by scheme, authority, and path
|
||||
// is pushed.
|
||||
void resource_pushed(const std::string_view &scheme,
|
||||
const std::string_view &authority,
|
||||
const std::string_view &path) {
|
||||
void resource_pushed(std::string_view scheme, std::string_view authority,
|
||||
std::string_view path) {
|
||||
if (!pushed_resources) {
|
||||
pushed_resources = std::make_unique<std::vector<
|
||||
std::tuple<std::string_view, std::string_view, std::string_view>>>();
|
||||
@@ -392,12 +388,12 @@ public:
|
||||
int push_request_headers();
|
||||
bool get_chunked_request() const;
|
||||
void set_chunked_request(bool f);
|
||||
int push_upload_data_chunk(const uint8_t *data, size_t datalen);
|
||||
int push_upload_data_chunk(std::span<const uint8_t> data);
|
||||
int end_upload_data();
|
||||
// Validates that received request body length and content-length
|
||||
// matches.
|
||||
bool validate_request_recv_body_length() const;
|
||||
void set_request_downstream_host(const std::string_view &host);
|
||||
void set_request_downstream_host(std::string_view host);
|
||||
bool expect_response_body() const;
|
||||
bool expect_response_trailer() const;
|
||||
void set_request_state(DownstreamState state);
|
||||
@@ -422,8 +418,7 @@ public:
|
||||
Response &response() { return resp_; }
|
||||
|
||||
// Rewrites the location response header field.
|
||||
void
|
||||
rewrite_location_response_header(const std::string_view &upstream_scheme);
|
||||
void rewrite_location_response_header(std::string_view upstream_scheme);
|
||||
|
||||
bool get_chunked_response() const;
|
||||
void set_chunked_response(bool f);
|
||||
@@ -518,7 +513,7 @@ public:
|
||||
// cookie is given in |name|. If an affinity cookie is found, it is
|
||||
// assigned to a member function, and is returned. If it is not
|
||||
// found, or is malformed, returns 0.
|
||||
uint32_t find_affinity_cookie(const std::string_view &name);
|
||||
uint32_t find_affinity_cookie(std::string_view name);
|
||||
// Set |h| as affinity cookie.
|
||||
void renew_affinity_cookie(uint32_t h);
|
||||
// Returns affinity cookie to send. If it does not need to be sent,
|
||||
@@ -526,7 +521,7 @@ public:
|
||||
// field, returns 0.
|
||||
uint32_t get_affinity_cookie_to_send() const;
|
||||
|
||||
void set_ws_key(const std::string_view &key);
|
||||
void set_ws_key(std::string_view key);
|
||||
|
||||
bool get_expect_100_continue() const;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
#include "shrpx_io_control.h"
|
||||
|
||||
@@ -47,7 +48,7 @@ public:
|
||||
virtual void detach_downstream(Downstream *downstream) = 0;
|
||||
|
||||
virtual int push_request_headers() = 0;
|
||||
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0;
|
||||
virtual int push_upload_data_chunk(std::span<const uint8_t> data) = 0;
|
||||
virtual int end_upload_data() = 0;
|
||||
|
||||
virtual void pause_read(IOCtrlReason reason) = 0;
|
||||
|
||||
@@ -58,7 +58,7 @@ void DownstreamQueue::mark_failure(Downstream *downstream) {
|
||||
}
|
||||
|
||||
DownstreamQueue::HostEntry &
|
||||
DownstreamQueue::find_host_entry(const std::string_view &host) {
|
||||
DownstreamQueue::find_host_entry(std::string_view host) {
|
||||
auto itr = host_entries_.find(host);
|
||||
if (itr == std::ranges::end(host_entries_)) {
|
||||
auto key = ImmutableString{host};
|
||||
@@ -69,8 +69,7 @@ DownstreamQueue::find_host_entry(const std::string_view &host) {
|
||||
return (*itr).second;
|
||||
}
|
||||
|
||||
std::string_view
|
||||
DownstreamQueue::make_host_key(const std::string_view &host) const {
|
||||
std::string_view DownstreamQueue::make_host_key(std::string_view host) const {
|
||||
return unified_host_ ? ""sv : host;
|
||||
}
|
||||
|
||||
@@ -95,7 +94,7 @@ void DownstreamQueue::mark_blocked(Downstream *downstream) {
|
||||
ent.blocked.append(link);
|
||||
}
|
||||
|
||||
bool DownstreamQueue::can_activate(const std::string_view &host) const {
|
||||
bool DownstreamQueue::can_activate(std::string_view host) const {
|
||||
auto itr = host_entries_.find(make_host_key(host));
|
||||
if (itr == std::ranges::end(host_entries_)) {
|
||||
return true;
|
||||
@@ -107,7 +106,7 @@ bool DownstreamQueue::can_activate(const std::string_view &host) const {
|
||||
namespace {
|
||||
bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent,
|
||||
DownstreamQueue::HostEntryMap &host_entries,
|
||||
const std::string_view &host) {
|
||||
std::string_view host) {
|
||||
if (ent.blocked.empty() && ent.num_active == 0) {
|
||||
host_entries.erase(host);
|
||||
return true;
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
void mark_blocked(Downstream *downstream);
|
||||
// Returns true if we can make downstream connection to given
|
||||
// |host|.
|
||||
bool can_activate(const std::string_view &host) const;
|
||||
bool can_activate(std::string_view host) const;
|
||||
// Removes and frees |downstream| object. If |downstream| is in
|
||||
// DispatchState::ACTIVE, and |next_blocked| is true, this function
|
||||
// may return Downstream object with the same target host in
|
||||
@@ -94,8 +94,8 @@ public:
|
||||
Downstream *remove_and_get_blocked(Downstream *downstream,
|
||||
bool next_blocked = true);
|
||||
Downstream *get_downstreams() const;
|
||||
HostEntry &find_host_entry(const std::string_view &host);
|
||||
std::string_view make_host_key(const std::string_view &host) const;
|
||||
HostEntry &find_host_entry(std::string_view host);
|
||||
std::string_view make_host_key(std::string_view host) const;
|
||||
std::string_view make_host_key(Downstream *downstream) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -52,7 +52,7 @@ DualDNSResolver::DualDNSResolver(struct ev_loop *loop, int family)
|
||||
}
|
||||
}
|
||||
|
||||
int DualDNSResolver::resolve(const std::string_view &host) {
|
||||
int DualDNSResolver::resolve(std::string_view host) {
|
||||
int rv4 = 0, rv6 = 0;
|
||||
if (family_ == AF_UNSPEC || family_ == AF_INET) {
|
||||
rv4 = resolv4_.resolve(host, AF_INET);
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
DualDNSResolver(struct ev_loop *loop, int family);
|
||||
|
||||
// Resolves |host|. |host| must be NULL-terminated string.
|
||||
int resolve(const std::string_view &host);
|
||||
int resolve(std::string_view host);
|
||||
CompleteCb get_complete_cb() const;
|
||||
void set_complete_cb(CompleteCb cb);
|
||||
DNSResolverStatus get_status(Address *result) const;
|
||||
|
||||
@@ -64,7 +64,7 @@ int HealthMonitorDownstreamConnection::push_request_headers() {
|
||||
}
|
||||
|
||||
int HealthMonitorDownstreamConnection::push_upload_data_chunk(
|
||||
const uint8_t *data, size_t datalen) {
|
||||
std::span<const uint8_t> data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ int HealthMonitorDownstreamConnection::end_upload_data() {
|
||||
resp.fs.add_header_token("content-length"sv, "0"sv, false,
|
||||
http2::HD_CONTENT_LENGTH);
|
||||
|
||||
if (upstream->send_reply(downstream_, nullptr, 0) != 0) {
|
||||
if (upstream->send_reply(downstream_, {}) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,29 +34,29 @@ class Worker;
|
||||
class HealthMonitorDownstreamConnection : public DownstreamConnection {
|
||||
public:
|
||||
HealthMonitorDownstreamConnection();
|
||||
virtual ~HealthMonitorDownstreamConnection();
|
||||
virtual int attach_downstream(Downstream *downstream);
|
||||
virtual void detach_downstream(Downstream *downstream);
|
||||
~HealthMonitorDownstreamConnection() override;
|
||||
int attach_downstream(Downstream *downstream) override;
|
||||
void detach_downstream(Downstream *downstream) override;
|
||||
|
||||
virtual int push_request_headers();
|
||||
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
|
||||
virtual int end_upload_data();
|
||||
int push_request_headers() override;
|
||||
int push_upload_data_chunk(std::span<const uint8_t> data) override;
|
||||
int end_upload_data() override;
|
||||
|
||||
virtual void pause_read(IOCtrlReason reason);
|
||||
virtual int resume_read(IOCtrlReason reason, size_t consumed);
|
||||
virtual void force_resume_read();
|
||||
void pause_read(IOCtrlReason reason) override;
|
||||
int resume_read(IOCtrlReason reason, size_t consumed) override;
|
||||
void force_resume_read() override;
|
||||
|
||||
virtual int on_read();
|
||||
virtual int on_write();
|
||||
int on_read() override;
|
||||
int on_write() override;
|
||||
|
||||
virtual void on_upstream_change(Upstream *upstream);
|
||||
void on_upstream_change(Upstream *upstream) override;
|
||||
|
||||
// true if this object is poolable.
|
||||
virtual bool poolable() const;
|
||||
bool poolable() const override;
|
||||
|
||||
virtual const std::shared_ptr<DownstreamAddrGroup> &
|
||||
get_downstream_addr_group() const;
|
||||
virtual DownstreamAddr *get_addr() const;
|
||||
const std::shared_ptr<DownstreamAddrGroup> &
|
||||
get_downstream_addr_group() const override;
|
||||
DownstreamAddr *get_addr() const override;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -56,10 +56,10 @@ std::string_view create_error_html(BlockAllocator &balloc,
|
||||
}
|
||||
|
||||
std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params,
|
||||
const std::string_view &node_by,
|
||||
const std::string_view &node_for,
|
||||
const std::string_view &host,
|
||||
const std::string_view &proto) {
|
||||
std::string_view node_by,
|
||||
std::string_view node_for,
|
||||
std::string_view host,
|
||||
std::string_view proto) {
|
||||
size_t len = 0;
|
||||
if ((params & FORWARDED_BY) && !node_by.empty()) {
|
||||
len += str_size("by=\"") + node_by.size() + str_size("\";");
|
||||
@@ -128,7 +128,7 @@ std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params,
|
||||
return as_string_view(std::ranges::begin(iov), p);
|
||||
}
|
||||
|
||||
std::string colorize_headers(const std::string_view &hdrs) {
|
||||
std::string colorize_headers(std::string_view hdrs) {
|
||||
std::string nhdrs;
|
||||
auto p = std::ranges::find(hdrs, '\n');
|
||||
if (p == std::ranges::end(hdrs)) {
|
||||
@@ -180,10 +180,9 @@ nghttp2_ssize select_padding_callback(nghttp2_session *session,
|
||||
}
|
||||
|
||||
std::string_view create_affinity_cookie(BlockAllocator &balloc,
|
||||
const std::string_view &name,
|
||||
std::string_view name,
|
||||
uint32_t affinity_cookie,
|
||||
const std::string_view &path,
|
||||
bool secure) {
|
||||
std::string_view path, bool secure) {
|
||||
static constexpr auto PATH_PREFIX = "; Path="sv;
|
||||
static constexpr auto SECURE = "; Secure"sv;
|
||||
// <name>=<value>[; Path=<path>][; Secure]
|
||||
@@ -213,7 +212,7 @@ std::string_view create_affinity_cookie(BlockAllocator &balloc,
|
||||
}
|
||||
|
||||
bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
|
||||
const std::string_view &scheme) {
|
||||
std::string_view scheme) {
|
||||
switch (secure) {
|
||||
case SessionAffinityCookieSecure::AUTO:
|
||||
return scheme == "https"sv;
|
||||
@@ -276,7 +275,7 @@ create_altsvc_header_value(BlockAllocator &balloc,
|
||||
return as_string_view(std::ranges::begin(iov), p);
|
||||
}
|
||||
|
||||
bool check_http_scheme(const std::string_view &scheme, bool encrypted) {
|
||||
bool check_http_scheme(std::string_view scheme, bool encrypted) {
|
||||
return encrypted ? scheme == "https"sv : scheme == "http"sv;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,13 +72,13 @@ OutputIt create_via_header_value(OutputIt dst, int major, int minor) {
|
||||
// |params| is bitwise-OR of zero or more of shrpx_forwarded_param
|
||||
// defined in shrpx_config.h.
|
||||
std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params,
|
||||
const std::string_view &node_by,
|
||||
const std::string_view &node_for,
|
||||
const std::string_view &host,
|
||||
const std::string_view &proto);
|
||||
std::string_view node_by,
|
||||
std::string_view node_for,
|
||||
std::string_view host,
|
||||
std::string_view proto);
|
||||
|
||||
// Adds ANSI color codes to HTTP headers |hdrs|.
|
||||
std::string colorize_headers(const std::string_view &hdrs);
|
||||
std::string colorize_headers(std::string_view hdrs);
|
||||
|
||||
nghttp2_ssize select_padding_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
@@ -88,15 +88,14 @@ nghttp2_ssize select_padding_callback(nghttp2_session *session,
|
||||
// not empty, "; <path>" is added. If |secure| is true, "; Secure" is
|
||||
// added.
|
||||
std::string_view create_affinity_cookie(BlockAllocator &balloc,
|
||||
const std::string_view &name,
|
||||
std::string_view name,
|
||||
uint32_t affinity_cookie,
|
||||
const std::string_view &path,
|
||||
bool secure);
|
||||
std::string_view path, bool secure);
|
||||
|
||||
// Returns true if |secure| indicates that Secure attribute should be
|
||||
// set.
|
||||
bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
|
||||
const std::string_view &scheme);
|
||||
std::string_view scheme);
|
||||
|
||||
// Returns RFC 7838 alt-svc header field value.
|
||||
std::string_view create_altsvc_header_value(BlockAllocator &balloc,
|
||||
@@ -106,7 +105,7 @@ std::string_view create_altsvc_header_value(BlockAllocator &balloc,
|
||||
// - scheme is https and encrypted is true
|
||||
// - scheme is http and encrypted is false
|
||||
// Otherwise returns false.
|
||||
bool check_http_scheme(const std::string_view &scheme, bool encrypted);
|
||||
bool check_http_scheme(std::string_view scheme, bool encrypted);
|
||||
|
||||
} // namespace http
|
||||
|
||||
|
||||
@@ -147,19 +147,19 @@ int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
|
||||
switch (downstream->get_response_state()) {
|
||||
case DownstreamState::MSG_RESET:
|
||||
case DownstreamState::MSG_BAD_HEADER:
|
||||
case DownstreamState::MSG_COMPLETE:
|
||||
break;
|
||||
return rv;
|
||||
default:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
|
||||
<< ", stream_id="
|
||||
<< downstream->get_downstream_stream_id()
|
||||
<< ", error_code=" << error_code;
|
||||
}
|
||||
rv = http2session_->submit_rst_stream(
|
||||
static_cast<int32_t>(downstream->get_downstream_stream_id()),
|
||||
error_code);
|
||||
break;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
|
||||
<< ", stream_id="
|
||||
<< downstream->get_downstream_stream_id()
|
||||
<< ", error_code=" << error_code;
|
||||
}
|
||||
rv = http2session_->submit_rst_stream(
|
||||
static_cast<int32_t>(downstream->get_downstream_stream_id()), error_code);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -506,19 +506,19 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
||||
size_t datalen) {
|
||||
int Http2DownstreamConnection::push_upload_data_chunk(
|
||||
std::span<const uint8_t> data) {
|
||||
if (!downstream_->get_request_header_sent()) {
|
||||
auto output = downstream_->get_blocked_request_buf();
|
||||
auto &req = downstream_->request();
|
||||
output->append(data, datalen);
|
||||
req.unconsumed_body_length += datalen;
|
||||
output->append(data);
|
||||
req.unconsumed_body_length += data.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rv;
|
||||
auto output = downstream_->get_request_buf();
|
||||
output->append(data, datalen);
|
||||
output->append(data);
|
||||
if (downstream_->get_downstream_stream_id() != -1) {
|
||||
rv = http2session_->resume_data(this);
|
||||
if (rv != 0) {
|
||||
|
||||
@@ -49,31 +49,31 @@ class DownstreamConnectionPool;
|
||||
class Http2DownstreamConnection : public DownstreamConnection {
|
||||
public:
|
||||
Http2DownstreamConnection(Http2Session *http2session);
|
||||
virtual ~Http2DownstreamConnection();
|
||||
virtual int attach_downstream(Downstream *downstream);
|
||||
virtual void detach_downstream(Downstream *downstream);
|
||||
~Http2DownstreamConnection() override;
|
||||
int attach_downstream(Downstream *downstream) override;
|
||||
void detach_downstream(Downstream *downstream) override;
|
||||
|
||||
virtual int push_request_headers();
|
||||
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
|
||||
virtual int end_upload_data();
|
||||
int push_request_headers() override;
|
||||
int push_upload_data_chunk(std::span<const uint8_t> data) override;
|
||||
int end_upload_data() override;
|
||||
|
||||
virtual void pause_read(IOCtrlReason reason) {}
|
||||
virtual int resume_read(IOCtrlReason reason, size_t consumed);
|
||||
virtual void force_resume_read() {}
|
||||
void pause_read(IOCtrlReason reason) override {}
|
||||
int resume_read(IOCtrlReason reason, size_t consumed) override;
|
||||
void force_resume_read() override {}
|
||||
|
||||
virtual int on_read();
|
||||
virtual int on_write();
|
||||
virtual int on_timeout();
|
||||
int on_read() override;
|
||||
int on_write() override;
|
||||
int on_timeout() override;
|
||||
|
||||
virtual void on_upstream_change(Upstream *upstream) {}
|
||||
void on_upstream_change(Upstream *upstream) override {}
|
||||
|
||||
// This object is not poolable because we don't have facility to
|
||||
// migrate to another Http2Session object.
|
||||
virtual bool poolable() const { return false; }
|
||||
bool poolable() const override { return false; }
|
||||
|
||||
virtual const std::shared_ptr<DownstreamAddrGroup> &
|
||||
get_downstream_addr_group() const;
|
||||
virtual DownstreamAddr *get_addr() const;
|
||||
const std::shared_ptr<DownstreamAddrGroup> &
|
||||
get_downstream_addr_group() const override;
|
||||
DownstreamAddr *get_addr() const override;
|
||||
|
||||
int send();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user