mirror of
https://github.com/nghttp2/nghttp2.git
synced 2026-03-29 09:19:18 +08:00
Compare commits
385 Commits
quic
...
254f2b3c42
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
254f2b3c42 | ||
|
|
633691e164 | ||
|
|
01bcc72f66 | ||
|
|
7ca255ff54 | ||
|
|
6430c98e86 | ||
|
|
32c2557bb7 | ||
|
|
3122a83900 | ||
|
|
75272a817e | ||
|
|
809d5af43e | ||
|
|
3b549caf90 | ||
|
|
42b659354d | ||
|
|
2275327794 | ||
|
|
02a5649343 | ||
|
|
3b0b9a458c | ||
|
|
6e6388e7c2 | ||
|
|
ea6f0c641d | ||
|
|
c883b18f2d | ||
|
|
97e69f7416 | ||
|
|
ad0c9eebf7 | ||
|
|
8a552631b4 | ||
|
|
cff8106908 | ||
|
|
4eb49ac28e | ||
|
|
deb390cf85 | ||
|
|
d91ae6987d | ||
|
|
8ddb2273b9 | ||
|
|
e1446fd57a | ||
|
|
02e6cad121 | ||
|
|
0b053e06d8 | ||
|
|
c3c0403dfa | ||
|
|
abc15c696d | ||
|
|
344d300cf9 | ||
|
|
dec233b9ef | ||
|
|
f695dc999b | ||
|
|
f92f81c05a | ||
|
|
3c4449c046 | ||
|
|
918e4ea46b | ||
|
|
d14d97ab68 | ||
|
|
2aed077761 | ||
|
|
68b2295f4e | ||
|
|
528d177847 | ||
|
|
deae6c95b1 | ||
|
|
7eb179069d | ||
|
|
47c33b8d03 | ||
|
|
94372fbe2a | ||
|
|
9e154297ff | ||
|
|
c4828dbd7c | ||
|
|
0d16db2c65 | ||
|
|
fb63ef305d | ||
|
|
089fc81d72 | ||
|
|
c20d175ff2 | ||
|
|
3985957c4d | ||
|
|
845a20b582 | ||
|
|
7af0c508be | ||
|
|
f8474b25f0 | ||
|
|
15a8d913ea | ||
|
|
65d3c9047f | ||
|
|
8c36971ea9 | ||
|
|
ba1dff187b | ||
|
|
8ecacc8ed2 | ||
|
|
9d41896663 | ||
|
|
18d4a9e4ff | ||
|
|
1745a30644 | ||
|
|
0cc7c598ff | ||
|
|
8c4fbb86d8 | ||
|
|
693431312c | ||
|
|
f3fca2a19a | ||
|
|
1ce9efc644 | ||
|
|
7055501efd | ||
|
|
c790ee64a4 | ||
|
|
9fb05d5ea2 | ||
|
|
3742acaf39 | ||
|
|
d8282de229 | ||
|
|
3a721a9dd5 | ||
|
|
0b6092446b | ||
|
|
59a76c6d39 | ||
|
|
fa7a916ef3 | ||
|
|
69c4187100 | ||
|
|
07128719c4 | ||
|
|
7471fa627d | ||
|
|
d7af5924ff | ||
|
|
a48e9d3d80 | ||
|
|
474a6db00c | ||
|
|
cb6aea9aa9 | ||
|
|
f4290c6497 | ||
|
|
086b85b8f9 | ||
|
|
abee658a60 | ||
|
|
87bdc21667 | ||
|
|
3e25ee8181 | ||
|
|
0266c458a3 | ||
|
|
d9c7631dcb | ||
|
|
df064fa2ba | ||
|
|
318e0c8447 | ||
|
|
17d5503bf2 | ||
|
|
19b4da6401 | ||
|
|
886dc93f18 | ||
|
|
407df2822e | ||
|
|
f6da0d342a | ||
|
|
7271537a15 | ||
|
|
d0e8efac4d | ||
|
|
27e6d56d83 | ||
|
|
c5122c12cb | ||
|
|
282050c596 | ||
|
|
308c73bfa2 | ||
|
|
c40309ae8e | ||
|
|
1c7a4ecc7f | ||
|
|
80cc623eb2 | ||
|
|
89457fd991 | ||
|
|
257043b8fb | ||
|
|
657d94b992 | ||
|
|
06dc7d5964 | ||
|
|
b50079524b | ||
|
|
cdf1f269ff | ||
|
|
738b562f39 | ||
|
|
58499f256b | ||
|
|
afb455ef80 | ||
|
|
f4515e9034 | ||
|
|
aab07d00d7 | ||
|
|
32ecfc6a86 | ||
|
|
e866f9fae7 | ||
|
|
a029f6ed2c | ||
|
|
5b6e2cb5e0 | ||
|
|
0264847a37 | ||
|
|
d276ca0adc | ||
|
|
6a099ee50a | ||
|
|
be88846972 | ||
|
|
9a6b623c25 | ||
|
|
97b36b8c74 | ||
|
|
0df332e7b8 | ||
|
|
2d7e6fbb11 | ||
|
|
fd107ab47c | ||
|
|
1320d7efab | ||
|
|
7cdc6cfa6d | ||
|
|
095ee9683d | ||
|
|
1e2081a1c5 | ||
|
|
e167e07a9a | ||
|
|
f3b9cd8404 | ||
|
|
8f9744c07b | ||
|
|
684a219e39 | ||
|
|
e2e6d827c7 | ||
|
|
f0108ece6f | ||
|
|
789b7a5ff1 | ||
|
|
0961295a82 | ||
|
|
fd060eb9f1 | ||
|
|
1feeda4514 | ||
|
|
6d29de0f1e | ||
|
|
74162850f0 | ||
|
|
8903bd1e8a | ||
|
|
4b79a4a10d | ||
|
|
8f419a4869 | ||
|
|
fcdac50f79 | ||
|
|
4541134c88 | ||
|
|
b5e5972c2a | ||
|
|
525d59fdf6 | ||
|
|
00f65afe20 | ||
|
|
fc402f5804 | ||
|
|
f74b6d9a43 | ||
|
|
ccaf2333ca | ||
|
|
0066bf8eed | ||
|
|
bc8f88f5fa | ||
|
|
10c9d917ad | ||
|
|
cc5f752f2d | ||
|
|
39b1a51ff4 | ||
|
|
a2e2e46af3 | ||
|
|
9d53a7e0a6 | ||
|
|
7ea57eaa18 | ||
|
|
1657a425c1 | ||
|
|
e929e92245 | ||
|
|
5994e48b28 | ||
|
|
50662c9c9e | ||
|
|
addd614e94 | ||
|
|
fbb228050a | ||
|
|
9bda8e266e | ||
|
|
d977005126 | ||
|
|
8b579bc7d0 | ||
|
|
ab16a11aa3 | ||
|
|
85347e12de | ||
|
|
67afbbbaa6 | ||
|
|
b743ee21f0 | ||
|
|
72702a042e | ||
|
|
649c69fa9e | ||
|
|
9fd0b87925 | ||
|
|
1c7a001489 | ||
|
|
47edc33b0d | ||
|
|
2afad0c650 | ||
|
|
fb53a6a686 | ||
|
|
31b5b78dc1 | ||
|
|
2f941c7fb3 | ||
|
|
ba483b4032 | ||
|
|
977b0ceee4 | ||
|
|
fcc20334da | ||
|
|
83c063346d | ||
|
|
c2e29ad06f | ||
|
|
9194d40da7 | ||
|
|
002073ef57 | ||
|
|
ef3066a1bd | ||
|
|
65db5b94e4 | ||
|
|
3122038c48 | ||
|
|
54fd0efdfe | ||
|
|
f0d1e50d5a | ||
|
|
a87ea20b7c | ||
|
|
8e7e40d0cc | ||
|
|
de4d4f6609 | ||
|
|
e01d61484d | ||
|
|
51f83087f2 | ||
|
|
17012654e1 | ||
|
|
e998d125ab | ||
|
|
95601d3179 | ||
|
|
0566a5833b | ||
|
|
c50459b81a | ||
|
|
0e52cf76eb | ||
|
|
0baf725073 | ||
|
|
e77fd7ddb9 | ||
|
|
e5cb5dca61 | ||
|
|
7941b559c5 | ||
|
|
58d81dbc52 | ||
|
|
2b4dc4496f | ||
|
|
c5e9d0096a | ||
|
|
c6f9780b1b | ||
|
|
ef694923f7 | ||
|
|
8d02203bb6 | ||
|
|
1e75be3b5d | ||
|
|
7d13891066 | ||
|
|
4292bd7ad9 | ||
|
|
82cd110dbe | ||
|
|
d2729193c7 | ||
|
|
87fb325357 | ||
|
|
fb8ff7b892 | ||
|
|
5aeae7444f | ||
|
|
c9b11e9fbf | ||
|
|
0005efa508 | ||
|
|
6931cb9d65 | ||
|
|
c1bcf0f11a | ||
|
|
717e7ae8b2 | ||
|
|
bed00fb8e1 | ||
|
|
2010401b81 | ||
|
|
23e09e3b3c | ||
|
|
80c9d46b70 | ||
|
|
0aa107426c | ||
|
|
1517c77d9c | ||
|
|
51bf79bb8c | ||
|
|
d88eadff13 | ||
|
|
0d35e8e15e | ||
|
|
a0066a1ccf | ||
|
|
7a5082e8c4 | ||
|
|
dfc345756c | ||
|
|
137da6adf6 | ||
|
|
8563ec5a7a | ||
|
|
8ac4bee3bc | ||
|
|
579fb478b5 | ||
|
|
33c580ebbf | ||
|
|
ff389b3e97 | ||
|
|
50fe8e7852 | ||
|
|
cdb6d19989 | ||
|
|
29694e2945 | ||
|
|
9fe08d3913 | ||
|
|
c07a0d9005 | ||
|
|
cbd45478e0 | ||
|
|
6f243108e9 | ||
|
|
0dcdf7ae21 | ||
|
|
e7ef2bec8b | ||
|
|
4f4dce82c6 | ||
|
|
a619e7a88c | ||
|
|
102d960106 | ||
|
|
7de71b29a0 | ||
|
|
4eced8a393 | ||
|
|
710b9c35e5 | ||
|
|
f46984d218 | ||
|
|
44663a7e6e | ||
|
|
446124f378 | ||
|
|
c45f2085d5 | ||
|
|
3abf62b41a | ||
|
|
9b2982510e | ||
|
|
48bb1ebe01 | ||
|
|
fe4c6e4c56 | ||
|
|
37bd9ffc48 | ||
|
|
b0548b4944 | ||
|
|
12425556c1 | ||
|
|
3ed2da562b | ||
|
|
354f46d8c5 | ||
|
|
e70f0db83c | ||
|
|
49b8c56fde | ||
|
|
940fdd5573 | ||
|
|
ef53db201e | ||
|
|
aeb0b0728d | ||
|
|
8b2746abf1 | ||
|
|
01da060496 | ||
|
|
20cbd269c4 | ||
|
|
7c2cd43dfa | ||
|
|
de5feff720 | ||
|
|
7342de837d | ||
|
|
aa2c648918 | ||
|
|
e914b50d16 | ||
|
|
f79554f918 | ||
|
|
213cc9c4b5 | ||
|
|
05f3b8fa0f | ||
|
|
bc53624133 | ||
|
|
5944d034da | ||
|
|
df400feb61 | ||
|
|
48e10c57da | ||
|
|
1eb818b64c | ||
|
|
0954932091 | ||
|
|
e584d9cd2e | ||
|
|
4d140ea6bd | ||
|
|
09a2e50fc2 | ||
|
|
35d8ef33ef | ||
|
|
f1ff2af47a | ||
|
|
d2d2c31ec7 | ||
|
|
95102c1c6c | ||
|
|
fa8c16ae01 | ||
|
|
7ca2a8213d | ||
|
|
1c8e5046e5 | ||
|
|
68a5652733 | ||
|
|
6b4be30c64 | ||
|
|
6ce952ad4a | ||
|
|
5ae62dd9d7 | ||
|
|
51987107a2 | ||
|
|
e4a8c4813c | ||
|
|
3d708f7dc4 | ||
|
|
4b5bcb56bc | ||
|
|
10ec8c9558 | ||
|
|
3900f758ea | ||
|
|
a3346fbad8 | ||
|
|
f73d58d74e | ||
|
|
813d5e1ddf | ||
|
|
acb661df72 | ||
|
|
4bc7710de9 | ||
|
|
b8c1f4f138 | ||
|
|
387b67472c | ||
|
|
b2c099bac6 | ||
|
|
1acebb1cc4 | ||
|
|
8d89a8dcb0 | ||
|
|
a60a34331b | ||
|
|
749015eb86 | ||
|
|
4b45142e72 | ||
|
|
76009ce7b9 | ||
|
|
2722119776 | ||
|
|
c724585bce | ||
|
|
0b61e46f95 | ||
|
|
5c0da486b9 | ||
|
|
9701e5e6e4 | ||
|
|
1684091234 | ||
|
|
a93eb8b8f5 | ||
|
|
c591ab5e6f | ||
|
|
b3fbebed55 | ||
|
|
4621f88441 | ||
|
|
747edb3a99 | ||
|
|
558970e281 | ||
|
|
73fd20a608 | ||
|
|
78c2c33b9e | ||
|
|
610add1f59 | ||
|
|
655510ce28 | ||
|
|
f7414700f4 | ||
|
|
53a860a5bf | ||
|
|
1aae450303 | ||
|
|
b3a2f8837c | ||
|
|
33d2a93294 | ||
|
|
2da0db70de | ||
|
|
8b5cbf8066 | ||
|
|
9668563801 | ||
|
|
ff7067f3a3 | ||
|
|
6b8b152444 | ||
|
|
3dbe3b3e7f | ||
|
|
7aa4bff97b | ||
|
|
6002fac9f1 | ||
|
|
231c6ac862 | ||
|
|
c3eb7e1634 | ||
|
|
05a6ee2b49 | ||
|
|
94d76c042d | ||
|
|
23ccaa6191 | ||
|
|
476e9d0a48 | ||
|
|
7cd5ed6fc6 | ||
|
|
750c23f319 | ||
|
|
bb36df8b2e | ||
|
|
470c43a986 | ||
|
|
8ea78e8361 | ||
|
|
9c748d20d5 | ||
|
|
af15b22b03 | ||
|
|
80c9c705b8 | ||
|
|
138419d232 | ||
|
|
8cee15bc5a | ||
|
|
8113974b26 | ||
|
|
2b70cefd48 | ||
|
|
16054d4bfd | ||
|
|
c2d4a53b67 | ||
|
|
3448b1c78c |
93
.github/workflows/build.yml
vendored
93
.github/workflows/build.yml
vendored
@@ -11,6 +11,15 @@ jobs:
|
||||
os: [ubuntu-20.04, macos-10.15]
|
||||
compiler: [gcc, clang]
|
||||
buildtool: [autotools, cmake]
|
||||
http3: [http3, no-http3]
|
||||
openssl: [openssl1, openssl3]
|
||||
exclude:
|
||||
- os: macos-10.15
|
||||
openssl: openssl3
|
||||
- http3: no-http3
|
||||
openssl: openssl3
|
||||
- os: macos-10.15
|
||||
compiler: gcc
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -34,6 +43,7 @@ jobs:
|
||||
libjansson-dev \
|
||||
libjemalloc-dev \
|
||||
libc-ares-dev \
|
||||
libelf-dev \
|
||||
cmake \
|
||||
cmake-data
|
||||
echo 'CPPFLAGS=-fsanitize=address,undefined -fno-sanitize-recover=undefined -g' >> $GITHUB_ENV
|
||||
@@ -52,8 +62,6 @@ jobs:
|
||||
pkg-config \
|
||||
libtool
|
||||
echo 'PKG_CONFIG_PATH=/usr/local/opt/libressl/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig' >> $GITHUB_ENV
|
||||
# This fixes infamous 'stdio.h not found' error.
|
||||
echo 'SDKROOT='"$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV
|
||||
- name: Setup clang (Linux)
|
||||
if: runner.os == 'Linux' && matrix.compiler == 'clang'
|
||||
run: |
|
||||
@@ -74,26 +82,97 @@ jobs:
|
||||
run: |
|
||||
echo 'CC=gcc' >> $GITHUB_ENV
|
||||
echo 'CXX=g++' >> $GITHUB_ENV
|
||||
- name: Build libbpf
|
||||
if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
|
||||
run: |
|
||||
git clone -b v0.4.0 https://github.com/libbpf/libbpf
|
||||
cd libbpf
|
||||
PREFIX=$PWD/build make -C src install
|
||||
|
||||
EXTRA_AUTOTOOLS_OPTS="--with-libbpf"
|
||||
EXTRA_CMAKE_OPTS="-DWITH_LIBBPF=1"
|
||||
|
||||
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
|
||||
echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
|
||||
- name: Build quictls/openssl v1.1.1
|
||||
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl1'
|
||||
run: |
|
||||
git clone --depth 1 -b OpenSSL_1_1_1m+quic https://github.com/quictls/openssl
|
||||
cd openssl
|
||||
./config enable-tls1_3 --prefix=$PWD/build
|
||||
make -j$(nproc)
|
||||
make install_sw
|
||||
- name: Build quictls/openssl v3.0.x
|
||||
if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3'
|
||||
run: |
|
||||
unset CPPFLAGS
|
||||
unset LDFLAGS
|
||||
|
||||
git clone --depth 1 -b openssl-3.0.1+quic https://github.com/quictls/openssl
|
||||
cd openssl
|
||||
./config enable-tls1_3 --prefix=$PWD/build --libdir=$PWD/build/lib
|
||||
make -j$(nproc)
|
||||
make install_sw
|
||||
- name: Build nghttp3
|
||||
if: matrix.http3 == 'http3'
|
||||
run: |
|
||||
git clone https://github.com/ngtcp2/nghttp3
|
||||
cd nghttp3
|
||||
git checkout 74a222fe0c89b7202bcdaf6ef27a232edffc85e3
|
||||
autoreconf -i
|
||||
./configure --prefix=$PWD/build --enable-lib-only
|
||||
make -j$(nproc) check
|
||||
make install
|
||||
- name: Build ngtcp2
|
||||
if: matrix.http3 == 'http3'
|
||||
run: |
|
||||
git clone --depth 1 -b v0.1.0 https://github.com/ngtcp2/ngtcp2
|
||||
cd ngtcp2
|
||||
autoreconf -i
|
||||
./configure --prefix=$PWD/build --enable-lib-only PKG_CONFIG_PATH="../openssl/build/lib/pkgconfig"
|
||||
make -j$(nproc) check
|
||||
make install
|
||||
- name: Setup extra environment variables for HTTP/3
|
||||
if: matrix.http3 == 'http3'
|
||||
run: |
|
||||
PKG_CONFIG_PATH="$PWD/openssl/build/lib/pkgconfig:$PWD/nghttp3/build/lib/pkgconfig:$PWD/ngtcp2/build/lib/pkgconfig:$PWD/libbpf/build/lib64/pkgconfig:$PKG_CONFIG_PATH"
|
||||
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/openssl/build/lib -Wl,-rpath,$PWD/libbpf/build/lib64"
|
||||
EXTRA_AUTOTOOLS_OPTS="--enable-http3 $EXTRA_AUTOTOOLS_OPTS"
|
||||
EXTRA_CMAKE_OPTS="-DENABLE_HTTP3=1 $EXTRA_CMAKE_OPTS"
|
||||
|
||||
echo 'PKG_CONFIG_PATH='"$PKG_CONFIG_PATH" >> $GITHUB_ENV
|
||||
echo 'LDFLAGS='"$LDFLAGS" >> $GITHUB_ENV
|
||||
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
|
||||
echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
|
||||
- name: Setup git submodules
|
||||
run: |
|
||||
git submodule update --init
|
||||
- name: Configure autotools
|
||||
if: matrix.buildtool == 'autotools'
|
||||
run: |
|
||||
autoreconf -i
|
||||
./configure --enable-werror --with-mruby
|
||||
./configure
|
||||
- name: Configure cmake
|
||||
if: matrix.buildtool == 'cmake'
|
||||
run: |
|
||||
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DCPPFLAGS="$CPPFLAGS" -DLDFLAGS="$LDFLAGS" .
|
||||
make dist
|
||||
VERSION=$(grep PACKAGE_VERSION config.h | cut -d' ' -f3 | tr -d '"')
|
||||
tar xf nghttp2-$VERSION.tar.gz
|
||||
cd nghttp2-$VERSION
|
||||
echo 'NGHTTP2_CMAKE_DIR='"$PWD" >> $GITHUB_ENV
|
||||
|
||||
# This fixes infamous 'stdio.h not found' error.
|
||||
echo 'SDKROOT='"$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV
|
||||
|
||||
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1 -DENABLE_APP=1 $EXTRA_CMAKE_OPTS -DCPPFLAGS="$CPPFLAGS" -DLDFLAGS="$LDFLAGS" .
|
||||
- name: Build nghttp2 with autotools
|
||||
if: matrix.buildtool == 'autotools'
|
||||
run: |
|
||||
make distcheck \
|
||||
DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=\"$CPPFLAGS\" LDFLAGS=\"$LDFLAGS\""
|
||||
DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --with-libev --enable-werror $EXTRA_AUTOTOOLS_OPTS CPPFLAGS=\"$CPPFLAGS\" LDFLAGS=\"$LDFLAGS\""
|
||||
- name: Build nghttp2 with cmake
|
||||
if: matrix.buildtool == 'cmake'
|
||||
run: |
|
||||
cd $NGHTTP2_CMAKE_DIR
|
||||
make
|
||||
make check
|
||||
- name: Integration test
|
||||
@@ -101,5 +180,5 @@ jobs:
|
||||
# artifacts.
|
||||
if: matrix.buildtool == 'cmake'
|
||||
run: |
|
||||
cd integration-tests
|
||||
cd $NGHTTP2_CMAKE_DIR/integration-tests
|
||||
make itprep it
|
||||
|
||||
89
.travis.yml
89
.travis.yml
@@ -1,89 +0,0 @@
|
||||
dist: xenial
|
||||
os:
|
||||
- linux
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
env:
|
||||
matrix:
|
||||
- CI_BUILD=cmake
|
||||
- CI_BUILD=autotools
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
compiler: clang
|
||||
osx_image: xcode10.2
|
||||
env: CI_BUILD=autotools
|
||||
language: cpp
|
||||
sudo: required
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-8
|
||||
- autoconf
|
||||
- automake
|
||||
- autotools-dev
|
||||
- libtool
|
||||
- pkg-config
|
||||
- zlib1g-dev
|
||||
- libcunit1-dev
|
||||
- libssl-dev
|
||||
- libxml2-dev
|
||||
- libev-dev
|
||||
- libevent-dev
|
||||
- libjansson-dev
|
||||
- libjemalloc-dev
|
||||
- libc-ares-dev
|
||||
- cmake
|
||||
- cmake-data
|
||||
homebrew:
|
||||
packages:
|
||||
- libev
|
||||
- libevent
|
||||
- c-ares
|
||||
- cunit
|
||||
- libressl
|
||||
before_install:
|
||||
- $CC --version
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-8" CC="gcc-8"; fi
|
||||
- $CC --version
|
||||
- go version
|
||||
- cmake --version
|
||||
before_script:
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
CPPFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address -fuse-ld=gold"
|
||||
fi
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
PKG_CONFIG_PATH="/usr/local/opt/libressl/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig"
|
||||
fi
|
||||
# Now build nghttp2
|
||||
- git submodule update --init
|
||||
- |
|
||||
if [ "$CI_BUILD" = "autotools" ]; then
|
||||
autoreconf -i && ./configure --with-mruby PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
fi
|
||||
- |
|
||||
if [ "$CI_BUILD" = "cmake" ]; then
|
||||
cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1
|
||||
fi
|
||||
script:
|
||||
- |
|
||||
if [ "$CI_BUILD" = "autotools" ]; then
|
||||
make distcheck DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=$CPPFLAGS LDFLAGS=\"$LDFLAGS\" PKG_CONFIG_PATH=$PKG_CONFIG_PATH"
|
||||
fi
|
||||
- |
|
||||
if [ "$CI_BUILD" = "cmake" ]; then
|
||||
make && make check
|
||||
fi
|
||||
- |
|
||||
if [ "$CI_BUILD" = "cmake" ]; then
|
||||
# Integration tests for nghttpx; autotools build erases build
|
||||
# for packaging test.
|
||||
cd integration-tests
|
||||
export GO111MODULE=on
|
||||
make it
|
||||
fi
|
||||
8
AUTHORS
8
AUTHORS
@@ -19,6 +19,7 @@ Alek Storm
|
||||
Alex Nalivko
|
||||
Alexandros Konstantinakis-Karmis
|
||||
Alexis La Goutte
|
||||
Amir Livneh
|
||||
Amir Pakdel
|
||||
Anders Bakken
|
||||
Andreas Pohl
|
||||
@@ -34,11 +35,13 @@ Bernard Spil
|
||||
Brendan Heinonen
|
||||
Brian Card
|
||||
Brian Suh
|
||||
Daniel Bevenius
|
||||
Daniel Evers
|
||||
Daniel Stenberg
|
||||
Dave Reisner
|
||||
David Beitey
|
||||
David Weekly
|
||||
Dmitri Tikhonov
|
||||
Dmitriy Vetutnev
|
||||
Don
|
||||
Dylan Plecki
|
||||
@@ -48,9 +51,12 @@ Fabian Wiesel
|
||||
Gabi Davar
|
||||
Gaël PORTAY
|
||||
Geoff Hill
|
||||
George Liu
|
||||
Gitai
|
||||
Google Inc.
|
||||
Hajime Fujita
|
||||
Jacky Tian
|
||||
Jacky_Yin
|
||||
Jacob Champion
|
||||
James M Snell
|
||||
Jan Kundrát
|
||||
@@ -76,6 +82,7 @@ MATSUMOTO Ryosuke
|
||||
Marc Bachmann
|
||||
Matt Rudary
|
||||
Matt Way
|
||||
Michael Kaufmann
|
||||
Mike Conlen
|
||||
Mike Frysinger
|
||||
Mike Lothian
|
||||
@@ -127,6 +134,7 @@ es
|
||||
fangdingjun
|
||||
jwchoi
|
||||
kumagi
|
||||
lhuang04
|
||||
lstefani
|
||||
makovich
|
||||
mod-h2-dev
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
# XXX using 1.8.90 instead of 1.9.0-DEV
|
||||
project(nghttp2 VERSION 1.44.90)
|
||||
project(nghttp2 VERSION 1.46.90)
|
||||
|
||||
# See versioning rule:
|
||||
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
set(LT_CURRENT 34)
|
||||
set(LT_REVISION 2)
|
||||
set(LT_AGE 20)
|
||||
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
set(LT_CURRENT 35)
|
||||
set(LT_REVISION 1)
|
||||
set(LT_AGE 21)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
include(Version)
|
||||
@@ -61,6 +61,18 @@ find_package(OpenSSL 1.0.1)
|
||||
find_package(Libev 4.11)
|
||||
find_package(Libcares 1.7.5)
|
||||
find_package(ZLIB 1.2.3)
|
||||
find_package(Libngtcp2 0.0.0)
|
||||
find_package(Libngtcp2_crypto_openssl 0.0.0)
|
||||
if(LIBNGTCP2_CRYPTO_OPENSSL_FOUND)
|
||||
set(HAVE_LIBNGTCP2_CRYPTO_OPENSSL 1)
|
||||
endif()
|
||||
find_package(Libnghttp3 0.0.0)
|
||||
if(WITH_LIBBPF)
|
||||
find_package(Libbpf 0.4.0)
|
||||
if(NOT LIBBPF_FOUND)
|
||||
message(FATAL_ERROR "libbpf was requested (WITH_LIBBPF=1) but not found.")
|
||||
endif()
|
||||
endif()
|
||||
if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
|
||||
set(ENABLE_APP_DEFAULT ON)
|
||||
else()
|
||||
@@ -167,7 +179,7 @@ endif()
|
||||
# case "$host" in
|
||||
# *android*)
|
||||
# android_build=yes
|
||||
# # android does not need -pthread, but needs followng 3 libs for C++
|
||||
# # android does not need -pthread, but needs following 3 libs for C++
|
||||
# APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
|
||||
|
||||
# dl: openssl requires libdl when it is statically linked.
|
||||
@@ -182,9 +194,18 @@ if(HAVE_CUNIT)
|
||||
endif()
|
||||
|
||||
# openssl (for src)
|
||||
include(CheckSymbolExists)
|
||||
set(HAVE_OPENSSL ${OPENSSL_FOUND})
|
||||
if(OPENSSL_FOUND)
|
||||
set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
|
||||
cmake_push_check_state()
|
||||
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}")
|
||||
check_symbol_exists(SSL_is_quic "openssl/ssl.h" HAVE_SSL_IS_QUIC)
|
||||
if(NOT HAVE_SSL_IS_QUIC)
|
||||
message(WARNING "OpenSSL in ${OPENSSL_LIBRARIES} dose not have SSL_is_quic. HTTP/3 support cannot be enabled")
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
else()
|
||||
set(OPENSSL_INCLUDE_DIRS "")
|
||||
set(OPENSSL_LIBRARIES "")
|
||||
@@ -223,11 +244,31 @@ if(ENABLE_ASIO_LIB)
|
||||
find_package(Boost 1.54.0 REQUIRED system thread)
|
||||
endif()
|
||||
|
||||
# libbpf (for bpf)
|
||||
set(HAVE_LIBBPF ${LIBBPF_FOUND})
|
||||
if(LIBBPF_FOUND)
|
||||
set(BPFCFLAGS -Wall -O2 -g)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# For Debian/Ubuntu
|
||||
set(EXTRABPFCFLAGS -I/usr/include/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu)
|
||||
endif()
|
||||
|
||||
check_c_source_compiles("
|
||||
#include <linux/bpf.h>
|
||||
int main() { enum bpf_stats_type foo; (void)foo; }" HAVE_BPF_STATS_TYPE)
|
||||
endif()
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL and libev
|
||||
if(ENABLE_APP AND NOT (ZLIB_FOUND AND OPENSSL_FOUND AND LIBEV_FOUND))
|
||||
message(FATAL_ERROR "Applications were requested (ENABLE_APP=1) but dependencies are not met.")
|
||||
endif()
|
||||
|
||||
# HTTP/3 requires quictls/openssl, libngtcp2, libngtcp2_crypto_openssl
|
||||
# and libnghttp3.
|
||||
if(ENABLE_HTTP3 AND NOT (HAVE_SSL_IS_QUIC AND LIBNGTCP2_FOUND AND LIBNGTCP2_CRYPTO_OPENSSL_FOUND AND LIBNGHTTP3_FOUND))
|
||||
message(FATAL_ERROR "HTTP/3 was requested (ENABLE_HTTP3=1) but dependencies are not met.")
|
||||
endif()
|
||||
|
||||
# HPACK tools requires jansson
|
||||
if(ENABLE_HPACK_TOOLS AND NOT HAVE_JANSSON)
|
||||
message(FATAL_ERROR "HPACK tools were requested (ENABLE_HPACK_TOOLS=1) but dependencies are not met.")
|
||||
@@ -448,11 +489,16 @@ foreach(name
|
||||
configure_file("${name}.in" "${name}" @ONLY)
|
||||
endforeach()
|
||||
|
||||
if(APPLE)
|
||||
add_definitions(-D__APPLE_USE_RFC_3542)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}" # for config.h
|
||||
)
|
||||
# For use in src/CMakeLists.txt
|
||||
set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${CMAKE_PROJECT_NAME}")
|
||||
set(PKGLIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_PROJECT_NAME}")
|
||||
|
||||
install(FILES README.rst DESTINATION "${CMAKE_INSTALL_DOCDIR}")
|
||||
|
||||
@@ -469,6 +515,7 @@ add_subdirectory(integration-tests)
|
||||
add_subdirectory(doc)
|
||||
add_subdirectory(contrib)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(bpf)
|
||||
|
||||
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
|
||||
@@ -499,6 +546,10 @@ message(STATUS "summary of build options:
|
||||
Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}')
|
||||
Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
|
||||
Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
|
||||
Libngtcp2: ${HAVE_LIBNGTCP2} (LIBS='${LIBNGTCP2_LIBRARIES}')
|
||||
Libngtcp2_crypto_openssl: ${HAVE_LIBNGTCP2_CRYPTO_OPENSSL} (LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}')
|
||||
Libnghttp3: ${HAVE_LIBNGHTTP3} (LIBS='${LIBNGHTTP3_LIBRARIES}')
|
||||
Libbpf: ${HAVE_LIBBPF} (LIBS='${LIBBPF_LIBRARIES}')
|
||||
Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')
|
||||
Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')
|
||||
Jemalloc: ${HAVE_JEMALLOC} (LIBS='${JEMALLOC_LIBRARIES}')
|
||||
@@ -517,6 +568,7 @@ message(STATUS "summary of build options:
|
||||
Examples: ${ENABLE_EXAMPLES}
|
||||
Python bindings:${ENABLE_PYTHON_BINDINGS}
|
||||
Threading: ${ENABLE_THREADS}
|
||||
HTTP/3(EXPERIMENTAL): ${ENABLE_HTTP3}
|
||||
")
|
||||
if(ENABLE_LIB_ONLY_DISABLED_OTHERS)
|
||||
message("Only the library will be built. To build other components "
|
||||
|
||||
@@ -17,14 +17,14 @@ option(ENABLE_LIB_ONLY "Build libnghttp2 only. This is a short hand for -DENAB
|
||||
option(ENABLE_STATIC_LIB "Build libnghttp2 in static mode also")
|
||||
option(ENABLE_SHARED_LIB "Build libnghttp2 as a shared library" ON)
|
||||
option(ENABLE_STATIC_CRT "Build libnghttp2 against the MS LIBCMT[d]")
|
||||
option(ENABLE_HTTP3 "Enable HTTP/3 support" OFF)
|
||||
|
||||
option(WITH_LIBXML2 "Use libxml2"
|
||||
${WITH_LIBXML2_DEFAULT})
|
||||
option(WITH_JEMALLOC "Use jemalloc"
|
||||
${WITH_JEMALLOC_DEFAULT})
|
||||
option(WITH_SPDYLAY "Use spdylay"
|
||||
${WITH_SPDYLAY_DEFAULT})
|
||||
option(WITH_MRUBY "Use mruby")
|
||||
option(WITH_NEVERBLEED "Use neverbleed")
|
||||
option(WITH_LIBBPF "Use libbpf")
|
||||
|
||||
# vim: ft=cmake:
|
||||
|
||||
12
Makefile.am
12
Makefile.am
@@ -20,7 +20,7 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||
SUBDIRS = lib third-party src bpf examples python tests integration-tests \
|
||||
doc contrib script
|
||||
|
||||
# Now with python setuptools, make uninstall will leave many files we
|
||||
@@ -46,16 +46,20 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
||||
cmake/FindLibevent.cmake \
|
||||
cmake/FindJansson.cmake \
|
||||
cmake/FindLibcares.cmake \
|
||||
cmake/FindSystemd.cmake
|
||||
cmake/FindSystemd.cmake \
|
||||
cmake/FindLibbpf.cmake \
|
||||
cmake/FindLibnghttp3.cmake \
|
||||
cmake/FindLibngtcp2.cmake \
|
||||
cmake/FindLibngtcp2_crypto_openssl.cmake
|
||||
|
||||
.PHONY: clang-format
|
||||
|
||||
# Format source files using clang-format. Don't format source files
|
||||
# under third-party directory since we are not responsible for thier
|
||||
# under third-party directory since we are not responsible for their
|
||||
# coding style.
|
||||
clang-format:
|
||||
CLANGFORMAT=`git config --get clangformat.binary`; \
|
||||
test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
|
||||
$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \
|
||||
src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \
|
||||
tests/*.{c,h}
|
||||
tests/*.{c,h} bpf/*.c
|
||||
|
||||
154
README.rst
154
README.rst
@@ -32,12 +32,14 @@ Public Test Server
|
||||
The following endpoints are available to try out our nghttp2
|
||||
implementation.
|
||||
|
||||
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
||||
* https://nghttp2.org/ (TLS + ALPN/NPN and HTTP/3)
|
||||
|
||||
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, and
|
||||
``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||
connection.
|
||||
|
||||
It also supports HTTP/3.
|
||||
|
||||
* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
|
||||
|
||||
``h2c`` and ``http/1.1``.
|
||||
@@ -145,6 +147,33 @@ minimizes the risk of private key leakage when serious bug like
|
||||
Heartbleed is exploited. The neverbleed is disabled by default. To
|
||||
enable it, use ``--with-neverbleed`` configure option.
|
||||
|
||||
To enable the experimental HTTP/3 support for h2load and nghttpx, the
|
||||
following libraries are required:
|
||||
|
||||
* `OpenSSL with QUIC support
|
||||
<https://github.com/quictls/openssl/tree/OpenSSL_1_1_1m+quic>`_; or
|
||||
`BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit
|
||||
f6ef1c560ae5af51e2df5d8d2175bed207b28b8f)
|
||||
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_
|
||||
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_
|
||||
|
||||
Use ``--enable-http3`` configure option to enable HTTP/3 feature for
|
||||
h2load and nghttpx.
|
||||
|
||||
In order to build optional eBPF program to direct an incoming QUIC UDP
|
||||
datagram to a correct socket for nghttpx, the following libraries are
|
||||
required:
|
||||
|
||||
* libbpf-dev >= 0.4.0
|
||||
|
||||
Use ``--with-libbpf`` configure option to build eBPF program.
|
||||
libelf-dev is needed to build libbpf.
|
||||
|
||||
For Ubuntu 20.04, you can build libbpf from `the source code
|
||||
<https://github.com/libbpf/libbpf/releases/tag/v0.4.0>`_. nghttpx
|
||||
requires eBPF program for reloading its configuration and hot swapping
|
||||
its executable.
|
||||
|
||||
Compiling libnghttp2 C source code requires a C99 compiler. gcc 4.8
|
||||
is known to be adequate. In order to compile the C++ source code, gcc
|
||||
>= 6.0 or clang >= 6.0 is required. C++ source code requires C++14
|
||||
@@ -307,6 +336,89 @@ The generated documents will not be installed with ``make install``.
|
||||
The online documentation is available at
|
||||
https://nghttp2.org/documentation/
|
||||
|
||||
Build HTTP/3 enabled h2load and nghttpx
|
||||
---------------------------------------
|
||||
|
||||
To build h2load and nghttpx with HTTP/3 feature enabled, run the
|
||||
configure script with ``--enable-http3``.
|
||||
|
||||
For nghttpx to reload configurations and swapping its executable while
|
||||
gracefully terminating old worker processes, eBPF is required. Run
|
||||
the configure script with ``--enable-http3 --with-libbpf`` to build
|
||||
eBPF program. The QUIC keying material must be set with
|
||||
``--frontend-quic-secret-file`` in order to keep the existing
|
||||
connections alive during reload.
|
||||
|
||||
The detailed steps to build HTTP/3 enabled h2load and nghttpx follow.
|
||||
|
||||
Build custom OpenSSL:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b OpenSSL_1_1_1m+quic https://github.com/quictls/openssl
|
||||
$ cd openssl
|
||||
$ ./config --prefix=$PWD/build --openssldir=/etc/ssl
|
||||
$ make -j$(nproc)
|
||||
$ make install_sw
|
||||
$ cd ..
|
||||
|
||||
Build nghttp3:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone https://github.com/ngtcp2/nghttp3
|
||||
$ cd nghttp3
|
||||
$ git checkout 74a222fe0c89b7202bcdaf6ef27a232edffc85e3
|
||||
$ autoreconf -i
|
||||
$ ./configure --prefix=$PWD/build --enable-lib-only
|
||||
$ make -j$(nproc)
|
||||
$ make install
|
||||
$ cd ..
|
||||
|
||||
Build ngtcp2:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b v0.1.0 https://github.com/ngtcp2/ngtcp2
|
||||
$ cd ngtcp2
|
||||
$ autoreconf -i
|
||||
$ ./configure --prefix=$PWD/build --enable-lib-only \
|
||||
PKG_CONFIG_PATH="$PWD/../openssl/build/lib/pkgconfig"
|
||||
$ make -j$(nproc)
|
||||
$ make install
|
||||
$ cd ..
|
||||
|
||||
If your Linux distribution does not have libbpf-dev >= 0.4.0, build
|
||||
from source:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone --depth 1 -b v0.4.0 https://github.com/libbpf/libbpf
|
||||
$ cd libbpf
|
||||
$ PREFIX=$PWD/build make -C src install
|
||||
$ cd ..
|
||||
|
||||
Build nghttp2:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone https://github.com/nghttp2/nghttp2
|
||||
$ cd nghttp2
|
||||
$ git submodule update --init
|
||||
$ autoreconf -i
|
||||
$ ./configure --with-mruby --with-neverbleed --enable-http3 --with-libbpf \
|
||||
--disable-python-bindings \
|
||||
CC=clang-12 CXX=clang++-12 \
|
||||
PKG_CONFIG_PATH="$PWD/../openssl/build/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig:$PWD/../ngtcp2/build/lib/pkgconfig:$PWD/../libbpf/build/lib64/pkgconfig" \
|
||||
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/../openssl/build/lib -Wl,-rpath,$PWD/../libbpf/build/lib64"
|
||||
$ make -j$(nproc)
|
||||
|
||||
The eBPF program ``reuseport_kern.o`` should be found under bpf
|
||||
directory. Pass ``--quic-bpf-program-file=bpf/reuseport_kern.o``
|
||||
option to nghttpx to load it. See also `HTTP/3 section in nghttpx -
|
||||
HTTP/2 proxy - HOW-TO
|
||||
<https://nghttp2.org/documentation/nghttpx-howto.html#http-3>`_.
|
||||
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
@@ -734,7 +846,7 @@ information. Here is sample output from ``nghttpd``:
|
||||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, and
|
||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/3, HTTP/2, and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
|
||||
push.
|
||||
|
||||
@@ -755,16 +867,16 @@ ticket keys among multiple ``nghttpx`` instances via memcached.
|
||||
|
||||
``nghttpx`` has 2 operation modes:
|
||||
|
||||
================== ================ ================ =============
|
||||
Mode option Frontend Backend Note
|
||||
================== ================ ================ =============
|
||||
default mode HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
|
||||
``--http2-proxy`` HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
|
||||
================== ================ ================ =============
|
||||
================== ======================== ================ =============
|
||||
Mode option Frontend Backend Note
|
||||
================== ======================== ================ =============
|
||||
default mode HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
|
||||
``--http2-proxy`` HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
|
||||
================== ======================== ================ =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens for HTTP/2, and HTTP/1.1 and can be
|
||||
deployed as a SSL/TLS terminator for existing web server.
|
||||
a reverse proxy and listens for HTTP/3, HTTP/2, and HTTP/1.1 and can
|
||||
be deployed as a SSL/TLS terminator for existing web server.
|
||||
|
||||
In all modes, the frontend connections are encrypted by SSL/TLS by
|
||||
default. To disable encryption, use the ``no-tls`` keyword in
|
||||
@@ -782,16 +894,16 @@ server:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
||||
[reverse proxy]
|
||||
Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
||||
[reverse proxy]
|
||||
|
||||
With the ``--http2-proxy`` option, it works as forward proxy, and it
|
||||
is so called secure HTTP/2 proxy:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid, ATS)
|
||||
Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid, ATS)
|
||||
|
||||
The ``Client`` in the above example needs to be configured to use
|
||||
``nghttpx`` as secure proxy.
|
||||
@@ -823,7 +935,7 @@ proxy through an HTTP proxy:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
||||
Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
||||
|
||||
--===================---> HTTP/2 Proxy
|
||||
(HTTP proxy tunnel) (e.g., nghttpx -s)
|
||||
@@ -831,8 +943,8 @@ proxy through an HTTP proxy:
|
||||
Benchmarking tool
|
||||
-----------------
|
||||
|
||||
The ``h2load`` program is a benchmarking tool for HTTP/2. The UI of
|
||||
``h2load`` is heavily inspired by ``weighttp``
|
||||
The ``h2load`` program is a benchmarking tool for HTTP/3, HTTP/2, and
|
||||
HTTP/1.1. The UI of ``h2load`` is heavily inspired by ``weighttp``
|
||||
(https://github.com/lighttpd/weighttp). The typical usage is as
|
||||
follows:
|
||||
|
||||
@@ -875,6 +987,14 @@ threads to avoid saturating a single core on client side.
|
||||
considered a DOS attack. Please only use it against your private
|
||||
servers.
|
||||
|
||||
If the experimental HTTP/3 is enabled, h2load can send requests to
|
||||
HTTP/3 server. To do this, specify ``h3`` to ``--npn-list`` option
|
||||
like so:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ h2load --npn-list h3 https://127.0.0.1:4433
|
||||
|
||||
HPACK tools
|
||||
-----------
|
||||
|
||||
|
||||
13
bpf/CMakeLists.txt
Normal file
13
bpf/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
if(LIBBPF_FOUND)
|
||||
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
COMMAND ${CMAKE_C_COMPILER} ${BPFCFLAGS} ${EXTRABPFCFLAGS} -I${LIBBPF_INCLUDE_DIRS} -target bpf -c reuseport_kern.c -o "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(bpf ALL
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
VERBATIM)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/${CMAKE_PROJECT_NAME}")
|
||||
endif()
|
||||
40
bpf/Makefile.am
Normal file
40
bpf/Makefile.am
Normal file
@@ -0,0 +1,40 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2021 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = CMakeLists.txt reuseport_kern.c
|
||||
|
||||
if HAVE_LIBBPF
|
||||
|
||||
bpf_pkglibdir = $(pkglibdir)
|
||||
bpf_pkglib_DATA = reuseport_kern.o
|
||||
|
||||
all: $(builddir)/reuseport_kern.o
|
||||
|
||||
$(builddir)/reuseport_kern.o: reuseport_kern.c
|
||||
$(CC) @LIBBPF_CFLAGS@ @BPFCFLAGS@ @EXTRABPFCFLAGS@ \
|
||||
-target bpf -c $< -o $@
|
||||
|
||||
clean-local:
|
||||
-rm -f reuseport_kern.o
|
||||
|
||||
endif # HAVE_LIBBPF
|
||||
663
bpf/reuseport_kern.c
Normal file
663
bpf/reuseport_kern.c
Normal file
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2021 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <linux/udp.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
/*
|
||||
* How to compile:
|
||||
*
|
||||
* clang-12 -O2 -Wall -target bpf -g -c reuseport_kern.c -o reuseport_kern.o \
|
||||
* -I/path/to/kernel/include
|
||||
*
|
||||
* See
|
||||
* https://www.kernel.org/doc/Documentation/kbuild/headers_install.txt
|
||||
* how to install kernel header files.
|
||||
*/
|
||||
|
||||
/* AES_CBC_decrypt_buffer: https://github.com/kokke/tiny-AES-c
|
||||
License is Public Domain. Commit hash:
|
||||
12e7744b4919e9d55de75b7ab566326a1c8e7a67 */
|
||||
|
||||
#define AES_BLOCKLEN \
|
||||
16 /* Block length in bytes - AES is 128b block \
|
||||
only */
|
||||
|
||||
#define AES_KEYLEN 16 /* Key length in bytes */
|
||||
#define AES_keyExpSize 176
|
||||
|
||||
struct AES_ctx {
|
||||
__u8 RoundKey[AES_keyExpSize];
|
||||
};
|
||||
|
||||
/* The number of columns comprising a state in AES. This is a constant
|
||||
in AES. Value=4 */
|
||||
#define Nb 4
|
||||
|
||||
#define Nk 4 /* The number of 32 bit words in a key. */
|
||||
#define Nr 10 /* The number of rounds in AES Cipher. */
|
||||
|
||||
/* state - array holding the intermediate results during
|
||||
decryption. */
|
||||
typedef __u8 state_t[4][4];
|
||||
|
||||
/* The lookup-tables are marked const so they can be placed in
|
||||
read-only storage instead of RAM The numbers below can be computed
|
||||
dynamically trading ROM for RAM - This can be useful in (embedded)
|
||||
bootloader applications, where ROM is often limited. */
|
||||
static const __u8 sbox[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
|
||||
0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
|
||||
0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
|
||||
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
|
||||
0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
|
||||
0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
|
||||
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
|
||||
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
|
||||
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
|
||||
0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
|
||||
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
|
||||
0xb0, 0x54, 0xbb, 0x16};
|
||||
|
||||
static const __u8 rsbox[256] = {
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
|
||||
0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
|
||||
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
|
||||
0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
|
||||
0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
|
||||
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
|
||||
0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
|
||||
0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
|
||||
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
|
||||
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
|
||||
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
|
||||
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
|
||||
0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
|
||||
0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
|
||||
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
|
||||
0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
|
||||
0x55, 0x21, 0x0c, 0x7d};
|
||||
|
||||
/* The round constant word array, Rcon[i], contains the values given
|
||||
by x to the power (i-1) being powers of x (x is denoted as {02}) in
|
||||
the field GF(2^8) */
|
||||
static const __u8 Rcon[11] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
|
||||
0x20, 0x40, 0x80, 0x1b, 0x36};
|
||||
|
||||
#define getSBoxValue(num) (sbox[(num)])
|
||||
|
||||
/* This function produces Nb(Nr+1) round keys. The round keys are used
|
||||
in each round to decrypt the states. */
|
||||
static void KeyExpansion(__u8 *RoundKey, const __u8 *Key) {
|
||||
unsigned i, j, k;
|
||||
__u8 tempa[4]; /* Used for the column/row operations */
|
||||
|
||||
/* The first round key is the key itself. */
|
||||
for (i = 0; i < Nk; ++i) {
|
||||
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
|
||||
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
|
||||
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
|
||||
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
|
||||
}
|
||||
|
||||
/* All other round keys are found from the previous round keys. */
|
||||
for (i = Nk; i < Nb * (Nr + 1); ++i) {
|
||||
{
|
||||
k = (i - 1) * 4;
|
||||
tempa[0] = RoundKey[k + 0];
|
||||
tempa[1] = RoundKey[k + 1];
|
||||
tempa[2] = RoundKey[k + 2];
|
||||
tempa[3] = RoundKey[k + 3];
|
||||
}
|
||||
|
||||
if (i % Nk == 0) {
|
||||
/* This function shifts the 4 bytes in a word to the left once.
|
||||
[a0,a1,a2,a3] becomes [a1,a2,a3,a0] */
|
||||
|
||||
/* Function RotWord() */
|
||||
{
|
||||
const __u8 u8tmp = tempa[0];
|
||||
tempa[0] = tempa[1];
|
||||
tempa[1] = tempa[2];
|
||||
tempa[2] = tempa[3];
|
||||
tempa[3] = u8tmp;
|
||||
}
|
||||
|
||||
/* SubWord() is a function that takes a four-byte input word and
|
||||
applies the S-box to each of the four bytes to produce an
|
||||
output word. */
|
||||
|
||||
/* Function Subword() */
|
||||
{
|
||||
tempa[0] = getSBoxValue(tempa[0]);
|
||||
tempa[1] = getSBoxValue(tempa[1]);
|
||||
tempa[2] = getSBoxValue(tempa[2]);
|
||||
tempa[3] = getSBoxValue(tempa[3]);
|
||||
}
|
||||
|
||||
tempa[0] = tempa[0] ^ Rcon[i / Nk];
|
||||
}
|
||||
j = i * 4;
|
||||
k = (i - Nk) * 4;
|
||||
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
|
||||
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
|
||||
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
|
||||
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void AES_init_ctx(struct AES_ctx *ctx, const __u8 *key) {
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
}
|
||||
|
||||
/* This function adds the round key to state. The round key is added
|
||||
to the state by an XOR function. */
|
||||
static void AddRoundKey(__u8 round, state_t *state, const __u8 *RoundKey) {
|
||||
__u8 i, j;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static __u8 xtime(__u8 x) { return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); }
|
||||
|
||||
#define Multiply(x, y) \
|
||||
(((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ \
|
||||
((y >> 2 & 1) * xtime(xtime(x))) ^ \
|
||||
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ \
|
||||
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))))
|
||||
|
||||
#define getSBoxInvert(num) (rsbox[(num)])
|
||||
|
||||
/* MixColumns function mixes the columns of the state matrix. The
|
||||
method used to multiply may be difficult to understand for the
|
||||
inexperienced. Please use the references to gain more
|
||||
information. */
|
||||
static void InvMixColumns(state_t *state) {
|
||||
int i;
|
||||
__u8 a, b, c, d;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
a = (*state)[i][0];
|
||||
b = (*state)[i][1];
|
||||
c = (*state)[i][2];
|
||||
d = (*state)[i][3];
|
||||
|
||||
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^
|
||||
Multiply(d, 0x09);
|
||||
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^
|
||||
Multiply(d, 0x0d);
|
||||
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^
|
||||
Multiply(d, 0x0b);
|
||||
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^
|
||||
Multiply(d, 0x0e);
|
||||
}
|
||||
}
|
||||
|
||||
extern __u32 LINUX_KERNEL_VERSION __kconfig;
|
||||
|
||||
/* The SubBytes Function Substitutes the values in the state matrix
|
||||
with values in an S-box. */
|
||||
static void InvSubBytes(state_t *state) {
|
||||
__u8 i, j;
|
||||
if (LINUX_KERNEL_VERSION < KERNEL_VERSION(5, 10, 0)) {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
/* Ubuntu 20.04 LTS kernel 5.4.0 needs this workaround
|
||||
otherwise "math between map_value pointer and register with
|
||||
unbounded min value is not allowed". 5.10.0 is a kernel
|
||||
version that works but it might not be the minimum
|
||||
version. */
|
||||
__u8 k = (*state)[j][i];
|
||||
(*state)[j][i] = k ? getSBoxInvert(k) : getSBoxInvert(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InvShiftRows(state_t *state) {
|
||||
__u8 temp;
|
||||
|
||||
/* Rotate first row 1 columns to right */
|
||||
temp = (*state)[3][1];
|
||||
(*state)[3][1] = (*state)[2][1];
|
||||
(*state)[2][1] = (*state)[1][1];
|
||||
(*state)[1][1] = (*state)[0][1];
|
||||
(*state)[0][1] = temp;
|
||||
|
||||
/* Rotate second row 2 columns to right */
|
||||
temp = (*state)[0][2];
|
||||
(*state)[0][2] = (*state)[2][2];
|
||||
(*state)[2][2] = temp;
|
||||
|
||||
temp = (*state)[1][2];
|
||||
(*state)[1][2] = (*state)[3][2];
|
||||
(*state)[3][2] = temp;
|
||||
|
||||
/* Rotate third row 3 columns to right */
|
||||
temp = (*state)[0][3];
|
||||
(*state)[0][3] = (*state)[1][3];
|
||||
(*state)[1][3] = (*state)[2][3];
|
||||
(*state)[2][3] = (*state)[3][3];
|
||||
(*state)[3][3] = temp;
|
||||
}
|
||||
|
||||
static void InvCipher(state_t *state, const __u8 *RoundKey) {
|
||||
/* Add the First round key to the state before starting the
|
||||
rounds. */
|
||||
AddRoundKey(Nr, state, RoundKey);
|
||||
|
||||
/* There will be Nr rounds. The first Nr-1 rounds are identical.
|
||||
These Nr rounds are executed in the loop below. Last one without
|
||||
InvMixColumn() */
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 1, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 2, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 3, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 4, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 5, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 6, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 7, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 8, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 9, state, RoundKey);
|
||||
InvMixColumns(state);
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(Nr - 10, state, RoundKey);
|
||||
}
|
||||
|
||||
static void AES_ECB_decrypt(const struct AES_ctx *ctx, __u8 *buf) {
|
||||
/* The next function call decrypts the PlainText with the Key using
|
||||
AES algorithm. */
|
||||
InvCipher((state_t *)buf, ctx->RoundKey);
|
||||
}
|
||||
|
||||
/* rol32: From linux kernel source code */
|
||||
|
||||
/**
|
||||
* rol32 - rotate a 32-bit value left
|
||||
* @word: value to rotate
|
||||
* @shift: bits to roll
|
||||
*/
|
||||
static inline __u32 rol32(__u32 word, unsigned int shift) {
|
||||
return (word << shift) | (word >> ((-shift) & 31));
|
||||
}
|
||||
|
||||
/* jhash.h: Jenkins hash support.
|
||||
*
|
||||
* Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
|
||||
*
|
||||
* https://burtleburtle.net/bob/hash/
|
||||
*
|
||||
* These are the credits from Bob's sources:
|
||||
*
|
||||
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
*
|
||||
* These are functions for producing 32-bit hashes for hash table lookup.
|
||||
* hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||
* are externally useful functions. Routines to test the hash are included
|
||||
* if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
* the public domain. It has no warranty.
|
||||
*
|
||||
* Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* I've modified Bob's hash to be useful in the Linux kernel, and
|
||||
* any bugs present are my fault.
|
||||
* Jozsef
|
||||
*/
|
||||
|
||||
/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
|
||||
#define __jhash_final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; \
|
||||
c -= rol32(b, 14); \
|
||||
a ^= c; \
|
||||
a -= rol32(c, 11); \
|
||||
b ^= a; \
|
||||
b -= rol32(a, 25); \
|
||||
c ^= b; \
|
||||
c -= rol32(b, 16); \
|
||||
a ^= c; \
|
||||
a -= rol32(c, 4); \
|
||||
b ^= a; \
|
||||
b -= rol32(a, 14); \
|
||||
c ^= b; \
|
||||
c -= rol32(b, 24); \
|
||||
}
|
||||
|
||||
/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
|
||||
static inline __u32 __jhash_nwords(__u32 a, __u32 b, __u32 c, __u32 initval) {
|
||||
a += initval;
|
||||
b += initval;
|
||||
c += initval;
|
||||
|
||||
__jhash_final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* An arbitrary initial parameter */
|
||||
#define JHASH_INITVAL 0xdeadbeef
|
||||
|
||||
static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) {
|
||||
return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
|
||||
}
|
||||
|
||||
struct bpf_map_def SEC("maps") cid_prefix_map = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.max_entries = 255,
|
||||
.key_size = sizeof(__u64),
|
||||
.value_size = sizeof(__u32),
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") reuseport_array = {
|
||||
.type = BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
|
||||
.max_entries = 255,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u32),
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") sk_info = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.max_entries = 3,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u64),
|
||||
};
|
||||
|
||||
typedef struct quic_hd {
|
||||
__u8 *dcid;
|
||||
__u32 dcidlen;
|
||||
__u32 dcid_offset;
|
||||
__u8 type;
|
||||
} quic_hd;
|
||||
|
||||
#define SV_DCIDLEN 20
|
||||
#define MAX_DCIDLEN 20
|
||||
#define MIN_DCIDLEN 8
|
||||
#define CID_PREFIXLEN 8
|
||||
#define CID_PREFIX_OFFSET 1
|
||||
|
||||
enum {
|
||||
NGTCP2_PKT_INITIAL = 0x0,
|
||||
NGTCP2_PKT_0RTT = 0x1,
|
||||
NGTCP2_PKT_HANDSHAKE = 0x2,
|
||||
NGTCP2_PKT_SHORT = 0x40,
|
||||
};
|
||||
|
||||
static inline int parse_quic(quic_hd *qhd, __u8 *data, __u8 *data_end) {
|
||||
__u8 *p;
|
||||
__u64 dcidlen;
|
||||
|
||||
if (*data & 0x80) {
|
||||
p = data + 1 + 4;
|
||||
|
||||
/* Do not check the actual DCID length because we might not buffer
|
||||
entire DCID here. */
|
||||
dcidlen = *p;
|
||||
|
||||
if (dcidlen > MAX_DCIDLEN || dcidlen < MIN_DCIDLEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
++p;
|
||||
|
||||
qhd->type = (*data & 0x30) >> 4;
|
||||
qhd->dcid = p;
|
||||
qhd->dcidlen = dcidlen;
|
||||
qhd->dcid_offset = 6;
|
||||
} else {
|
||||
qhd->type = NGTCP2_PKT_SHORT;
|
||||
qhd->dcid = data + 1;
|
||||
qhd->dcidlen = SV_DCIDLEN;
|
||||
qhd->dcid_offset = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u32 hash(const __u8 *data, __u32 datalen, __u32 initval) {
|
||||
__u32 a, b;
|
||||
|
||||
a = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||||
b = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
|
||||
|
||||
return jhash_2words(a, b, initval);
|
||||
}
|
||||
|
||||
static __u32 sk_index_from_dcid(const quic_hd *qhd,
|
||||
const struct sk_reuseport_md *reuse_md,
|
||||
__u64 num_socks) {
|
||||
__u32 len = qhd->dcidlen;
|
||||
__u32 h = reuse_md->hash;
|
||||
__u8 hbuf[8];
|
||||
|
||||
if (len > 16) {
|
||||
__builtin_memset(hbuf, 0, sizeof(hbuf));
|
||||
|
||||
switch (len) {
|
||||
case 20:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 4);
|
||||
break;
|
||||
case 19:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 3);
|
||||
break;
|
||||
case 18:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 2);
|
||||
break;
|
||||
case 17:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 16, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
h = hash(hbuf, sizeof(hbuf), h);
|
||||
len = 16;
|
||||
}
|
||||
|
||||
if (len > 8) {
|
||||
__builtin_memset(hbuf, 0, sizeof(hbuf));
|
||||
|
||||
switch (len) {
|
||||
case 16:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 8);
|
||||
break;
|
||||
case 15:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 7);
|
||||
break;
|
||||
case 14:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 6);
|
||||
break;
|
||||
case 13:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 5);
|
||||
break;
|
||||
case 12:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 4);
|
||||
break;
|
||||
case 11:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 3);
|
||||
break;
|
||||
case 10:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 2);
|
||||
break;
|
||||
case 9:
|
||||
__builtin_memcpy(hbuf, qhd->dcid + 8, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
h = hash(hbuf, sizeof(hbuf), h);
|
||||
len = 8;
|
||||
}
|
||||
|
||||
return hash(qhd->dcid, len, h) % num_socks;
|
||||
}
|
||||
|
||||
SEC("sk_reuseport")
|
||||
int select_reuseport(struct sk_reuseport_md *reuse_md) {
|
||||
__u32 sk_index, *psk_index;
|
||||
__u64 *pnum_socks, *pkey;
|
||||
__u32 zero = 0, key_high_idx = 1, key_low_idx = 2;
|
||||
int rv;
|
||||
quic_hd qhd;
|
||||
__u8 qpktbuf[6 + MAX_DCIDLEN];
|
||||
struct AES_ctx aes_ctx;
|
||||
__u8 key[AES_KEYLEN];
|
||||
__u8 *cid_prefix;
|
||||
|
||||
if (bpf_skb_load_bytes(reuse_md, sizeof(struct udphdr), qpktbuf,
|
||||
sizeof(qpktbuf)) != 0) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
pnum_socks = bpf_map_lookup_elem(&sk_info, &zero);
|
||||
if (pnum_socks == NULL) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
pkey = bpf_map_lookup_elem(&sk_info, &key_high_idx);
|
||||
if (pkey == NULL) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
__builtin_memcpy(key, pkey, sizeof(*pkey));
|
||||
|
||||
pkey = bpf_map_lookup_elem(&sk_info, &key_low_idx);
|
||||
if (pkey == NULL) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
__builtin_memcpy(key + sizeof(*pkey), pkey, sizeof(*pkey));
|
||||
|
||||
rv = parse_quic(&qhd, qpktbuf, qpktbuf + sizeof(qpktbuf));
|
||||
if (rv != 0) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
AES_init_ctx(&aes_ctx, key);
|
||||
|
||||
switch (qhd.type) {
|
||||
case NGTCP2_PKT_INITIAL:
|
||||
case NGTCP2_PKT_0RTT:
|
||||
if (qhd.dcidlen == SV_DCIDLEN) {
|
||||
cid_prefix = qhd.dcid + CID_PREFIX_OFFSET;
|
||||
AES_ECB_decrypt(&aes_ctx, cid_prefix);
|
||||
|
||||
psk_index = bpf_map_lookup_elem(&cid_prefix_map, cid_prefix);
|
||||
if (psk_index != NULL) {
|
||||
sk_index = *psk_index;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks);
|
||||
|
||||
break;
|
||||
case NGTCP2_PKT_HANDSHAKE:
|
||||
case NGTCP2_PKT_SHORT:
|
||||
if (qhd.dcidlen != SV_DCIDLEN) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
cid_prefix = qhd.dcid + CID_PREFIX_OFFSET;
|
||||
AES_ECB_decrypt(&aes_ctx, cid_prefix);
|
||||
|
||||
psk_index = bpf_map_lookup_elem(&cid_prefix_map, cid_prefix);
|
||||
if (psk_index == NULL) {
|
||||
sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
sk_index = *psk_index;
|
||||
|
||||
break;
|
||||
default:
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
rv = bpf_sk_select_reuseport(reuse_md, &reuseport_array, &sk_index, 0);
|
||||
if (rv != 0) {
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
return SK_PASS;
|
||||
}
|
||||
32
cmake/FindLibbpf.cmake
Normal file
32
cmake/FindLibbpf.cmake
Normal file
@@ -0,0 +1,32 @@
|
||||
# - Try to find libbpf
|
||||
# Once done this will define
|
||||
# LIBBPF_FOUND - System has libbpf
|
||||
# LIBBPF_INCLUDE_DIRS - The libbpf include directories
|
||||
# LIBBPF_LIBRARIES - The libraries needed to use libbpf
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBBPF QUIET libbpf)
|
||||
|
||||
find_path(LIBBPF_INCLUDE_DIR
|
||||
NAMES bpf/bpf.h
|
||||
HINTS ${PC_LIBBPF_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBBPF_LIBRARY
|
||||
NAMES bpf
|
||||
HINTS ${PC_LIBBPF_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBBPF_FOUND
|
||||
# to TRUE if all listed variables are TRUE and the requested version
|
||||
# matches.
|
||||
find_package_handle_standard_args(Libbpf REQUIRED_VARS
|
||||
LIBBPF_LIBRARY LIBBPF_INCLUDE_DIR
|
||||
VERSION_VAR LIBBPF_VERSION)
|
||||
|
||||
if(LIBBPF_FOUND)
|
||||
set(LIBBPF_LIBRARIES ${LIBBPF_LIBRARY})
|
||||
set(LIBBPF_INCLUDE_DIRS ${LIBBPF_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBBPF_INCLUDE_DIR LIBBPF_LIBRARY)
|
||||
41
cmake/FindLibnghttp3.cmake
Normal file
41
cmake/FindLibnghttp3.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
# - Try to find libnghttp3
|
||||
# Once done this will define
|
||||
# LIBNGHTTP3_FOUND - System has libnghttp3
|
||||
# LIBNGHTTP3_INCLUDE_DIRS - The libnghttp3 include directories
|
||||
# LIBNGHTTP3_LIBRARIES - The libraries needed to use libnghttp3
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBNGHTTP3 QUIET libnghttp3)
|
||||
|
||||
find_path(LIBNGHTTP3_INCLUDE_DIR
|
||||
NAMES nghttp3/nghttp3.h
|
||||
HINTS ${PC_LIBNGHTTP3_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBNGHTTP3_LIBRARY
|
||||
NAMES nghttp3
|
||||
HINTS ${PC_LIBNGHTTP3_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBNGHTTP3_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+NGHTTP3_VERSION[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBNGHTTP3_INCLUDE_DIR}/nghttp3/version.h"
|
||||
LIBNGHTTP3_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBNGHTTP3_VERSION "${LIBNGHTTP3_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBNGHTTP3_FOUND
|
||||
# to TRUE if all listed variables are TRUE and the requested version
|
||||
# matches.
|
||||
find_package_handle_standard_args(Libnghttp3 REQUIRED_VARS
|
||||
LIBNGHTTP3_LIBRARY LIBNGHTTP3_INCLUDE_DIR
|
||||
VERSION_VAR LIBNGHTTP3_VERSION)
|
||||
|
||||
if(LIBNGHTTP3_FOUND)
|
||||
set(LIBNGHTTP3_LIBRARIES ${LIBNGHTTP3_LIBRARY})
|
||||
set(LIBNGHTTP3_INCLUDE_DIRS ${LIBNGHTTP3_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBNGHTTP3_INCLUDE_DIR LIBNGHTTP3_LIBRARY)
|
||||
41
cmake/FindLibngtcp2.cmake
Normal file
41
cmake/FindLibngtcp2.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
# - Try to find libngtcp2
|
||||
# Once done this will define
|
||||
# LIBNGTCP2_FOUND - System has libngtcp2
|
||||
# LIBNGTCP2_INCLUDE_DIRS - The libngtcp2 include directories
|
||||
# LIBNGTCP2_LIBRARIES - The libraries needed to use libngtcp2
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBNGTCP2 QUIET libngtcp2)
|
||||
|
||||
find_path(LIBNGTCP2_INCLUDE_DIR
|
||||
NAMES ngtcp2/ngtcp2.h
|
||||
HINTS ${PC_LIBNGTCP2_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBNGTCP2_LIBRARY
|
||||
NAMES ngtcp2
|
||||
HINTS ${PC_LIBNGTCP2_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBNGTCP2_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBNGTCP2_INCLUDE_DIR}/ngtcp2/version.h"
|
||||
LIBNGTCP2_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBNGTCP2_VERSION "${LIBNGTCP2_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBNGTCP2_FOUND
|
||||
# to TRUE if all listed variables are TRUE and the requested version
|
||||
# matches.
|
||||
find_package_handle_standard_args(Libngtcp2 REQUIRED_VARS
|
||||
LIBNGTCP2_LIBRARY LIBNGTCP2_INCLUDE_DIR
|
||||
VERSION_VAR LIBNGTCP2_VERSION)
|
||||
|
||||
if(LIBNGTCP2_FOUND)
|
||||
set(LIBNGTCP2_LIBRARIES ${LIBNGTCP2_LIBRARY})
|
||||
set(LIBNGTCP2_INCLUDE_DIRS ${LIBNGTCP2_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBNGTCP2_INCLUDE_DIR LIBNGTCP2_LIBRARY)
|
||||
43
cmake/FindLibngtcp2_crypto_openssl.cmake
Normal file
43
cmake/FindLibngtcp2_crypto_openssl.cmake
Normal file
@@ -0,0 +1,43 @@
|
||||
# - Try to find libngtcp2_crypto_openssl
|
||||
# Once done this will define
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND - System has libngtcp2_crypto_openssl
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS - The libngtcp2_crypto_openssl include directories
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES - The libraries needed to use libngtcp2_crypto_openssl
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBNGTCP2_CRYPTO_OPENSSL QUIET libngtcp2_crypto_openssl)
|
||||
|
||||
find_path(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
|
||||
NAMES ngtcp2/ngtcp2_crypto_openssl.h
|
||||
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
|
||||
NAMES ngtcp2_crypto_openssl
|
||||
HINTS ${PC_LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR}/ngtcp2/version.h"
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_VERSION "${LIBNGTCP2_CRYPTO_OPENSSL_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set
|
||||
# LIBNGTCP2_CRYPTO_OPENSSL_FOUND to TRUE if all listed variables are
|
||||
# TRUE and the requested version matches.
|
||||
find_package_handle_standard_args(Libngtcp2_crypto_openssl REQUIRED_VARS
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
|
||||
VERSION_VAR LIBNGTCP2_CRYPTO_OPENSSL_VERSION)
|
||||
|
||||
if(LIBNGTCP2_CRYPTO_OPENSSL_FOUND)
|
||||
set(LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES ${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY})
|
||||
set(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIR
|
||||
LIBNGTCP2_CRYPTO_OPENSSL_LIBRARY)
|
||||
@@ -15,5 +15,5 @@ find_library(SYSTEMD_LIBRARIES NAMES systemd ${PC_SYSTEMD_LIBRARY_DIRS})
|
||||
find_path(SYSTEMD_INCLUDE_DIRS systemd/sd-login.h HINTS ${PC_SYSTEMD_INCLUDE_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SYSTEMD DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
|
||||
find_package_handle_standard_args(Systemd DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
|
||||
mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
|
||||
|
||||
@@ -78,3 +78,15 @@
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#cmakedefine HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if HTTP/3 is enabled. */
|
||||
#cmakedefine ENABLE_HTTP3 1
|
||||
|
||||
/* Define to 1 if you have `libbpf` library. */
|
||||
#cmakedefine HAVE_LIBBPF 1
|
||||
|
||||
/* Define to 1 if you have enum bpf_stats_type in linux/bpf.h. */
|
||||
#cmakedefine HAVE_BPF_STATS_TYPE 1
|
||||
|
||||
/* Define to 1 if you have `libngtcp2_crypto_openssl` library. */
|
||||
#cmakedefine HAVE_LIBNGTCP2_CRYPTO_OPENSSL
|
||||
|
||||
236
configure.ac
236
configure.ac
@@ -22,10 +22,10 @@ dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
dnl Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [1.45.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.47.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -43,10 +43,10 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 34)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_AGE, 20)
|
||||
dnl https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 35)
|
||||
AC_SUBST(LT_REVISION, 1)
|
||||
AC_SUBST(LT_AGE, 21)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
@@ -107,6 +107,11 @@ AC_ARG_ENABLE([lib-only],
|
||||
[Build libnghttp2 only. This is a short hand for --disable-app --disable-examples --disable-hpack-tools --disable-python-bindings])],
|
||||
[request_lib_only=$enableval], [request_lib_only=no])
|
||||
|
||||
AC_ARG_ENABLE([http3],
|
||||
[AS_HELP_STRING([--enable-http3],
|
||||
[(EXPERIMENTAL) Enable HTTP/3. This requires ngtcp2, nghttp3, and a custom OpenSSL.])],
|
||||
[request_http3=$enableval], [request_http3=no])
|
||||
|
||||
AC_ARG_WITH([libxml2],
|
||||
[AS_HELP_STRING([--with-libxml2],
|
||||
[Use libxml2 [default=check]])],
|
||||
@@ -172,6 +177,21 @@ AC_ARG_WITH([cython],
|
||||
[Use cython in given PATH])],
|
||||
[cython_path=$withval], [])
|
||||
|
||||
AC_ARG_WITH([libngtcp2],
|
||||
[AS_HELP_STRING([--with-libngtcp2],
|
||||
[Use libngtcp2 [default=check]])],
|
||||
[request_libngtcp2=$withval], [request_libngtcp2=check])
|
||||
|
||||
AC_ARG_WITH([libnghttp3],
|
||||
[AS_HELP_STRING([--with-libnghttp3],
|
||||
[Use libnghttp3 [default=check]])],
|
||||
[request_libnghttp3=$withval], [request_libnghttp3=check])
|
||||
|
||||
AC_ARG_WITH([libbpf],
|
||||
[AS_HELP_STRING([--with-libbpf],
|
||||
[Use libbpf [default=no]])],
|
||||
[request_libbpf=$withval], [request_libbpf=no])
|
||||
|
||||
dnl Define variables
|
||||
AC_ARG_VAR([CYTHON], [the Cython executable])
|
||||
|
||||
@@ -185,6 +205,8 @@ AC_ARG_VAR([JEMALLOC_LIBS], [linker flags for jemalloc, skipping any checks])
|
||||
AC_ARG_VAR([LIBTOOL_LDFLAGS],
|
||||
[libtool specific flags (e.g., -static-libtool-libs)])
|
||||
|
||||
AC_ARG_VAR([BPFCFLAGS], [C compiler flags for bpf program])
|
||||
|
||||
dnl Checks for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
@@ -198,6 +220,11 @@ PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AM_PATH_PYTHON([3.8],, [:])
|
||||
|
||||
if test "x$request_python_bindings" = "xyes" &&
|
||||
test "x$PYTHON" = "x:"; then
|
||||
AC_MSG_ERROR([python was requested (enable-python-bindings) but not found])
|
||||
fi
|
||||
|
||||
if [test "x$request_lib_only" = "xyes"]; then
|
||||
request_app=no
|
||||
request_hpack_tools=no
|
||||
@@ -205,8 +232,10 @@ if [test "x$request_lib_only" = "xyes"]; then
|
||||
request_python_bindings=no
|
||||
fi
|
||||
|
||||
if [test "x$request_python_bindings" != "xno"]; then
|
||||
AX_PYTHON_DEVEL([>= '3.8'])
|
||||
if test "x$request_python_bindings" != "xno" &&
|
||||
test "x$PYTHON" != "x:"; then
|
||||
# version check is broken
|
||||
AX_PYTHON_DEVEL()
|
||||
fi
|
||||
|
||||
if test "x${cython_path}" = "x"; then
|
||||
@@ -245,6 +274,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
]],
|
||||
[[
|
||||
std::vector<std::future<int>> v;
|
||||
(void)v;
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STD_FUTURE], [1],
|
||||
[Define to 1 if you have the `std::future`.])
|
||||
@@ -319,7 +349,7 @@ APPLDFLAGS=
|
||||
case "$host_os" in
|
||||
*android*)
|
||||
android_build=yes
|
||||
# android does not need -pthread, but needs followng 3 libs for C++
|
||||
# android does not need -pthread, but needs following 3 libs for C++
|
||||
APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
|
||||
;;
|
||||
*)
|
||||
@@ -334,6 +364,13 @@ case "$host_os" in
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${build}" in
|
||||
*-apple-darwin*)
|
||||
EXTRA_DEFS="-D__APPLE_USE_RFC_3542"
|
||||
AC_SUBST([EXTRA_DEFS])
|
||||
;;
|
||||
esac
|
||||
|
||||
# zlib
|
||||
have_zlib=no
|
||||
if test "x${request_zlib}" != "xno"; then
|
||||
@@ -431,6 +468,37 @@ if test "x${request_openssl}" != "xno"; then
|
||||
[have_openssl=yes], [have_openssl=no])
|
||||
if test "x${have_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
else
|
||||
save_CFLAGS="$CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
CFLAGS="$OPENSSL_CFLAGS $CFLAGS"
|
||||
LIBS="$OPENSSL_LIBS $LIBS"
|
||||
|
||||
# quictls/openssl has SSL_is_quic.
|
||||
have_ssl_is_quic=no
|
||||
AC_MSG_CHECKING([for SSL_is_quic])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <openssl/ssl.h>
|
||||
]], [[
|
||||
SSL *ssl = NULL;
|
||||
SSL_is_quic(ssl);
|
||||
]])],
|
||||
[AC_MSG_RESULT([yes]); have_ssl_is_quic=yes],
|
||||
[AC_MSG_RESULT([no]); have_ssl_is_quic=no])
|
||||
|
||||
# boringssl has SSL_set_quic_early_data_context.
|
||||
AC_MSG_CHECKING([for SSL_set_quic_early_data_context])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <openssl/ssl.h>
|
||||
]], [[
|
||||
SSL *ssl = NULL;
|
||||
SSL_set_quic_early_data_context(ssl, NULL, 0);
|
||||
]])],
|
||||
[AC_MSG_RESULT([yes]); have_boringssl_quic=yes],
|
||||
[AC_MSG_RESULT([no]); have_boringssl_quic=no])
|
||||
|
||||
CFLAGS="$save_CFLAGS"
|
||||
LIBS="$save_LIBS"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -454,6 +522,125 @@ if test "x${request_libcares}" = "xyes" &&
|
||||
AC_MSG_ERROR([libcares was requested (--with-libcares) but not found])
|
||||
fi
|
||||
|
||||
# ngtcp2 (for src)
|
||||
have_libngtcp2=no
|
||||
if test "x${request_libngtcp2}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.1.0], [have_libngtcp2=yes],
|
||||
[have_libngtcp2=no])
|
||||
if test "x${have_libngtcp2}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libngtcp2}" = "xyes" &&
|
||||
test "x${have_libngtcp2}" != "xyes"; then
|
||||
AC_MSG_ERROR([libngtcp2 was requested (--with-libngtcp2) but not found])
|
||||
fi
|
||||
|
||||
# ngtcp2_crypto_openssl (for src)
|
||||
have_libngtcp2_crypto_openssl=no
|
||||
if test "x${have_ssl_is_quic}" = "xyes" &&
|
||||
test "x${request_libngtcp2}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_OPENSSL],
|
||||
[libngtcp2_crypto_openssl >= 0.0.0],
|
||||
[have_libngtcp2_crypto_openssl=yes],
|
||||
[have_libngtcp2_crypto_openssl=no])
|
||||
if test "x${have_libngtcp2_crypto_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_OPENSSL_PKG_ERRORS)
|
||||
else
|
||||
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_OPENSSL], [1],
|
||||
[Define to 1 if you have `libngtcp2_crypto_openssl` library.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${have_ssl_is_quic}" = "xyes" &&
|
||||
test "x${request_libngtcp2}" = "xyes" &&
|
||||
test "x${have_libngtcp2_crypto_openssl}" != "xyes"; then
|
||||
AC_MSG_ERROR([libngtcp2_crypto_openssl was requested (--with-libngtcp2) but not found])
|
||||
fi
|
||||
|
||||
# ngtcp2_crypto_boringssl (for src)
|
||||
have_libngtcp2_crypto_boringssl=no
|
||||
if test "x${have_boringssl_quic}" = "xyes" &&
|
||||
test "x${request_libngtcp2}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_BORINGSSL],
|
||||
[libngtcp2_crypto_boringssl >= 0.0.0],
|
||||
[have_libngtcp2_crypto_boringssl=yes],
|
||||
[have_libngtcp2_crypto_boringssl=no])
|
||||
if test "x${have_libngtcp2_crypto_boringssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS)
|
||||
else
|
||||
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_BORINGSSL], [1],
|
||||
[Define to 1 if you have `libngtcp2_crypto_boringssl` library.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${have_boringssl_quic}" = "xyes" &&
|
||||
test "x${request_libngtcp2}" = "xyes" &&
|
||||
test "x${have_libngtcp2_crypto_boringssl}" != "xyes"; then
|
||||
AC_MSG_ERROR([libngtcp2_crypto_boringssl was requested (--with-libngtcp2) but not found])
|
||||
fi
|
||||
|
||||
# nghttp3 (for src)
|
||||
have_libnghttp3=no
|
||||
if test "x${request_libnghttp3}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.1.0], [have_libnghttp3=yes],
|
||||
[have_libnghttp3=no])
|
||||
if test "x${have_libnghttp3}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBNGHTTP3_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libnghttp3}" = "xyes" &&
|
||||
test "x${have_libnghttp3}" != "xyes"; then
|
||||
AC_MSG_ERROR([libnghttp3 was requested (--with-libnghttp3) but not found])
|
||||
fi
|
||||
|
||||
# libbpf (for src)
|
||||
have_libbpf=no
|
||||
if test "x${request_libbpf}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBBPF], [libbpf >= 0.4.0], [have_libbpf=yes],
|
||||
[have_libbpf=no])
|
||||
if test "x${have_libbpf}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_LIBBPF], [1], [Define to 1 if you have `libbpf` library.])
|
||||
if test "x${BPFCFLAGS}" = "x"; then
|
||||
BPFCFLAGS="-Wall -O2 -g"
|
||||
fi
|
||||
# Add the include path for Debian
|
||||
EXTRABPFCFLAGS="-I/usr/include/$host_cpu-$host_os"
|
||||
AC_SUBST([EXTRABPFCFLAGS])
|
||||
|
||||
AC_MSG_CHECKING([whether enum bpf_stats_type is defined in linux/bpf.h])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <linux/bpf.h>
|
||||
]],
|
||||
[[
|
||||
enum bpf_stats_type foo;
|
||||
(void)foo;
|
||||
]])],
|
||||
[have_bpf_stats_type=yes],
|
||||
[have_bpf_stats_type=no])
|
||||
|
||||
if test "x${have_bpf_stats_type}" = "xyes"; then
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_BPF_STATS_TYPE], [1],
|
||||
[Define to 1 if you have enum bpf_stats_type in linux/bpf.h.])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
else
|
||||
AC_MSG_NOTICE($LIBBPF_PKG_ERRORS)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_libbpf}" = "xyes" &&
|
||||
test "x${have_libbpf}" != "xyes"; then
|
||||
AC_MSG_ERROR([libbpf was requested (--with-libbpf) but not found])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_LIBBPF], [ test "x${have_libbpf}" = "xyes" ])
|
||||
|
||||
# libevent_openssl (for examples)
|
||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||
have_libevent_openssl=no
|
||||
@@ -598,6 +785,26 @@ fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
|
||||
|
||||
# Check HTTP/3 support
|
||||
enable_http3=no
|
||||
if test "x${request_http3}" != "xno" &&
|
||||
(test "x${have_ssl_is_quic}" = "xyes" ||
|
||||
test "x${have_boringssl_quic}" = "xyes") &&
|
||||
test "x${have_libngtcp2}" = "xyes" &&
|
||||
(test "x${have_libngtcp2_crypto_openssl}" = "xyes" ||
|
||||
test "x${have_libngtcp2_crypto_boringssl}" = "xyes") &&
|
||||
test "x${have_libnghttp3}" = "xyes"; then
|
||||
enable_http3=yes
|
||||
AC_DEFINE([ENABLE_HTTP3], [1], [Define to 1 if HTTP/3 is enabled.])
|
||||
fi
|
||||
|
||||
if test "x${request_http3}" = "xyes" &&
|
||||
test "x${enable_http3}" != "xyes"; then
|
||||
AC_MSG_ERROR([HTTP/3 was requested (--enable-http3) but dependencies are not met.])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_HTTP3], [ test "x${enable_http3}" = "xyes" ])
|
||||
|
||||
enable_hpack_tools=no
|
||||
# HPACK tools requires jansson
|
||||
if test "x${request_hpack_tools}" != "xno" &&
|
||||
@@ -939,6 +1146,7 @@ AC_CONFIG_FILES([
|
||||
src/Makefile
|
||||
src/includes/Makefile
|
||||
src/libnghttp2_asio.pc
|
||||
bpf/Makefile
|
||||
examples/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
@@ -990,7 +1198,11 @@ AC_MSG_NOTICE([summary of build options:
|
||||
WARNCXXFLAGS: ${WARNCXXFLAGS}
|
||||
CXX1XCXXFLAGS: ${CXX1XCXXFLAGS}
|
||||
EXTRACFLAG: ${EXTRACFLAG}
|
||||
BPFCFLAGS: ${BPFCFLAGS}
|
||||
EXTRABPFCFLAGS: ${EXTRABPFCFLAGS}
|
||||
LIBS: ${LIBS}
|
||||
DEFS: ${DEFS}
|
||||
EXTRA_DEFS: ${EXTRA_DEFS}
|
||||
Library:
|
||||
Shared: ${enable_shared}
|
||||
Static: ${enable_static}
|
||||
@@ -1011,6 +1223,11 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}')
|
||||
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
|
||||
Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
|
||||
libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}')
|
||||
libngtcp2_crypto_openssl: ${have_libngtcp2_crypto_openssl} (CFLAGS='${LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBS}')
|
||||
libngtcp2_crypto_boringssl: ${have_libngtcp2_crypto_boringssl} (CFLAGS='${LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_BORINGSSL_LIBS}')
|
||||
libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}')
|
||||
libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}')
|
||||
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
|
||||
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
|
||||
Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}')
|
||||
@@ -1032,4 +1249,5 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Threading: ${enable_threads}
|
||||
HTTP/3 (EXPERIMENTAL): ${enable_http3}
|
||||
])
|
||||
|
||||
@@ -184,9 +184,9 @@ set(EXTRA_DIST
|
||||
sources/python-apiref.rst
|
||||
sources/building-android-binary.rst
|
||||
sources/contribute.rst
|
||||
_exts/sphinxcontrib/LICENSE.rubydomain
|
||||
_exts/sphinxcontrib/__init__.py
|
||||
_exts/sphinxcontrib/rubydomain.py
|
||||
_exts/rubydomain/LICENSE.rubydomain
|
||||
_exts/rubydomain/__init__.py
|
||||
_exts/rubydomain/rubydomain.py
|
||||
_themes/sphinx_rtd_theme/__init__.py
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html
|
||||
_themes/sphinx_rtd_theme/footer.html
|
||||
|
||||
@@ -30,6 +30,8 @@ APIDOCS= \
|
||||
nghttp2_check_authority.rst \
|
||||
nghttp2_check_header_name.rst \
|
||||
nghttp2_check_header_value.rst \
|
||||
nghttp2_check_method.rst \
|
||||
nghttp2_check_path.rst \
|
||||
nghttp2_hd_deflate_bound.rst \
|
||||
nghttp2_hd_deflate_change_table_size.rst \
|
||||
nghttp2_hd_deflate_del.rst \
|
||||
@@ -204,9 +206,9 @@ EXTRA_DIST = \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
sources/security.rst \
|
||||
_exts/sphinxcontrib/LICENSE.rubydomain \
|
||||
_exts/sphinxcontrib/__init__.py \
|
||||
_exts/sphinxcontrib/rubydomain.py \
|
||||
_exts/rubydomain/LICENSE.rubydomain \
|
||||
_exts/rubydomain/__init__.py \
|
||||
_exts/rubydomain/rubydomain.py \
|
||||
_themes/sphinx_rtd_theme/__init__.py \
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
@@ -270,7 +272,7 @@ EXTRA_DIST = \
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = manual
|
||||
|
||||
|
||||
@@ -493,7 +493,7 @@ class RubyModuleIndex(Index):
|
||||
# list of all modules, sorted by module name
|
||||
modules = sorted(_iteritems(self.domain.data['modules']),
|
||||
key=lambda x: x[0].lower())
|
||||
# sort out collapsable modules
|
||||
# sort out collapsible modules
|
||||
prev_modname = ''
|
||||
num_toplevels = 0
|
||||
for modname, (docname, synopsis, platforms, deprecated) in modules:
|
||||
@@ -8,7 +8,7 @@ _h2load()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --connect-to --header-table-size --requests --log-file --base-uri --h1 --threads --npn-list --rate-period --rps --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --warm-up-time --duration --header ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--requests --clients --threads --input-file --max-concurrent-streams --window-bits --connection-window-bits --header --ciphers --tls13-ciphers --no-tls-proto --data --rate --rate-period --duration --warm-up-time --connection-active-timeout --connection-inactivity-timeout --timing-script-file --base-uri --npn-list --h1 --header-table-size --encoder-header-table-size --log-file --qlog-file-base --connect-to --rps --groups --no-udp-gso --max-udp-payload-size --verbose --version --help ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
import subprocess
|
||||
import io
|
||||
import re
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttp()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --encoder-header-table-size --padding --hexdump --max-concurrent-streams --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --expect-continue --stat --no-verify-peer --header ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--verbose --null-out --remote-name --timeout --window-bits --connection-window-bits --get-assets --stat --header --trailer --cert --key --data --multiply --upgrade --weight --peer-max-concurrent-streams --header-table-size --encoder-header-table-size --padding --har --color --continuation --no-content-length --no-dep --hexdump --no-push --max-concurrent-streams --expect-continue --no-verify-peer --version --help ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpd()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--htdocs --verbose --daemon --echo-upload --error-gzip --push --header-table-size --encoder-header-table-size --padding --hexdump --max-concurrent-streams --no-tls --connection-window-bits --mime-types-file --no-content-length --workers --version --color --early-response --dh-param-file --trailer --address --window-bits --verify-client --help ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--address --daemon --verify-client --htdocs --verbose --no-tls --header-table-size --encoder-header-table-size --color --push --padding --max-concurrent-streams --workers --error-gzip --window-bits --connection-window-bits --dh-param-file --early-response --trailer --hexdump --echo-upload --mime-types-file --no-content-length --version --help ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --tls13-client-ciphers --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --tls-session-cache-memcached-cert-file --no-via --mruby-file --no-server-push --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --tls-no-postpone-early-data --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --frontend-http2-window-size --tls-ticket-key-memcached-max-retry --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --no-http2-cipher-block-list --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ignore-per-pattern-mruby-error --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --no-strip-incoming-early-data --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --tls-max-early-data --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --tls13-ciphers --client-no-http2-cipher-block-list --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--backend --frontend --backlog --backend-address-family --backend-http-proxy-uri --workers --single-thread --read-rate --read-burst --write-rate --write-burst --worker-read-rate --worker-read-burst --worker-write-rate --worker-write-burst --worker-frontend-connections --backend-connections-per-host --backend-connections-per-frontend --rlimit-nofile --rlimit-memlock --backend-request-buffer --backend-response-buffer --fastopen --no-kqueue --frontend-http2-read-timeout --frontend-http3-read-timeout --frontend-read-timeout --frontend-write-timeout --frontend-keep-alive-timeout --stream-read-timeout --stream-write-timeout --backend-read-timeout --backend-write-timeout --backend-connect-timeout --backend-keep-alive-timeout --listener-disable-timeout --frontend-http2-setting-timeout --backend-http2-settings-timeout --backend-max-backoff --ciphers --tls13-ciphers --client-ciphers --tls13-client-ciphers --ecdh-curves --insecure --cacert --private-key-passwd-file --subcert --dh-param-file --npn-list --verify-client --verify-client-cacert --verify-client-tolerate-expired --client-private-key-file --client-cert-file --tls-min-proto-version --tls-max-proto-version --tls-ticket-key-file --tls-ticket-key-memcached --tls-ticket-key-memcached-address-family --tls-ticket-key-memcached-interval --tls-ticket-key-memcached-max-retry --tls-ticket-key-memcached-max-fail --tls-ticket-key-cipher --tls-ticket-key-memcached-cert-file --tls-ticket-key-memcached-private-key-file --fetch-ocsp-response-file --ocsp-update-interval --ocsp-startup --no-verify-ocsp --no-ocsp --tls-session-cache-memcached --tls-session-cache-memcached-address-family --tls-session-cache-memcached-cert-file --tls-session-cache-memcached-private-key-file --tls-dyn-rec-warmup-threshold --tls-dyn-rec-idle-timeout --no-http2-cipher-block-list --client-no-http2-cipher-block-list --tls-sct-dir --psk-secrets --client-psk-secrets --tls-no-postpone-early-data --tls-max-early-data --frontend-http2-max-concurrent-streams --backend-http2-max-concurrent-streams --frontend-http2-window-size --frontend-http2-connection-window-size --backend-http2-window-size --backend-http2-connection-window-size --http2-no-cookie-crumbling --padding --no-server-push --frontend-http2-optimize-write-buffer-size --frontend-http2-optimize-window-size --frontend-http2-encoder-dynamic-table-size --frontend-http2-decoder-dynamic-table-size --backend-http2-encoder-dynamic-table-size --backend-http2-decoder-dynamic-table-size --http2-proxy --log-level --accesslog-file --accesslog-syslog --accesslog-format --accesslog-write-early --errorlog-file --errorlog-syslog --syslog-facility --add-x-forwarded-for --strip-incoming-x-forwarded-for --no-add-x-forwarded-proto --no-strip-incoming-x-forwarded-proto --add-forwarded --strip-incoming-forwarded --forwarded-by --forwarded-for --no-via --no-strip-incoming-early-data --no-location-rewrite --host-rewrite --altsvc --http2-altsvc --add-request-header --add-response-header --request-header-field-buffer --max-request-header-fields --response-header-field-buffer --max-response-header-fields --error-page --server-name --no-server-rewrite --redirect-https-port --api-max-request-body --dns-cache-timeout --dns-lookup-timeout --dns-max-try --frontend-max-requests --frontend-http2-dump-request-header --frontend-http2-dump-response-header --frontend-frame-debug --daemon --pid-file --user --single-process --max-worker-processes --worker-process-grace-shutdown-period --mruby-file --ignore-per-pattern-mruby-error --frontend-quic-idle-timeout --frontend-quic-debug-log --quic-bpf-program-file --frontend-quic-early-data --frontend-quic-qlog-dir --frontend-quic-require-token --frontend-quic-congestion-controller --frontend-quic-secret-file --quic-server-id --frontend-quic-initial-rtt --no-quic-bpf --frontend-http3-window-size --frontend-http3-connection-window-size --frontend-http3-max-window-size --frontend-http3-max-connection-window-size --frontend-http3-max-concurrent-streams --conf --include --version --help ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -41,7 +41,7 @@ import sys, os
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts'))
|
||||
sys.path.insert(0, os.path.abspath('@top_srcdir@/doc/_exts'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
@@ -50,7 +50,7 @@ sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts'))
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinxcontrib.rubydomain']
|
||||
extensions = ['rubydomain.rubydomain']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['@top_srcdir@/_templates']
|
||||
|
||||
42
doc/h2load.1
42
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2"
|
||||
.TH "H2LOAD" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -101,6 +101,7 @@ Default: \fB1\fP
|
||||
.TP
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)\-1.
|
||||
For QUIC, <N> is capped to 26 (roughly 64MiB).
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
@@ -120,13 +121,21 @@ Add/Override a header to the requests.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for TLSv1.2 or ealier. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
.sp
|
||||
Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls13\-ciphers=<SUITE>
|
||||
Set allowed cipher list for TLSv1.3. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
.sp
|
||||
Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
@@ -285,6 +294,16 @@ to buffering. Status code is \-1 for failed streams.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-qlog\-file\-base=<PATH>
|
||||
Enable qlog output and specify base file name for qlogs.
|
||||
Qlog is emitted for each connection.
|
||||
For a given base name "base", each output file name
|
||||
becomes "base.M.N.qlog" where M is worker ID and N is
|
||||
client ID (e.g. "base.0.3.qlog").
|
||||
Only effective in QUIC runs.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-connect\-to=<HOST>[:<PORT>]
|
||||
Host and port to connect instead of using the authority
|
||||
in <URI>.
|
||||
@@ -297,6 +316,23 @@ Specify request per second for each client. \fI\%\-\-rps\fP and
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-groups=<GROUPS>
|
||||
Specify the supported groups.
|
||||
.sp
|
||||
Default: \fBX25519:P\-256:P\-384:P\-521\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-udp\-gso
|
||||
Disable UDP GSO.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-udp\-payload\-size=<SIZE>
|
||||
Specify the maximum outgoing UDP datagram payload size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Output debug information.
|
||||
.UNINDENT
|
||||
|
||||
@@ -76,6 +76,7 @@ OPTIONS
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||
For QUIC, <N> is capped to 26 (roughly 64MiB).
|
||||
|
||||
Default: ``30``
|
||||
|
||||
@@ -92,11 +93,18 @@ OPTIONS
|
||||
|
||||
.. option:: --ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for TLSv1.2 or ealier. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
|
||||
Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
|
||||
|
||||
.. option:: --tls13-ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list for TLSv1.3. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
|
||||
Default: ``TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256``
|
||||
|
||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
@@ -240,6 +248,15 @@ OPTIONS
|
||||
appear slightly out of order with multiple threads due
|
||||
to buffering. Status code is -1 for failed streams.
|
||||
|
||||
.. option:: --qlog-file-base=<PATH>
|
||||
|
||||
Enable qlog output and specify base file name for qlogs.
|
||||
Qlog is emitted for each connection.
|
||||
For a given base name "base", each output file name
|
||||
becomes "base.M.N.qlog" where M is worker ID and N is
|
||||
client ID (e.g. "base.0.3.qlog").
|
||||
Only effective in QUIC runs.
|
||||
|
||||
.. option:: --connect-to=<HOST>[:<PORT>]
|
||||
|
||||
Host and port to connect instead of using the authority
|
||||
@@ -250,6 +267,20 @@ OPTIONS
|
||||
Specify request per second for each client. :option:`--rps` and
|
||||
:option:`--timing-script-file` are mutually exclusive.
|
||||
|
||||
.. option:: --groups=<GROUPS>
|
||||
|
||||
Specify the supported groups.
|
||||
|
||||
Default: ``X25519:P-256:P-384:P-521``
|
||||
|
||||
.. option:: --no-udp-gso
|
||||
|
||||
Disable UDP GSO.
|
||||
|
||||
.. option:: --max-udp-payload-size=<SIZE>
|
||||
|
||||
Specify the maximum outgoing UDP datagram payload size.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Output debug information.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2"
|
||||
.TH "NGHTTP" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 client
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 server
|
||||
.
|
||||
|
||||
260
doc/nghttpx.1
260
doc/nghttpx.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "Jul 18, 2021" "1.45.0-DEV" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "Oct 19, 2021" "1.46.0" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 proxy
|
||||
.
|
||||
@@ -35,7 +35,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
\fBnghttpx\fP [OPTIONS]... [<PRIVATE_KEY> <CERT>]
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
A reverse proxy for HTTP/2, and HTTP/1.
|
||||
A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <PRIVATE_KEY>
|
||||
@@ -140,12 +140,13 @@ parameters are: "proto=<PROTO>", "tls",
|
||||
"affinity=<METHOD>", "dns", "redirect\-if\-not\-tls",
|
||||
"upgrade\-scheme", "mruby=<PATH>",
|
||||
"read\-timeout=<DURATION>", "write\-timeout=<DURATION>",
|
||||
"group=<GROUP>", "group\-weight=<N>", and "weight=<N>".
|
||||
The parameter consists of keyword, and optionally
|
||||
followed by "=" and value. For example, the parameter
|
||||
"proto=h2" consists of the keyword "proto" and value
|
||||
"h2". The parameter "tls" consists of the keyword "tls"
|
||||
without value. Each parameter is described as follows.
|
||||
"group=<GROUP>", "group\-weight=<N>", "weight=<N>", and
|
||||
"dnf". The parameter consists of keyword, and
|
||||
optionally followed by "=" and value. For example, the
|
||||
parameter "proto=h2" consists of the keyword "proto" and
|
||||
value "h2". The parameter "tls" consists of the keyword
|
||||
"tls" without value. Each parameter is described as
|
||||
follows.
|
||||
.sp
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -276,6 +277,13 @@ weight than weight 2. If this parameter is omitted,
|
||||
weight becomes 1. "weight" is ignored if session
|
||||
affinity is enabled.
|
||||
.sp
|
||||
If "dnf" parameter is specified, an incoming request is
|
||||
not forwarded to a backend and just consumed along with
|
||||
the request body (actually a backend server never be
|
||||
contacted). It is expected that the HTTP response is
|
||||
generated by mruby script (see "mruby=<PATH>" parameter
|
||||
above). "dnf" is an abbreviation of "do not forward".
|
||||
.sp
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. In order to include ":"
|
||||
in <PATTERN>, one has to specify "%3A" (which is
|
||||
@@ -323,6 +331,12 @@ To accept PROXY protocol version 1 and 2 on frontend
|
||||
connection, specify "proxyproto" parameter. This is
|
||||
disabled by default.
|
||||
.sp
|
||||
To receive HTTP/3 (QUIC) traffic, specify "quic"
|
||||
parameter. It makes nghttpx listen on UDP port rather
|
||||
than TCP port. UNIX domain socket, "api", and
|
||||
"healthmon" parameters cannot be used with "quic"
|
||||
parameter.
|
||||
.sp
|
||||
Default: \fB*,3000\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
@@ -489,6 +503,15 @@ Default: \fB0\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-rlimit\-memlock=<N>
|
||||
Set maximum number of bytes of memory that may be locked
|
||||
into RAM. If 0 is given, nghttpx does not set the
|
||||
limit.
|
||||
.sp
|
||||
Default: \fB0\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-backend\-request\-buffer=<SIZE>
|
||||
Set buffer size used to store backend request.
|
||||
.sp
|
||||
@@ -528,6 +551,13 @@ Default: \fB3m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/3 frontend connection.
|
||||
.sp
|
||||
Default: \fB3m\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-read\-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
.sp
|
||||
@@ -1068,12 +1098,13 @@ option. But be aware its implications.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-tls\-no\-postpone\-early\-data
|
||||
By default, nghttpx postpones forwarding HTTP requests
|
||||
sent in early data, including those sent in partially in
|
||||
it, until TLS handshake finishes. If all backend server
|
||||
recognizes "Early\-Data" header field, using this option
|
||||
makes nghttpx not postpone forwarding request and get
|
||||
full potential of 0\-RTT data.
|
||||
By default, except for QUIC connections, nghttpx
|
||||
postpones forwarding HTTP requests sent in early data,
|
||||
including those sent in partially in it, until TLS
|
||||
handshake finishes. If all backend server recognizes
|
||||
"Early\-Data" header field, using this option makes
|
||||
nghttpx not postpone forwarding request and get full
|
||||
potential of 0\-RTT data.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1478,13 +1509,21 @@ not be altered regardless of this option.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt\-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: \fI\%\-\-altsvc\fP=h2,443
|
||||
alternative service. <HOST>, <ORIGIN> and <PARAMS> are
|
||||
optional. Empty <HOST> and <ORIGIN> are allowed and
|
||||
they are treated as nothing is specified. They are
|
||||
advertised in alt\-svc header field only in HTTP/1.1
|
||||
frontend. This option can be used multiple times to
|
||||
specify multiple alternative services.
|
||||
Example: \fI\%\-\-altsvc\fP="h2,443,,,ma=3600; persist=1"
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-http2\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
Just like \fI\%\-\-altsvc\fP option, but this altsvc is only sent
|
||||
in HTTP/2 frontend.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1675,6 +1714,33 @@ process. nghttpx still spawns additional process if
|
||||
neverbleed is used. In the single process mode, the
|
||||
signal handling feature is disabled.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-worker\-processes=<N>
|
||||
The maximum number of worker processes. nghttpx spawns
|
||||
new worker process when it reloads its configuration.
|
||||
The previous worker process enters graceful termination
|
||||
period and will terminate when it finishes handling the
|
||||
existing connections. However, if reloading
|
||||
configurations happen very frequently, the worker
|
||||
processes might be piled up if they take a bit long time
|
||||
to finish the existing connections. With this option,
|
||||
if the number of worker processes exceeds the given
|
||||
value, the oldest worker process is terminated
|
||||
immediately. Specifying 0 means no limit and it is the
|
||||
default behaviour.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-worker\-process\-grace\-shutdown\-period=<DURATION>
|
||||
Maximum period for a worker process to terminate
|
||||
gracefully. When a worker process enters in graceful
|
||||
shutdown period (e.g., when nghttpx reloads its
|
||||
configuration) and it does not finish handling the
|
||||
existing connections in the given period of time, it is
|
||||
immediately terminated. Specifying 0 means no limit and
|
||||
it is the default behaviour.
|
||||
.UNINDENT
|
||||
.SS Scripting
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1688,6 +1754,160 @@ Ignore mruby compile error for per\-pattern mruby script
|
||||
file. If error occurred, it is treated as if no mruby
|
||||
file were specified for the pattern.
|
||||
.UNINDENT
|
||||
.SS HTTP/3 and QUIC
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-idle\-timeout=<DURATION>
|
||||
Specify an idle timeout for QUIC connection.
|
||||
.sp
|
||||
Default: \fB30s\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-debug\-log
|
||||
Output QUIC debug log to \fI/dev/stderr.\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-quic\-bpf\-program\-file=<PATH>
|
||||
Specify a path to eBPF program file reuseport_kern.o to
|
||||
direct an incoming QUIC UDP datagram to a correct
|
||||
socket.
|
||||
.sp
|
||||
Default: \fB/usr/local/lib/nghttp2/reuseport_kern.o\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-early\-data
|
||||
Enable early data on frontend QUIC connections. nghttpx
|
||||
sends "Early\-Data" header field to a backend server if a
|
||||
request is received in early data and handshake has not
|
||||
finished. All backend servers should deal with possibly
|
||||
replayed requests.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-qlog\-dir=<DIR>
|
||||
Specify a directory where a qlog file is written for
|
||||
frontend QUIC connections. A qlog file is created per
|
||||
each QUIC connection. The file name is ISO8601 basic
|
||||
format, followed by "\-", server Source Connection ID and
|
||||
".qlog".
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-require\-token
|
||||
Require an address validation token for a frontend QUIC
|
||||
connection. Server sends a token in Retry packet or
|
||||
NEW_TOKEN frame in the previous connection.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-congestion\-controller=<CC>
|
||||
Specify a congestion controller algorithm for a frontend
|
||||
QUIC connection. <CC> should be either "cubic" or
|
||||
"bbr".
|
||||
.sp
|
||||
Default: \fBcubic\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-secret\-file=<PATH>
|
||||
Path to file that contains secure random data to be used
|
||||
as QUIC keying materials. It is used to derive keys for
|
||||
encrypting tokens and Connection IDs. It is not used to
|
||||
encrypt QUIC packets. Each line of this file must
|
||||
contain exactly 136 bytes hex\-encoded string (when
|
||||
decoded the byte string is 68 bytes long). The first 2
|
||||
bits of decoded byte string are used to identify the
|
||||
keying material. An empty line or a line which starts
|
||||
\(aq#\(aq is ignored. The file can contain more than one
|
||||
keying materials. Because the identifier is 2 bits, at
|
||||
most 4 keying materials are read and the remaining data
|
||||
is discarded. The first keying material in the file is
|
||||
primarily used for encryption and decryption for new
|
||||
connection. The other ones are used to decrypt data for
|
||||
the existing connections. Specifying multiple keying
|
||||
materials enables key rotation. Please note that key
|
||||
rotation does not occur automatically. User should
|
||||
update files or change options values and restart
|
||||
nghttpx gracefully. If opening or reading given file
|
||||
fails, all loaded keying materials are discarded and it
|
||||
is treated as if none of this option is given. If this
|
||||
option is not given or an error occurred while opening
|
||||
or reading a file, a keying material is generated
|
||||
internally on startup and reload.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-quic\-server\-id=<HEXSTRING>
|
||||
Specify server ID encoded in Connection ID to identify
|
||||
this particular server instance. Connection ID is
|
||||
encrypted and this part is not visible in public. It
|
||||
must be 4 bytes long and must be encoded in hex string
|
||||
(which is 8 bytes long). If this option is omitted, a
|
||||
random server ID is generated on startup and
|
||||
configuration reload.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-quic\-initial\-rtt=<DURATION>
|
||||
Specify the initial RTT of the frontend QUIC connection.
|
||||
.sp
|
||||
Default: \fB333ms\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-quic\-bpf
|
||||
Disable eBPF.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-window\-size=<SIZE>
|
||||
Sets the per\-stream initial window size of HTTP/3
|
||||
frontend connection.
|
||||
.sp
|
||||
Default: \fB256K\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-connection\-window\-size=<SIZE>
|
||||
Sets the per\-connection window size of HTTP/3 frontend
|
||||
connection.
|
||||
.sp
|
||||
Default: \fB1M\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-max\-window\-size=<SIZE>
|
||||
Sets the maximum per\-stream window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by \fI\%\-\-frontend\-http3\-window\-size\fP
|
||||
and the window size grows up to <SIZE> bytes.
|
||||
.sp
|
||||
Default: \fB6M\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-max\-connection\-window\-size=<SIZE>
|
||||
Sets the maximum per\-connection window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by
|
||||
\fI\%\-\-frontend\-http3\-connection\-window\-size\fP and the window
|
||||
size grows up to <SIZE> bytes.
|
||||
.sp
|
||||
Default: \fB8M\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-frontend\-http3\-max\-concurrent\-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
frontend HTTP/3 connection.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.SS Misc
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
||||
@@ -14,7 +14,7 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
A reverse proxy for HTTP/2, and HTTP/1.
|
||||
A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
@@ -124,12 +124,13 @@ Connections
|
||||
"affinity=<METHOD>", "dns", "redirect-if-not-tls",
|
||||
"upgrade-scheme", "mruby=<PATH>",
|
||||
"read-timeout=<DURATION>", "write-timeout=<DURATION>",
|
||||
"group=<GROUP>", "group-weight=<N>", and "weight=<N>".
|
||||
The parameter consists of keyword, and optionally
|
||||
followed by "=" and value. For example, the parameter
|
||||
"proto=h2" consists of the keyword "proto" and value
|
||||
"h2". The parameter "tls" consists of the keyword "tls"
|
||||
without value. Each parameter is described as follows.
|
||||
"group=<GROUP>", "group-weight=<N>", "weight=<N>", and
|
||||
"dnf". The parameter consists of keyword, and
|
||||
optionally followed by "=" and value. For example, the
|
||||
parameter "proto=h2" consists of the keyword "proto" and
|
||||
value "h2". The parameter "tls" consists of the keyword
|
||||
"tls" without value. Each parameter is described as
|
||||
follows.
|
||||
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
@@ -260,6 +261,13 @@ Connections
|
||||
weight becomes 1. "weight" is ignored if session
|
||||
affinity is enabled.
|
||||
|
||||
If "dnf" parameter is specified, an incoming request is
|
||||
not forwarded to a backend and just consumed along with
|
||||
the request body (actually a backend server never be
|
||||
contacted). It is expected that the HTTP response is
|
||||
generated by mruby script (see "mruby=<PATH>" parameter
|
||||
above). "dnf" is an abbreviation of "do not forward".
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. In order to include ":"
|
||||
in <PATTERN>, one has to specify "%3A" (which is
|
||||
@@ -307,6 +315,12 @@ Connections
|
||||
connection, specify "proxyproto" parameter. This is
|
||||
disabled by default.
|
||||
|
||||
To receive HTTP/3 (QUIC) traffic, specify "quic"
|
||||
parameter. It makes nghttpx listen on UDP port rather
|
||||
than TCP port. UNIX domain socket, "api", and
|
||||
"healthmon" parameters cannot be used with "quic"
|
||||
parameter.
|
||||
|
||||
|
||||
Default: ``*,3000``
|
||||
|
||||
@@ -458,6 +472,14 @@ Performance
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --rlimit-memlock=<N>
|
||||
|
||||
Set maximum number of bytes of memory that may be locked
|
||||
into RAM. If 0 is given, nghttpx does not set the
|
||||
limit.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-request-buffer=<SIZE>
|
||||
|
||||
Set buffer size used to store backend request.
|
||||
@@ -495,6 +517,12 @@ Timeout
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-http3-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/3 frontend connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
@@ -983,12 +1011,13 @@ SSL/TLS
|
||||
|
||||
.. option:: --tls-no-postpone-early-data
|
||||
|
||||
By default, nghttpx postpones forwarding HTTP requests
|
||||
sent in early data, including those sent in partially in
|
||||
it, until TLS handshake finishes. If all backend server
|
||||
recognizes "Early-Data" header field, using this option
|
||||
makes nghttpx not postpone forwarding request and get
|
||||
full potential of 0-RTT data.
|
||||
By default, except for QUIC connections, nghttpx
|
||||
postpones forwarding HTTP requests sent in early data,
|
||||
including those sent in partially in it, until TLS
|
||||
handshake finishes. If all backend server recognizes
|
||||
"Early-Data" header field, using this option makes
|
||||
nghttpx not postpone forwarding request and get full
|
||||
potential of 0-RTT data.
|
||||
|
||||
.. option:: --tls-max-early-data=<SIZE>
|
||||
|
||||
@@ -1338,14 +1367,21 @@ HTTP
|
||||
mode. When :option:`--http2-proxy` is used, these headers will
|
||||
not be altered regardless of this option.
|
||||
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: :option:`--altsvc`\=h2,443
|
||||
alternative service. <HOST>, <ORIGIN> and <PARAMS> are
|
||||
optional. Empty <HOST> and <ORIGIN> are allowed and
|
||||
they are treated as nothing is specified. They are
|
||||
advertised in alt-svc header field only in HTTP/1.1
|
||||
frontend. This option can be used multiple times to
|
||||
specify multiple alternative services.
|
||||
Example: :option:`--altsvc`\="h2,443,,,ma=3600; persist=1"
|
||||
|
||||
.. option:: --http2-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
||||
|
||||
Just like :option:`--altsvc` option, but this altsvc is only sent
|
||||
in HTTP/2 frontend.
|
||||
|
||||
.. option:: --add-request-header=<HEADER>
|
||||
|
||||
@@ -1526,6 +1562,31 @@ Process
|
||||
neverbleed is used. In the single process mode, the
|
||||
signal handling feature is disabled.
|
||||
|
||||
.. option:: --max-worker-processes=<N>
|
||||
|
||||
The maximum number of worker processes. nghttpx spawns
|
||||
new worker process when it reloads its configuration.
|
||||
The previous worker process enters graceful termination
|
||||
period and will terminate when it finishes handling the
|
||||
existing connections. However, if reloading
|
||||
configurations happen very frequently, the worker
|
||||
processes might be piled up if they take a bit long time
|
||||
to finish the existing connections. With this option,
|
||||
if the number of worker processes exceeds the given
|
||||
value, the oldest worker process is terminated
|
||||
immediately. Specifying 0 means no limit and it is the
|
||||
default behaviour.
|
||||
|
||||
.. option:: --worker-process-grace-shutdown-period=<DURATION>
|
||||
|
||||
Maximum period for a worker process to terminate
|
||||
gracefully. When a worker process enters in graceful
|
||||
shutdown period (e.g., when nghttpx reloads its
|
||||
configuration) and it does not finish handling the
|
||||
existing connections in the given period of time, it is
|
||||
immediately terminated. Specifying 0 means no limit and
|
||||
it is the default behaviour.
|
||||
|
||||
|
||||
Scripting
|
||||
~~~~~~~~~
|
||||
@@ -1541,6 +1602,147 @@ Scripting
|
||||
file were specified for the pattern.
|
||||
|
||||
|
||||
HTTP/3 and QUIC
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: --frontend-quic-idle-timeout=<DURATION>
|
||||
|
||||
Specify an idle timeout for QUIC connection.
|
||||
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --frontend-quic-debug-log
|
||||
|
||||
Output QUIC debug log to */dev/stderr.*
|
||||
|
||||
.. option:: --quic-bpf-program-file=<PATH>
|
||||
|
||||
Specify a path to eBPF program file reuseport_kern.o to
|
||||
direct an incoming QUIC UDP datagram to a correct
|
||||
socket.
|
||||
|
||||
Default: ``/usr/local/lib/nghttp2/reuseport_kern.o``
|
||||
|
||||
.. option:: --frontend-quic-early-data
|
||||
|
||||
Enable early data on frontend QUIC connections. nghttpx
|
||||
sends "Early-Data" header field to a backend server if a
|
||||
request is received in early data and handshake has not
|
||||
finished. All backend servers should deal with possibly
|
||||
replayed requests.
|
||||
|
||||
.. option:: --frontend-quic-qlog-dir=<DIR>
|
||||
|
||||
Specify a directory where a qlog file is written for
|
||||
frontend QUIC connections. A qlog file is created per
|
||||
each QUIC connection. The file name is ISO8601 basic
|
||||
format, followed by "-", server Source Connection ID and
|
||||
".qlog".
|
||||
|
||||
.. option:: --frontend-quic-require-token
|
||||
|
||||
Require an address validation token for a frontend QUIC
|
||||
connection. Server sends a token in Retry packet or
|
||||
NEW_TOKEN frame in the previous connection.
|
||||
|
||||
.. option:: --frontend-quic-congestion-controller=<CC>
|
||||
|
||||
Specify a congestion controller algorithm for a frontend
|
||||
QUIC connection. <CC> should be either "cubic" or
|
||||
"bbr".
|
||||
|
||||
Default: ``cubic``
|
||||
|
||||
.. option:: --frontend-quic-secret-file=<PATH>
|
||||
|
||||
Path to file that contains secure random data to be used
|
||||
as QUIC keying materials. It is used to derive keys for
|
||||
encrypting tokens and Connection IDs. It is not used to
|
||||
encrypt QUIC packets. Each line of this file must
|
||||
contain exactly 136 bytes hex-encoded string (when
|
||||
decoded the byte string is 68 bytes long). The first 2
|
||||
bits of decoded byte string are used to identify the
|
||||
keying material. An empty line or a line which starts
|
||||
'#' is ignored. The file can contain more than one
|
||||
keying materials. Because the identifier is 2 bits, at
|
||||
most 4 keying materials are read and the remaining data
|
||||
is discarded. The first keying material in the file is
|
||||
primarily used for encryption and decryption for new
|
||||
connection. The other ones are used to decrypt data for
|
||||
the existing connections. Specifying multiple keying
|
||||
materials enables key rotation. Please note that key
|
||||
rotation does not occur automatically. User should
|
||||
update files or change options values and restart
|
||||
nghttpx gracefully. If opening or reading given file
|
||||
fails, all loaded keying materials are discarded and it
|
||||
is treated as if none of this option is given. If this
|
||||
option is not given or an error occurred while opening
|
||||
or reading a file, a keying material is generated
|
||||
internally on startup and reload.
|
||||
|
||||
.. option:: --quic-server-id=<HEXSTRING>
|
||||
|
||||
Specify server ID encoded in Connection ID to identify
|
||||
this particular server instance. Connection ID is
|
||||
encrypted and this part is not visible in public. It
|
||||
must be 4 bytes long and must be encoded in hex string
|
||||
(which is 8 bytes long). If this option is omitted, a
|
||||
random server ID is generated on startup and
|
||||
configuration reload.
|
||||
|
||||
.. option:: --frontend-quic-initial-rtt=<DURATION>
|
||||
|
||||
Specify the initial RTT of the frontend QUIC connection.
|
||||
|
||||
Default: ``333ms``
|
||||
|
||||
.. option:: --no-quic-bpf
|
||||
|
||||
Disable eBPF.
|
||||
|
||||
.. option:: --frontend-http3-window-size=<SIZE>
|
||||
|
||||
Sets the per-stream initial window size of HTTP/3
|
||||
frontend connection.
|
||||
|
||||
Default: ``256K``
|
||||
|
||||
.. option:: --frontend-http3-connection-window-size=<SIZE>
|
||||
|
||||
Sets the per-connection window size of HTTP/3 frontend
|
||||
connection.
|
||||
|
||||
Default: ``1M``
|
||||
|
||||
.. option:: --frontend-http3-max-window-size=<SIZE>
|
||||
|
||||
Sets the maximum per-stream window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by :option:`--frontend-http3-window-size`
|
||||
and the window size grows up to <SIZE> bytes.
|
||||
|
||||
Default: ``6M``
|
||||
|
||||
.. option:: --frontend-http3-max-connection-window-size=<SIZE>
|
||||
|
||||
Sets the maximum per-connection window size of HTTP/3
|
||||
frontend connection. The window size is adjusted based
|
||||
on the receiving rate of stream data. The initial value
|
||||
is the value specified by
|
||||
:option:`--frontend-http3-connection-window-size` and the window
|
||||
size grows up to <SIZE> bytes.
|
||||
|
||||
Default: ``8M``
|
||||
|
||||
.. option:: --frontend-http3-max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
frontend HTTP/3 connection.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Misc
|
||||
~~~~
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Architecture
|
||||
|
||||
The most notable point in nghttp2 library architecture is it does not
|
||||
perform any I/O. nghttp2 only performs HTTP/2 protocol stuff based on
|
||||
input byte strings. It will calls callback functions set by
|
||||
input byte strings. It will call callback functions set by
|
||||
applications while processing input. The output of nghttp2 is just
|
||||
byte string. An application is responsible to send these output to
|
||||
the remote peer. The callback functions may be called while producing
|
||||
|
||||
@@ -131,3 +131,12 @@ specify ``unix:`` followed by the path to UNIX domain socket. For
|
||||
example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use
|
||||
``--base-uri=unix:/tmp/nghttpx.sock``. h2load uses scheme, host and
|
||||
port in the first URI in command-line or input file.
|
||||
|
||||
HTTP/3
|
||||
------
|
||||
|
||||
h2load supports HTTP/3 if it is built with HTTP/3 enabled. HTTP/3
|
||||
support is experimental.
|
||||
|
||||
In order to send HTTP/3 request, specify ``h3`` to
|
||||
:option:`--npn-list`.
|
||||
|
||||
@@ -14,8 +14,8 @@ Default mode
|
||||
|
||||
If nghttpx is invoked without :option:`--http2-proxy`, it operates in
|
||||
default mode. In this mode, it works as reverse proxy (gateway) for
|
||||
both HTTP/2 and HTTP/1 clients to backend servers. This is also known
|
||||
as "HTTP/2 router".
|
||||
HTTP/3, HTTP/2 and HTTP/1 clients to backend servers. This is also
|
||||
known as "HTTP/2 router".
|
||||
|
||||
By default, frontend connection is encrypted using SSL/TLS. So
|
||||
server's private key and certificate must be supplied to the command
|
||||
@@ -28,6 +28,9 @@ the frontend, and an HTTP/1 connection can be upgraded to HTTP/2 using
|
||||
HTTP Upgrade. Starting HTTP/2 connection by sending HTTP/2 connection
|
||||
preface is also supported.
|
||||
|
||||
In order to receive HTTP/3 traffic, use ``quic`` parameter in
|
||||
:option:`--frontend` option (.e.g, ``--frontend='*,443;quic'``)
|
||||
|
||||
nghttpx can listen on multiple frontend addresses. This is achieved
|
||||
by using multiple :option:`--frontend` options. For each frontend
|
||||
address, TLS can be enabled or disabled.
|
||||
@@ -509,6 +512,60 @@ Bootstrapping WebSockets with HTTP/2 for both frontend and backend
|
||||
connections. This feature is enabled by default and no configuration
|
||||
is required.
|
||||
|
||||
WebSockets over HTTP/3 is also supported.
|
||||
|
||||
HTTP/3
|
||||
------
|
||||
|
||||
nghttpx supports HTTP/3 if it is built with HTTP/3 support enabled.
|
||||
HTTP/3 support is experimental.
|
||||
|
||||
In order to listen UDP port to receive HTTP/3 traffic,
|
||||
:option:`--frontend` option must have ``quic`` parameter:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
frontend=*,443;quic
|
||||
|
||||
The above example makes nghttpx receive HTTP/3 traffic on UDP
|
||||
port 443.
|
||||
|
||||
nghttpx does not support HTTP/3 on backend connection.
|
||||
|
||||
Hot swapping (SIGUSR2) or configuration reload (SIGHUP) require eBPF
|
||||
program. Without eBPF, old worker processes keep getting HTTP/3
|
||||
traffic and do not work as intended. The QUIC keying material to
|
||||
encrypt Connection ID must be set with
|
||||
:option:`--frontend-quic-secret-file` and must provide the existing
|
||||
keys in order to keep the existing connections alive during reload.
|
||||
|
||||
The construction of Connection ID closely follows Block Cipher CID
|
||||
Algorithm described in `QUIC-LB draft
|
||||
<https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers>`_.
|
||||
A Connection ID that nghttpx generates is always 20 bytes long. It
|
||||
uses first 2 bits as a configuration ID. The remaining bits in the
|
||||
first byte are reserved and random. The next 4 bytes are server ID.
|
||||
The next 4 bytes are used to route UDP datagram to a correct
|
||||
``SO_REUSEPORT`` socket. The remaining bytes are randomly generated.
|
||||
The server ID and the next 12 bytes are encrypted with AES-ECB. The
|
||||
key is derived from the keying materials stored in a file specified by
|
||||
:option:`--frontend-quic-secret-file`. The first 2 bits of keying
|
||||
material in the file is used as a configuration ID. The remaining
|
||||
bits and following 3 bytes are reserved and unused. The next 32 bytes
|
||||
are used as an initial secret. The remaining 32 bytes are used as a
|
||||
salt. The encryption key is generated by `HKDF
|
||||
<https://datatracker.ietf.org/doc/html/rfc5869>`_ with SHA256 and
|
||||
these keying materials and ``connection id encryption key`` as info.
|
||||
|
||||
In order announce that HTTP/3 endpoint is available, you should
|
||||
specify alt-svc header field. For example, the following options send
|
||||
alt-svc header field in HTTP/1.1 and HTTP/2 response:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
altsvc=h3,443,,,ma=3600
|
||||
http2-altsvc=h3,443,,,ma=3600
|
||||
|
||||
Migration from nghttpx v1.18.x or earlier
|
||||
-----------------------------------------
|
||||
|
||||
|
||||
78
docker/Dockerfile
Normal file
78
docker/Dockerfile
Normal file
@@ -0,0 +1,78 @@
|
||||
FROM debian:11 as build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
git clang make binutils autoconf automake autotools-dev libtool \
|
||||
pkg-config \
|
||||
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
|
||||
libelf-dev
|
||||
|
||||
RUN git clone --depth 1 -b OpenSSL_1_1_1m+quic https://github.com/quictls/openssl && \
|
||||
cd openssl && \
|
||||
./config --openssldir=/etc/ssl && \
|
||||
make -j$(nproc) && \
|
||||
make install_sw && \
|
||||
cd .. && \
|
||||
rm -rf openssl
|
||||
|
||||
RUN git clone --depth 1 https://github.com/ngtcp2/nghttp3 && \
|
||||
cd nghttp3 && \
|
||||
autoreconf -i && \
|
||||
./configure --enable-lib-only && \
|
||||
make -j$(nproc) && \
|
||||
make install-strip && \
|
||||
cd .. && \
|
||||
rm -rf nghttp3
|
||||
|
||||
RUN git clone --depth 1 -b v0.1.0 https://github.com/ngtcp2/ngtcp2 && \
|
||||
cd ngtcp2 && \
|
||||
autoreconf -i && \
|
||||
./configure --enable-lib-only \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a -ldl -lpthread" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
|
||||
make -j$(nproc) && \
|
||||
make install-strip && \
|
||||
cd .. && \
|
||||
rm -rf ngtcp2
|
||||
|
||||
RUN git clone --depth 1 -b v0.4.0 https://github.com/libbpf/libbpf && \
|
||||
cd libbpf && \
|
||||
PREFIX=/usr/local make -C src install && \
|
||||
cd .. && \
|
||||
rm -rf libbpf
|
||||
|
||||
RUN git clone --depth 1 https://github.com/nghttp2/nghttp2.git && \
|
||||
cd nghttp2 && \
|
||||
git submodule update --init && \
|
||||
autoreconf -i && \
|
||||
./configure --disable-examples --disable-hpack-tools \
|
||||
--disable-python-bindings --with-mruby --with-neverbleed \
|
||||
--enable-http3 --with-libbpf \
|
||||
CC=clang CXX=clang++ \
|
||||
LIBTOOL_LDFLAGS="-static-libtool-libs" \
|
||||
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a -ldl -pthread" \
|
||||
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" \
|
||||
LDFLAGS="-static-libgcc -static-libstdc++" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
|
||||
make -j$(nproc) install-strip && \
|
||||
cd .. && \
|
||||
rm -rf nghttp2
|
||||
|
||||
FROM gcr.io/distroless/base-debian11
|
||||
|
||||
COPY --from=build \
|
||||
/usr/local/share/nghttp2/ \
|
||||
/usr/local/share/nghttp2/
|
||||
COPY --from=build \
|
||||
/usr/local/bin/h2load \
|
||||
/usr/local/bin/nghttpx \
|
||||
/usr/local/bin/nghttp \
|
||||
/usr/local/bin/nghttpd \
|
||||
/usr/local/bin/
|
||||
COPY --from=build /usr/local/lib/nghttp2/reuseport_kern.o \
|
||||
/usr/local/lib/nghttp2/
|
||||
25
docker/README.rst
Normal file
25
docker/README.rst
Normal file
@@ -0,0 +1,25 @@
|
||||
Dockerfile
|
||||
==========
|
||||
|
||||
Dockerfile creates the applications bundled with nghttp2.
|
||||
These applications are:
|
||||
|
||||
- nghttp
|
||||
- nghttpd
|
||||
- nghttpx
|
||||
- h2load
|
||||
|
||||
HTTP/3 and eBPF features are enabled.
|
||||
|
||||
In order to run nghttpx with HTTP/3 endpoint, you need to run the
|
||||
image with the escalated privilege and higher memlock value. Here is
|
||||
the example command-line to run nghttpx to listen to HTTP/3 on port
|
||||
443, assuming that the current directory contains a private key and a
|
||||
certificate in server.key and server.crt respectively :
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ docker run --rm -it -v $PWD:/shared --net=host --privileged \
|
||||
nghttp2 nghttpx \
|
||||
/shared/server.key /shared/server.crt \
|
||||
-f'*,443;quic' --rlimit-memlock 524288
|
||||
@@ -380,6 +380,10 @@ static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||
#endif /* !OPENSSL_NO_NEXTPROTONEG */
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
|
||||
}
|
||||
|
||||
static void ssl_handshake(SSL *ssl, int fd) {
|
||||
@@ -544,7 +548,7 @@ static void fetch_uri(const struct URI *uri) {
|
||||
if (fd == -1) {
|
||||
die("Could not open file descriptor");
|
||||
}
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
ssl_ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (ssl_ctx == NULL) {
|
||||
dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
@@ -715,8 +719,18 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, 0);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||
/* No explicit initialization is required. */
|
||||
#elif defined(OPENSSL_IS_BORINGSSL)
|
||||
CRYPTO_library_init();
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) */
|
||||
OPENSSL_config(NULL);
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) */
|
||||
|
||||
rv = parse_uri(&uri, argv[1]);
|
||||
if (rv != 0) {
|
||||
|
||||
@@ -44,7 +44,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
|
||||
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final);
|
||||
|
||||
int main() {
|
||||
int main(void) {
|
||||
int rv;
|
||||
nghttp2_hd_deflater *deflater;
|
||||
nghttp2_hd_inflater *inflater;
|
||||
|
||||
@@ -328,7 +328,7 @@ static int select_next_proto_cb(SSL *ssl, unsigned char **out,
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX *create_ssl_ctx(void) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
ssl_ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
@@ -617,8 +617,18 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||
/* No explicit initialization is required. */
|
||||
#elif defined(OPENSSL_IS_BORINGSSL)
|
||||
CRYPTO_library_init();
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) */
|
||||
OPENSSL_config(NULL);
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) */
|
||||
|
||||
run(argv[1]);
|
||||
return 0;
|
||||
|
||||
@@ -142,9 +142,8 @@ static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
ssl_ctx = SSL_CTX_new(TLS_server_method());
|
||||
if (!ssl_ctx) {
|
||||
errx(1, "Could not create SSL/TLS context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
@@ -153,14 +152,23 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
|
||||
errx(1, "SSL_CTX_set1_curves_list failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
{
|
||||
EC_KEY *ecdh;
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
}
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
|
||||
errx(1, "Could not read private key file %s", key_file);
|
||||
@@ -809,8 +817,18 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||
/* No explicit initialization is required. */
|
||||
#elif defined(OPENSSL_IS_BORINGSSL)
|
||||
CRYPTO_library_init();
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) */
|
||||
OPENSSL_config(NULL);
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) */
|
||||
|
||||
run(argv[1], argv[2], argv[3]);
|
||||
return 0;
|
||||
|
||||
29
genmethodchartbl.py
Executable file
29
genmethodchartbl.py
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
if i < 0x21:
|
||||
return \
|
||||
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
|
||||
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
|
||||
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
|
||||
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
|
||||
'SPC '][i]
|
||||
elif i == 0x7f:
|
||||
return 'DEL '
|
||||
|
||||
for i in range(256):
|
||||
if chr(i) in ["!" , "#" , "$" , "%" , "&" , "'" , "*",
|
||||
"+" , "-" , "." , "^" , "_" , "`" , "|" , "~"] or\
|
||||
('0' <= chr(i) and chr(i) <= '9') or \
|
||||
('A' <= chr(i) and chr(i) <= 'Z') or \
|
||||
('a' <= chr(i) and chr(i) <= 'z'):
|
||||
sys.stdout.write('1 /* {} */, '.format(chr(i)))
|
||||
elif (0x21 <= i and i < 0x7f):
|
||||
sys.stdout.write('0 /* {} */, '.format(chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('0 /* {} */, '.format(hex(i)))
|
||||
else:
|
||||
sys.stdout.write('0 /* {} */, '.format(name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
sys.stdout.write('\n')
|
||||
@@ -177,6 +177,27 @@ OPTIONS = [
|
||||
"tls13-ciphers",
|
||||
"tls13-client-ciphers",
|
||||
"no-strip-incoming-early-data",
|
||||
"quic-bpf-program-file",
|
||||
"no-quic-bpf",
|
||||
"http2-altsvc",
|
||||
"frontend-http3-read-timeout",
|
||||
"frontend-quic-idle-timeout",
|
||||
"frontend-quic-debug-log",
|
||||
"frontend-http3-window-size",
|
||||
"frontend-http3-connection-window-size",
|
||||
"frontend-http3-max-window-size",
|
||||
"frontend-http3-max-connection-window-size",
|
||||
"frontend-http3-max-concurrent-streams",
|
||||
"frontend-quic-early-data",
|
||||
"frontend-quic-qlog-dir",
|
||||
"frontend-quic-require-token",
|
||||
"frontend-quic-congestion-controller",
|
||||
"quic-server-id",
|
||||
"frontend-quic-secret-file",
|
||||
"rlimit-memlock",
|
||||
"max-worker-processes",
|
||||
"worker-process-grace-shutdown-period",
|
||||
"frontend-quic-initial-rtt",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
||||
23
genpathchartbl.py
Executable file
23
genpathchartbl.py
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
if i < 0x21:
|
||||
return \
|
||||
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
|
||||
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
|
||||
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
|
||||
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
|
||||
'SPC '][i]
|
||||
elif i == 0x7f:
|
||||
return 'DEL '
|
||||
|
||||
for i in range(256):
|
||||
if (0x21 <= i and i < 0x7f):
|
||||
sys.stdout.write('1 /* {} */, '.format(chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('1 /* {} */, '.format(hex(i)))
|
||||
else:
|
||||
sys.stdout.write('0 /* {} */, '.format(name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
sys.stdout.write('\n')
|
||||
@@ -20,8 +20,6 @@ for i in range(256):
|
||||
sys.stdout.write('1 /* {} */, '.format(chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('1 /* {} */, '.format(hex(i)))
|
||||
elif 0 == i:
|
||||
sys.stdout.write('1 /* NUL */, ')
|
||||
else:
|
||||
sys.stdout.write('0 /* {} */, '.format(name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
|
||||
@@ -5,14 +5,15 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/websocket"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
// TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
|
||||
@@ -34,7 +35,7 @@ func TestH1H1PlainGET(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with
|
||||
// Connetion: close request header field works.
|
||||
// Connection: close request header field works.
|
||||
func TestH1H1PlainGETClose(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
@@ -1171,3 +1172,31 @@ Content-Length: 1000000
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1ChunkedEndsPrematurely tests that an HTTP/1.1 request fails
|
||||
// if the backend chunked encoded response ends prematurely.
|
||||
func TestH1H1ChunkedEndsPrematurely(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
conn, bufrw, err := hj.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
_, err := st.http1(requestParam{
|
||||
name: "TestH1H1ChunkedEndsPrematurely",
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("st.http1() should fail")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +565,7 @@ func TestH2H1BadResponseCL(t *testing.T) {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
want := http2.ErrCodeProtocol
|
||||
want := http2.ErrCodeInternal
|
||||
if res.errCode != want {
|
||||
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
|
||||
}
|
||||
@@ -2838,3 +2838,35 @@ func TestH2ResponseBeforeRequestEnd(t *testing.T) {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1ChunkedEndsPrematurely tests that a stream is reset if the
|
||||
// backend chunked encoded response ends prematurely.
|
||||
func TestH2H1ChunkedEndsPrematurely(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
conn, bufrw, err := hj.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ChunkedEndsPrematurely",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
|
||||
t.Errorf("res.errCode = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3054,7 +3054,7 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||
* @function
|
||||
*
|
||||
* Processes data |in| as an input from the remote endpoint. The
|
||||
* |inlen| indicates the number of bytes in the |in|.
|
||||
* |inlen| indicates the number of bytes to receive in the |in|.
|
||||
*
|
||||
* This function behaves like `nghttp2_session_recv()` except that it
|
||||
* does not use :type:`nghttp2_recv_callback` to receive data; the
|
||||
@@ -3063,7 +3063,7 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||
* are called in the same way as they are in `nghttp2_session_recv()`.
|
||||
*
|
||||
* In the current implementation, this function always tries to
|
||||
* processes all input data unless either an error occurs or
|
||||
* processes |inlen| bytes of input data unless either an error occurs or
|
||||
* :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from
|
||||
* :type:`nghttp2_on_header_callback` or
|
||||
* :type:`nghttp2_on_data_chunk_recv_callback`. If
|
||||
@@ -4839,7 +4839,31 @@ NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len);
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns nonzero if the |value| which is supposed to the value of
|
||||
* Returns nonzero if the |value| which is supposed to be the value of
|
||||
* the :method header field is valid according to
|
||||
* https://datatracker.ietf.org/doc/html/rfc7231#section-4 and
|
||||
* https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_check_method(const uint8_t *value, size_t len);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns nonzero if the |value| which is supposed to be the value of
|
||||
* the :path header field is valid according to
|
||||
* https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.3
|
||||
*
|
||||
* |value| is valid if it merely consists of the allowed characters.
|
||||
* In particular, it does not check whether |value| follows the syntax
|
||||
* of path. The allowed characters are all characters valid by
|
||||
* `nghttp2_check_header_value` minus SPC and HT.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_check_path(const uint8_t *value, size_t len);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns nonzero if the |value| which is supposed to be the value of the
|
||||
* :authority or host header field is valid according to
|
||||
* https://tools.ietf.org/html/rfc3986#section-3.2
|
||||
*
|
||||
|
||||
@@ -99,7 +99,7 @@ void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem);
|
||||
* |new_cap|. If extensions took place, buffer pointers in |buf| will
|
||||
* change.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the followings
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
|
||||
@@ -654,8 +654,6 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
|
||||
var_gift_payloadlen = 0;
|
||||
}
|
||||
|
||||
payloadlen -= var_gift_payloadlen;
|
||||
|
||||
if (!var_gift_payloadlen) {
|
||||
var_gift_payload = NULL;
|
||||
} else {
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14)
|
||||
|
||||
#define NGHTTP2_MAX_PAYLOADLEN 16384
|
||||
/* The one frame buffer length for tranmission. We may use several of
|
||||
/* The one frame buffer length for transmission. We may use several of
|
||||
them to support CONTINUATION. To account for Pad Length field, we
|
||||
allocate extra 1 byte, which saves extra large memcopying. */
|
||||
#define NGHTTP2_FRAMEBUF_CHUNKLEN \
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
/* Maximum headers block size to send, calculated using
|
||||
nghttp2_hd_deflate_bound(). This is the default value, and can be
|
||||
overridden by nghttp2_option_set_max_send_header_block_size(). */
|
||||
overridden by nghttp2_option_set_max_send_header_block_length(). */
|
||||
#define NGHTTP2_MAX_HEADERSLEN 65536
|
||||
|
||||
/* The number of bytes for each SETTINGS entry */
|
||||
|
||||
@@ -1263,6 +1263,8 @@ int nghttp2_hd_inflate_change_table_size(
|
||||
return NGHTTP2_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
|
||||
|
||||
/* It seems that encoder is not required to send dynamic table size
|
||||
update if the table size is not changed after applying
|
||||
SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this
|
||||
@@ -1275,13 +1277,12 @@ int nghttp2_hd_inflate_change_table_size(
|
||||
/* Remember minimum value, and validate that encoder sends the
|
||||
value less than or equal to this. */
|
||||
inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size;
|
||||
|
||||
inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size;
|
||||
|
||||
hd_context_shrink_table_size(&inflater->ctx, NULL);
|
||||
}
|
||||
|
||||
inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
|
||||
|
||||
inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size;
|
||||
|
||||
hd_context_shrink_table_size(&inflater->ctx, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -507,7 +507,166 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generated by genauthroitychartbl.py */
|
||||
/* Generated by genmethodchartbl.py */
|
||||
static char VALID_METHOD_CHARS[] = {
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
|
||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
||||
0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
|
||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
|
||||
0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
|
||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
||||
1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
|
||||
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
|
||||
0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
|
||||
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
|
||||
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
|
||||
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
||||
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
|
||||
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
|
||||
1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */,
|
||||
0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
|
||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
|
||||
1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
|
||||
0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
|
||||
0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
|
||||
0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
|
||||
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
|
||||
0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
|
||||
0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
|
||||
0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
|
||||
0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
|
||||
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
|
||||
0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
|
||||
0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
|
||||
0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
|
||||
0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
|
||||
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
|
||||
0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
|
||||
0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
|
||||
0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
|
||||
0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
|
||||
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
|
||||
0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
|
||||
0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
|
||||
0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
|
||||
0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
|
||||
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
|
||||
0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
|
||||
0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
|
||||
0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
|
||||
0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
|
||||
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
|
||||
0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
|
||||
0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
|
||||
0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
|
||||
};
|
||||
|
||||
int nghttp2_check_method(const uint8_t *value, size_t len) {
|
||||
const uint8_t *last;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
for (last = value + len; value != last; ++value) {
|
||||
if (!VALID_METHOD_CHARS[*value]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generated by genpathchartbl.py */
|
||||
static char VALID_PATH_CHARS[] = {
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
|
||||
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
|
||||
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
|
||||
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
|
||||
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
|
||||
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
|
||||
0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
|
||||
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
|
||||
1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
|
||||
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
|
||||
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
|
||||
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
|
||||
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
|
||||
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
|
||||
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
|
||||
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
|
||||
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
|
||||
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
|
||||
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
|
||||
1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
|
||||
1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
|
||||
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
|
||||
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
|
||||
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
||||
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
|
||||
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
|
||||
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
|
||||
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
|
||||
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
|
||||
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
|
||||
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
|
||||
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
|
||||
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
|
||||
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
|
||||
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
|
||||
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
|
||||
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
|
||||
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
|
||||
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
|
||||
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
|
||||
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
|
||||
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
|
||||
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
|
||||
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
|
||||
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
|
||||
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
|
||||
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
|
||||
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
|
||||
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
|
||||
1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
|
||||
1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
|
||||
1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
|
||||
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
|
||||
1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
|
||||
1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
|
||||
1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
|
||||
1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
|
||||
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
|
||||
1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
|
||||
1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
|
||||
1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
|
||||
};
|
||||
|
||||
int nghttp2_check_path(const uint8_t *value, size_t len) {
|
||||
const uint8_t *last;
|
||||
for (last = value + len; value != last; ++value) {
|
||||
if (!VALID_PATH_CHARS[*value]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generated by genauthoritychartbl.py */
|
||||
static char VALID_AUTHORITY_CHARS[] = {
|
||||
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
|
||||
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
|
||||
|
||||
@@ -360,12 +360,21 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
return NGHTTP2_ERR_IGN_HTTP_HEADER;
|
||||
}
|
||||
|
||||
if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
|
||||
nv->token == NGHTTP2_TOKEN_HOST) {
|
||||
switch (nv->token) {
|
||||
case NGHTTP2_TOKEN__METHOD:
|
||||
rv = nghttp2_check_method(nv->value->base, nv->value->len);
|
||||
break;
|
||||
case NGHTTP2_TOKEN__PATH:
|
||||
rv = nghttp2_check_path(nv->value->base, nv->value->len);
|
||||
break;
|
||||
case NGHTTP2_TOKEN__AUTHORITY:
|
||||
case NGHTTP2_TOKEN_HOST:
|
||||
rv = nghttp2_check_authority(nv->value->base, nv->value->len);
|
||||
} else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
|
||||
break;
|
||||
case NGHTTP2_TOKEN__SCHEME:
|
||||
rv = check_scheme(nv->value->base, nv->value->len);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ static int map_resize(nghttp2_map *map, uint32_t new_tablelen,
|
||||
nghttp2_map_bucket *new_table;
|
||||
nghttp2_map_bucket *bkt;
|
||||
int rv;
|
||||
(void)rv;
|
||||
|
||||
new_table =
|
||||
nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket));
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#if defined(WIN32)
|
||||
/* Windows requires ws2_32 library for ntonl family functions. We
|
||||
define inline functions for those function so that we don't have
|
||||
dependeny on that lib. */
|
||||
dependency on that lib. */
|
||||
|
||||
# ifdef _MSC_VER
|
||||
# define STIN static __inline
|
||||
|
||||
@@ -111,7 +111,7 @@ struct nghttp2_outbound_item {
|
||||
to this structure to avoid frequent memory allocation. */
|
||||
nghttp2_ext_frame_payload ext_frame_payload;
|
||||
nghttp2_aux_data aux_data;
|
||||
/* The priority used in priority comparion. Smaller is served
|
||||
/* The priority used in priority comparison. Smaller is served
|
||||
earlier. For PING, SETTINGS and non-DATA frames (excluding
|
||||
response HEADERS frame) have dedicated cycle value defined above.
|
||||
For DATA frame, cycle is computed by taking into account of
|
||||
|
||||
@@ -114,7 +114,7 @@ typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg);
|
||||
void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
|
||||
|
||||
/*
|
||||
* Applys |fun| to each item in |pq|. The |arg| is passed as arg
|
||||
* Applies |fun| to each item in |pq|. The |arg| is passed as arg
|
||||
* parameter to callback function. This function must not change the
|
||||
* ordering key. If the return value from callback is nonzero, this
|
||||
* function returns 1 immediately without iterating remaining items.
|
||||
|
||||
@@ -5341,7 +5341,7 @@ static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {
|
||||
|
||||
/*
|
||||
* This function returns the effective payload length in the data of
|
||||
* length |readlen| when the remaning payload is |payloadleft|. The
|
||||
* length |readlen| when the remaining payload is |payloadleft|. The
|
||||
* |payloadleft| does not include |readlen|. If padding was started
|
||||
* strictly before this data chunk, this function returns -1.
|
||||
*/
|
||||
|
||||
@@ -408,7 +408,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code);
|
||||
|
||||
/*
|
||||
* Adds PING frame. This is a convenient functin built on top of
|
||||
* Adds PING frame. This is a convenient function built on top of
|
||||
* nghttp2_session_add_frame() to add PING easily.
|
||||
*
|
||||
* If the |opaque_data| is not NULL, it must point to 8 bytes memory
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "nghttp2_frame.h"
|
||||
|
||||
/* Maximum distance between any two stream's cycle in the same
|
||||
prirority queue. Imagine stream A's cycle is A, and stream B's
|
||||
priority queue. Imagine stream A's cycle is A, and stream B's
|
||||
cycle is B, and A < B. The cycle is unsigned 32 bit integer, it
|
||||
may get overflow. Because of how we calculate the next cycle
|
||||
value, if B - A is less than or equals to
|
||||
|
||||
@@ -492,8 +492,6 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
|
||||
return nghttp2_session_update_recv_stream_window_size(session, stream, 0,
|
||||
1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 21
|
||||
#serial 23
|
||||
|
||||
AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
|
||||
AC_DEFUN([AX_PYTHON_DEVEL],[
|
||||
@@ -135,16 +135,25 @@ variable to configure. See ``configure --help'' for reference.
|
||||
#
|
||||
# Check if you have distutils, else fail
|
||||
#
|
||||
AC_MSG_CHECKING([for the distutils Python package])
|
||||
ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
|
||||
AC_MSG_CHECKING([for the sysconfig Python package])
|
||||
ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`
|
||||
if test $? -eq 0; then
|
||||
AC_MSG_RESULT([yes])
|
||||
IMPORT_SYSCONFIG="import sysconfig"
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([cannot import Python module "distutils".
|
||||
|
||||
AC_MSG_CHECKING([for the distutils Python package])
|
||||
ac_sysconfig_result=`$PYTHON -c "from distutils import sysconfig" 2>&1`
|
||||
if test $? -eq 0; then
|
||||
AC_MSG_RESULT([yes])
|
||||
IMPORT_SYSCONFIG="from distutils import sysconfig"
|
||||
else
|
||||
AC_MSG_ERROR([cannot import Python module "distutils".
|
||||
Please check your Python installation. The error was:
|
||||
$ac_distutils_result])
|
||||
PYTHON_VERSION=""
|
||||
$ac_sysconfig_result])
|
||||
PYTHON_VERSION=""
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
@@ -152,10 +161,19 @@ $ac_distutils_result])
|
||||
#
|
||||
AC_MSG_CHECKING([for Python include path])
|
||||
if test -z "$PYTHON_CPPFLAGS"; then
|
||||
python_path=`$PYTHON -c "import distutils.sysconfig; \
|
||||
print (distutils.sysconfig.get_python_inc ());"`
|
||||
plat_python_path=`$PYTHON -c "import distutils.sysconfig; \
|
||||
print (distutils.sysconfig.get_python_inc (plat_specific=1));"`
|
||||
if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
|
||||
# sysconfig module has different functions
|
||||
python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_path ('include'));"`
|
||||
plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_path ('platinclude'));"`
|
||||
else
|
||||
# old distutils way
|
||||
python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_python_inc ());"`
|
||||
plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_python_inc (plat_specific=1));"`
|
||||
fi
|
||||
if test -n "${python_path}"; then
|
||||
if test "${plat_python_path}" != "${python_path}"; then
|
||||
python_path="-I$python_path -I$plat_python_path"
|
||||
@@ -179,7 +197,7 @@ $ac_distutils_result])
|
||||
|
||||
# join all versioning strings, on some systems
|
||||
# major/minor numbers could be in different list elements
|
||||
from distutils.sysconfig import *
|
||||
from sysconfig import *
|
||||
e = get_config_var('VERSION')
|
||||
if e is not None:
|
||||
print(e)
|
||||
@@ -202,8 +220,8 @@ EOD`
|
||||
ac_python_libdir=`cat<<EOD | $PYTHON -
|
||||
|
||||
# There should be only one
|
||||
import distutils.sysconfig
|
||||
e = distutils.sysconfig.get_config_var('LIBDIR')
|
||||
$IMPORT_SYSCONFIG
|
||||
e = sysconfig.get_config_var('LIBDIR')
|
||||
if e is not None:
|
||||
print (e)
|
||||
EOD`
|
||||
@@ -211,8 +229,8 @@ EOD`
|
||||
# Now, for the library:
|
||||
ac_python_library=`cat<<EOD | $PYTHON -
|
||||
|
||||
import distutils.sysconfig
|
||||
c = distutils.sysconfig.get_config_vars()
|
||||
$IMPORT_SYSCONFIG
|
||||
c = sysconfig.get_config_vars()
|
||||
if 'LDVERSION' in c:
|
||||
print ('python'+c[['LDVERSION']])
|
||||
else:
|
||||
@@ -231,7 +249,7 @@ EOD`
|
||||
else
|
||||
# old way: use libpython from python_configdir
|
||||
ac_python_libdir=`$PYTHON -c \
|
||||
"from distutils.sysconfig import get_python_lib as f; \
|
||||
"from sysconfig import get_python_lib as f; \
|
||||
import os; \
|
||||
print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
|
||||
PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version"
|
||||
@@ -252,19 +270,42 @@ EOD`
|
||||
#
|
||||
AC_MSG_CHECKING([for Python site-packages path])
|
||||
if test -z "$PYTHON_SITE_PKG"; then
|
||||
PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
|
||||
print (distutils.sysconfig.get_python_lib(0,0));"`
|
||||
if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
|
||||
PYTHON_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_path('purelib'));"`
|
||||
else
|
||||
# distutils.sysconfig way
|
||||
PYTHON_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_python_lib(0,0));"`
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT([$PYTHON_SITE_PKG])
|
||||
AC_SUBST([PYTHON_SITE_PKG])
|
||||
|
||||
#
|
||||
# Check for platform-specific site packages
|
||||
#
|
||||
AC_MSG_CHECKING([for Python platform specific site-packages path])
|
||||
if test -z "$PYTHON_SITE_PKG"; then
|
||||
if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
|
||||
PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_path('platlib'));"`
|
||||
else
|
||||
# distutils.sysconfig way
|
||||
PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
print (sysconfig.get_python_lib(1,0));"`
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT([$PYTHON_PLATFORM_SITE_PKG])
|
||||
AC_SUBST([PYTHON_PLATFORM_SITE_PKG])
|
||||
|
||||
#
|
||||
# libraries which must be linked in when embedding
|
||||
#
|
||||
AC_MSG_CHECKING(python extra libraries)
|
||||
if test -z "$PYTHON_EXTRA_LIBS"; then
|
||||
PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
|
||||
conf = distutils.sysconfig.get_config_var; \
|
||||
PYTHON_EXTRA_LIBS=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
conf = sysconfig.get_config_var; \
|
||||
print (conf('LIBS') + ' ' + conf('SYSLIBS'))"`
|
||||
fi
|
||||
AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
|
||||
@@ -275,8 +316,8 @@ EOD`
|
||||
#
|
||||
AC_MSG_CHECKING(python extra linking flags)
|
||||
if test -z "$PYTHON_EXTRA_LDFLAGS"; then
|
||||
PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
|
||||
conf = distutils.sysconfig.get_config_var; \
|
||||
PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "$IMPORT_SYSCONFIG; \
|
||||
conf = sysconfig.get_config_var; \
|
||||
print (conf('LINKFORSHARED'))"`
|
||||
fi
|
||||
AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
|
||||
|
||||
@@ -701,7 +701,7 @@ cdef class _HTTP2SessionCoreBase:
|
||||
if outbuflen == 0:
|
||||
break
|
||||
if outbuflen < 0:
|
||||
raise Exception('nghttp2_session_mem_send faild: {}'.format\
|
||||
raise Exception('nghttp2_session_mem_send failed: {}'.format\
|
||||
(_strerror(outbuflen)))
|
||||
self.transport.write(outbuf[:outbuflen])
|
||||
|
||||
@@ -1057,8 +1057,7 @@ if asyncio:
|
||||
"""HTTP/2 request (stream) handler base class.
|
||||
|
||||
The class is used to handle the HTTP/2 stream. By default, it does
|
||||
not nothing. It must be subclassed to handle each event callback
|
||||
method.
|
||||
nothing. It must be subclassed to handle each event callback method.
|
||||
|
||||
The first callback method invoked is on_headers(). It is called
|
||||
when HEADERS frame, which includes request header fields, is
|
||||
@@ -1084,7 +1083,7 @@ if asyncio:
|
||||
address.
|
||||
|
||||
client_certificate
|
||||
May contain the client certifcate in its non-binary form
|
||||
May contain the client certificate in its non-binary form
|
||||
|
||||
stream_id
|
||||
Stream ID of this stream
|
||||
|
||||
@@ -15,10 +15,14 @@ include_directories(
|
||||
${JEMALLOC_INCLUDE_DIRS}
|
||||
${LIBXML2_INCLUDE_DIRS}
|
||||
${LIBEV_INCLUDE_DIRS}
|
||||
${LIBNGHTTP3_INCLUDE_DIRS}
|
||||
${LIBNGTCP2_INCLUDE_DIRS}
|
||||
${LIBNGTCP2_CRYPTO_OPENSSL_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${LIBCARES_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
${LIBBPF_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# XXX per-target?
|
||||
@@ -27,11 +31,15 @@ link_libraries(
|
||||
${JEMALLOC_LIBRARIES}
|
||||
${LIBXML2_LIBRARIES}
|
||||
${LIBEV_LIBRARIES}
|
||||
${LIBNGHTTP3_LIBRARIES}
|
||||
${LIBNGTCP2_LIBRARIES}
|
||||
${LIBNGTCP2_CRYPTO_OPENSSL_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${LIBCARES_LIBRARIES}
|
||||
${JANSSON_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${APP_LIBRARIES}
|
||||
${LIBBPF_LIBRARIES}
|
||||
)
|
||||
|
||||
if(ENABLE_APP)
|
||||
@@ -67,7 +75,13 @@ if(ENABLE_APP)
|
||||
h2load_http2_session.cc
|
||||
h2load_http1_session.cc
|
||||
)
|
||||
|
||||
if(ENABLE_HTTP3)
|
||||
list(APPEND H2LOAD_SOURCES
|
||||
h2load_http3_session.cc
|
||||
h2load_quic.cc
|
||||
quic.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
# Common libnhttpx sources (used for nghttpx and unit tests)
|
||||
set(NGHTTPX_SRCS
|
||||
@@ -104,6 +118,7 @@ if(ENABLE_APP)
|
||||
shrpx_router.cc
|
||||
shrpx_api_downstream_connection.cc
|
||||
shrpx_health_monitor_downstream_connection.cc
|
||||
shrpx_null_downstream_connection.cc
|
||||
shrpx_exec.cc
|
||||
shrpx_dns_resolver.cc
|
||||
shrpx_dual_dns_resolver.cc
|
||||
@@ -119,6 +134,16 @@ if(ENABLE_APP)
|
||||
shrpx_mruby_module_response.cc
|
||||
)
|
||||
endif()
|
||||
if(ENABLE_HTTP3)
|
||||
list(APPEND NGHTTPX_SRCS
|
||||
shrpx_quic.cc
|
||||
shrpx_quic_listener.cc
|
||||
shrpx_quic_connection_handler.cc
|
||||
shrpx_http3_upstream.cc
|
||||
http3.cc
|
||||
quic.cc
|
||||
)
|
||||
endif()
|
||||
add_library(nghttpx_static STATIC ${NGHTTPX_SRCS})
|
||||
set_target_properties(nghttpx_static PROPERTIES ARCHIVE_OUTPUT_NAME nghttpx)
|
||||
|
||||
@@ -189,7 +214,10 @@ if(ENABLE_APP)
|
||||
add_executable(nghttpx ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:llhttp>
|
||||
$<TARGET_OBJECTS:url-parser>
|
||||
)
|
||||
target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"")
|
||||
target_compile_definitions(nghttpx PRIVATE
|
||||
"-DPKGDATADIR=\"${PKGDATADIR}\""
|
||||
"-DPKGLIBDIR=\"${PKGLIBDIR}\""
|
||||
)
|
||||
target_link_libraries(nghttpx nghttpx_static)
|
||||
add_executable(h2load ${H2LOAD_SOURCES} $<TARGET_OBJECTS:llhttp>
|
||||
$<TARGET_OBJECTS:url-parser>
|
||||
|
||||
@@ -52,8 +52,13 @@
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
|
||||
#include "ssl_compat.h"
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/dh.h>
|
||||
#if OPENSSL_3_0_0_API
|
||||
# include <openssl/decoder.h>
|
||||
#endif // OPENSSL_3_0_0_API
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
@@ -2105,7 +2110,7 @@ int HttpServer::run() {
|
||||
std::vector<unsigned char> next_proto;
|
||||
|
||||
if (!config_->no_tls) {
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
ssl_ctx = SSL_CTX_new(TLS_server_method());
|
||||
if (!ssl_ctx) {
|
||||
std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
return -1;
|
||||
@@ -2138,15 +2143,13 @@ int HttpServer::run() {
|
||||
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
|
||||
|
||||
#ifndef OPENSSL_NO_EC
|
||||
|
||||
// Disabled SSL_CTX_set_ecdh_auto, because computational cost of
|
||||
// chosen curve is much higher than P-256.
|
||||
|
||||
// #if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
// SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
|
||||
// #else // OPENSSL_VERSION_NUBMER < 0x10002000L
|
||||
// Use P-256, which is sufficiently secure at the time of this
|
||||
// writing.
|
||||
# if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
|
||||
std::cerr << "SSL_CTX_set1_curves_list failed: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
return -1;
|
||||
}
|
||||
# else // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (ecdh == nullptr) {
|
||||
std::cerr << "EC_KEY_new_by_curv_name failed: "
|
||||
@@ -2155,19 +2158,36 @@ int HttpServer::run() {
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
|
||||
|
||||
#endif // OPENSSL_NO_EC
|
||||
# endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||
#endif // OPENSSL_NO_EC
|
||||
|
||||
if (!config_->dh_param_file.empty()) {
|
||||
// Read DH parameters from file
|
||||
auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r");
|
||||
auto bio = BIO_new_file(config_->dh_param_file.c_str(), "rb");
|
||||
if (bio == nullptr) {
|
||||
std::cerr << "BIO_new_file() failed: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if OPENSSL_3_0_0_API
|
||||
EVP_PKEY *dh = nullptr;
|
||||
auto dctx = OSSL_DECODER_CTX_new_for_pkey(
|
||||
&dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
|
||||
nullptr, nullptr);
|
||||
|
||||
if (!OSSL_DECODER_from_bio(dctx, bio)) {
|
||||
std::cerr << "OSSL_DECODER_from_bio() failed: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, dh) != 1) {
|
||||
std::cerr << "SSL_CTX_set0_tmp_dh_pkey failed: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
#else // !OPENSSL_3_0_0_API
|
||||
auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
|
||||
|
||||
if (dh == nullptr) {
|
||||
@@ -2178,6 +2198,7 @@ int HttpServer::run() {
|
||||
|
||||
SSL_CTX_set_tmp_dh(ssl_ctx, dh);
|
||||
DH_free(dh);
|
||||
#endif // !OPENSSL_3_0_0_API
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ private:
|
||||
};
|
||||
|
||||
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, int *eof,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data);
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -35,6 +35,7 @@ AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS)
|
||||
AM_CPPFLAGS = \
|
||||
-DPKGDATADIR='"$(pkgdatadir)"' \
|
||||
-DPKGLIBDIR='"$(pkglibdir)"' \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
-I$(top_builddir)/lib/includes \
|
||||
-I$(top_srcdir)/lib \
|
||||
@@ -44,10 +45,16 @@ AM_CPPFLAGS = \
|
||||
@JEMALLOC_CFLAGS@ \
|
||||
@LIBXML2_CFLAGS@ \
|
||||
@LIBEV_CFLAGS@ \
|
||||
@LIBNGHTTP3_CFLAGS@ \
|
||||
@LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ \
|
||||
@LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ \
|
||||
@LIBNGTCP2_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@LIBCARES_CFLAGS@ \
|
||||
@JANSSON_CFLAGS@ \
|
||||
@LIBBPF_CFLAGS@ \
|
||||
@ZLIB_CFLAGS@ \
|
||||
@EXTRA_DEFS@ \
|
||||
@DEFS@
|
||||
AM_LDFLAGS = @LIBTOOL_LDFLAGS@
|
||||
|
||||
@@ -57,10 +64,15 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
@JEMALLOC_LIBS@ \
|
||||
@LIBXML2_LIBS@ \
|
||||
@LIBEV_LIBS@ \
|
||||
@LIBNGHTTP3_LIBS@ \
|
||||
@LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ \
|
||||
@LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ \
|
||||
@LIBNGTCP2_LIBS@ \
|
||||
@OPENSSL_LIBS@ \
|
||||
@LIBCARES_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
@JANSSON_LIBS@ \
|
||||
@LIBBPF_LIBS@ \
|
||||
@ZLIB_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
||||
@@ -99,6 +111,13 @@ h2load_SOURCES = util.cc util.h \
|
||||
h2load_http2_session.cc h2load_http2_session.h \
|
||||
h2load_http1_session.cc h2load_http1_session.h
|
||||
|
||||
if ENABLE_HTTP3
|
||||
h2load_SOURCES += \
|
||||
h2load_http3_session.cc h2load_http3_session.h \
|
||||
h2load_quic.cc h2load_quic.h \
|
||||
quic.cc quic.h
|
||||
endif # ENABLE_HTTP3
|
||||
|
||||
NGHTTPX_SRCS = \
|
||||
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
||||
app_helper.cc app_helper.h \
|
||||
@@ -139,6 +158,7 @@ NGHTTPX_SRCS = \
|
||||
shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \
|
||||
shrpx_health_monitor_downstream_connection.cc \
|
||||
shrpx_health_monitor_downstream_connection.h \
|
||||
shrpx_null_downstream_connection.cc shrpx_null_downstream_connection.h \
|
||||
shrpx_exec.cc shrpx_exec.h \
|
||||
shrpx_dns_resolver.cc shrpx_dns_resolver.h \
|
||||
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
|
||||
@@ -155,6 +175,16 @@ NGHTTPX_SRCS += \
|
||||
shrpx_mruby_module_response.cc shrpx_mruby_module_response.h
|
||||
endif # HAVE_MRUBY
|
||||
|
||||
if ENABLE_HTTP3
|
||||
NGHTTPX_SRCS += \
|
||||
shrpx_quic.cc shrpx_quic.h \
|
||||
shrpx_quic_listener.cc shrpx_quic_listener.h \
|
||||
shrpx_quic_connection_handler.cc shrpx_quic_connection_handler.h \
|
||||
shrpx_http3_upstream.cc shrpx_http3_upstream.h \
|
||||
http3.cc http3.h \
|
||||
quic.cc quic.h
|
||||
endif # ENABLE_HTTP3
|
||||
|
||||
noinst_LIBRARIES = libnghttpx.a
|
||||
libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
|
||||
libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS}
|
||||
|
||||
428
src/h2load.cc
428
src/h2load.cc
@@ -34,6 +34,8 @@
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#include <sys/mman.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
@@ -48,10 +50,18 @@
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include <ngtcp2/ngtcp2.h>
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#include "url-parser/url_parser.h"
|
||||
|
||||
#include "h2load_http1_session.h"
|
||||
#include "h2load_http2_session.h"
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include "h2load_http3_session.h"
|
||||
# include "h2load_quic.h"
|
||||
#endif // ENABLE_HTTP3
|
||||
#include "tls.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
@@ -71,9 +81,24 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
namespace {
|
||||
std::ofstream keylog_file;
|
||||
void keylog_callback(const SSL *ssl, const char *line) {
|
||||
keylog_file.write(line, strlen(line));
|
||||
keylog_file.put('\n');
|
||||
keylog_file.flush();
|
||||
}
|
||||
} // namespace
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
Config::Config()
|
||||
: ciphers(tls::DEFAULT_CIPHER_LIST),
|
||||
tls13_ciphers("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_"
|
||||
"CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256"),
|
||||
groups("X25519:P-256:P-384:P-521"),
|
||||
data_length(-1),
|
||||
data(nullptr),
|
||||
addrs(nullptr),
|
||||
nreqs(1),
|
||||
nclients(1),
|
||||
@@ -81,6 +106,7 @@ Config::Config()
|
||||
max_concurrent_streams(1),
|
||||
window_bits(30),
|
||||
connection_window_bits(30),
|
||||
max_frame_size(16_k),
|
||||
rate(0),
|
||||
rate_period(1.0),
|
||||
duration(0.0),
|
||||
@@ -92,6 +118,7 @@ Config::Config()
|
||||
encoder_header_table_size(4_k),
|
||||
data_fd(-1),
|
||||
log_fd(-1),
|
||||
qlog_file_base(),
|
||||
port(0),
|
||||
default_port(0),
|
||||
connect_to_port(0),
|
||||
@@ -99,7 +126,9 @@ Config::Config()
|
||||
timing_script(false),
|
||||
base_uri_unix(false),
|
||||
unix_addr{},
|
||||
rps(0.) {}
|
||||
rps(0.),
|
||||
no_udp_gso(false),
|
||||
max_udp_payload_size(0) {}
|
||||
|
||||
Config::~Config() {
|
||||
if (addrs) {
|
||||
@@ -119,6 +148,14 @@ bool Config::is_rate_mode() const { return (this->rate != 0); }
|
||||
bool Config::is_timing_based_mode() const { return (this->duration > 0); }
|
||||
bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
|
||||
bool Config::rps_enabled() const { return this->rps > 0.0; }
|
||||
bool Config::is_quic() const {
|
||||
#ifdef ENABLE_HTTP3
|
||||
return !npn_list.empty() &&
|
||||
(npn_list[0] == NGHTTP3_ALPN_H3 || npn_list[0] == "\x5h3-29");
|
||||
#else // !ENABLE_HTTP3
|
||||
return false;
|
||||
#endif // !ENABLE_HTTP3
|
||||
}
|
||||
Config config;
|
||||
|
||||
namespace {
|
||||
@@ -138,7 +175,9 @@ Stats::Stats(size_t req_todo, size_t nclients)
|
||||
bytes_head(0),
|
||||
bytes_head_decomp(0),
|
||||
bytes_body(0),
|
||||
status() {}
|
||||
status(),
|
||||
udp_dgram_recv(0),
|
||||
udp_dgram_sent(0) {}
|
||||
|
||||
Stream::Stream() : req_stat{}, status_success(-1) {}
|
||||
|
||||
@@ -195,8 +234,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
delete client;
|
||||
return;
|
||||
}
|
||||
writecb(loop, &client->wev, revents);
|
||||
// client->disconnect() and client->fail() may be called
|
||||
client->signal_write();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -409,6 +447,9 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
cstat{},
|
||||
worker(worker),
|
||||
ssl(nullptr),
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic{},
|
||||
#endif // ENABLE_HTTP3
|
||||
next_addr(config.addrs),
|
||||
current_addr(nullptr),
|
||||
reqidx(0),
|
||||
@@ -420,6 +461,7 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
req_done(0),
|
||||
id(id),
|
||||
fd(-1),
|
||||
local_addr{},
|
||||
new_connection_requested(false),
|
||||
final(false),
|
||||
rps_duration_started(0),
|
||||
@@ -449,11 +491,22 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
|
||||
|
||||
ev_timer_init(&rps_watcher, rps_cb, 0., 0.);
|
||||
rps_watcher.data = this;
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
ev_timer_init(&quic.pkt_timer, quic_pkt_timeout_cb, 0., 0.);
|
||||
quic.pkt_timer.data = this;
|
||||
#endif // ENABLE_HTTP3
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
disconnect();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
quic_free();
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
if (ssl) {
|
||||
SSL_free(ssl);
|
||||
}
|
||||
@@ -466,26 +519,59 @@ int Client::do_read() { return readfn(*this); }
|
||||
int Client::do_write() { return writefn(*this); }
|
||||
|
||||
int Client::make_socket(addrinfo *addr) {
|
||||
fd = util::create_nonblock_socket(addr->ai_family);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (config.scheme == "https") {
|
||||
if (!ssl) {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
int rv;
|
||||
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
fd = util::create_nonblock_udp_socket(addr->ai_family);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto config = worker->config;
|
||||
|
||||
if (!util::numeric_host(config->host.c_str())) {
|
||||
SSL_set_tlsext_host_name(ssl, config->host.c_str());
|
||||
rv = util::bind_any_addr_udp(fd, addr->ai_family);
|
||||
if (rv != 0) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSL_set_fd(ssl, fd);
|
||||
SSL_set_connect_state(ssl);
|
||||
socklen_t addrlen = sizeof(local_addr.su.storage);
|
||||
rv = getsockname(fd, &local_addr.su.sa, &addrlen);
|
||||
if (rv == -1) {
|
||||
return -1;
|
||||
}
|
||||
local_addr.len = addrlen;
|
||||
|
||||
if (quic_init(&local_addr.su.sa, local_addr.len, addr->ai_addr,
|
||||
addr->ai_addrlen) != 0) {
|
||||
std::cerr << "quic_init failed" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
} else {
|
||||
fd = util::create_nonblock_socket(addr->ai_family);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (config.scheme == "https") {
|
||||
if (!ssl) {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
}
|
||||
|
||||
SSL_set_fd(ssl, fd);
|
||||
SSL_set_connect_state(ssl);
|
||||
}
|
||||
}
|
||||
|
||||
auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
|
||||
if (ssl && !util::numeric_host(config.host.c_str())) {
|
||||
SSL_set_tlsext_host_name(ssl, config.host.c_str());
|
||||
}
|
||||
|
||||
if (config.is_quic()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
if (ssl) {
|
||||
SSL_free(ssl);
|
||||
@@ -542,13 +628,22 @@ int Client::connect() {
|
||||
current_addr = addr;
|
||||
}
|
||||
|
||||
writefn = &Client::connected;
|
||||
|
||||
ev_io_set(&rev, fd, EV_READ);
|
||||
ev_io_set(&wev, fd, EV_WRITE);
|
||||
|
||||
ev_io_start(worker->loop, &wev);
|
||||
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
ev_io_start(worker->loop, &rev);
|
||||
|
||||
readfn = &Client::read_quic;
|
||||
writefn = &Client::write_quic;
|
||||
#endif // ENABLE_HTTP3
|
||||
} else {
|
||||
writefn = &Client::connected;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -603,6 +698,15 @@ void Client::fail() {
|
||||
void Client::disconnect() {
|
||||
record_client_end_time();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
quic_close_connection();
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
ev_timer_stop(worker->loop, &quic.pkt_timer);
|
||||
#endif // ENABLE_HTTP3
|
||||
ev_timer_stop(worker->loop, &conn_inactivity_watcher);
|
||||
ev_timer_stop(worker->loop, &conn_active_watcher);
|
||||
ev_timer_stop(worker->loop, &rps_watcher);
|
||||
@@ -726,6 +830,16 @@ void print_server_tmp_key(SSL *ssl) {
|
||||
std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl;
|
||||
break;
|
||||
case EVP_PKEY_EC: {
|
||||
# if OPENSSL_3_0_0_API
|
||||
std::array<char, 64> curve_name;
|
||||
const char *cname;
|
||||
if (!EVP_PKEY_get_utf8_string_param(key, "group", curve_name.data(),
|
||||
curve_name.size(), nullptr)) {
|
||||
cname = "<unknown>";
|
||||
} else {
|
||||
cname = curve_name.data();
|
||||
}
|
||||
# else // !OPENSSL_3_0_0_API
|
||||
auto ec = EVP_PKEY_get1_EC_KEY(key);
|
||||
auto ec_del = defer(EC_KEY_free, ec);
|
||||
auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
|
||||
@@ -733,6 +847,7 @@ void print_server_tmp_key(SSL *ssl) {
|
||||
if (!cname) {
|
||||
cname = OBJ_nid2sn(nid);
|
||||
}
|
||||
# endif // !OPENSSL_3_0_0_API
|
||||
|
||||
std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits"
|
||||
<< std::endl;
|
||||
@@ -765,7 +880,14 @@ void Client::report_app_info() {
|
||||
}
|
||||
|
||||
void Client::terminate_session() {
|
||||
session->terminate();
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
quic.close_requested = true;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
if (session) {
|
||||
session->terminate();
|
||||
}
|
||||
// http1 session needs writecb to tear down session.
|
||||
signal_write();
|
||||
}
|
||||
@@ -963,7 +1085,15 @@ int Client::connection_made() {
|
||||
|
||||
if (next_proto) {
|
||||
auto proto = StringRef{next_proto, next_proto_len};
|
||||
if (util::check_h2_is_selected(proto)) {
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
assert(session);
|
||||
if (!util::streq(StringRef{&NGHTTP3_ALPN_H3[1]}, proto) &&
|
||||
!util::streq_l("h3-29", proto)) {
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
} else if (util::check_h2_is_selected(proto)) {
|
||||
session = std::make_unique<Http2Session>(this);
|
||||
} else if (util::streq(NGHTTP2_H1_1, proto)) {
|
||||
session = std::make_unique<Http1Session>(this);
|
||||
@@ -972,6 +1102,9 @@ int Client::connection_made() {
|
||||
// Just assign next_proto to selected_proto anyway to show the
|
||||
// negotiation result.
|
||||
selected_proto = proto.str();
|
||||
} else if (config.is_quic()) {
|
||||
std::cerr << "QUIC requires ALPN negotiation" << std::endl;
|
||||
return -1;
|
||||
} else {
|
||||
std::cout << "No protocol negotiated. Fallback behaviour may be activated"
|
||||
<< std::endl;
|
||||
@@ -1285,6 +1418,46 @@ int Client::write_tls() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
int Client::write_udp(const sockaddr *addr, socklen_t addrlen,
|
||||
const uint8_t *data, size_t datalen, size_t gso_size) {
|
||||
iovec msg_iov;
|
||||
msg_iov.iov_base = const_cast<uint8_t *>(data);
|
||||
msg_iov.iov_len = datalen;
|
||||
|
||||
msghdr msg{};
|
||||
msg.msg_name = const_cast<sockaddr *>(addr);
|
||||
msg.msg_namelen = addrlen;
|
||||
msg.msg_iov = &msg_iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
# ifdef UDP_SEGMENT
|
||||
std::array<uint8_t, CMSG_SPACE(sizeof(uint16_t))> msg_ctrl{};
|
||||
if (gso_size && datalen > gso_size) {
|
||||
msg.msg_control = msg_ctrl.data();
|
||||
msg.msg_controllen = msg_ctrl.size();
|
||||
|
||||
auto cm = CMSG_FIRSTHDR(&msg);
|
||||
cm->cmsg_level = SOL_UDP;
|
||||
cm->cmsg_type = UDP_SEGMENT;
|
||||
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
|
||||
*(reinterpret_cast<uint16_t *>(CMSG_DATA(cm))) = gso_size;
|
||||
}
|
||||
# endif // UDP_SEGMENT
|
||||
|
||||
auto nwrite = sendmsg(fd, &msg, 0);
|
||||
if (nwrite < 0) {
|
||||
std::cerr << "sendto: errno=" << errno << std::endl;
|
||||
} else {
|
||||
++worker->stats.udp_dgram_sent;
|
||||
}
|
||||
|
||||
ev_io_stop(worker->loop, &wev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
void Client::record_request_time(RequestStat *req_stat) {
|
||||
req_stat->request_time = std::chrono::steady_clock::now();
|
||||
req_stat->request_wall_time = std::chrono::system_clock::now();
|
||||
@@ -1344,7 +1517,8 @@ int get_ev_loop_flags() {
|
||||
|
||||
Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
|
||||
size_t rate, size_t max_samples, Config *config)
|
||||
: stats(req_todo, nclients),
|
||||
: randgen(util::make_mt19937()),
|
||||
stats(req_todo, nclients),
|
||||
loop(ev_loop_new(get_ev_loop_flags())),
|
||||
ssl_ctx(ssl_ctx),
|
||||
config(config),
|
||||
@@ -1403,7 +1577,7 @@ Worker::~Worker() {
|
||||
|
||||
void Worker::stop_all_clients() {
|
||||
for (auto client : clients) {
|
||||
if (client && client->session) {
|
||||
if (client) {
|
||||
client->terminate_session();
|
||||
}
|
||||
}
|
||||
@@ -1936,8 +2110,14 @@ Options:
|
||||
http/1.1 is used, this specifies the number of HTTP
|
||||
pipelining requests in-flight.
|
||||
Default: 1
|
||||
-f, --max-frame-size=<SIZE>
|
||||
Maximum frame size that the local endpoint is willing to
|
||||
receive.
|
||||
Default: )"
|
||||
<< util::utos_unit(config.max_frame_size) << R"(
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)-1.
|
||||
For QUIC, <N> is capped to 26 (roughly 64MiB).
|
||||
Default: )"
|
||||
<< config.window_bits << R"(
|
||||
-W, --connection-window-bits=<N>
|
||||
@@ -1948,10 +2128,15 @@ Options:
|
||||
-H, --header=<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
--ciphers=<SUITE>
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
Set allowed cipher list for TLSv1.2 or earlier. The
|
||||
format of the string is described in OpenSSL ciphers(1).
|
||||
Default: )"
|
||||
<< config.ciphers << R"(
|
||||
--tls13-ciphers=<SUITE>
|
||||
Set allowed cipher list for TLSv1.3. The format of the
|
||||
string is described in OpenSSL ciphers(1).
|
||||
Default: )"
|
||||
<< config.tls13_ciphers << R"(
|
||||
-p, --no-tls-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
@@ -2065,11 +2250,25 @@ Options:
|
||||
response time when using one worker thread, but may
|
||||
appear slightly out of order with multiple threads due
|
||||
to buffering. Status code is -1 for failed streams.
|
||||
--qlog-file-base=<PATH>
|
||||
Enable qlog output and specify base file name for qlogs.
|
||||
Qlog is emitted for each connection. For a given base
|
||||
name "base", each output file name becomes
|
||||
"base.M.N.sqlog" where M is worker ID and N is client ID
|
||||
(e.g. "base.0.3.sqlog"). Only effective in QUIC runs.
|
||||
--connect-to=<HOST>[:<PORT>]
|
||||
Host and port to connect instead of using the authority
|
||||
in <URI>.
|
||||
--rps=<N> Specify request per second for each client. --rps and
|
||||
--timing-script-file are mutually exclusive.
|
||||
--groups=<GROUPS>
|
||||
Specify the supported groups.
|
||||
Default: )"
|
||||
<< config.groups << R"(
|
||||
--no-udp-gso
|
||||
Disable UDP GSO.
|
||||
--max-udp-payload-size=<SIZE>
|
||||
Specify the maximum outgoing UDP datagram payload size.
|
||||
-v, --verbose
|
||||
Output debug information.
|
||||
--version Display version information and exit.
|
||||
@@ -2097,6 +2296,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
std::string datafile;
|
||||
std::string logfile;
|
||||
std::string qlog_base;
|
||||
bool nreqs_set_manually = false;
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
@@ -2107,6 +2307,7 @@ int main(int argc, char **argv) {
|
||||
{"threads", required_argument, nullptr, 't'},
|
||||
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
||||
{"window-bits", required_argument, nullptr, 'w'},
|
||||
{"max-frame-size", required_argument, nullptr, 'f'},
|
||||
{"connection-window-bits", required_argument, nullptr, 'W'},
|
||||
{"input-file", required_argument, nullptr, 'i'},
|
||||
{"header", required_argument, nullptr, 'H'},
|
||||
@@ -2130,10 +2331,15 @@ int main(int argc, char **argv) {
|
||||
{"log-file", required_argument, &flag, 10},
|
||||
{"connect-to", required_argument, &flag, 11},
|
||||
{"rps", required_argument, &flag, 12},
|
||||
{"groups", required_argument, &flag, 13},
|
||||
{"tls13-ciphers", required_argument, &flag, 14},
|
||||
{"no-udp-gso", no_argument, &flag, 15},
|
||||
{"qlog-file-base", required_argument, &flag, 16},
|
||||
{"max-udp-payload-size", required_argument, &flag, 17},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
auto c = getopt_long(argc, argv,
|
||||
"hvW:c:d:m:n:p:t:w:H:i:r:T:N:D:B:", long_options,
|
||||
"hvW:c:d:m:n:p:t:w:f:H:i:r:T:N:D:B:", long_options,
|
||||
&option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@@ -2179,6 +2385,24 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
auto n = util::parse_uint_with_unit(optarg);
|
||||
if (n == -1) {
|
||||
std::cerr << "--max-frame-size: bad option value: " << optarg
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (static_cast<uint64_t>(n) < 16_k) {
|
||||
std::cerr << "--max-frame-size: minimum 16384" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (static_cast<uint64_t>(n) > 16_m - 1) {
|
||||
std::cerr << "--max-frame-size: maximum 16777215" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.max_frame_size = n;
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
char *header = optarg;
|
||||
// Skip first possible ':' in the header name
|
||||
@@ -2380,6 +2604,38 @@ int main(int argc, char **argv) {
|
||||
config.rps = v;
|
||||
break;
|
||||
}
|
||||
case 13:
|
||||
// --groups
|
||||
config.groups = optarg;
|
||||
break;
|
||||
case 14:
|
||||
// --tls13-ciphers
|
||||
config.tls13_ciphers = optarg;
|
||||
break;
|
||||
case 15:
|
||||
// --no-udp-gso
|
||||
config.no_udp_gso = true;
|
||||
break;
|
||||
case 16:
|
||||
// --qlog-file-base
|
||||
qlog_base = optarg;
|
||||
break;
|
||||
case 17: {
|
||||
// --max-udp-payload-size
|
||||
auto n = util::parse_uint_with_unit(optarg);
|
||||
if (n == -1) {
|
||||
std::cerr << "--max-udp-payload-size: bad option value: " << optarg
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (static_cast<uint64_t>(n) > 64_k) {
|
||||
std::cerr << "--max-udp-payload-size: must not exceed 65536"
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.max_udp_payload_size = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -2546,6 +2802,13 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.data_length = data_stat.st_size;
|
||||
auto addr = mmap(nullptr, config.data_length, PROT_READ, MAP_SHARED,
|
||||
config.data_fd, 0);
|
||||
if (addr == MAP_FAILED) {
|
||||
std::cerr << "-d: Could not mmap file " << datafile << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.data = static_cast<uint8_t *>(addr);
|
||||
}
|
||||
|
||||
if (!logfile.empty()) {
|
||||
@@ -2557,11 +2820,23 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!qlog_base.empty()) {
|
||||
if (!config.is_quic()) {
|
||||
std::cerr
|
||||
<< "Warning: --qlog-file-base: only effective in quic, ignoring."
|
||||
<< std::endl;
|
||||
} else {
|
||||
#ifdef ENABLE_HTTP3
|
||||
config.qlog_file_base = qlog_base;
|
||||
#endif // ENABLE_HTTP3
|
||||
}
|
||||
}
|
||||
|
||||
struct sigaction act {};
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
|
||||
auto ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
auto ssl_ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!ssl_ctx) {
|
||||
std::cerr << "Failed to create SSL_CTX: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
@@ -2576,9 +2851,14 @@ int main(int argc, char **argv) {
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
|
||||
if (nghttp2::tls::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
if (config.is_quic()) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
#endif // ENABLE_HTTP3
|
||||
} else if (nghttp2::tls::ssl_ctx_set_proto_versions(
|
||||
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
|
||||
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
|
||||
std::cerr << "Could not set TLS versions" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -2590,6 +2870,27 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
if (SSL_CTX_set_ciphersuites(ssl_ctx, config.tls13_ciphers.c_str()) == 0) {
|
||||
std::cerr << "SSL_CTX_set_ciphersuites with " << config.tls13_ciphers
|
||||
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr)
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
|
||||
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
if (SSL_CTX_set1_groups_list(ssl_ctx, config.groups.c_str()) != 1) {
|
||||
std::cerr << "SSL_CTX_set1_groups_list failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#else // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
|
||||
if (SSL_CTX_set1_curves_list(ssl_ctx, config.groups.c_str()) != 1) {
|
||||
std::cerr << "SSL_CTX_set1_curves_list failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
|
||||
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
|
||||
nullptr);
|
||||
@@ -2604,6 +2905,16 @@ int main(int argc, char **argv) {
|
||||
SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
auto keylog_filename = getenv("SSLKEYLOGFILE");
|
||||
if (keylog_filename) {
|
||||
keylog_file.open(keylog_filename, std::ios_base::app);
|
||||
if (keylog_file) {
|
||||
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
|
||||
}
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION;
|
||||
Headers shared_nva;
|
||||
shared_nva.emplace_back(":scheme", config.scheme);
|
||||
@@ -2822,6 +3133,8 @@ int main(int argc, char **argv) {
|
||||
stats.bytes_head += s.bytes_head;
|
||||
stats.bytes_head_decomp += s.bytes_head_decomp;
|
||||
stats.bytes_body += s.bytes_body;
|
||||
stats.udp_dgram_recv += s.udp_dgram_recv;
|
||||
stats.udp_dgram_sent += s.udp_dgram_sent;
|
||||
|
||||
for (size_t i = 0; i < stats.status.size(); ++i) {
|
||||
stats.status[i] += s.status[i];
|
||||
@@ -2880,30 +3193,37 @@ traffic: )" << util::utos_funit(stats.bytes_total)
|
||||
<< util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
|
||||
<< ") headers (space savings " << header_space_savings * 100
|
||||
<< "%), " << util::utos_funit(stats.bytes_body) << "B ("
|
||||
<< stats.bytes_body << R"() data
|
||||
min max mean sd +/- sd
|
||||
<< stats.bytes_body << R"() data)" << std::endl;
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (config.is_quic()) {
|
||||
std::cout << "UDP datagram: " << stats.udp_dgram_sent << " sent, "
|
||||
<< stats.udp_dgram_recv << " received" << std::endl;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
std::cout
|
||||
<< R"( min max mean sd +/- sd
|
||||
time for request: )"
|
||||
<< std::setw(10) << util::format_duration(ts.request.min) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.max) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.mean) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.sd)
|
||||
<< std::setw(9) << util::dtos(ts.request.within_sd) << "%"
|
||||
<< "\ntime for connect: " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.sd) << std::setw(9)
|
||||
<< util::dtos(ts.connect.within_sd) << "%"
|
||||
<< "\ntime to 1st byte: " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.sd) << std::setw(9)
|
||||
<< util::dtos(ts.ttfb.within_sd) << "%"
|
||||
<< "\nreq/s : " << std::setw(10) << ts.rps.min << " "
|
||||
<< std::setw(10) << ts.rps.max << " " << std::setw(10)
|
||||
<< ts.rps.mean << " " << std::setw(10) << ts.rps.sd << std::setw(9)
|
||||
<< util::dtos(ts.rps.within_sd) << "%" << std::endl;
|
||||
<< std::setw(10) << util::format_duration(ts.request.min) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.max) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.mean) << " "
|
||||
<< std::setw(10) << util::format_duration(ts.request.sd) << std::setw(9)
|
||||
<< util::dtos(ts.request.within_sd) << "%"
|
||||
<< "\ntime for connect: " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.sd) << std::setw(9)
|
||||
<< util::dtos(ts.connect.within_sd) << "%"
|
||||
<< "\ntime to 1st byte: " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.sd) << std::setw(9)
|
||||
<< util::dtos(ts.ttfb.within_sd) << "%"
|
||||
<< "\nreq/s : " << std::setw(10) << ts.rps.min << " "
|
||||
<< std::setw(10) << ts.rps.max << " " << std::setw(10) << ts.rps.mean
|
||||
<< " " << std::setw(10) << ts.rps.sd << std::setw(9)
|
||||
<< util::dtos(ts.rps.within_sd) << "%" << std::endl;
|
||||
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
|
||||
|
||||
69
src/h2load.h
69
src/h2load.h
@@ -45,11 +45,19 @@
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include <ngtcp2/ngtcp2.h>
|
||||
# include <ngtcp2/ngtcp2_crypto.h>
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "http2.h"
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include "quic.h"
|
||||
#endif // ENABLE_HTTP3
|
||||
#include "memchunk.h"
|
||||
#include "template.h"
|
||||
|
||||
@@ -72,8 +80,13 @@ struct Config {
|
||||
std::string connect_to_host;
|
||||
std::string ifile;
|
||||
std::string ciphers;
|
||||
std::string tls13_ciphers;
|
||||
// supported groups (or curves).
|
||||
std::string groups;
|
||||
// length of upload data
|
||||
int64_t data_length;
|
||||
// memory mapped upload data
|
||||
uint8_t *data;
|
||||
addrinfo *addrs;
|
||||
size_t nreqs;
|
||||
size_t nclients;
|
||||
@@ -82,6 +95,7 @@ struct Config {
|
||||
ssize_t max_concurrent_streams;
|
||||
size_t window_bits;
|
||||
size_t connection_window_bits;
|
||||
size_t max_frame_size;
|
||||
// rate at which connections should be made
|
||||
size_t rate;
|
||||
ev_tstamp rate_period;
|
||||
@@ -100,6 +114,8 @@ struct Config {
|
||||
int data_fd;
|
||||
// file descriptor to write per-request stats to.
|
||||
int log_fd;
|
||||
// base file name of qlog output files
|
||||
std::string qlog_file_base;
|
||||
uint16_t port;
|
||||
uint16_t default_port;
|
||||
uint16_t connect_to_port;
|
||||
@@ -116,6 +132,10 @@ struct Config {
|
||||
std::vector<std::string> npn_list;
|
||||
// The number of request per second for each client.
|
||||
double rps;
|
||||
// Disables GSO for UDP connections.
|
||||
bool no_udp_gso;
|
||||
// The maximum UDP datagram payload size to send.
|
||||
size_t max_udp_payload_size;
|
||||
|
||||
Config();
|
||||
~Config();
|
||||
@@ -124,6 +144,7 @@ struct Config {
|
||||
bool is_timing_based_mode() const;
|
||||
bool has_base_uri() const;
|
||||
bool rps_enabled() const;
|
||||
bool is_quic() const;
|
||||
};
|
||||
|
||||
struct RequestStat {
|
||||
@@ -220,6 +241,10 @@ struct Stats {
|
||||
std::vector<RequestStat> req_stats;
|
||||
// The statistics per client
|
||||
std::vector<ClientStat> client_stats;
|
||||
// The number of UDP datagrams received.
|
||||
size_t udp_dgram_recv;
|
||||
// The number of UDP datagrams sent.
|
||||
size_t udp_dgram_sent;
|
||||
};
|
||||
|
||||
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
|
||||
@@ -245,6 +270,7 @@ struct Sampling {
|
||||
|
||||
struct Worker {
|
||||
MemchunkPool mcpool;
|
||||
std::mt19937 randgen;
|
||||
Stats stats;
|
||||
Sampling request_times_smp;
|
||||
Sampling client_smp;
|
||||
@@ -309,6 +335,15 @@ struct Client {
|
||||
std::function<int(Client &)> readfn, writefn;
|
||||
Worker *worker;
|
||||
SSL *ssl;
|
||||
#ifdef ENABLE_HTTP3
|
||||
struct {
|
||||
ev_timer pkt_timer;
|
||||
ngtcp2_conn *conn;
|
||||
quic::Error last_error;
|
||||
bool close_requested;
|
||||
FILE *qlog_file;
|
||||
} quic;
|
||||
#endif // ENABLE_HTTP3
|
||||
ev_timer request_timeout_watcher;
|
||||
addrinfo *next_addr;
|
||||
// Address for the current address. When try_new_connection() is
|
||||
@@ -332,6 +367,7 @@ struct Client {
|
||||
// The client id per worker
|
||||
uint32_t id;
|
||||
int fd;
|
||||
Address local_addr;
|
||||
ev_timer conn_active_watcher;
|
||||
ev_timer conn_inactivity_watcher;
|
||||
std::string selected_proto;
|
||||
@@ -419,6 +455,39 @@ struct Client {
|
||||
void record_client_end_time();
|
||||
|
||||
void signal_write();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
// QUIC
|
||||
int quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
const sockaddr *remote_addr, socklen_t remote_addrlen);
|
||||
void quic_free();
|
||||
int read_quic();
|
||||
int write_quic();
|
||||
int write_udp(const sockaddr *addr, socklen_t addrlen, const uint8_t *data,
|
||||
size_t datalen, size_t gso_size);
|
||||
void quic_close_connection();
|
||||
|
||||
int quic_handshake_completed();
|
||||
int quic_recv_stream_data(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen);
|
||||
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);
|
||||
int quic_stream_stop_sending(int64_t stream_id, uint64_t app_error_code);
|
||||
int quic_extend_max_local_streams();
|
||||
|
||||
int quic_on_rx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
|
||||
size_t secretlen);
|
||||
int quic_on_tx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
|
||||
size_t secretlen);
|
||||
void quic_set_tls_alert(uint8_t alert);
|
||||
|
||||
void quic_write_client_handshake(ngtcp2_crypto_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);
|
||||
#endif // ENABLE_HTTP3
|
||||
};
|
||||
|
||||
} // namespace h2load
|
||||
|
||||
@@ -215,7 +215,7 @@ void Http2Session::on_connect() {
|
||||
|
||||
nghttp2_option_del(opt);
|
||||
|
||||
std::array<nghttp2_settings_entry, 3> iv;
|
||||
std::array<nghttp2_settings_entry, 4> iv;
|
||||
size_t niv = 2;
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
|
||||
iv[0].value = 0;
|
||||
@@ -227,6 +227,11 @@ void Http2Session::on_connect() {
|
||||
iv[niv].value = config->header_table_size;
|
||||
++niv;
|
||||
}
|
||||
if (config->max_frame_size != 16_k) {
|
||||
iv[niv].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
|
||||
iv[niv].value = config->max_frame_size;
|
||||
++niv;
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), niv);
|
||||
|
||||
|
||||
425
src/h2load_http3_session.cc
Normal file
425
src/h2load_http3_session.cc
Normal file
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 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 "h2load_http3_session.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "h2load.h"
|
||||
|
||||
namespace h2load {
|
||||
|
||||
Http3Session::Http3Session(Client *client)
|
||||
: client_(client), conn_(nullptr), npending_request_(0), reqidx_(0) {}
|
||||
|
||||
Http3Session::~Http3Session() { nghttp3_conn_del(conn_); }
|
||||
|
||||
void Http3Session::on_connect() {}
|
||||
|
||||
int Http3Session::submit_request() {
|
||||
if (npending_request_) {
|
||||
++npending_request_;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto config = client_->worker->config;
|
||||
reqidx_ = client_->reqidx;
|
||||
|
||||
if (++client_->reqidx == config->nva.size()) {
|
||||
client_->reqidx = 0;
|
||||
}
|
||||
|
||||
auto stream_id = submit_request_internal();
|
||||
if (stream_id < 0) {
|
||||
if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
|
||||
++npending_request_;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
nghttp3_ssize read_data(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec,
|
||||
size_t veccnt, uint32_t *pflags, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
|
||||
s->read_data(vec, veccnt, pflags);
|
||||
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::read_data(nghttp3_vec *vec, size_t veccnt,
|
||||
uint32_t *pflags) {
|
||||
assert(veccnt > 0);
|
||||
|
||||
auto config = client_->worker->config;
|
||||
|
||||
vec[0].base = config->data;
|
||||
vec[0].len = config->data_length;
|
||||
*pflags |= NGHTTP3_DATA_FLAG_EOF;
|
||||
}
|
||||
|
||||
int64_t Http3Session::submit_request_internal() {
|
||||
int rv;
|
||||
int64_t stream_id;
|
||||
|
||||
auto config = client_->worker->config;
|
||||
auto &nva = config->nva[reqidx_];
|
||||
|
||||
rv = ngtcp2_conn_open_bidi_stream(client_->quic.conn, &stream_id, nullptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nghttp3_data_reader dr{};
|
||||
dr.read_data = h2load::read_data;
|
||||
|
||||
rv = nghttp3_conn_submit_request(
|
||||
conn_, stream_id, reinterpret_cast<nghttp3_nv *>(nva.data()), nva.size(),
|
||||
config->data_fd == -1 ? nullptr : &dr, nullptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
client_->on_request(stream_id);
|
||||
auto req_stat = client_->get_req_stat(stream_id);
|
||||
assert(req_stat);
|
||||
client_->record_request_time(req_stat);
|
||||
|
||||
return stream_id;
|
||||
}
|
||||
|
||||
int Http3Session::on_read(const uint8_t *data, size_t len) { return -1; }
|
||||
|
||||
int Http3Session::on_write() { return -1; }
|
||||
|
||||
void Http3Session::terminate() {}
|
||||
|
||||
size_t Http3Session::max_concurrent_streams() {
|
||||
return (size_t)client_->worker->config->max_concurrent_streams;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_close(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
if (s->stream_close(stream_id, app_error_code) != 0) {
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Http3Session::stream_close(int64_t stream_id, uint64_t app_error_code) {
|
||||
if (!ngtcp2_is_bidi_stream(stream_id)) {
|
||||
assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
|
||||
ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
|
||||
}
|
||||
client_->on_stream_close(stream_id, app_error_code == NGHTTP3_H3_NO_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::recv_data(int64_t stream_id, const uint8_t *data,
|
||||
size_t datalen) {
|
||||
client_->record_ttfb();
|
||||
client_->worker->stats.bytes_body += datalen;
|
||||
consume(stream_id, datalen);
|
||||
}
|
||||
|
||||
namespace {
|
||||
int deferred_consume(nghttp3_conn *conn, int64_t stream_id, size_t nconsumed,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
s->consume(stream_id, nconsumed);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::consume(int64_t stream_id, size_t nconsumed) {
|
||||
ngtcp2_conn_extend_max_stream_offset(client_->quic.conn, stream_id,
|
||||
nconsumed);
|
||||
ngtcp2_conn_extend_max_offset(client_->quic.conn, nconsumed);
|
||||
}
|
||||
|
||||
namespace {
|
||||
int begin_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
s->begin_headers(stream_id);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Http3Session::begin_headers(int64_t stream_id) {
|
||||
auto payloadlen = nghttp3_conn_get_frame_payload_left(conn_, stream_id);
|
||||
assert(payloadlen > 0);
|
||||
|
||||
client_->worker->stats.bytes_head += payloadlen;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
|
||||
nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags,
|
||||
void *user_data, void *stream_user_data) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stop_sending(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code,
|
||||
void *user_data, void *stream_user_data) {
|
||||
auto s = static_cast<Http3Session *>(user_data);
|
||||
if (s->stop_sending(stream_id, app_error_code) != 0) {
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Http3Session::stop_sending(int64_t stream_id, uint64_t app_error_code) {
|
||||
auto rv = ngtcp2_conn_shutdown_stream_read(client_->quic.conn, stream_id,
|
||||
app_error_code);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::close_stream(int64_t stream_id, uint64_t app_error_code) {
|
||||
auto rv = nghttp3_conn_close_stream(conn_, stream_id, app_error_code);
|
||||
switch (rv) {
|
||||
case 0:
|
||||
return 0;
|
||||
case NGHTTP3_ERR_STREAM_NOT_FOUND:
|
||||
if (!ngtcp2_is_bidi_stream(stream_id)) {
|
||||
assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id));
|
||||
ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int Http3Session::shutdown_stream_read(int64_t stream_id) {
|
||||
auto rv = nghttp3_conn_shutdown_stream_read(conn_, stream_id);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::extend_max_local_streams() {
|
||||
auto config = client_->worker->config;
|
||||
|
||||
for (; npending_request_; --npending_request_) {
|
||||
auto stream_id = submit_request_internal();
|
||||
if (stream_id < 0) {
|
||||
if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (++reqidx_ == config->nva.size()) {
|
||||
reqidx_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::init_conn() {
|
||||
int rv;
|
||||
|
||||
assert(conn_ == nullptr);
|
||||
|
||||
if (ngtcp2_conn_get_max_local_streams_uni(client_->quic.conn) < 3) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nghttp3_callbacks callbacks{
|
||||
nullptr, // acked_stream_data
|
||||
h2load::stream_close,
|
||||
h2load::recv_data,
|
||||
h2load::deferred_consume,
|
||||
h2load::begin_headers,
|
||||
h2load::recv_header,
|
||||
nullptr, // end_headers
|
||||
nullptr, // begin_trailers
|
||||
h2load::recv_header,
|
||||
nullptr, // end_trailers
|
||||
h2load::stop_sending,
|
||||
};
|
||||
|
||||
auto config = client_->worker->config;
|
||||
|
||||
nghttp3_settings settings;
|
||||
nghttp3_settings_default(&settings);
|
||||
settings.qpack_max_dtable_capacity = config->header_table_size;
|
||||
settings.qpack_blocked_streams = 100;
|
||||
|
||||
auto mem = nghttp3_mem_default();
|
||||
|
||||
rv = nghttp3_conn_client_new(&conn_, &callbacks, &settings, mem, this);
|
||||
if (rv != 0) {
|
||||
std::cerr << "nghttp3_conn_client_new: " << nghttp3_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t ctrl_stream_id;
|
||||
|
||||
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &ctrl_stream_id, NULL);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = nghttp3_conn_bind_control_stream(conn_, ctrl_stream_id);
|
||||
if (rv != 0) {
|
||||
std::cerr << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t qpack_enc_stream_id, qpack_dec_stream_id;
|
||||
|
||||
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_enc_stream_id,
|
||||
NULL);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_dec_stream_id,
|
||||
NULL);
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = nghttp3_conn_bind_qpack_streams(conn_, qpack_enc_stream_id,
|
||||
qpack_dec_stream_id);
|
||||
if (rv != 0) {
|
||||
std::cerr << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv)
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
auto nconsumed = nghttp3_conn_read_stream(
|
||||
conn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN);
|
||||
if (nconsumed < 0) {
|
||||
std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
|
||||
<< std::endl;
|
||||
client_->quic.last_error = quic::err_application(nconsumed);
|
||||
return -1;
|
||||
}
|
||||
return nconsumed;
|
||||
}
|
||||
|
||||
ssize_t Http3Session::write_stream(int64_t &stream_id, int &fin,
|
||||
nghttp3_vec *vec, size_t veccnt) {
|
||||
auto sveccnt =
|
||||
nghttp3_conn_writev_stream(conn_, &stream_id, &fin, vec, veccnt);
|
||||
if (sveccnt < 0) {
|
||||
client_->quic.last_error = quic::err_application(sveccnt);
|
||||
return -1;
|
||||
}
|
||||
return sveccnt;
|
||||
}
|
||||
|
||||
int Http3Session::block_stream(int64_t stream_id) {
|
||||
auto rv = nghttp3_conn_block_stream(conn_, stream_id);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::shutdown_stream_write(int64_t stream_id) {
|
||||
auto rv = nghttp3_conn_shutdown_stream_write(conn_, stream_id);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
|
||||
auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http3Session::add_ack_offset(int64_t stream_id, size_t datalen) {
|
||||
auto rv = nghttp3_conn_add_ack_offset(conn_, stream_id, datalen);
|
||||
if (rv != 0) {
|
||||
client_->quic.last_error = quic::err_application(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace h2load
|
||||
81
src/h2load_http3_session.h
Normal file
81
src/h2load_http3_session.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 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 H2LOAD_HTTP3_SESSION_H
|
||||
#define H2LOAD_HTTP3_SESSION_H
|
||||
|
||||
#include "h2load_session.h"
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
namespace h2load {
|
||||
|
||||
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();
|
||||
|
||||
int init_conn();
|
||||
int stream_close(int64_t stream_id, uint64_t app_error_code);
|
||||
void recv_data(int64_t stream_id, const uint8_t *data, size_t datalen);
|
||||
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);
|
||||
int stop_sending(int64_t stream_id, uint64_t app_error_code);
|
||||
|
||||
int close_stream(int64_t stream_id, uint64_t app_error_code);
|
||||
int shutdown_stream_read(int64_t stream_id);
|
||||
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 write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
|
||||
size_t veccnt);
|
||||
int block_stream(int64_t stream_id);
|
||||
int shutdown_stream_write(int64_t stream_id);
|
||||
int add_write_offset(int64_t stream_id, size_t ndatalen);
|
||||
int add_ack_offset(int64_t stream_id, size_t datalen);
|
||||
|
||||
void read_data(nghttp3_vec *vec, size_t veccnt, uint32_t *pflags);
|
||||
|
||||
private:
|
||||
Client *client_;
|
||||
nghttp3_conn *conn_;
|
||||
size_t npending_request_;
|
||||
size_t reqidx_;
|
||||
};
|
||||
|
||||
} // namespace h2load
|
||||
|
||||
#endif // H2LOAD_HTTP3_SESSION_H
|
||||
790
src/h2load_quic.cc
Normal file
790
src/h2load_quic.cc
Normal file
@@ -0,0 +1,790 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 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 "h2load_quic.h"
|
||||
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
|
||||
# include <ngtcp2/ngtcp2_crypto_openssl.h>
|
||||
#endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
|
||||
#ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
|
||||
# include <ngtcp2/ngtcp2_crypto_boringssl.h>
|
||||
#endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "h2load_http3_session.h"
|
||||
|
||||
namespace h2load {
|
||||
|
||||
namespace {
|
||||
int handshake_completed(ngtcp2_conn *conn, void *user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
|
||||
if (c->quic_handshake_completed() != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_handshake_completed() { return connection_made(); }
|
||||
|
||||
namespace {
|
||||
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) {
|
||||
// TODO Better to do this gracefully rather than
|
||||
// NGTCP2_ERR_CALLBACK_FAILURE. Perhaps, call
|
||||
// ngtcp2_conn_write_application_close() ?
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_recv_stream_data(uint32_t flags, int64_t stream_id,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
if (worker->current_phase == Phase::MAIN_DURATION) {
|
||||
worker->stats.bytes_total += datalen;
|
||||
}
|
||||
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
auto nconsumed = s->read_stream(flags, stream_id, data, datalen);
|
||||
if (nconsumed == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngtcp2_conn_extend_max_stream_offset(quic.conn, stream_id, nconsumed);
|
||||
ngtcp2_conn_extend_max_offset(quic.conn, nconsumed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int acked_stream_data_offset(ngtcp2_conn *conn, int64_t stream_id,
|
||||
uint64_t offset, uint64_t datalen, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_acked_stream_data_offset(stream_id, datalen) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_acked_stream_data_offset(int64_t stream_id, size_t datalen) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->add_ack_offset(stream_id, datalen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
||||
uint64_t app_error_code, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
|
||||
if (!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
|
||||
app_error_code = NGHTTP3_H3_NO_ERROR;
|
||||
}
|
||||
|
||||
if (c->quic_stream_close(stream_id, app_error_code) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_stream_close(int64_t stream_id, uint64_t app_error_code) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->close_stream(stream_id, app_error_code) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
|
||||
uint64_t app_error_code, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_stream_reset(stream_id, app_error_code) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_stream_reset(int64_t stream_id, uint64_t app_error_code) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->shutdown_stream_read(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
|
||||
uint64_t app_error_code, void *user_data,
|
||||
void *stream_user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
if (c->quic_stream_stop_sending(stream_id, app_error_code) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_stream_stop_sending(int64_t stream_id,
|
||||
uint64_t app_error_code) {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->shutdown_stream_read(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int extend_max_local_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams,
|
||||
void *user_data) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
|
||||
if (c->quic_extend_max_local_streams() != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_extend_max_local_streams() {
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
if (s->extend_max_local_streams() != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token,
|
||||
size_t cidlen, void *user_data) {
|
||||
if (RAND_bytes(cid->data, cidlen) != 1) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
cid->datalen = cidlen;
|
||||
|
||||
if (RAND_bytes(token, NGTCP2_STATELESS_RESET_TOKENLEN) != 1) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void debug_log_printf(void *user_data, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int generate_cid(ngtcp2_cid &dest) {
|
||||
dest.datalen = 8;
|
||||
|
||||
if (RAND_bytes(dest.data, dest.datalen) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
ngtcp2_tstamp timestamp(struct ev_loop *loop) {
|
||||
return ev_now(loop) * NGTCP2_SECONDS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
|
||||
namespace {
|
||||
int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
|
||||
const uint8_t *rx_secret, const uint8_t *tx_secret,
|
||||
size_t secret_len) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
auto level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
|
||||
|
||||
if (c->quic_on_rx_secret(level, rx_secret, secret_len) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c->quic_on_tx_secret(level, tx_secret, secret_len) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
|
||||
const uint8_t *data, size_t len) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
c->quic_write_client_handshake(
|
||||
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level), data, len);
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int flush_flight(SSL *ssl) { return 1; }
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
c->quic_set_tls_alert(alert);
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto quic_method = SSL_QUIC_METHOD{
|
||||
set_encryption_secrets,
|
||||
add_handshake_data,
|
||||
flush_flight,
|
||||
send_alert,
|
||||
};
|
||||
} // namespace
|
||||
#endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
|
||||
|
||||
#ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
|
||||
namespace {
|
||||
int set_read_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secretlen) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
|
||||
if (c->quic_on_rx_secret(
|
||||
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), secret,
|
||||
secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int set_write_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secretlen) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
|
||||
if (c->quic_on_tx_secret(
|
||||
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), secret,
|
||||
secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int add_handshake_data(SSL *ssl, ssl_encryption_level_t ssl_level,
|
||||
const uint8_t *data, size_t len) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
c->quic_write_client_handshake(
|
||||
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), data, len);
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int flush_flight(SSL *ssl) { return 1; }
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int send_alert(SSL *ssl, ssl_encryption_level_t level, uint8_t alert) {
|
||||
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
|
||||
c->quic_set_tls_alert(alert);
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto quic_method = SSL_QUIC_METHOD{
|
||||
set_read_secret, set_write_secret, add_handshake_data,
|
||||
flush_flight, send_alert,
|
||||
};
|
||||
} // namespace
|
||||
#endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
|
||||
|
||||
// qlog write callback -- excerpted from ngtcp2/examples/client_base.cc
|
||||
namespace {
|
||||
void qlog_write_cb(void *user_data, uint32_t flags, const void *data,
|
||||
size_t datalen) {
|
||||
auto c = static_cast<Client *>(user_data);
|
||||
c->quic_write_qlog(data, datalen);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Client::quic_write_qlog(const void *data, size_t datalen) {
|
||||
assert(quic.qlog_file != nullptr);
|
||||
fwrite(data, 1, datalen, quic.qlog_file);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx) {
|
||||
util::random_bytes(dest, dest + destlen,
|
||||
*static_cast<std::mt19937 *>(rand_ctx->native_handle));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
|
||||
const sockaddr *remote_addr, socklen_t remote_addrlen) {
|
||||
int rv;
|
||||
|
||||
if (!ssl) {
|
||||
ssl = SSL_new(worker->ssl_ctx);
|
||||
|
||||
SSL_set_app_data(ssl, this);
|
||||
SSL_set_connect_state(ssl);
|
||||
SSL_set_quic_method(ssl, &quic_method);
|
||||
SSL_set_quic_use_legacy_codepoint(ssl, 0);
|
||||
}
|
||||
|
||||
auto callbacks = ngtcp2_callbacks{
|
||||
ngtcp2_crypto_client_initial_cb,
|
||||
nullptr, // recv_client_initial
|
||||
ngtcp2_crypto_recv_crypto_data_cb,
|
||||
h2load::handshake_completed,
|
||||
nullptr, // recv_version_negotiation
|
||||
ngtcp2_crypto_encrypt_cb,
|
||||
ngtcp2_crypto_decrypt_cb,
|
||||
ngtcp2_crypto_hp_mask_cb,
|
||||
h2load::recv_stream_data,
|
||||
h2load::acked_stream_data_offset,
|
||||
nullptr, // stream_open
|
||||
h2load::stream_close,
|
||||
nullptr, // recv_stateless_reset
|
||||
ngtcp2_crypto_recv_retry_cb,
|
||||
h2load::extend_max_local_streams_bidi,
|
||||
nullptr, // extend_max_local_streams_uni
|
||||
h2load::rand,
|
||||
get_new_connection_id,
|
||||
nullptr, // remove_connection_id
|
||||
ngtcp2_crypto_update_key_cb,
|
||||
nullptr, // path_validation
|
||||
nullptr, // select_preferred_addr
|
||||
h2load::stream_reset,
|
||||
nullptr, // extend_max_remote_streams_bidi
|
||||
nullptr, // extend_max_remote_streams_uni
|
||||
nullptr, // extend_max_stream_data
|
||||
nullptr, // dcid_status
|
||||
nullptr, // handshake_confirmed
|
||||
nullptr, // recv_new_token
|
||||
ngtcp2_crypto_delete_crypto_aead_ctx_cb,
|
||||
ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
|
||||
nullptr, // recv_datagram
|
||||
nullptr, // ack_datagram
|
||||
nullptr, // lost_datagram
|
||||
ngtcp2_crypto_get_path_challenge_data_cb,
|
||||
h2load::stream_stop_sending,
|
||||
};
|
||||
|
||||
ngtcp2_cid scid, dcid;
|
||||
if (generate_cid(scid) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (generate_cid(dcid) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto config = worker->config;
|
||||
|
||||
ngtcp2_settings settings;
|
||||
ngtcp2_settings_default(&settings);
|
||||
if (config->verbose) {
|
||||
settings.log_printf = debug_log_printf;
|
||||
}
|
||||
settings.initial_ts = timestamp(worker->loop);
|
||||
settings.rand_ctx.native_handle = &worker->randgen;
|
||||
if (!config->qlog_file_base.empty()) {
|
||||
assert(quic.qlog_file == nullptr);
|
||||
auto path = config->qlog_file_base;
|
||||
path += '.';
|
||||
path += util::utos(worker->id);
|
||||
path += '.';
|
||||
path += util::utos(id);
|
||||
path += ".sqlog";
|
||||
quic.qlog_file = fopen(path.c_str(), "w");
|
||||
if (quic.qlog_file == nullptr) {
|
||||
std::cerr << "Failed to open a qlog file: " << path << std::endl;
|
||||
return -1;
|
||||
}
|
||||
settings.qlog.write = qlog_write_cb;
|
||||
}
|
||||
if (config->max_udp_payload_size) {
|
||||
settings.max_udp_payload_size = config->max_udp_payload_size;
|
||||
settings.no_udp_payload_size_shaping = 1;
|
||||
}
|
||||
|
||||
ngtcp2_transport_params params;
|
||||
ngtcp2_transport_params_default(¶ms);
|
||||
auto max_stream_data =
|
||||
std::min((1 << 26) - 1, (1 << config->window_bits) - 1);
|
||||
params.initial_max_stream_data_bidi_local = max_stream_data;
|
||||
params.initial_max_stream_data_uni = max_stream_data;
|
||||
params.initial_max_data = (1 << config->connection_window_bits) - 1;
|
||||
params.initial_max_streams_bidi = 0;
|
||||
params.initial_max_streams_uni = 100;
|
||||
params.max_idle_timeout = 30 * NGTCP2_SECONDS;
|
||||
|
||||
auto path = ngtcp2_path{
|
||||
{
|
||||
const_cast<sockaddr *>(local_addr),
|
||||
local_addrlen,
|
||||
},
|
||||
{
|
||||
const_cast<sockaddr *>(remote_addr),
|
||||
remote_addrlen,
|
||||
},
|
||||
};
|
||||
|
||||
assert(config->npn_list.size());
|
||||
|
||||
uint32_t quic_version;
|
||||
|
||||
if (config->npn_list[0] == NGHTTP3_ALPN_H3) {
|
||||
quic_version = NGTCP2_PROTO_VER_V1;
|
||||
} else {
|
||||
quic_version = NGTCP2_PROTO_VER_MIN;
|
||||
}
|
||||
|
||||
rv = ngtcp2_conn_client_new(&quic.conn, &dcid, &scid, &path, quic_version,
|
||||
&callbacks, &settings, ¶ms, nullptr, this);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngtcp2_conn_set_tls_native_handle(quic.conn, ssl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::quic_free() {
|
||||
ngtcp2_conn_del(quic.conn);
|
||||
if (quic.qlog_file != nullptr) {
|
||||
fclose(quic.qlog_file);
|
||||
quic.qlog_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::quic_close_connection() {
|
||||
if (!quic.conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
|
||||
ngtcp2_ssize nwrite;
|
||||
ngtcp2_path_storage ps;
|
||||
ngtcp2_path_storage_zero(&ps);
|
||||
|
||||
switch (quic.last_error.type) {
|
||||
case quic::ErrorType::TransportVersionNegotiation:
|
||||
return;
|
||||
case quic::ErrorType::Transport:
|
||||
nwrite = ngtcp2_conn_write_connection_close(
|
||||
quic.conn, &ps.path, nullptr, buf.data(), buf.size(),
|
||||
quic.last_error.code, nullptr, 0, timestamp(worker->loop));
|
||||
break;
|
||||
case quic::ErrorType::Application:
|
||||
nwrite = ngtcp2_conn_write_application_close(
|
||||
quic.conn, &ps.path, nullptr, buf.data(), buf.size(),
|
||||
quic.last_error.code, nullptr, 0, timestamp(worker->loop));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (nwrite < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
write_udp(reinterpret_cast<sockaddr *>(ps.path.remote.addr),
|
||||
ps.path.remote.addrlen, buf.data(), nwrite, 0);
|
||||
}
|
||||
|
||||
int Client::quic_on_rx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
|
||||
size_t secretlen) {
|
||||
if (ngtcp2_crypto_derive_and_install_rx_key(quic.conn, nullptr, nullptr,
|
||||
nullptr, level, secret,
|
||||
secretlen) != 0) {
|
||||
std::cerr << "ngtcp2_crypto_derive_and_install_rx_key() failed"
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (level == NGTCP2_CRYPTO_LEVEL_APPLICATION) {
|
||||
auto s = std::make_unique<Http3Session>(this);
|
||||
if (s->init_conn() == -1) {
|
||||
return -1;
|
||||
}
|
||||
session = std::move(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Client::quic_on_tx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
|
||||
size_t secretlen) {
|
||||
if (ngtcp2_crypto_derive_and_install_tx_key(quic.conn, nullptr, nullptr,
|
||||
nullptr, level, secret,
|
||||
secretlen) != 0) {
|
||||
std::cerr << "ngtcp2_crypto_derive_and_install_tx_key() failed"
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::quic_set_tls_alert(uint8_t alert) {
|
||||
quic.last_error = quic::err_transport_tls(alert);
|
||||
}
|
||||
|
||||
void Client::quic_write_client_handshake(ngtcp2_crypto_level level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
assert(level < 2);
|
||||
|
||||
ngtcp2_conn_submit_crypto_data(quic.conn, level, data, datalen);
|
||||
}
|
||||
|
||||
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto c = static_cast<Client *>(w->data);
|
||||
|
||||
if (c->quic_pkt_timeout() != 0) {
|
||||
c->fail();
|
||||
c->worker->free_client(c);
|
||||
delete c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int Client::quic_pkt_timeout() {
|
||||
int rv;
|
||||
auto now = timestamp(worker->loop);
|
||||
|
||||
rv = ngtcp2_conn_handle_expiry(quic.conn, now);
|
||||
if (rv != 0) {
|
||||
quic.last_error = quic::err_transport(NGTCP2_ERR_INTERNAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return write_quic();
|
||||
}
|
||||
|
||||
void Client::quic_restart_pkt_timer() {
|
||||
auto expiry = ngtcp2_conn_get_expiry(quic.conn);
|
||||
auto now = timestamp(worker->loop);
|
||||
auto t = expiry > now ? static_cast<ev_tstamp>(expiry - now) / NGTCP2_SECONDS
|
||||
: 1e-9;
|
||||
quic.pkt_timer.repeat = t;
|
||||
ev_timer_again(worker->loop, &quic.pkt_timer);
|
||||
}
|
||||
|
||||
int Client::read_quic() {
|
||||
std::array<uint8_t, 65536> buf;
|
||||
sockaddr_union su;
|
||||
socklen_t addrlen = sizeof(su);
|
||||
int rv;
|
||||
size_t pktcnt = 0;
|
||||
ngtcp2_pkt_info pi{};
|
||||
|
||||
for (;;) {
|
||||
auto nread =
|
||||
recvfrom(fd, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
|
||||
if (nread == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(quic.conn);
|
||||
|
||||
++worker->stats.udp_dgram_recv;
|
||||
|
||||
auto path = ngtcp2_path{
|
||||
{
|
||||
&local_addr.su.sa,
|
||||
static_cast<socklen_t>(local_addr.len),
|
||||
},
|
||||
{
|
||||
&su.sa,
|
||||
addrlen,
|
||||
},
|
||||
};
|
||||
|
||||
rv = ngtcp2_conn_read_pkt(quic.conn, &path, &pi, buf.data(), nread,
|
||||
timestamp(worker->loop));
|
||||
if (rv != 0) {
|
||||
std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
|
||||
|
||||
if (!quic.last_error.code) {
|
||||
quic.last_error = quic::err_transport(rv);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (++pktcnt == 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Client::write_quic() {
|
||||
ev_io_stop(worker->loop, &wev);
|
||||
|
||||
if (quic.close_requested) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::array<nghttp3_vec, 16> vec;
|
||||
size_t pktcnt = 0;
|
||||
auto max_udp_payload_size =
|
||||
ngtcp2_conn_get_path_max_udp_payload_size(quic.conn);
|
||||
size_t max_pktcnt =
|
||||
#ifdef UDP_SEGMENT
|
||||
worker->config->no_udp_gso
|
||||
? 1
|
||||
: std::min(static_cast<size_t>(10),
|
||||
static_cast<size_t>(64_k / max_udp_payload_size));
|
||||
#else // !UDP_SEGMENT
|
||||
1;
|
||||
#endif // !UDP_SEGMENT
|
||||
std::array<uint8_t, 64_k> buf;
|
||||
uint8_t *bufpos = buf.data();
|
||||
ngtcp2_path_storage ps;
|
||||
|
||||
ngtcp2_path_storage_zero(&ps);
|
||||
|
||||
auto s = static_cast<Http3Session *>(session.get());
|
||||
|
||||
for (;;) {
|
||||
int64_t stream_id = -1;
|
||||
int fin = 0;
|
||||
ssize_t sveccnt = 0;
|
||||
|
||||
if (session && ngtcp2_conn_get_max_data_left(quic.conn)) {
|
||||
sveccnt = s->write_stream(stream_id, fin, vec.data(), vec.size());
|
||||
if (sveccnt == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ngtcp2_ssize ndatalen;
|
||||
auto v = vec.data();
|
||||
auto vcnt = static_cast<size_t>(sveccnt);
|
||||
|
||||
uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE;
|
||||
if (fin) {
|
||||
flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
|
||||
}
|
||||
|
||||
auto nwrite = ngtcp2_conn_writev_stream(
|
||||
quic.conn, &ps.path, nullptr, bufpos, max_udp_payload_size, &ndatalen,
|
||||
flags, stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt,
|
||||
timestamp(worker->loop));
|
||||
if (nwrite < 0) {
|
||||
switch (nwrite) {
|
||||
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
|
||||
assert(ndatalen == -1);
|
||||
if (s->block_stream(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
case NGTCP2_ERR_STREAM_SHUT_WR:
|
||||
assert(ndatalen == -1);
|
||||
if (s->shutdown_stream_write(stream_id) != 0) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
case NGTCP2_ERR_WRITE_MORE:
|
||||
assert(ndatalen >= 0);
|
||||
if (s->add_write_offset(stream_id, ndatalen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
quic.last_error = quic::err_transport(nwrite);
|
||||
return -1;
|
||||
} else if (ndatalen >= 0 && s->add_write_offset(stream_id, ndatalen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
quic_restart_pkt_timer();
|
||||
|
||||
if (nwrite == 0) {
|
||||
if (bufpos - buf.data()) {
|
||||
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
|
||||
bufpos - buf.data(), max_udp_payload_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bufpos += nwrite;
|
||||
|
||||
// Assume that the path does not change.
|
||||
if (++pktcnt == max_pktcnt ||
|
||||
static_cast<size_t>(nwrite) < max_udp_payload_size) {
|
||||
write_udp(ps.path.remote.addr, ps.path.remote.addrlen, buf.data(),
|
||||
bufpos - buf.data(), max_udp_payload_size);
|
||||
signal_write();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace h2load
|
||||
38
src/h2load_quic.h
Normal file
38
src/h2load_quic.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 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 H2LOAD_QUIC_H
|
||||
#define H2LOAD_QUIC_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#include "h2load.h"
|
||||
|
||||
namespace h2load {
|
||||
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents);
|
||||
} // namespace h2load
|
||||
|
||||
#endif // H2LOAD_QUIC_H
|
||||
@@ -916,7 +916,7 @@ void test_http2_rewrite_clean_path(void) {
|
||||
CU_ASSERT("/delta%3A" == http2::rewrite_clean_path(
|
||||
balloc, StringRef::from_lit("/delta%3a")));
|
||||
|
||||
// path component is normalized before mathcing
|
||||
// path component is normalized before matching
|
||||
CU_ASSERT(
|
||||
"/alpha/bravo/" ==
|
||||
http2::rewrite_clean_path(
|
||||
|
||||
206
src/http3.cc
Normal file
206
src/http3.cc
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2021 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "http3.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace http3 {
|
||||
|
||||
namespace {
|
||||
nghttp3_nv make_nv_internal(const std::string &name, const std::string &value,
|
||||
bool never_index, uint8_t nv_flags) {
|
||||
uint8_t flags;
|
||||
|
||||
flags = nv_flags |
|
||||
(never_index ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE);
|
||||
|
||||
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
|
||||
value.size(), flags};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
nghttp3_nv make_nv_internal(const StringRef &name, const StringRef &value,
|
||||
bool never_index, uint8_t nv_flags) {
|
||||
uint8_t flags;
|
||||
|
||||
flags = nv_flags |
|
||||
(never_index ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE);
|
||||
|
||||
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
|
||||
value.size(), flags};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
nghttp3_nv make_nv(const std::string &name, const std::string &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index, NGHTTP3_NV_FLAG_NONE);
|
||||
}
|
||||
|
||||
nghttp3_nv make_nv(const StringRef &name, const StringRef &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index, NGHTTP3_NV_FLAG_NONE);
|
||||
}
|
||||
|
||||
nghttp3_nv make_nv_nocopy(const std::string &name, const std::string &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index,
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME |
|
||||
NGHTTP3_NV_FLAG_NO_COPY_VALUE);
|
||||
}
|
||||
|
||||
nghttp3_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
|
||||
bool never_index) {
|
||||
return make_nv_internal(name, value, never_index,
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME |
|
||||
NGHTTP3_NV_FLAG_NO_COPY_VALUE);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void copy_headers_to_nva_internal(std::vector<nghttp3_nv> &nva,
|
||||
const HeaderRefs &headers, uint8_t nv_flags,
|
||||
uint32_t flags) {
|
||||
auto it_forwarded = std::end(headers);
|
||||
auto it_xff = std::end(headers);
|
||||
auto it_xfp = std::end(headers);
|
||||
auto it_via = std::end(headers);
|
||||
|
||||
for (auto it = std::begin(headers); it != std::end(headers); ++it) {
|
||||
auto kv = &(*it);
|
||||
if (kv->name.empty() || kv->name[0] == ':') {
|
||||
continue;
|
||||
}
|
||||
switch (kv->token) {
|
||||
case http2::HD_COOKIE:
|
||||
case http2::HD_CONNECTION:
|
||||
case http2::HD_HOST:
|
||||
case http2::HD_HTTP2_SETTINGS:
|
||||
case http2::HD_KEEP_ALIVE:
|
||||
case http2::HD_PROXY_CONNECTION:
|
||||
case http2::HD_SERVER:
|
||||
case http2::HD_TE:
|
||||
case http2::HD_TRANSFER_ENCODING:
|
||||
case http2::HD_UPGRADE:
|
||||
continue;
|
||||
case http2::HD_EARLY_DATA:
|
||||
if (flags & http2::HDOP_STRIP_EARLY_DATA) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case http2::HD_SEC_WEBSOCKET_ACCEPT:
|
||||
if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case http2::HD_SEC_WEBSOCKET_KEY:
|
||||
if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_KEY) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case http2::HD_FORWARDED:
|
||||
if (flags & http2::HDOP_STRIP_FORWARDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_forwarded == std::end(headers)) {
|
||||
it_forwarded = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_forwarded);
|
||||
it_forwarded = it;
|
||||
break;
|
||||
case http2::HD_X_FORWARDED_FOR:
|
||||
if (flags & http2::HDOP_STRIP_X_FORWARDED_FOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xff == std::end(headers)) {
|
||||
it_xff = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xff);
|
||||
it_xff = it;
|
||||
break;
|
||||
case http2::HD_X_FORWARDED_PROTO:
|
||||
if (flags & http2::HDOP_STRIP_X_FORWARDED_PROTO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_xfp == std::end(headers)) {
|
||||
it_xfp = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_xfp);
|
||||
it_xfp = it;
|
||||
break;
|
||||
case http2::HD_VIA:
|
||||
if (flags & http2::HDOP_STRIP_VIA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it_via == std::end(headers)) {
|
||||
it_via = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
kv = &(*it_via);
|
||||
it_via = it;
|
||||
break;
|
||||
}
|
||||
nva.push_back(
|
||||
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void copy_headers_to_nva(std::vector<nghttp3_nv> &nva,
|
||||
const HeaderRefs &headers, uint32_t flags) {
|
||||
copy_headers_to_nva_internal(nva, headers, NGHTTP3_NV_FLAG_NONE, flags);
|
||||
}
|
||||
|
||||
void copy_headers_to_nva_nocopy(std::vector<nghttp3_nv> &nva,
|
||||
const HeaderRefs &headers, uint32_t flags) {
|
||||
copy_headers_to_nva_internal(
|
||||
nva, headers,
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE, flags);
|
||||
}
|
||||
|
||||
int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
|
||||
size_t valuelen) {
|
||||
if (!nghttp3_check_header_name(name, namelen)) {
|
||||
return 0;
|
||||
}
|
||||
if (!nghttp3_check_header_value(value, valuelen)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace http3
|
||||
|
||||
} // namespace nghttp2
|
||||
123
src/http3.h
Normal file
123
src/http3.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2021 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef HTTP3_H
|
||||
#define HTTP3_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
#include "http2.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace http3 {
|
||||
|
||||
// Creates nghttp3_nv using |name| and |value| and returns it. The
|
||||
// returned value only references the data pointer to name.c_str() and
|
||||
// value.c_str(). If |no_index| is true, nghttp3_nv flags member has
|
||||
// NGHTTP3_NV_FLAG_NEVER_INDEX flag set.
|
||||
nghttp3_nv make_nv(const std::string &name, const std::string &value,
|
||||
bool never_index = false);
|
||||
|
||||
nghttp3_nv make_nv(const StringRef &name, const StringRef &value,
|
||||
bool never_index = false);
|
||||
|
||||
nghttp3_nv make_nv_nocopy(const std::string &name, const std::string &value,
|
||||
bool never_index = false);
|
||||
|
||||
nghttp3_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
|
||||
bool never_index = false);
|
||||
|
||||
// Create nghttp3_nv from string literal |name| and |value|.
|
||||
template <size_t N, size_t M>
|
||||
constexpr nghttp3_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) {
|
||||
return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1,
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
|
||||
}
|
||||
|
||||
// Create nghttp3_nv from string literal |name| and c-string |value|.
|
||||
template <size_t N>
|
||||
nghttp3_nv make_nv_lc(const char (&name)[N], const char *value) {
|
||||
return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME};
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
nghttp3_nv make_nv_lc_nocopy(const char (&name)[N], const char *value) {
|
||||
return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
|
||||
}
|
||||
|
||||
// Create nghttp3_nv from string literal |name| and std::string
|
||||
// |value|.
|
||||
template <size_t N>
|
||||
nghttp3_nv make_nv_ls(const char (&name)[N], const std::string &value) {
|
||||
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME};
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
nghttp3_nv make_nv_ls_nocopy(const char (&name)[N], const std::string &value) {
|
||||
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
nghttp3_nv make_nv_ls_nocopy(const char (&name)[N], const StringRef &value) {
|
||||
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
|
||||
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE};
|
||||
}
|
||||
|
||||
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
||||
// before this call (its element's token field is assigned). Certain
|
||||
// headers, including disallowed headers in HTTP/3 spec and headers
|
||||
// which require special handling (i.e. via), are not copied. |flags|
|
||||
// is one or more of HeaderBuildOp flags. They tell function that
|
||||
// certain header fields should not be added.
|
||||
void copy_headers_to_nva(std::vector<nghttp3_nv> &nva,
|
||||
const HeaderRefs &headers, uint32_t flags);
|
||||
|
||||
// Just like copy_headers_to_nva(), but this adds
|
||||
// NGHTTP3_NV_FLAG_NO_COPY_NAME and NGHTTP3_NV_FLAG_NO_COPY_VALUE.
|
||||
void copy_headers_to_nva_nocopy(std::vector<nghttp3_nv> &nva,
|
||||
const HeaderRefs &headers, uint32_t flags);
|
||||
|
||||
// Checks the header name/value pair using nghttp3_check_header_name()
|
||||
// and nghttp3_check_header_value(). If both function returns nonzero,
|
||||
// this function returns nonzero.
|
||||
int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
|
||||
size_t valuelen);
|
||||
|
||||
} // namespace http3
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // HTTP3_H
|
||||
@@ -113,13 +113,22 @@ template <typename T> struct Pool {
|
||||
|
||||
template <typename Memchunk> struct Memchunks {
|
||||
Memchunks(Pool<Memchunk> *pool)
|
||||
: pool(pool), head(nullptr), tail(nullptr), len(0) {}
|
||||
: pool(pool),
|
||||
head(nullptr),
|
||||
tail(nullptr),
|
||||
len(0),
|
||||
mark(nullptr),
|
||||
mark_pos(nullptr),
|
||||
mark_offset(0) {}
|
||||
Memchunks(const Memchunks &) = delete;
|
||||
Memchunks(Memchunks &&other) noexcept
|
||||
: pool{other.pool}, // keep other.pool
|
||||
head{std::exchange(other.head, nullptr)},
|
||||
tail{std::exchange(other.tail, nullptr)},
|
||||
len{std::exchange(other.len, 0)} {}
|
||||
len{std::exchange(other.len, 0)},
|
||||
mark{std::exchange(other.mark, nullptr)},
|
||||
mark_pos{std::exchange(other.mark_pos, nullptr)},
|
||||
mark_offset{std::exchange(other.mark_offset, 0)} {}
|
||||
Memchunks &operator=(const Memchunks &) = delete;
|
||||
Memchunks &operator=(Memchunks &&other) noexcept {
|
||||
if (this == &other) {
|
||||
@@ -132,6 +141,9 @@ template <typename Memchunk> struct Memchunks {
|
||||
head = std::exchange(other.head, nullptr);
|
||||
tail = std::exchange(other.tail, nullptr);
|
||||
len = std::exchange(other.len, 0);
|
||||
mark = std::exchange(other.mark, nullptr);
|
||||
mark_pos = std::exchange(other.mark_pos, nullptr);
|
||||
mark_offset = std::exchange(other.mark_offset, 0);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -200,6 +212,8 @@ template <typename Memchunk> struct Memchunks {
|
||||
return len;
|
||||
}
|
||||
size_t remove(void *dest, size_t count) {
|
||||
assert(mark == nullptr);
|
||||
|
||||
if (!tail || count == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -231,6 +245,8 @@ template <typename Memchunk> struct Memchunks {
|
||||
return first - static_cast<uint8_t *>(dest);
|
||||
}
|
||||
size_t remove(Memchunks &dest, size_t count) {
|
||||
assert(mark == nullptr);
|
||||
|
||||
if (!tail || count == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -262,6 +278,7 @@ template <typename Memchunk> struct Memchunks {
|
||||
}
|
||||
size_t remove(Memchunks &dest) {
|
||||
assert(pool == dest.pool);
|
||||
assert(mark == nullptr);
|
||||
|
||||
if (head == nullptr) {
|
||||
return 0;
|
||||
@@ -284,6 +301,8 @@ template <typename Memchunk> struct Memchunks {
|
||||
return n;
|
||||
}
|
||||
size_t drain(size_t count) {
|
||||
assert(mark == nullptr);
|
||||
|
||||
auto ndata = count;
|
||||
auto m = head;
|
||||
while (m) {
|
||||
@@ -305,6 +324,38 @@ template <typename Memchunk> struct Memchunks {
|
||||
}
|
||||
return ndata - count;
|
||||
}
|
||||
size_t drain_mark(size_t count) {
|
||||
auto ndata = count;
|
||||
auto m = head;
|
||||
while (m) {
|
||||
auto next = m->next;
|
||||
auto n = std::min(count, m->len());
|
||||
m->pos += n;
|
||||
count -= n;
|
||||
len -= n;
|
||||
mark_offset -= n;
|
||||
|
||||
if (m->len() > 0) {
|
||||
assert(mark != m || m->pos <= mark_pos);
|
||||
break;
|
||||
}
|
||||
if (mark == m) {
|
||||
assert(m->pos <= mark_pos);
|
||||
|
||||
mark = nullptr;
|
||||
mark_pos = nullptr;
|
||||
mark_offset = 0;
|
||||
}
|
||||
|
||||
pool->recycle(m);
|
||||
m = next;
|
||||
}
|
||||
head = m;
|
||||
if (head == nullptr) {
|
||||
tail = nullptr;
|
||||
}
|
||||
return ndata - count;
|
||||
}
|
||||
int riovec(struct iovec *iov, int iovcnt) const {
|
||||
if (!head) {
|
||||
return 0;
|
||||
@@ -317,7 +368,41 @@ template <typename Memchunk> struct Memchunks {
|
||||
}
|
||||
return i;
|
||||
}
|
||||
int riovec_mark(struct iovec *iov, int iovcnt) {
|
||||
if (!head || iovcnt == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
Memchunk *m;
|
||||
if (mark) {
|
||||
if (mark_pos != mark->last) {
|
||||
iov[0].iov_base = mark_pos;
|
||||
iov[0].iov_len = mark->len() - (mark_pos - mark->pos);
|
||||
|
||||
mark_pos = mark->last;
|
||||
mark_offset += iov[0].iov_len;
|
||||
i = 1;
|
||||
}
|
||||
m = mark->next;
|
||||
} else {
|
||||
i = 0;
|
||||
m = head;
|
||||
}
|
||||
|
||||
for (; i < iovcnt && m; ++i, m = m->next) {
|
||||
iov[i].iov_base = m->pos;
|
||||
iov[i].iov_len = m->len();
|
||||
|
||||
mark = m;
|
||||
mark_pos = m->last;
|
||||
mark_offset += m->len();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
size_t rleft() const { return len; }
|
||||
size_t rleft_mark() const { return len - mark_offset; }
|
||||
void reset() {
|
||||
for (auto m = head; m;) {
|
||||
auto next = m->next;
|
||||
@@ -325,12 +410,17 @@ template <typename Memchunk> struct Memchunks {
|
||||
m = next;
|
||||
}
|
||||
len = 0;
|
||||
head = tail = nullptr;
|
||||
head = tail = mark = nullptr;
|
||||
mark_pos = nullptr;
|
||||
mark_offset = 0;
|
||||
}
|
||||
|
||||
Pool<Memchunk> *pool;
|
||||
Memchunk *head, *tail;
|
||||
size_t len;
|
||||
Memchunk *mark;
|
||||
uint8_t *mark_pos;
|
||||
size_t mark_offset;
|
||||
};
|
||||
|
||||
// Wrapper around Memchunks to offer "peeking" functionality.
|
||||
|
||||
@@ -2268,7 +2268,7 @@ int communicate(
|
||||
auto loop = EV_DEFAULT;
|
||||
SSL_CTX *ssl_ctx = nullptr;
|
||||
if (scheme == "https") {
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
ssl_ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!ssl_ctx) {
|
||||
std::cerr << "[ERROR] Failed to create SSL_CTX: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
|
||||
|
||||
60
src/quic.cc
Normal file
60
src/quic.cc
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 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 "quic.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
#include "template.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace quic {
|
||||
|
||||
Error err_transport(int liberr) {
|
||||
if (liberr == NGTCP2_ERR_RECV_VERSION_NEGOTIATION) {
|
||||
return {ErrorType::TransportVersionNegotiation, 0};
|
||||
}
|
||||
return {ErrorType::Transport,
|
||||
ngtcp2_err_infer_quic_transport_error_code(liberr)};
|
||||
}
|
||||
|
||||
Error err_transport_idle_timeout() {
|
||||
return {ErrorType::TransportIdleTimeout, 0};
|
||||
}
|
||||
|
||||
Error err_transport_tls(int alert) {
|
||||
return {ErrorType::Transport, ngtcp2_err_infer_quic_transport_error_code(
|
||||
NGTCP2_CRYPTO_ERROR | alert)};
|
||||
}
|
||||
|
||||
Error err_application(int liberr) {
|
||||
return {ErrorType::Application,
|
||||
nghttp3_err_infer_quic_app_error_code(liberr)};
|
||||
}
|
||||
|
||||
} // namespace quic
|
||||
56
src/quic.h
Normal file
56
src/quic.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2019 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 QUIC_H
|
||||
#define QUIC_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
namespace quic {
|
||||
|
||||
enum class ErrorType {
|
||||
Transport,
|
||||
TransportVersionNegotiation,
|
||||
TransportIdleTimeout,
|
||||
Application,
|
||||
};
|
||||
|
||||
struct Error {
|
||||
Error(ErrorType type, uint64_t code) : type(type), code(code) {}
|
||||
Error() : type(ErrorType::Transport), code(0) {}
|
||||
|
||||
ErrorType type;
|
||||
uint64_t code;
|
||||
};
|
||||
|
||||
Error err_transport(int liberr);
|
||||
Error err_transport_idle_timeout();
|
||||
Error err_transport_tls(int alert);
|
||||
Error err_application(int liberr);
|
||||
|
||||
} // namespace quic
|
||||
|
||||
#endif // QUIC_H
|
||||
@@ -135,6 +135,8 @@ int main(int argc, char *argv[]) {
|
||||
shrpx::test_shrpx_http_create_via_header_value) ||
|
||||
!CU_add_test(pSuite, "http_create_affinity_cookie",
|
||||
shrpx::test_shrpx_http_create_affinity_cookie) ||
|
||||
!CU_add_test(pSuite, "http_create_atlsvc_header_field_value",
|
||||
shrpx::test_shrpx_http_create_altsvc_header_value) ||
|
||||
!CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) ||
|
||||
!CU_add_test(pSuite, "router_match_wildcard",
|
||||
shrpx::test_shrpx_router_match_wildcard) ||
|
||||
@@ -197,6 +199,7 @@ int main(int argc, char *argv[]) {
|
||||
shrpx::test_util_extract_host) ||
|
||||
!CU_add_test(pSuite, "util_split_hostport",
|
||||
shrpx::test_util_split_hostport) ||
|
||||
!CU_add_test(pSuite, "util_split_str", shrpx::test_util_split_str) ||
|
||||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
|
||||
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
|
||||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||
|
||||
|
||||
886
src/shrpx.cc
886
src/shrpx.cc
File diff suppressed because it is too large
Load Diff
@@ -48,4 +48,11 @@
|
||||
inline int initgroups(const char *user, gid_t group) { return 0; }
|
||||
#endif // defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
|
||||
|
||||
#ifndef HAVE_BPF_STATS_TYPE
|
||||
/* Newer kernel should have this defined in linux/bpf.h */
|
||||
enum bpf_stats_type {
|
||||
BPF_STATS_RUN_TIME = 0,
|
||||
};
|
||||
#endif // !HAVE_BPF_STATS_TYPE
|
||||
|
||||
#endif // SHRPX_H
|
||||
|
||||
@@ -463,7 +463,7 @@ int APIDownstreamConnection::on_read() { return 0; }
|
||||
|
||||
int APIDownstreamConnection::on_write() { return 0; }
|
||||
|
||||
void APIDownstreamConnection::on_upstream_change(Upstream *uptream) {}
|
||||
void APIDownstreamConnection::on_upstream_change(Upstream *upstream) {}
|
||||
|
||||
bool APIDownstreamConnection::poolable() const { return false; }
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
virtual int on_read();
|
||||
virtual int on_write();
|
||||
|
||||
virtual void on_upstream_change(Upstream *uptream);
|
||||
virtual void on_upstream_change(Upstream *upstream);
|
||||
|
||||
// true if this object is poolable.
|
||||
virtual bool poolable() const;
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
#include "shrpx_connect_blocker.h"
|
||||
#include "shrpx_api_downstream_connection.h"
|
||||
#include "shrpx_health_monitor_downstream_connection.h"
|
||||
#include "shrpx_null_downstream_connection.h"
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include "shrpx_http3_upstream.h"
|
||||
#endif // ENABLE_HTTP3
|
||||
#include "shrpx_log.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
@@ -285,6 +289,20 @@ int ClientHandler::write_tls() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
int ClientHandler::read_quic(const UpstreamAddr *faddr,
|
||||
const Address &remote_addr,
|
||||
const Address &local_addr,
|
||||
const ngtcp2_pkt_info &pi, const uint8_t *data,
|
||||
size_t datalen) {
|
||||
auto upstream = static_cast<Http3Upstream *>(upstream_.get());
|
||||
|
||||
return upstream->on_read(faddr, remote_addr, local_addr, pi, data, datalen);
|
||||
}
|
||||
|
||||
int ClientHandler::write_quic() { return upstream_->on_write(); }
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
int ClientHandler::upstream_noop() { return 0; }
|
||||
|
||||
int ClientHandler::upstream_read() {
|
||||
@@ -401,7 +419,8 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
get_config()->conn.upstream.ratelimit.write,
|
||||
get_config()->conn.upstream.ratelimit.read, writecb, readcb,
|
||||
timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
|
||||
get_config()->tls.dyn_rec.idle_timeout, Proto::NONE),
|
||||
get_config()->tls.dyn_rec.idle_timeout,
|
||||
faddr->quic ? Proto::HTTP3 : Proto::NONE),
|
||||
ipaddr_(make_string_ref(balloc_, ipaddr)),
|
||||
port_(make_string_ref(balloc_, port)),
|
||||
faddr_(faddr),
|
||||
@@ -417,19 +436,23 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||
|
||||
reneg_shutdown_timer_.data = this;
|
||||
|
||||
conn_.rlimit.startw();
|
||||
if (!faddr->quic) {
|
||||
conn_.rlimit.startw();
|
||||
}
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
|
||||
auto config = get_config();
|
||||
|
||||
if (faddr_->accept_proxy_protocol ||
|
||||
config->conn.upstream.accept_proxy_protocol) {
|
||||
read_ = &ClientHandler::read_clear;
|
||||
write_ = &ClientHandler::noop;
|
||||
on_read_ = &ClientHandler::proxy_protocol_read;
|
||||
on_write_ = &ClientHandler::upstream_noop;
|
||||
} else {
|
||||
setup_upstream_io_callback();
|
||||
if (!faddr->quic) {
|
||||
if (faddr_->accept_proxy_protocol ||
|
||||
config->conn.upstream.accept_proxy_protocol) {
|
||||
read_ = &ClientHandler::read_clear;
|
||||
write_ = &ClientHandler::noop;
|
||||
on_read_ = &ClientHandler::proxy_protocol_read;
|
||||
on_write_ = &ClientHandler::upstream_noop;
|
||||
} else {
|
||||
setup_upstream_io_callback();
|
||||
}
|
||||
}
|
||||
|
||||
auto &fwdconf = config->http.forwarded;
|
||||
@@ -491,6 +514,18 @@ void ClientHandler::setup_upstream_io_callback() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
void ClientHandler::setup_http3_upstream(
|
||||
std::unique_ptr<Http3Upstream> &&upstream) {
|
||||
upstream_ = std::move(upstream);
|
||||
write_ = &ClientHandler::write_quic;
|
||||
|
||||
auto config = get_config();
|
||||
|
||||
reset_upstream_read_timeout(config->conn.upstream.timeout.http3_read);
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
ClientHandler::~ClientHandler() {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "Deleting";
|
||||
@@ -511,7 +546,8 @@ ClientHandler::~ClientHandler() {
|
||||
|
||||
// TODO If backend is http/2, and it is in CONNECTED state, signal
|
||||
// it and make it loopbreak when output is zero.
|
||||
if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0) {
|
||||
if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0 &&
|
||||
worker_stat->num_close_waits == 0) {
|
||||
ev_break(conn_.loop);
|
||||
}
|
||||
|
||||
@@ -848,7 +884,6 @@ DownstreamAddr *ClientHandler::get_downstream_addr(int &err,
|
||||
err = -1;
|
||||
return nullptr;
|
||||
}
|
||||
aff_idx = i;
|
||||
}
|
||||
|
||||
return addr;
|
||||
@@ -973,6 +1008,13 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||
}
|
||||
|
||||
auto &group = groups[group_idx];
|
||||
|
||||
if (group->shared_addr->dnf) {
|
||||
auto dconn = std::make_unique<NullDownstreamConnection>(group);
|
||||
dconn->set_client_handler(this);
|
||||
return dconn;
|
||||
}
|
||||
|
||||
auto addr = get_downstream_addr(err, group.get(), downstream);
|
||||
if (addr == nullptr) {
|
||||
return nullptr;
|
||||
@@ -1556,4 +1598,13 @@ StringRef ClientHandler::get_alpn() const { return alpn_; }
|
||||
|
||||
BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
|
||||
|
||||
void ClientHandler::set_alpn_from_conn() {
|
||||
const unsigned char *alpn;
|
||||
unsigned int alpnlen;
|
||||
|
||||
SSL_get0_alpn_selected(conn_.tls.ssl, &alpn, &alpnlen);
|
||||
|
||||
alpn_ = make_string_ref(balloc_, StringRef{alpn, alpnlen});
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -53,6 +53,9 @@ class Downstream;
|
||||
struct WorkerStat;
|
||||
struct DownstreamAddrGroup;
|
||||
struct DownstreamAddr;
|
||||
#ifdef ENABLE_HTTP3
|
||||
class Http3Upstream;
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
class ClientHandler {
|
||||
public:
|
||||
@@ -143,6 +146,14 @@ public:
|
||||
|
||||
void setup_upstream_io_callback();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
void setup_http3_upstream(std::unique_ptr<Http3Upstream> &&upstream);
|
||||
int read_quic(const UpstreamAddr *faddr, const Address &remote_addr,
|
||||
const Address &local_addr, const ngtcp2_pkt_info &pi,
|
||||
const uint8_t *data, size_t datalen);
|
||||
int write_quic();
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
// Returns string suitable for use in "by" parameter of Forwarded
|
||||
// header field.
|
||||
StringRef get_forwarded_by() const;
|
||||
@@ -177,6 +188,8 @@ public:
|
||||
|
||||
BlockAllocator &get_block_allocator();
|
||||
|
||||
void set_alpn_from_conn();
|
||||
|
||||
private:
|
||||
// Allocator to allocate memory for connection-wide objects. Make
|
||||
// sure that the allocations must be bounded, and not proportional
|
||||
|
||||
@@ -230,10 +230,81 @@ read_tls_ticket_key_file(const std::vector<StringRef> &files,
|
||||
return ticket_keys;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
std::shared_ptr<QUICKeyingMaterials>
|
||||
read_quic_secret_file(const StringRef &path) {
|
||||
constexpr size_t expectedlen =
|
||||
SHRPX_QUIC_SECRET_RESERVEDLEN + SHRPX_QUIC_SECRETLEN + SHRPX_QUIC_SALTLEN;
|
||||
|
||||
auto qkms = std::make_shared<QUICKeyingMaterials>();
|
||||
auto &kms = qkms->keying_materials;
|
||||
|
||||
std::ifstream f(path.c_str());
|
||||
if (!f) {
|
||||
LOG(ERROR) << "frontend-quic-secret-file: could not open file " << path;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::array<char, 4096> buf;
|
||||
|
||||
while (f.getline(buf.data(), buf.size())) {
|
||||
auto len = strlen(buf.data());
|
||||
if (len == 0 || buf[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto s = StringRef{std::begin(buf), std::begin(buf) + len};
|
||||
if (s.size() != expectedlen * 2 || !util::is_hex_string(s)) {
|
||||
LOG(ERROR) << "frontend-quic-secret-file: each line must be a "
|
||||
<< expectedlen * 2 << " bytes hex encoded string";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
kms.emplace_back();
|
||||
auto &qkm = kms.back();
|
||||
|
||||
auto p = std::begin(s);
|
||||
|
||||
util::decode_hex(std::begin(qkm.reserved),
|
||||
StringRef{p, p + qkm.reserved.size()});
|
||||
p += qkm.reserved.size() * 2;
|
||||
util::decode_hex(std::begin(qkm.secret),
|
||||
StringRef{p, p + qkm.secret.size()});
|
||||
p += qkm.secret.size() * 2;
|
||||
util::decode_hex(std::begin(qkm.salt), StringRef{p, p + qkm.salt.size()});
|
||||
p += qkm.salt.size() * 2;
|
||||
|
||||
assert(static_cast<size_t>(p - std::begin(s)) == expectedlen * 2);
|
||||
|
||||
qkm.id = qkm.reserved[0] & 0xc0;
|
||||
|
||||
if (kms.size() == 4) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (f.bad() || (!f.eof() && f.fail())) {
|
||||
LOG(ERROR)
|
||||
<< "frontend-quic-secret-file: error occurred while reading file "
|
||||
<< path;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (kms.empty()) {
|
||||
LOG(WARN)
|
||||
<< "frontend-quic-secret-file: no keying materials are present in file "
|
||||
<< path;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return qkms;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
FILE *open_file_for_write(const char *filename) {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
|
||||
#if defined O_CLOEXEC
|
||||
#ifdef O_CLOEXEC
|
||||
auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR);
|
||||
#else
|
||||
@@ -372,6 +443,57 @@ int parse_int(T *dest, const StringRef &opt, const char *optarg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int parse_altsvc(AltSvc &altsvc, const StringRef &opt,
|
||||
const StringRef &optarg) {
|
||||
// PROTOID, PORT, HOST, ORIGIN, PARAMS.
|
||||
auto tokens = util::split_str(optarg, ',', 5);
|
||||
|
||||
if (tokens.size() < 2) {
|
||||
// Requires at least protocol_id and port
|
||||
LOG(ERROR) << opt << ": too few parameters: " << optarg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int port;
|
||||
|
||||
if (parse_uint(&port, opt, tokens[1]) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (port < 1 ||
|
||||
port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
|
||||
LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
|
||||
return -1;
|
||||
}
|
||||
|
||||
altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
|
||||
|
||||
altsvc.port = port;
|
||||
altsvc.service = make_string_ref(config->balloc, tokens[1]);
|
||||
|
||||
if (tokens.size() > 2) {
|
||||
if (!tokens[2].empty()) {
|
||||
altsvc.host = make_string_ref(config->balloc, tokens[2]);
|
||||
}
|
||||
|
||||
if (tokens.size() > 3) {
|
||||
if (!tokens[3].empty()) {
|
||||
altsvc.origin = make_string_ref(config->balloc, tokens[3]);
|
||||
}
|
||||
|
||||
if (tokens.size() > 4) {
|
||||
if (!tokens[4].empty()) {
|
||||
altsvc.params = make_string_ref(config->balloc, tokens[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// generated by gennghttpxfun.py
|
||||
LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
|
||||
@@ -785,6 +907,7 @@ struct UpstreamParams {
|
||||
bool tls;
|
||||
bool sni_fwd;
|
||||
bool proxyproto;
|
||||
bool quic;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -819,6 +942,13 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
|
||||
out.alt_mode = UpstreamAltMode::HEALTHMON;
|
||||
} else if (util::strieq_l("proxyproto", param)) {
|
||||
out.proxyproto = true;
|
||||
} else if (util::strieq_l("quic", param)) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
out.quic = true;
|
||||
#else // !ENABLE_HTTP3
|
||||
LOG(ERROR) << "quic: QUIC is disabled at compile time";
|
||||
return -1;
|
||||
#endif // !ENABLE_HTTP3
|
||||
} else if (!param.empty()) {
|
||||
LOG(ERROR) << "frontend: " << param << ": unknown keyword";
|
||||
return -1;
|
||||
@@ -851,6 +981,7 @@ struct DownstreamParams {
|
||||
bool dns;
|
||||
bool redirect_if_not_tls;
|
||||
bool upgrade_scheme;
|
||||
bool dnf;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -1025,6 +1156,8 @@ int parse_downstream_params(DownstreamParams &out,
|
||||
return -1;
|
||||
}
|
||||
out.group_weight = n;
|
||||
} else if (util::strieq_l("dnf", param)) {
|
||||
out.dnf = true;
|
||||
} else if (!param.empty()) {
|
||||
LOG(ERROR) << "backend: " << param << ": unknown keyword";
|
||||
return -1;
|
||||
@@ -1089,6 +1222,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
|
||||
addr.dns = params.dns;
|
||||
addr.upgrade_scheme = params.upgrade_scheme;
|
||||
addr.dnf = params.dnf;
|
||||
|
||||
auto &routerconf = downstreamconf.router;
|
||||
auto &router = routerconf.router;
|
||||
@@ -1189,6 +1323,14 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// All backends in the same group must have the same dnf
|
||||
// setting. If some backends do not specify dnf, and there is
|
||||
// at least one backend with dnf, it is used for all backends in
|
||||
// the group. In general, multiple backends are not necessary
|
||||
// for dnf because there is no need for load balancing.
|
||||
if (params.dnf) {
|
||||
g.dnf = true;
|
||||
}
|
||||
|
||||
g.addrs.push_back(addr);
|
||||
continue;
|
||||
@@ -1213,6 +1355,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
|
||||
g.timeout.read = params.read_timeout;
|
||||
g.timeout.write = params.write_timeout;
|
||||
g.dnf = params.dnf;
|
||||
|
||||
if (pattern[0] == '*') {
|
||||
// wildcard pattern
|
||||
@@ -1795,6 +1938,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_SERVER_NAME;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (util::strieq_l("no-quic-bp", name, 10)) {
|
||||
return SHRPX_OPTID_NO_QUIC_BPF;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("tls-sct-di", name, 10)) {
|
||||
return SHRPX_OPTID_TLS_SCT_DIR;
|
||||
@@ -1838,6 +1986,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_BACKEND_IPV6;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (util::strieq_l("http2-altsv", name, 11)) {
|
||||
return SHRPX_OPTID_HTTP2_ALTSVC;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("host-rewrit", name, 11)) {
|
||||
return SHRPX_OPTID_HOST_REWRITE;
|
||||
@@ -1901,6 +2054,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
case 14:
|
||||
switch (name[13]) {
|
||||
case 'd':
|
||||
if (util::strieq_l("quic-server-i", name, 13)) {
|
||||
return SHRPX_OPTID_QUIC_SERVER_ID;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("accesslog-fil", name, 13)) {
|
||||
return SHRPX_OPTID_ACCESSLOG_FILE;
|
||||
@@ -1911,6 +2069,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_NO_SERVER_PUSH;
|
||||
}
|
||||
break;
|
||||
case 'k':
|
||||
if (util::strieq_l("rlimit-memloc", name, 13)) {
|
||||
return SHRPX_OPTID_RLIMIT_MEMLOCK;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (util::strieq_l("no-verify-ocs", name, 13)) {
|
||||
return SHRPX_OPTID_NO_VERIFY_OCSP;
|
||||
@@ -2090,6 +2253,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("max-worker-processe", name, 19)) {
|
||||
return SHRPX_OPTID_MAX_WORKER_PROCESSES;
|
||||
}
|
||||
if (util::strieq_l("tls13-client-cipher", name, 19)) {
|
||||
return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
|
||||
}
|
||||
@@ -2119,6 +2285,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("quic-bpf-program-fil", name, 20)) {
|
||||
return SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (util::strieq_l("accept-proxy-protoco", name, 20)) {
|
||||
return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
|
||||
@@ -2168,6 +2339,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("backend-request-buffe", name, 21)) {
|
||||
return SHRPX_OPTID_BACKEND_REQUEST_BUFFER;
|
||||
}
|
||||
if (util::strieq_l("frontend-quic-qlog-di", name, 21)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("frontend-write-timeou", name, 21)) {
|
||||
@@ -2191,6 +2365,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE;
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
if (util::strieq_l("frontend-quic-debug-lo", name, 22)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("backend-response-buffe", name, 22)) {
|
||||
return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER;
|
||||
@@ -2205,6 +2384,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
break;
|
||||
case 24:
|
||||
switch (name[23]) {
|
||||
case 'a':
|
||||
if (util::strieq_l("frontend-quic-early-dat", name, 23)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (util::strieq_l("strip-incoming-forwarde", name, 23)) {
|
||||
return SHRPX_OPTID_STRIP_INCOMING_FORWARDED;
|
||||
@@ -2239,6 +2423,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("backend-http2-window-siz", name, 24)) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("frontend-quic-secret-fil", name, 24)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE;
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) {
|
||||
@@ -2253,6 +2440,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("frontend-quic-initial-rt", name, 24)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 26:
|
||||
@@ -2266,6 +2458,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("frontend-http3-window-siz", name, 25)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
|
||||
@@ -2279,6 +2474,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
|
||||
return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("frontend-quic-idle-timeou", name, 25)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
|
||||
return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
|
||||
}
|
||||
@@ -2295,6 +2493,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (util::strieq_l("frontend-quic-require-toke", name, 26)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("request-header-field-buffe", name, 26)) {
|
||||
return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
|
||||
@@ -2309,6 +2512,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("frontend-http3-read-timeou", name, 26)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) {
|
||||
return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
|
||||
}
|
||||
@@ -2354,6 +2560,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("frontend-http3-max-window-siz", name, 29)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) {
|
||||
return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
|
||||
@@ -2452,11 +2663,19 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
|
||||
}
|
||||
if (util::strieq_l("frontend-quic-congestion-controlle", name, 34)) {
|
||||
return SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 36:
|
||||
switch (name[35]) {
|
||||
case 'd':
|
||||
if (util::strieq_l("worker-process-grace-shutdown-perio", name, 35)) {
|
||||
return SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
|
||||
@@ -2483,6 +2702,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-connection-window-siz", name, 36)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("frontend-http3-connection-window-siz", name, 36)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
|
||||
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
|
||||
}
|
||||
@@ -2494,6 +2716,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
if (util::strieq_l("frontend-http2-max-concurrent-stream", name, 36)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS;
|
||||
}
|
||||
if (util::strieq_l("frontend-http3-max-concurrent-stream", name, 36)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -2542,6 +2767,10 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||
40)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE;
|
||||
}
|
||||
if (util::strieq_l("frontend-http3-max-connection-window-siz", name,
|
||||
40)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name,
|
||||
40)) {
|
||||
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
|
||||
@@ -2624,7 +2853,6 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_FRONTEND: {
|
||||
auto &listenerconf = config->conn.listener;
|
||||
auto &apiconf = config->api;
|
||||
|
||||
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
|
||||
@@ -2642,23 +2870,49 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (params.quic) {
|
||||
if (params.alt_mode != UpstreamAltMode::NONE) {
|
||||
LOG(ERROR) << "frontend: api or healthmon cannot be used with quic";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!params.tls) {
|
||||
LOG(ERROR) << "frontend: quic requires TLS";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
UpstreamAddr addr{};
|
||||
addr.fd = -1;
|
||||
addr.tls = params.tls;
|
||||
addr.sni_fwd = params.sni_fwd;
|
||||
addr.alt_mode = params.alt_mode;
|
||||
addr.accept_proxy_protocol = params.proxyproto;
|
||||
addr.quic = params.quic;
|
||||
|
||||
if (addr.alt_mode == UpstreamAltMode::API) {
|
||||
apiconf.enabled = true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
auto &addrs = params.quic ? config->conn.quic_listener.addrs
|
||||
: config->conn.listener.addrs;
|
||||
#else // !ENABLE_HTTP3
|
||||
auto &addrs = config->conn.listener.addrs;
|
||||
#endif // !ENABLE_HTTP3
|
||||
|
||||
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
|
||||
if (addr.quic) {
|
||||
LOG(ERROR) << "frontend: quic cannot be used on UNIX domain socket";
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
|
||||
addr.host = make_string_ref(config->balloc, StringRef{path, addr_end});
|
||||
addr.host_unix = true;
|
||||
addr.index = addrs.size();
|
||||
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
addrs.push_back(std::move(addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2673,21 +2927,25 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
|
||||
if (util::numeric_host(host, AF_INET)) {
|
||||
addr.family = AF_INET;
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
addr.index = addrs.size();
|
||||
addrs.push_back(std::move(addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (util::numeric_host(host, AF_INET6)) {
|
||||
addr.family = AF_INET6;
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
addr.index = addrs.size();
|
||||
addrs.push_back(std::move(addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr.family = AF_INET;
|
||||
listenerconf.addrs.push_back(addr);
|
||||
addr.index = addrs.size();
|
||||
addrs.push_back(addr);
|
||||
|
||||
addr.family = AF_INET6;
|
||||
listenerconf.addrs.push_back(std::move(addr));
|
||||
addr.index = addrs.size();
|
||||
addrs.push_back(std::move(addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3136,45 +3394,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
case SHRPX_OPTID_PADDING:
|
||||
return parse_uint(&config->padding, opt, optarg);
|
||||
case SHRPX_OPTID_ALTSVC: {
|
||||
auto tokens = util::split_str(optarg, ',');
|
||||
|
||||
if (tokens.size() < 2) {
|
||||
// Requires at least protocol_id and port
|
||||
LOG(ERROR) << opt << ": too few parameters: " << optarg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tokens.size() > 4) {
|
||||
// We only need protocol_id, port, host and origin
|
||||
LOG(ERROR) << opt << ": too many parameters: " << optarg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int port;
|
||||
|
||||
if (parse_uint(&port, opt, tokens[1]) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (port < 1 ||
|
||||
port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
|
||||
LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
|
||||
return -1;
|
||||
}
|
||||
|
||||
AltSvc altsvc{};
|
||||
|
||||
altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
|
||||
|
||||
altsvc.port = port;
|
||||
altsvc.service = make_string_ref(config->balloc, tokens[1]);
|
||||
|
||||
if (tokens.size() > 2) {
|
||||
altsvc.host = make_string_ref(config->balloc, tokens[2]);
|
||||
|
||||
if (tokens.size() > 3) {
|
||||
altsvc.origin = make_string_ref(config->balloc, tokens[3]);
|
||||
}
|
||||
if (parse_altsvc(altsvc, opt, optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->http.altsvcs.push_back(std::move(altsvc));
|
||||
@@ -3731,7 +3954,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
"65535], inclusive";
|
||||
return -1;
|
||||
}
|
||||
config->http.redirect_https_port = optarg;
|
||||
config->http.redirect_https_port = make_string_ref(config->balloc, optarg);
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_FRONTEND_MAX_REQUESTS:
|
||||
@@ -3779,6 +4002,168 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||
config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE:
|
||||
#ifdef ENABLE_HTTP3
|
||||
config->quic.bpf.prog_file = make_string_ref(config->balloc, optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_NO_QUIC_BPF:
|
||||
#ifdef ENABLE_HTTP3
|
||||
config->quic.bpf.disabled = util::strieq_l("yes", optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_HTTP2_ALTSVC: {
|
||||
AltSvc altsvc{};
|
||||
|
||||
if (parse_altsvc(altsvc, opt, optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->http.http2_altsvcs.push_back(std::move(altsvc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT:
|
||||
#ifdef ENABLE_HTTP3
|
||||
return parse_duration(&config->conn.upstream.timeout.http3_read, opt,
|
||||
optarg);
|
||||
#else // !ENABLE_HTTP3
|
||||
return 0;
|
||||
#endif // !ENABLE_HTTP3
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT:
|
||||
#ifdef ENABLE_HTTP3
|
||||
return parse_duration(&config->quic.upstream.timeout.idle, opt, optarg);
|
||||
#else // !ENABLE_HTTP3
|
||||
return 0;
|
||||
#endif // !ENABLE_HTTP3
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG:
|
||||
#ifdef ENABLE_HTTP3
|
||||
config->quic.upstream.debug.log = util::strieq_l("yes", optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE:
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (parse_uint_with_unit(&config->http3.upstream.window_size, opt,
|
||||
optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE:
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (parse_uint_with_unit(&config->http3.upstream.connection_window_size,
|
||||
opt, optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE:
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (parse_uint_with_unit(&config->http3.upstream.max_window_size, opt,
|
||||
optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE:
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (parse_uint_with_unit(&config->http3.upstream.max_connection_window_size,
|
||||
opt, optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS:
|
||||
#ifdef ENABLE_HTTP3
|
||||
return parse_uint(&config->http3.upstream.max_concurrent_streams, opt,
|
||||
optarg);
|
||||
#else // !ENABLE_HTTP3
|
||||
return 0;
|
||||
#endif // !ENABLE_HTTP3
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA:
|
||||
#ifdef ENABLE_HTTP3
|
||||
config->quic.upstream.early_data = util::strieq_l("yes", optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR:
|
||||
#ifdef ENABLE_HTTP3
|
||||
config->quic.upstream.qlog.dir = make_string_ref(config->balloc, optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN:
|
||||
#ifdef ENABLE_HTTP3
|
||||
config->quic.upstream.require_token = util::strieq_l("yes", optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER:
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (util::strieq_l("cubic", optarg)) {
|
||||
config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_CUBIC;
|
||||
} else if (util::strieq_l("bbr", optarg)) {
|
||||
config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR;
|
||||
} else {
|
||||
LOG(ERROR) << opt << ": must be either cubic or bbr";
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_QUIC_SERVER_ID:
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (optarg.size() != config->quic.server_id.size() * 2 ||
|
||||
!util::is_hex_string(optarg)) {
|
||||
LOG(ERROR) << opt << ": must be a hex-string";
|
||||
return -1;
|
||||
}
|
||||
util::decode_hex(std::begin(config->quic.server_id), optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE:
|
||||
#ifdef ENABLE_HTTP3
|
||||
config->quic.upstream.secret_file = make_string_ref(config->balloc, optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_RLIMIT_MEMLOCK: {
|
||||
int n;
|
||||
|
||||
if (parse_uint(&n, opt, optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->rlimit_memlock = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_MAX_WORKER_PROCESSES:
|
||||
return parse_uint(&config->max_worker_processes, opt, optarg);
|
||||
case SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD:
|
||||
return parse_duration(&config->worker_process_grace_shutdown_period, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT: {
|
||||
#ifdef ENABLE_HTTP3
|
||||
return parse_duration(&config->quic.upstream.initial_rtt, opt, optarg);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
@@ -3967,6 +4352,8 @@ StringRef strproto(Proto proto) {
|
||||
return StringRef::from_lit("http/1.1");
|
||||
case Proto::HTTP2:
|
||||
return StringRef::from_lit("h2");
|
||||
case Proto::HTTP3:
|
||||
return StringRef::from_lit("h3");
|
||||
case Proto::MEMCACHED:
|
||||
return StringRef::from_lit("memcached");
|
||||
}
|
||||
@@ -4076,7 +4463,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
if (!g.mruby_file.empty()) {
|
||||
if (mruby::create_mruby_context(g.mruby_file) == nullptr) {
|
||||
LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL)
|
||||
<< "backend: Could not compile mruby flie for pattern "
|
||||
<< "backend: Could not compile mruby file for pattern "
|
||||
<< g.pattern;
|
||||
if (!config->ignore_per_pattern_mruby_error) {
|
||||
return -1;
|
||||
@@ -4111,6 +4498,8 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
|
||||
auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0;
|
||||
|
||||
std::array<char, util::max_hostport> hostport_buf;
|
||||
|
||||
for (auto &g : addr_groups) {
|
||||
std::unordered_map<StringRef, uint32_t> wgchk;
|
||||
for (auto &addr : g.addrs) {
|
||||
@@ -4156,7 +4545,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
|
||||
|
||||
auto hostport =
|
||||
util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
|
||||
util::make_hostport(std::begin(hostport_buf), addr.host, addr.port);
|
||||
|
||||
if (!addr.dns) {
|
||||
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
|
||||
|
||||
@@ -51,6 +51,9 @@
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "shrpx_router.h"
|
||||
#if ENABLE_HTTP3
|
||||
# include "shrpx_quic.h"
|
||||
#endif // ENABLE_HTTP3
|
||||
#include "template.h"
|
||||
#include "http2.h"
|
||||
#include "network.h"
|
||||
@@ -360,6 +363,44 @@ constexpr auto SHRPX_OPT_TLS13_CLIENT_CIPHERS =
|
||||
StringRef::from_lit("tls13-client-ciphers");
|
||||
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA =
|
||||
StringRef::from_lit("no-strip-incoming-early-data");
|
||||
constexpr auto SHRPX_OPT_QUIC_BPF_PROGRAM_FILE =
|
||||
StringRef::from_lit("quic-bpf-program-file");
|
||||
constexpr auto SHRPX_OPT_NO_QUIC_BPF = StringRef::from_lit("no-quic-bpf");
|
||||
constexpr auto SHRPX_OPT_HTTP2_ALTSVC = StringRef::from_lit("http2-altsvc");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT =
|
||||
StringRef::from_lit("frontend-http3-read-timeout");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT =
|
||||
StringRef::from_lit("frontend-quic-idle-timeout");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG =
|
||||
StringRef::from_lit("frontend-quic-debug-log");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE =
|
||||
StringRef::from_lit("frontend-http3-window-size");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE =
|
||||
StringRef::from_lit("frontend-http3-connection-window-size");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE =
|
||||
StringRef::from_lit("frontend-http3-max-window-size");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE =
|
||||
StringRef::from_lit("frontend-http3-max-connection-window-size");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS =
|
||||
StringRef::from_lit("frontend-http3-max-concurrent-streams");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA =
|
||||
StringRef::from_lit("frontend-quic-early-data");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR =
|
||||
StringRef::from_lit("frontend-quic-qlog-dir");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN =
|
||||
StringRef::from_lit("frontend-quic-require-token");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER =
|
||||
StringRef::from_lit("frontend-quic-congestion-controller");
|
||||
constexpr auto SHRPX_OPT_QUIC_SERVER_ID = StringRef::from_lit("quic-server-id");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE =
|
||||
StringRef::from_lit("frontend-quic-secret-file");
|
||||
constexpr auto SHRPX_OPT_RLIMIT_MEMLOCK = StringRef::from_lit("rlimit-memlock");
|
||||
constexpr auto SHRPX_OPT_MAX_WORKER_PROCESSES =
|
||||
StringRef::from_lit("max-worker-processes");
|
||||
constexpr auto SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD =
|
||||
StringRef::from_lit("worker-process-grace-shutdown-period");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT =
|
||||
StringRef::from_lit("frontend-quic-initial-rtt");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
@@ -370,6 +411,7 @@ enum class Proto {
|
||||
NONE,
|
||||
HTTP1,
|
||||
HTTP2,
|
||||
HTTP3,
|
||||
MEMCACHED,
|
||||
};
|
||||
|
||||
@@ -419,7 +461,7 @@ enum class ForwardedNode {
|
||||
};
|
||||
|
||||
struct AltSvc {
|
||||
StringRef protocol_id, host, origin, service;
|
||||
StringRef protocol_id, host, origin, service, params;
|
||||
|
||||
uint16_t port;
|
||||
};
|
||||
@@ -434,6 +476,8 @@ enum class UpstreamAltMode {
|
||||
};
|
||||
|
||||
struct UpstreamAddr {
|
||||
// The unique index of this address.
|
||||
size_t index;
|
||||
// The frontend address (e.g., FQDN, hostname, IP address). If
|
||||
// |host_unix| is true, this is UNIX domain socket path. This must
|
||||
// be NULL terminated string.
|
||||
@@ -458,6 +502,7 @@ struct UpstreamAddr {
|
||||
bool sni_fwd;
|
||||
// true if client is supposed to send PROXY protocol v1 header.
|
||||
bool accept_proxy_protocol;
|
||||
bool quic;
|
||||
int fd;
|
||||
};
|
||||
|
||||
@@ -494,6 +539,8 @@ struct DownstreamAddrConfig {
|
||||
// variant (e.g., "https") when forwarding request to a backend
|
||||
// connected by TLS connection.
|
||||
bool upgrade_scheme;
|
||||
// true if a request should not be forwarded to a backend.
|
||||
bool dnf;
|
||||
};
|
||||
|
||||
// Mapping hash to idx which is an index into
|
||||
@@ -510,6 +557,7 @@ struct DownstreamAddrGroupConfig {
|
||||
: pattern(pattern),
|
||||
affinity{SessionAffinity::NONE},
|
||||
redirect_if_not_tls(false),
|
||||
dnf{false},
|
||||
timeout{} {}
|
||||
|
||||
StringRef pattern;
|
||||
@@ -523,6 +571,8 @@ struct DownstreamAddrGroupConfig {
|
||||
// true if this group requires that client connection must be TLS,
|
||||
// and the request must be redirected to https URI.
|
||||
bool redirect_if_not_tls;
|
||||
// true if a request should not be forwarded to a backend.
|
||||
bool dnf;
|
||||
// Timeouts for backend connection.
|
||||
struct {
|
||||
ev_tstamp read;
|
||||
@@ -561,6 +611,22 @@ struct TLSCertificate {
|
||||
std::vector<uint8_t> sct_data;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
struct QUICKeyingMaterial {
|
||||
std::array<uint8_t, SHRPX_QUIC_SECRET_RESERVEDLEN> reserved;
|
||||
std::array<uint8_t, SHRPX_QUIC_SECRETLEN> secret;
|
||||
std::array<uint8_t, SHRPX_QUIC_SALTLEN> salt;
|
||||
std::array<uint8_t, SHRPX_QUIC_CID_ENCRYPTION_KEYLEN> cid_encryption_key;
|
||||
// Identifier of this keying material. Only the first 2 bits are
|
||||
// used.
|
||||
uint8_t id;
|
||||
};
|
||||
|
||||
struct QUICKeyingMaterials {
|
||||
std::vector<QUICKeyingMaterial> keying_materials;
|
||||
};
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
struct HttpProxy {
|
||||
Address addr;
|
||||
// host in http proxy URI
|
||||
@@ -625,7 +691,7 @@ struct TLSConfig {
|
||||
ev_tstamp idle_timeout;
|
||||
} dyn_rec;
|
||||
|
||||
// OCSP realted configurations
|
||||
// OCSP related configurations
|
||||
struct {
|
||||
ev_tstamp update_interval;
|
||||
StringRef fetch_ocsp_response_file;
|
||||
@@ -698,6 +764,42 @@ struct TLSConfig {
|
||||
bool no_postpone_early_data;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
struct QUICConfig {
|
||||
struct {
|
||||
struct {
|
||||
ev_tstamp idle;
|
||||
} timeout;
|
||||
struct {
|
||||
bool log;
|
||||
} debug;
|
||||
struct {
|
||||
StringRef dir;
|
||||
} qlog;
|
||||
ngtcp2_cc_algo congestion_controller;
|
||||
bool early_data;
|
||||
bool require_token;
|
||||
StringRef secret_file;
|
||||
ev_tstamp initial_rtt;
|
||||
} upstream;
|
||||
struct {
|
||||
StringRef prog_file;
|
||||
bool disabled;
|
||||
} bpf;
|
||||
std::array<uint8_t, SHRPX_QUIC_SERVER_IDLEN> server_id;
|
||||
};
|
||||
|
||||
struct Http3Config {
|
||||
struct {
|
||||
size_t max_concurrent_streams;
|
||||
int32_t window_size;
|
||||
int32_t connection_window_size;
|
||||
int32_t max_window_size;
|
||||
int32_t max_connection_window_size;
|
||||
} upstream;
|
||||
};
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
// custom error page
|
||||
struct ErrorPage {
|
||||
// not NULL-terminated
|
||||
@@ -734,6 +836,11 @@ struct HttpConfig {
|
||||
bool strip_incoming;
|
||||
} early_data;
|
||||
std::vector<AltSvc> altsvcs;
|
||||
// altsvcs serialized in a wire format.
|
||||
StringRef altsvc_header_value;
|
||||
std::vector<AltSvc> http2_altsvcs;
|
||||
// http2_altsvcs serialized in a wire format.
|
||||
StringRef http2_altsvc_header_value;
|
||||
std::vector<ErrorPage> error_pages;
|
||||
HeaderRefs add_request_headers;
|
||||
HeaderRefs add_response_headers;
|
||||
@@ -903,9 +1010,16 @@ struct ConnectionConfig {
|
||||
int fastopen;
|
||||
} listener;
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
struct {
|
||||
std::vector<UpstreamAddr> addrs;
|
||||
} quic_listener;
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
struct {
|
||||
struct {
|
||||
ev_tstamp http2_read;
|
||||
ev_tstamp http3_read;
|
||||
ev_tstamp read;
|
||||
ev_tstamp write;
|
||||
ev_tstamp idle_read;
|
||||
@@ -946,6 +1060,9 @@ struct Config {
|
||||
http{},
|
||||
http2{},
|
||||
tls{},
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic{},
|
||||
#endif // ENABLE_HTTP3
|
||||
logging{},
|
||||
conn{},
|
||||
api{},
|
||||
@@ -954,6 +1071,7 @@ struct Config {
|
||||
num_worker{0},
|
||||
padding{0},
|
||||
rlimit_nofile{0},
|
||||
rlimit_memlock{0},
|
||||
uid{0},
|
||||
gid{0},
|
||||
pid{0},
|
||||
@@ -963,7 +1081,10 @@ struct Config {
|
||||
single_process{false},
|
||||
single_thread{false},
|
||||
ignore_per_pattern_mruby_error{false},
|
||||
ev_loop_flags{0} {}
|
||||
ev_loop_flags{0},
|
||||
max_worker_processes{0},
|
||||
worker_process_grace_shutdown_period{0.} {
|
||||
}
|
||||
~Config();
|
||||
|
||||
Config(Config &&) = delete;
|
||||
@@ -979,6 +1100,10 @@ struct Config {
|
||||
HttpConfig http;
|
||||
Http2Config http2;
|
||||
TLSConfig tls;
|
||||
#ifdef ENABLE_HTTP3
|
||||
QUICConfig quic;
|
||||
Http3Config http3;
|
||||
#endif // ENABLE_HTTP3
|
||||
LoggingConfig logging;
|
||||
ConnectionConfig conn;
|
||||
APIConfig api;
|
||||
@@ -997,6 +1122,7 @@ struct Config {
|
||||
size_t num_worker;
|
||||
size_t padding;
|
||||
size_t rlimit_nofile;
|
||||
size_t rlimit_memlock;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
@@ -1011,6 +1137,8 @@ struct Config {
|
||||
bool ignore_per_pattern_mruby_error;
|
||||
// flags passed to ev_default_loop() and ev_loop_new()
|
||||
int ev_loop_flags;
|
||||
size_t max_worker_processes;
|
||||
ev_tstamp worker_process_grace_shutdown_period;
|
||||
};
|
||||
|
||||
const Config *get_config();
|
||||
@@ -1103,13 +1231,28 @@ enum {
|
||||
SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS,
|
||||
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE,
|
||||
SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE,
|
||||
SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS,
|
||||
SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE,
|
||||
SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE,
|
||||
SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE,
|
||||
SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_MAX_REQUESTS,
|
||||
SHRPX_OPTID_FRONTEND_NO_TLS,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN,
|
||||
SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE,
|
||||
SHRPX_OPTID_FRONTEND_READ_TIMEOUT,
|
||||
SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT,
|
||||
SHRPX_OPTID_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_HOST_REWRITE,
|
||||
SHRPX_OPTID_HTTP2_ALTSVC,
|
||||
SHRPX_OPTID_HTTP2_BRIDGE,
|
||||
SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS,
|
||||
SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING,
|
||||
@@ -1122,6 +1265,7 @@ enum {
|
||||
SHRPX_OPTID_MAX_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
|
||||
SHRPX_OPTID_MAX_WORKER_PROCESSES,
|
||||
SHRPX_OPTID_MRUBY_FILE,
|
||||
SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO,
|
||||
SHRPX_OPTID_NO_HOST_REWRITE,
|
||||
@@ -1130,6 +1274,7 @@ enum {
|
||||
SHRPX_OPTID_NO_KQUEUE,
|
||||
SHRPX_OPTID_NO_LOCATION_REWRITE,
|
||||
SHRPX_OPTID_NO_OCSP,
|
||||
SHRPX_OPTID_NO_QUIC_BPF,
|
||||
SHRPX_OPTID_NO_SERVER_PUSH,
|
||||
SHRPX_OPTID_NO_SERVER_REWRITE,
|
||||
SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA,
|
||||
@@ -1144,11 +1289,14 @@ enum {
|
||||
SHRPX_OPTID_PRIVATE_KEY_FILE,
|
||||
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
|
||||
SHRPX_OPTID_PSK_SECRETS,
|
||||
SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE,
|
||||
SHRPX_OPTID_QUIC_SERVER_ID,
|
||||
SHRPX_OPTID_READ_BURST,
|
||||
SHRPX_OPTID_READ_RATE,
|
||||
SHRPX_OPTID_REDIRECT_HTTPS_PORT,
|
||||
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_RLIMIT_MEMLOCK,
|
||||
SHRPX_OPTID_RLIMIT_NOFILE,
|
||||
SHRPX_OPTID_SERVER_NAME,
|
||||
SHRPX_OPTID_SINGLE_PROCESS,
|
||||
@@ -1189,6 +1337,7 @@ enum {
|
||||
SHRPX_OPTID_VERIFY_CLIENT_CACERT,
|
||||
SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED,
|
||||
SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS,
|
||||
SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD,
|
||||
SHRPX_OPTID_WORKER_READ_BURST,
|
||||
SHRPX_OPTID_WORKER_READ_RATE,
|
||||
SHRPX_OPTID_WORKER_WRITE_BURST,
|
||||
@@ -1253,6 +1402,11 @@ std::unique_ptr<TicketKeys>
|
||||
read_tls_ticket_key_file(const std::vector<StringRef> &files,
|
||||
const EVP_CIPHER *cipher, const EVP_MD *hmac);
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
std::shared_ptr<QUICKeyingMaterials>
|
||||
read_quic_secret_file(const StringRef &path);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
// Returns string representation of |proto|.
|
||||
StringRef strproto(Proto proto);
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
||||
read_timeout(read_timeout) {
|
||||
|
||||
ev_io_init(&wev, writecb, fd, EV_WRITE);
|
||||
ev_io_init(&rev, readcb, fd, EV_READ);
|
||||
ev_io_init(&rev, readcb, proto == Proto::HTTP3 ? 0 : fd, EV_READ);
|
||||
|
||||
wev.data = this;
|
||||
rev.data = this;
|
||||
@@ -128,7 +128,7 @@ void Connection::disconnect() {
|
||||
tls.early_data_finish = false;
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
if (proto != Proto::HTTP3 && fd != -1) {
|
||||
shutdown(fd, SHUT_WR);
|
||||
close(fd);
|
||||
fd = -1;
|
||||
@@ -312,10 +312,13 @@ BIO_METHOD *create_bio_method() {
|
||||
void Connection::set_ssl(SSL *ssl) {
|
||||
tls.ssl = ssl;
|
||||
|
||||
auto &tlsconf = get_config()->tls;
|
||||
auto bio = BIO_new(tlsconf.bio_method);
|
||||
BIO_set_data(bio, this);
|
||||
SSL_set_bio(tls.ssl, bio, bio);
|
||||
if (proto != Proto::HTTP3) {
|
||||
auto &tlsconf = get_config()->tls;
|
||||
auto bio = BIO_new(tlsconf.bio_method);
|
||||
BIO_set_data(bio, this);
|
||||
SSL_set_bio(tls.ssl, bio, bio);
|
||||
}
|
||||
|
||||
SSL_set_app_data(tls.ssl, this);
|
||||
}
|
||||
|
||||
@@ -394,11 +397,14 @@ int Connection::tls_handshake() {
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
#if OPENSSL_1_1_1_API || defined(OPENSSL_IS_BORINGSSL)
|
||||
auto &tlsconf = get_config()->tls;
|
||||
#endif // OPENSSL_1_1_1_API || defined(OPENSSL_IS_BORINGSSL)
|
||||
|
||||
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
if (!tls.server_handshake || tls.early_data_finish) {
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
} else {
|
||||
auto &tlsconf = get_config()->tls;
|
||||
for (;;) {
|
||||
size_t nread;
|
||||
|
||||
@@ -446,9 +452,9 @@ int Connection::tls_handshake() {
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // !OPENSSL_1_1_1_API
|
||||
#else // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
#endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
@@ -496,7 +502,12 @@ int Connection::tls_handshake() {
|
||||
// Don't send handshake data if handshake was completed in OpenSSL
|
||||
// routine. We have to check HTTP/2 requirement if HTTP/2 was
|
||||
// negotiated before sending finished message to the peer.
|
||||
if (rv != 1 && tls.wbuf.rleft()) {
|
||||
if ((rv != 1
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
|| SSL_in_init(tls.ssl)
|
||||
#endif // OPENSSL_IS_BORINGSSL
|
||||
) &&
|
||||
tls.wbuf.rleft()) {
|
||||
// First write indicates that resumption stuff has done.
|
||||
if (tls.handshake_state != TLSHandshakeState::WRITE_STARTED) {
|
||||
tls.handshake_state = TLSHandshakeState::WRITE_STARTED;
|
||||
@@ -532,6 +543,40 @@ int Connection::tls_handshake() {
|
||||
return SHRPX_ERR_INPROGRESS;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
if (!tlsconf.no_postpone_early_data && SSL_in_early_data(tls.ssl) &&
|
||||
SSL_in_init(tls.ssl)) {
|
||||
auto nread = SSL_read(tls.ssl, buf.data(), buf.size());
|
||||
if (nread <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, nread);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return SHRPX_ERR_EOF;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_read: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
default:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
} else {
|
||||
tls.earlybuf.append(buf.data(), nread);
|
||||
}
|
||||
|
||||
if (SSL_in_init(tls.ssl)) {
|
||||
return SHRPX_ERR_INPROGRESS;
|
||||
}
|
||||
}
|
||||
#endif // OPENSSL_IS_BORINGSSL
|
||||
|
||||
// Handshake was done
|
||||
|
||||
rv = check_http2_requirement();
|
||||
@@ -568,6 +613,36 @@ int Connection::write_tls_pending_handshake() {
|
||||
tls.wbuf.drain(nwrite);
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
if (!SSL_in_init(tls.ssl)) {
|
||||
// This will send a session ticket.
|
||||
auto nwrite = SSL_write(tls.ssl, "", 0);
|
||||
if (nwrite < 0) {
|
||||
auto err = SSL_get_error(tls.ssl, nwrite);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Close connection due to TLS renegotiation";
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_write: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
default:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_write: SSL_get_error returned " << err;
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // OPENSSL_IS_BORINGSSL
|
||||
|
||||
// We have to start read watcher, since later stage of code expects
|
||||
// this.
|
||||
rlimit.startw();
|
||||
@@ -678,7 +753,7 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
|
||||
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
|
||||
// get_write_limit() may return smaller length than previously
|
||||
// passed to SSL_write, which violates OpenSSL assumption. To avoid
|
||||
// this, we keep last legnth passed to SSL_write to
|
||||
// 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());
|
||||
@@ -695,7 +770,7 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
int rv;
|
||||
if (SSL_is_init_finished(tls.ssl)) {
|
||||
rv = SSL_write(tls.ssl, data, len);
|
||||
@@ -707,9 +782,9 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
|
||||
rv = nwrite;
|
||||
}
|
||||
}
|
||||
#else // !OPENSSL_1_1_1_API
|
||||
#else // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
|
||||
auto rv = SSL_write(tls.ssl, data, len);
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
#endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
@@ -756,7 +831,7 @@ ssize_t Connection::read_tls(void *data, size_t len) {
|
||||
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
|
||||
// rlimit_.avail() or rlimit_.avail() may return different length
|
||||
// than the length previously passed to SSL_read, which violates
|
||||
// OpenSSL assumption. To avoid this, we keep last legnth passed
|
||||
// OpenSSL assumption. To avoid this, we keep last length passed
|
||||
// to SSL_read to tls_last_readlen_ if SSL_read indicated I/O
|
||||
// blocking.
|
||||
if (tls.last_readlen == 0) {
|
||||
@@ -769,7 +844,7 @@ ssize_t Connection::read_tls(void *data, size_t len) {
|
||||
tls.last_readlen = 0;
|
||||
}
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
if (!tls.early_data_finish) {
|
||||
// TLSv1.3 handshake is still going on.
|
||||
size_t nread;
|
||||
@@ -808,7 +883,7 @@ ssize_t Connection::read_tls(void *data, size_t len) {
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
#endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
||||
|
||||
auto rv = SSL_read(tls.ssl, data, len);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "shrpx_memcached_dispatcher.h"
|
||||
#include "shrpx_signal.h"
|
||||
#include "shrpx_log.h"
|
||||
#include "xsi_strerror.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
||||
@@ -112,7 +113,11 @@ void serial_event_async_cb(struct ev_loop *loop, ev_async *w, int revent) {
|
||||
} // namespace
|
||||
|
||||
ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
|
||||
: gen_(gen),
|
||||
:
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic_ipc_fd_(-1),
|
||||
#endif // ENABLE_HTTP3
|
||||
gen_(gen),
|
||||
single_worker_(nullptr),
|
||||
loop_(loop),
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
@@ -156,6 +161,19 @@ ConnectionHandler::~ConnectionHandler() {
|
||||
ev_timer_stop(loop_, &ocsp_timer_);
|
||||
ev_timer_stop(loop_, &disable_acceptor_timer_);
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
for (auto ssl_ctx : quic_all_ssl_ctx_) {
|
||||
if (ssl_ctx == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto tls_ctx_data =
|
||||
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
delete tls_ctx_data;
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
for (auto ssl_ctx : all_ssl_ctx_) {
|
||||
auto tls_ctx_data =
|
||||
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||
@@ -179,24 +197,24 @@ void ConnectionHandler::set_ticket_keys_to_worker(
|
||||
}
|
||||
|
||||
void ConnectionHandler::worker_reopen_log_files() {
|
||||
WorkerEvent wev{};
|
||||
|
||||
wev.type = WorkerEventType::REOPEN_LOG;
|
||||
|
||||
for (auto &worker : workers_) {
|
||||
worker->send(wev);
|
||||
WorkerEvent wev{};
|
||||
|
||||
wev.type = WorkerEventType::REOPEN_LOG;
|
||||
|
||||
worker->send(std::move(wev));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionHandler::worker_replace_downstream(
|
||||
std::shared_ptr<DownstreamConfig> downstreamconf) {
|
||||
WorkerEvent wev{};
|
||||
|
||||
wev.type = WorkerEventType::REPLACE_DOWNSTREAM;
|
||||
wev.downstreamconf = std::move(downstreamconf);
|
||||
|
||||
for (auto &worker : workers_) {
|
||||
worker->send(wev);
|
||||
WorkerEvent wev{};
|
||||
|
||||
wev.type = WorkerEventType::REPLACE_DOWNSTREAM;
|
||||
wev.downstreamconf = downstreamconf;
|
||||
|
||||
worker->send(std::move(wev));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +227,18 @@ int ConnectionHandler::create_single_worker() {
|
||||
nb_
|
||||
#endif // HAVE_NEVERBLEED
|
||||
);
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic_cert_tree_ = tls::create_cert_lookup_tree();
|
||||
auto quic_sv_ssl_ctx = tls::setup_quic_server_ssl_context(
|
||||
quic_all_ssl_ctx_, quic_indexed_ssl_ctx_, quic_cert_tree_.get()
|
||||
# ifdef HAVE_NEVERBLEED
|
||||
,
|
||||
nb_
|
||||
# endif // HAVE_NEVERBLEED
|
||||
);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_
|
||||
@@ -217,6 +247,9 @@ int ConnectionHandler::create_single_worker() {
|
||||
|
||||
if (cl_ssl_ctx) {
|
||||
all_ssl_ctx_.push_back(cl_ssl_ctx);
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic_all_ssl_ctx_.push_back(nullptr);
|
||||
#endif // ENABLE_HTTP3
|
||||
}
|
||||
|
||||
auto config = get_config();
|
||||
@@ -233,11 +266,30 @@ int ConnectionHandler::create_single_worker() {
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic_all_ssl_ctx_.push_back(nullptr);
|
||||
#endif // ENABLE_HTTP3
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF)
|
||||
quic_bpf_refs_.resize(config->conn.quic_listener.addrs.size());
|
||||
#endif // ENABLE_HTTP3 && HAVE_LIBBPF
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
assert(cid_prefixes_.size() == 1);
|
||||
const auto &cid_prefix = cid_prefixes_[0];
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
single_worker_ = std::make_unique<Worker>(
|
||||
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic_sv_ssl_ctx, quic_cert_tree_.get(), cid_prefix.data(),
|
||||
cid_prefix.size(),
|
||||
# ifdef HAVE_LIBBPF
|
||||
/* index = */ 0,
|
||||
# endif // HAVE_LIBBPF
|
||||
#endif // ENABLE_HTTP3
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (single_worker_->create_mruby_context() != 0) {
|
||||
@@ -245,6 +297,12 @@ int ConnectionHandler::create_single_worker() {
|
||||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
if (single_worker_->setup_quic_server_socket() != 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -260,6 +318,18 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
nb_
|
||||
# endif // HAVE_NEVERBLEED
|
||||
);
|
||||
|
||||
# ifdef ENABLE_HTTP3
|
||||
quic_cert_tree_ = tls::create_cert_lookup_tree();
|
||||
auto quic_sv_ssl_ctx = tls::setup_quic_server_ssl_context(
|
||||
quic_all_ssl_ctx_, quic_indexed_ssl_ctx_, quic_cert_tree_.get()
|
||||
# ifdef HAVE_NEVERBLEED
|
||||
,
|
||||
nb_
|
||||
# endif // HAVE_NEVERBLEED
|
||||
);
|
||||
# endif // ENABLE_HTTP3
|
||||
|
||||
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
|
||||
# ifdef HAVE_NEVERBLEED
|
||||
nb_
|
||||
@@ -268,12 +338,19 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
|
||||
if (cl_ssl_ctx) {
|
||||
all_ssl_ctx_.push_back(cl_ssl_ctx);
|
||||
# ifdef ENABLE_HTTP3
|
||||
quic_all_ssl_ctx_.push_back(nullptr);
|
||||
# endif // ENABLE_HTTP3
|
||||
}
|
||||
|
||||
auto config = get_config();
|
||||
auto &tlsconf = config->tls;
|
||||
auto &apiconf = config->api;
|
||||
|
||||
# if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF)
|
||||
quic_bpf_refs_.resize(config->conn.quic_listener.addrs.size());
|
||||
# endif // ENABLE_HTTP3 && HAVE_LIBBPF
|
||||
|
||||
// We have dedicated worker for API request processing.
|
||||
if (apiconf.enabled) {
|
||||
++num;
|
||||
@@ -291,14 +368,32 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(session_cache_ssl_ctx);
|
||||
# ifdef ENABLE_HTTP3
|
||||
quic_all_ssl_ctx_.push_back(nullptr);
|
||||
# endif // ENABLE_HTTP3
|
||||
}
|
||||
}
|
||||
|
||||
# ifdef ENABLE_HTTP3
|
||||
assert(cid_prefixes_.size() == num);
|
||||
# endif // ENABLE_HTTP3
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
auto loop = ev_loop_new(config->ev_loop_flags);
|
||||
|
||||
# ifdef ENABLE_HTTP3
|
||||
const auto &cid_prefix = cid_prefixes_[i];
|
||||
# endif // ENABLE_HTTP3
|
||||
|
||||
auto worker = std::make_unique<Worker>(
|
||||
loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
# ifdef ENABLE_HTTP3
|
||||
quic_sv_ssl_ctx, quic_cert_tree_.get(), cid_prefix.data(),
|
||||
cid_prefix.size(),
|
||||
# ifdef HAVE_LIBBPF
|
||||
i,
|
||||
# endif // HAVE_LIBBPF
|
||||
# endif // ENABLE_HTTP3
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
# ifdef HAVE_MRUBY
|
||||
if (worker->create_mruby_context() != 0) {
|
||||
@@ -306,6 +401,13 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
}
|
||||
# endif // HAVE_MRUBY
|
||||
|
||||
# ifdef ENABLE_HTTP3
|
||||
if ((!apiconf.enabled || i != 0) &&
|
||||
worker->setup_quic_server_socket() != 0) {
|
||||
return -1;
|
||||
}
|
||||
# endif // ENABLE_HTTP3
|
||||
|
||||
workers_.push_back(std::move(worker));
|
||||
worker_loops_.push_back(loop);
|
||||
|
||||
@@ -345,15 +447,15 @@ void ConnectionHandler::graceful_shutdown_worker() {
|
||||
return;
|
||||
}
|
||||
|
||||
WorkerEvent wev{};
|
||||
wev.type = WorkerEventType::GRACEFUL_SHUTDOWN;
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LLOG(INFO, this) << "Sending graceful shutdown signal to worker";
|
||||
}
|
||||
|
||||
for (auto &worker : workers_) {
|
||||
worker->send(wev);
|
||||
WorkerEvent wev{};
|
||||
wev.type = WorkerEventType::GRACEFUL_SHUTDOWN;
|
||||
|
||||
worker->send(std::move(wev));
|
||||
}
|
||||
|
||||
#ifndef NOTHREADS
|
||||
@@ -436,7 +538,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
|
||||
wev.client_addrlen = addrlen;
|
||||
wev.faddr = faddr;
|
||||
|
||||
worker->send(wev);
|
||||
worker->send(std::move(wev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -602,6 +704,9 @@ void ConnectionHandler::handle_ocsp_complete() {
|
||||
ev_child_stop(loop_, &ocsp_.chldev);
|
||||
|
||||
assert(ocsp_.next < all_ssl_ctx_.size());
|
||||
#ifdef ENABLE_HTTP3
|
||||
assert(all_ssl_ctx_.size() == quic_all_ssl_ctx_.size());
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
|
||||
auto tls_ctx_data =
|
||||
@@ -629,6 +734,32 @@ void ConnectionHandler::handle_ocsp_complete() {
|
||||
if (tlsconf.ocsp.no_verify ||
|
||||
tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
|
||||
ocsp_.resp.size()) == 0) {
|
||||
#ifdef ENABLE_HTTP3
|
||||
// We have list of SSL_CTX with the same certificate in
|
||||
// quic_all_ssl_ctx_ as well. Some SSL_CTXs are missing there in
|
||||
// that case we get nullptr.
|
||||
auto quic_ssl_ctx = quic_all_ssl_ctx_[ocsp_.next];
|
||||
if (quic_ssl_ctx) {
|
||||
# ifndef OPENSSL_IS_BORINGSSL
|
||||
auto quic_tls_ctx_data = static_cast<tls::TLSContextData *>(
|
||||
SSL_CTX_get_app_data(quic_ssl_ctx));
|
||||
# ifdef HAVE_ATOMIC_STD_SHARED_PTR
|
||||
std::atomic_store_explicit(
|
||||
&quic_tls_ctx_data->ocsp_data,
|
||||
std::make_shared<std::vector<uint8_t>>(ocsp_.resp),
|
||||
std::memory_order_release);
|
||||
# else // !HAVE_ATOMIC_STD_SHARED_PTR
|
||||
std::lock_guard<std::mutex> g(quic_tls_ctx_data->mu);
|
||||
quic_tls_ctx_data->ocsp_data =
|
||||
std::make_shared<std::vector<uint8_t>>(ocsp_.resp);
|
||||
# endif // !HAVE_ATOMIC_STD_SHARED_PTR
|
||||
# else // OPENSSL_IS_BORINGSSL
|
||||
SSL_CTX_set_ocsp_response(quic_ssl_ctx, ocsp_.resp.data(),
|
||||
ocsp_.resp.size());
|
||||
# endif // OPENSSL_IS_BORINGSSL
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
# ifdef HAVE_ATOMIC_STD_SHARED_PTR
|
||||
std::atomic_store_explicit(
|
||||
@@ -809,6 +940,9 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
|
||||
nullptr);
|
||||
|
||||
all_ssl_ctx_.push_back(ssl_ctx);
|
||||
#ifdef ENABLE_HTTP3
|
||||
quic_all_ssl_ctx_.push_back(nullptr);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
return ssl_ctx;
|
||||
}
|
||||
@@ -871,8 +1005,329 @@ ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
|
||||
return indexed_ssl_ctx_[idx];
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
const std::vector<SSL_CTX *> &
|
||||
ConnectionHandler::get_quic_indexed_ssl_ctx(size_t idx) const {
|
||||
return quic_indexed_ssl_ctx_[idx];
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
|
||||
enable_acceptor_on_ocsp_completion_ = f;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
int ConnectionHandler::forward_quic_packet(
|
||||
const UpstreamAddr *faddr, const Address &remote_addr,
|
||||
const Address &local_addr, const ngtcp2_pkt_info &pi,
|
||||
const uint8_t *cid_prefix, const uint8_t *data, size_t datalen) {
|
||||
assert(!get_config()->single_thread);
|
||||
|
||||
for (auto &worker : workers_) {
|
||||
if (!std::equal(cid_prefix, cid_prefix + SHRPX_QUIC_CID_PREFIXLEN,
|
||||
worker->get_cid_prefix())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WorkerEvent wev{};
|
||||
wev.type = WorkerEventType::QUIC_PKT_FORWARD;
|
||||
wev.quic_pkt = std::make_unique<QUICPacket>(faddr->index, remote_addr,
|
||||
local_addr, pi, data, datalen);
|
||||
|
||||
worker->send(std::move(wev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ConnectionHandler::set_quic_keying_materials(
|
||||
std::shared_ptr<QUICKeyingMaterials> qkms) {
|
||||
quic_keying_materials_ = std::move(qkms);
|
||||
}
|
||||
|
||||
const std::shared_ptr<QUICKeyingMaterials> &
|
||||
ConnectionHandler::get_quic_keying_materials() const {
|
||||
return quic_keying_materials_;
|
||||
}
|
||||
|
||||
void ConnectionHandler::set_cid_prefixes(
|
||||
const std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
|
||||
&cid_prefixes) {
|
||||
cid_prefixes_ = cid_prefixes;
|
||||
}
|
||||
|
||||
QUICLingeringWorkerProcess *
|
||||
ConnectionHandler::match_quic_lingering_worker_process_cid_prefix(
|
||||
const uint8_t *dcid, size_t dcidlen) {
|
||||
assert(dcidlen >= SHRPX_QUIC_CID_PREFIXLEN);
|
||||
|
||||
for (auto &lwps : quic_lingering_worker_processes_) {
|
||||
for (auto &cid_prefix : lwps.cid_prefixes) {
|
||||
if (std::equal(std::begin(cid_prefix), std::end(cid_prefix), dcid)) {
|
||||
return &lwps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
# ifdef HAVE_LIBBPF
|
||||
std::vector<BPFRef> &ConnectionHandler::get_quic_bpf_refs() {
|
||||
return quic_bpf_refs_;
|
||||
}
|
||||
|
||||
void ConnectionHandler::unload_bpf_objects() {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
|
||||
LOG(NOTICE) << "Unloading BPF objects";
|
||||
|
||||
for (auto &ref : quic_bpf_refs_) {
|
||||
if (ref.obj == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bpf_object__unload(ref.obj) != 0) {
|
||||
LOG(WARN) << "Failed to unload bpf object: "
|
||||
<< xsi_strerror(errno, errbuf.data(), errbuf.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
ref.obj = nullptr;
|
||||
}
|
||||
}
|
||||
# endif // HAVE_LIBBPF
|
||||
|
||||
void ConnectionHandler::set_quic_ipc_fd(int fd) { quic_ipc_fd_ = fd; }
|
||||
|
||||
void ConnectionHandler::set_quic_lingering_worker_processes(
|
||||
const std::vector<QUICLingeringWorkerProcess> &quic_lwps) {
|
||||
quic_lingering_worker_processes_ = quic_lwps;
|
||||
}
|
||||
|
||||
int ConnectionHandler::forward_quic_packet_to_lingering_worker_process(
|
||||
QUICLingeringWorkerProcess *quic_lwp, const Address &remote_addr,
|
||||
const Address &local_addr, const ngtcp2_pkt_info &pi, const uint8_t *data,
|
||||
size_t datalen) {
|
||||
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);
|
||||
|
||||
auto p = header.data();
|
||||
|
||||
*p++ = static_cast<uint8_t>(QUICIPCType::DGRAM_FORWARD);
|
||||
*p++ = static_cast<uint8_t>(remote_addr.len - 1);
|
||||
p = std::copy_n(reinterpret_cast<const uint8_t *>(&remote_addr.su),
|
||||
remote_addr.len, p);
|
||||
*p++ = static_cast<uint8_t>(local_addr.len - 1);
|
||||
p = std::copy_n(reinterpret_cast<const uint8_t *>(&local_addr.su),
|
||||
local_addr.len, p);
|
||||
*p++ = pi.ecn;
|
||||
|
||||
iovec msg_iov[] = {
|
||||
{
|
||||
.iov_base = header.data(),
|
||||
.iov_len = static_cast<size_t>(p - header.data()),
|
||||
},
|
||||
{
|
||||
.iov_base = const_cast<uint8_t *>(data),
|
||||
.iov_len = datalen,
|
||||
},
|
||||
};
|
||||
|
||||
msghdr msg{};
|
||||
msg.msg_iov = msg_iov;
|
||||
msg.msg_iovlen = array_size(msg_iov);
|
||||
|
||||
ssize_t nwrite;
|
||||
|
||||
while ((nwrite = sendmsg(quic_lwp->quic_ipc_fd, &msg, 0)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (nwrite == -1) {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Failed to send QUIC IPC message: "
|
||||
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConnectionHandler::quic_ipc_read() {
|
||||
std::array<uint8_t, 65536> buf;
|
||||
|
||||
ssize_t nread;
|
||||
|
||||
while ((nread = recv(quic_ipc_fd_, buf.data(), buf.size(), 0)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (nread == -1) {
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
|
||||
auto error = errno;
|
||||
LOG(ERROR) << "Failed to read data from QUIC IPC channel: "
|
||||
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nread == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t len = 1 + 1 + 1 + 1;
|
||||
|
||||
// Wire format:
|
||||
// TYPE(1) REMOTE_ADDRLEN(1) REMOTE_ADDR(N) LOCAL_ADDRLEN(1) LOCAL_ADDR(N)
|
||||
// ECN(1) DGRAM_PAYLOAD(N)
|
||||
//
|
||||
// When encoding, REMOTE_ADDRLEN and LOCAL_ADDRLEN are decremented
|
||||
// by 1.
|
||||
if (static_cast<size_t>(nread) < len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto p = buf.data();
|
||||
if (*p != static_cast<uint8_t>(QUICIPCType::DGRAM_FORWARD)) {
|
||||
LOG(ERROR) << "Unknown QUICIPCType: " << static_cast<uint32_t>(*p);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
++p;
|
||||
|
||||
auto pkt = std::make_unique<QUICPacket>();
|
||||
|
||||
auto remote_addrlen = static_cast<size_t>(*p++) + 1;
|
||||
if (remote_addrlen > sizeof(sockaddr_storage)) {
|
||||
LOG(ERROR) << "The length of remote address is too large: "
|
||||
<< remote_addrlen;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
len += remote_addrlen;
|
||||
|
||||
if (static_cast<size_t>(nread) < len) {
|
||||
LOG(ERROR) << "Insufficient QUIC IPC message length";
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkt->remote_addr.len = remote_addrlen;
|
||||
memcpy(&pkt->remote_addr.su, p, remote_addrlen);
|
||||
|
||||
p += remote_addrlen;
|
||||
|
||||
auto local_addrlen = static_cast<size_t>(*p++) + 1;
|
||||
if (local_addrlen > sizeof(sockaddr_storage)) {
|
||||
LOG(ERROR) << "The length of local address is too large: " << local_addrlen;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
len += local_addrlen;
|
||||
|
||||
if (static_cast<size_t>(nread) < len) {
|
||||
LOG(ERROR) << "Insufficient QUIC IPC message length";
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkt->local_addr.len = local_addrlen;
|
||||
memcpy(&pkt->local_addr.su, p, local_addrlen);
|
||||
|
||||
p += local_addrlen;
|
||||
|
||||
pkt->pi.ecn = *p++;
|
||||
|
||||
auto datalen = nread - (p - buf.data());
|
||||
|
||||
pkt->data.assign(p, p + datalen);
|
||||
|
||||
// At the moment, UpstreamAddr index is unknown.
|
||||
pkt->upstream_addr_index = static_cast<size_t>(-1);
|
||||
|
||||
uint32_t version;
|
||||
const uint8_t *dcid;
|
||||
size_t dcidlen;
|
||||
const uint8_t *scid;
|
||||
size_t scidlen;
|
||||
|
||||
auto rv =
|
||||
ngtcp2_pkt_decode_version_cid(&version, &dcid, &dcidlen, &scid, &scidlen,
|
||||
p, datalen, SHRPX_QUIC_SCIDLEN);
|
||||
if (rv < 0) {
|
||||
LOG(ERROR) << "ngtcp2_pkt_decode_version_cid: " << ngtcp2_strerror(rv);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dcidlen != SHRPX_QUIC_SCIDLEN) {
|
||||
LOG(ERROR) << "DCID length is invalid";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (single_worker_) {
|
||||
auto faddr = single_worker_->find_quic_upstream_addr(pkt->local_addr);
|
||||
if (faddr == nullptr) {
|
||||
LOG(ERROR) << "No suitable upstream address found";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto quic_conn_handler = single_worker_->get_quic_connection_handler();
|
||||
|
||||
// Ignore return value
|
||||
quic_conn_handler->handle_packet(faddr, pkt->remote_addr, pkt->local_addr,
|
||||
pkt->pi, pkt->data.data(),
|
||||
pkt->data.size());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &qkm = quic_keying_materials_->keying_materials.front();
|
||||
|
||||
std::array<uint8_t, SHRPX_QUIC_DECRYPTED_DCIDLEN> decrypted_dcid;
|
||||
|
||||
if (decrypt_quic_connection_id(decrypted_dcid.data(),
|
||||
dcid + SHRPX_QUIC_CID_PREFIX_OFFSET,
|
||||
qkm.cid_encryption_key.data()) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (auto &worker : workers_) {
|
||||
if (!std::equal(std::begin(decrypted_dcid),
|
||||
std::begin(decrypted_dcid) + SHRPX_QUIC_CID_PREFIXLEN,
|
||||
worker->get_cid_prefix())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WorkerEvent wev{
|
||||
.type = WorkerEventType::QUIC_PKT_FORWARD,
|
||||
.quic_pkt = std::move(pkt),
|
||||
};
|
||||
worker->send(std::move(wev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "No worker to match CID prefix";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
# include <future>
|
||||
#endif // NOTHREADS
|
||||
|
||||
#ifdef HAVE_LIBBPF
|
||||
# include <bpf/libbpf.h>
|
||||
#endif // HAVE_LIBBPF
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <ev.h>
|
||||
@@ -99,6 +103,35 @@ struct SerialEvent {
|
||||
std::shared_ptr<DownstreamConfig> downstreamconf;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
# ifdef HAVE_LIBBPF
|
||||
struct BPFRef {
|
||||
bpf_object *obj;
|
||||
int reuseport_array;
|
||||
int cid_prefix_map;
|
||||
};
|
||||
# endif // HAVE_LIBBPF
|
||||
|
||||
// QUIC IPC message type.
|
||||
enum class QUICIPCType {
|
||||
NONE,
|
||||
// Send forwarded QUIC UDP datagram and its metadata.
|
||||
DGRAM_FORWARD,
|
||||
};
|
||||
|
||||
// WorkerProcesses which are in graceful shutdown period.
|
||||
struct QUICLingeringWorkerProcess {
|
||||
QUICLingeringWorkerProcess(
|
||||
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes,
|
||||
int quic_ipc_fd)
|
||||
: cid_prefixes{std::move(cid_prefixes)}, quic_ipc_fd{quic_ipc_fd} {}
|
||||
|
||||
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
|
||||
// Socket to send QUIC IPC message to this worker process.
|
||||
int quic_ipc_fd;
|
||||
};
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
class ConnectionHandler {
|
||||
public:
|
||||
ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen);
|
||||
@@ -130,7 +163,7 @@ public:
|
||||
|
||||
// Cancels ocsp update process
|
||||
void cancel_ocsp_update();
|
||||
// Starts ocsp update for certficate |cert_file|.
|
||||
// Starts ocsp update for certificate |cert_file|.
|
||||
int start_ocsp_update(const char *cert_file);
|
||||
// Reads incoming data from ocsp update process
|
||||
void read_ocsp_chunk();
|
||||
@@ -159,6 +192,45 @@ public:
|
||||
SSL_CTX *get_ssl_ctx(size_t idx) const;
|
||||
|
||||
const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const;
|
||||
#ifdef ENABLE_HTTP3
|
||||
const std::vector<SSL_CTX *> &get_quic_indexed_ssl_ctx(size_t idx) const;
|
||||
|
||||
int forward_quic_packet(const UpstreamAddr *faddr, const Address &remote_addr,
|
||||
const Address &local_addr, const ngtcp2_pkt_info &pi,
|
||||
const uint8_t *cid_prefix, const uint8_t *data,
|
||||
size_t datalen);
|
||||
|
||||
void set_quic_keying_materials(std::shared_ptr<QUICKeyingMaterials> qkms);
|
||||
const std::shared_ptr<QUICKeyingMaterials> &get_quic_keying_materials() const;
|
||||
|
||||
void set_cid_prefixes(
|
||||
const std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
|
||||
&cid_prefixes);
|
||||
|
||||
void set_quic_lingering_worker_processes(
|
||||
const std::vector<QUICLingeringWorkerProcess> &quic_lwps);
|
||||
|
||||
// Return matching QUICLingeringWorkerProcess which has a CID prefix
|
||||
// such that |dcid| starts with it. If no such
|
||||
// QUICLingeringWorkerProcess, it returns nullptr.
|
||||
QUICLingeringWorkerProcess *
|
||||
match_quic_lingering_worker_process_cid_prefix(const uint8_t *dcid,
|
||||
size_t dcidlen);
|
||||
|
||||
int forward_quic_packet_to_lingering_worker_process(
|
||||
QUICLingeringWorkerProcess *quic_lwp, const Address &remote_addr,
|
||||
const Address &local_addr, const ngtcp2_pkt_info &pi, const uint8_t *data,
|
||||
size_t datalen);
|
||||
|
||||
void set_quic_ipc_fd(int fd);
|
||||
|
||||
int quic_ipc_read();
|
||||
|
||||
# ifdef HAVE_LIBBPF
|
||||
std::vector<BPFRef> &get_quic_bpf_refs();
|
||||
void unload_bpf_objects();
|
||||
# endif // HAVE_LIBBPF
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
void set_neverbleed(neverbleed_t *nb);
|
||||
@@ -187,6 +259,19 @@ private:
|
||||
// selection among them are performed by hostname presented by SNI,
|
||||
// and signature algorithm presented by client.
|
||||
std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_;
|
||||
#ifdef ENABLE_HTTP3
|
||||
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes_;
|
||||
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
|
||||
lingering_cid_prefixes_;
|
||||
int quic_ipc_fd_;
|
||||
std::vector<QUICLingeringWorkerProcess> quic_lingering_worker_processes_;
|
||||
# ifdef HAVE_LIBBPF
|
||||
std::vector<BPFRef> quic_bpf_refs_;
|
||||
# endif // HAVE_LIBBPF
|
||||
std::shared_ptr<QUICKeyingMaterials> quic_keying_materials_;
|
||||
std::vector<SSL_CTX *> quic_all_ssl_ctx_;
|
||||
std::vector<std::vector<SSL_CTX *>> quic_indexed_ssl_ctx_;
|
||||
#endif // ENABLE_HTTP3
|
||||
OCSPUpdateContext ocsp_;
|
||||
std::mt19937 &gen_;
|
||||
// ev_loop for each worker
|
||||
@@ -203,6 +288,9 @@ private:
|
||||
// Otherwise, nullptr and workers_ has instances of Worker instead.
|
||||
std::unique_ptr<Worker> single_worker_;
|
||||
std::unique_ptr<tls::CertLookupTree> cert_tree_;
|
||||
#ifdef ENABLE_HTTP3
|
||||
std::unique_ptr<tls::CertLookupTree> quic_cert_tree_;
|
||||
#endif // ENABLE_HTTP3
|
||||
std::unique_ptr<MemcachedDispatcher> tls_ticket_key_memcached_dispatcher_;
|
||||
// Current TLS session ticket keys. Note that TLS connection does
|
||||
// not refer to this field directly. They use TicketKeys object in
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
int on_write(int fd);
|
||||
int on_timeout();
|
||||
// Calls this function when DNS query finished.
|
||||
void on_result(int staus, hostent *hostent);
|
||||
void on_result(int status, hostent *hostent);
|
||||
void reset_timeout();
|
||||
|
||||
void start_rev(int fd);
|
||||
|
||||
@@ -113,7 +113,7 @@ void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
|
||||
// upstream could be nullptr for unittests
|
||||
Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||
int32_t stream_id)
|
||||
int64_t stream_id)
|
||||
: dlnext(nullptr),
|
||||
dlprev(nullptr),
|
||||
response_sent_body_length(0),
|
||||
@@ -145,7 +145,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||
accesslog_written_(false),
|
||||
new_affinity_cookie_(false),
|
||||
blocked_request_data_eof_(false),
|
||||
expect_100_continue_(false) {
|
||||
expect_100_continue_(false),
|
||||
stop_reading_(false) {
|
||||
|
||||
auto &timeoutconf = get_config()->http2.timeout;
|
||||
|
||||
@@ -164,6 +165,9 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||
downstream_wtimer_.data = this;
|
||||
|
||||
rcbufs_.reserve(32);
|
||||
#ifdef ENABLE_HTTP3
|
||||
rcbufs3_.reserve(32);
|
||||
#endif // ENABLE_HTTP3
|
||||
}
|
||||
|
||||
Downstream::~Downstream() {
|
||||
@@ -203,6 +207,12 @@ Downstream::~Downstream() {
|
||||
// explicitly.
|
||||
dconn_.reset();
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
for (auto rcbuf : rcbufs3_) {
|
||||
nghttp3_rcbuf_decref(rcbuf);
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
for (auto rcbuf : rcbufs_) {
|
||||
nghttp2_rcbuf_decref(rcbuf);
|
||||
}
|
||||
@@ -605,9 +615,9 @@ void Downstream::reset_upstream(Upstream *upstream) {
|
||||
|
||||
Upstream *Downstream::get_upstream() const { return upstream_; }
|
||||
|
||||
void Downstream::set_stream_id(int32_t stream_id) { stream_id_ = stream_id; }
|
||||
void Downstream::set_stream_id(int64_t stream_id) { stream_id_ = stream_id; }
|
||||
|
||||
int32_t Downstream::get_stream_id() const { return stream_id_; }
|
||||
int64_t Downstream::get_stream_id() const { return stream_id_; }
|
||||
|
||||
void Downstream::set_request_state(DownstreamState state) {
|
||||
request_state_ = state;
|
||||
@@ -886,7 +896,8 @@ bool Downstream::get_non_final_response() const {
|
||||
}
|
||||
|
||||
bool Downstream::supports_non_final_response() const {
|
||||
return req_.http_major == 2 || (req_.http_major == 1 && req_.http_minor == 1);
|
||||
return req_.http_major == 3 || req_.http_major == 2 ||
|
||||
(req_.http_major == 1 && req_.http_minor == 1);
|
||||
}
|
||||
|
||||
bool Downstream::get_upgraded() const { return upgraded_; }
|
||||
@@ -904,11 +915,11 @@ StringRef Downstream::get_http2_settings() const {
|
||||
return http2_settings->value;
|
||||
}
|
||||
|
||||
void Downstream::set_downstream_stream_id(int32_t stream_id) {
|
||||
void Downstream::set_downstream_stream_id(int64_t stream_id) {
|
||||
downstream_stream_id_ = stream_id;
|
||||
}
|
||||
|
||||
int32_t Downstream::get_downstream_stream_id() const {
|
||||
int64_t Downstream::get_downstream_stream_id() const {
|
||||
return downstream_stream_id_;
|
||||
}
|
||||
|
||||
@@ -937,7 +948,8 @@ bool Downstream::expect_response_trailer() const {
|
||||
// In HTTP/2, if final response HEADERS does not bear END_STREAM it
|
||||
// is possible trailer fields might come, regardless of request
|
||||
// method or status code.
|
||||
return !resp_.headers_only && resp_.http_major == 2;
|
||||
return !resp_.headers_only &&
|
||||
(resp_.http_major == 3 || resp_.http_major == 2);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -1115,11 +1127,11 @@ DefaultMemchunks Downstream::pop_response_buf() {
|
||||
return std::move(response_buf_);
|
||||
}
|
||||
|
||||
void Downstream::set_assoc_stream_id(int32_t stream_id) {
|
||||
void Downstream::set_assoc_stream_id(int64_t stream_id) {
|
||||
assoc_stream_id_ = stream_id;
|
||||
}
|
||||
|
||||
int32_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; }
|
||||
int64_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; }
|
||||
|
||||
BlockAllocator &Downstream::get_block_allocator() { return balloc_; }
|
||||
|
||||
@@ -1128,6 +1140,13 @@ void Downstream::add_rcbuf(nghttp2_rcbuf *rcbuf) {
|
||||
rcbufs_.push_back(rcbuf);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
void Downstream::add_rcbuf(nghttp3_rcbuf *rcbuf) {
|
||||
nghttp3_rcbuf_incref(rcbuf);
|
||||
rcbufs3_.push_back(rcbuf);
|
||||
}
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
void Downstream::set_downstream_addr_group(
|
||||
const std::shared_ptr<DownstreamAddrGroup> &group) {
|
||||
group_ = group;
|
||||
@@ -1169,4 +1188,8 @@ bool Downstream::get_expect_100_continue() const {
|
||||
return expect_100_continue_;
|
||||
}
|
||||
|
||||
bool Downstream::get_stop_reading() const { return stop_reading_; }
|
||||
|
||||
void Downstream::set_stop_reading(bool f) { stop_reading_ = f; }
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#ifdef ENABLE_HTTP3
|
||||
# include <nghttp3/nghttp3.h>
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
#include "llhttp.h"
|
||||
|
||||
#include "shrpx_io_control.h"
|
||||
@@ -319,20 +323,20 @@ enum class DispatchState {
|
||||
|
||||
class Downstream {
|
||||
public:
|
||||
Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id);
|
||||
Downstream(Upstream *upstream, MemchunkPool *mcpool, int64_t stream_id);
|
||||
~Downstream();
|
||||
void reset_upstream(Upstream *upstream);
|
||||
Upstream *get_upstream() const;
|
||||
void set_stream_id(int32_t stream_id);
|
||||
int32_t get_stream_id() const;
|
||||
void set_assoc_stream_id(int32_t stream_id);
|
||||
int32_t get_assoc_stream_id() const;
|
||||
void set_stream_id(int64_t stream_id);
|
||||
int64_t get_stream_id() const;
|
||||
void set_assoc_stream_id(int64_t stream_id);
|
||||
int64_t get_assoc_stream_id() const;
|
||||
void pause_read(IOCtrlReason reason);
|
||||
int resume_read(IOCtrlReason reason, size_t consumed);
|
||||
void force_resume_read();
|
||||
// Set stream ID for downstream HTTP2 connection.
|
||||
void set_downstream_stream_id(int32_t stream_id);
|
||||
int32_t get_downstream_stream_id() const;
|
||||
void set_downstream_stream_id(int64_t stream_id);
|
||||
int64_t get_downstream_stream_id() const;
|
||||
|
||||
int attach_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
||||
void detach_downstream_connection();
|
||||
@@ -488,6 +492,9 @@ public:
|
||||
BlockAllocator &get_block_allocator();
|
||||
|
||||
void add_rcbuf(nghttp2_rcbuf *rcbuf);
|
||||
#ifdef ENABLE_HTTP3
|
||||
void add_rcbuf(nghttp3_rcbuf *rcbuf);
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
void
|
||||
set_downstream_addr_group(const std::shared_ptr<DownstreamAddrGroup> &group);
|
||||
@@ -513,6 +520,9 @@ public:
|
||||
|
||||
bool get_expect_100_continue() const;
|
||||
|
||||
bool get_stop_reading() const;
|
||||
void set_stop_reading(bool f);
|
||||
|
||||
enum {
|
||||
EVENT_ERROR = 0x1,
|
||||
EVENT_TIMEOUT = 0x2,
|
||||
@@ -527,6 +537,9 @@ private:
|
||||
BlockAllocator balloc_;
|
||||
|
||||
std::vector<nghttp2_rcbuf *> rcbufs_;
|
||||
#ifdef ENABLE_HTTP3
|
||||
std::vector<nghttp3_rcbuf *> rcbufs3_;
|
||||
#endif // ENABLE_HTTP3
|
||||
|
||||
Request req_;
|
||||
Response resp_;
|
||||
@@ -566,12 +579,12 @@ private:
|
||||
// How many times we tried in backend connection
|
||||
size_t num_retry_;
|
||||
// The stream ID in frontend connection
|
||||
int32_t stream_id_;
|
||||
int64_t stream_id_;
|
||||
// The associated stream ID in frontend connection if this is pushed
|
||||
// stream.
|
||||
int32_t assoc_stream_id_;
|
||||
int64_t assoc_stream_id_;
|
||||
// stream ID in backend connection
|
||||
int32_t downstream_stream_id_;
|
||||
int64_t downstream_stream_id_;
|
||||
// RST_STREAM error_code from downstream HTTP2 connection
|
||||
uint32_t response_rst_stream_error_code_;
|
||||
// An affinity cookie value.
|
||||
@@ -606,6 +619,7 @@ private:
|
||||
bool blocked_request_data_eof_;
|
||||
// true if request contains "expect: 100-continue" header field.
|
||||
bool expect_100_continue_;
|
||||
bool stop_reading_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
virtual int on_write() = 0;
|
||||
virtual int on_timeout() { return 0; }
|
||||
|
||||
virtual void on_upstream_change(Upstream *uptream) = 0;
|
||||
virtual void on_upstream_change(Upstream *upstream) = 0;
|
||||
|
||||
// true if this object is poolable.
|
||||
virtual bool poolable() const = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user