summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-11-01 15:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-11-01 15:49:50 +0000
commitf352f98160a8cfe9bd7f67a18d4b48475ffab88a (patch)
tree3bc3374fe1ed133e0abd5501957fe05594856b56
parentAdding debian version 1.56.0-1. (diff)
downloadnghttp2-f352f98160a8cfe9bd7f67a18d4b48475ffab88a.tar.xz
nghttp2-f352f98160a8cfe9bd7f67a18d4b48475ffab88a.zip
Merging upstream version 1.57.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.github/dependabot.yml6
-rw-r--r--.github/workflows/build.yml17
-rw-r--r--.github/workflows/fuzz.yml2
-rw-r--r--CMakeLists.txt12
-rw-r--r--Makefile.am2
-rw-r--r--README.rst6
-rw-r--r--cmakeconfig.h.in9
-rw-r--r--configure.ac29
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/h2load.12
-rw-r--r--doc/nghttp.12
-rw-r--r--doc/nghttpd.12
-rw-r--r--doc/nghttpx.116
-rw-r--r--doc/nghttpx.1.rst14
-rw-r--r--docker/Dockerfile8
-rw-r--r--go.mod8
-rw-r--r--go.sum16
-rw-r--r--lib/CMakeLists.txt2
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/includes/nghttp2/nghttp2.h17
-rw-r--r--lib/nghttp2_option.c7
-rw-r--r--lib/nghttp2_option.h6
-rw-r--r--lib/nghttp2_ratelim.c75
-rw-r--r--lib/nghttp2_ratelim.h57
-rw-r--r--lib/nghttp2_session.c34
-rw-r--r--lib/nghttp2_session.h12
-rw-r--r--lib/nghttp2_time.c62
-rw-r--r--lib/nghttp2_time.h38
-rw-r--r--src/HttpServer.cc50
-rw-r--r--src/shrpx.cc20
-rw-r--r--src/shrpx_connection.cc13
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/main.c7
-rw-r--r--tests/nghttp2_ratelim_test.c101
-rw-r--r--tests/nghttp2_ratelim_test.h35
-rw-r--r--tests/nghttp2_session_test.c103
-rw-r--r--tests/nghttp2_session_test.h1
38 files changed, 711 insertions, 92 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..8c139c7
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+- package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index abce9ef..262c471 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,11 +6,11 @@ permissions: read-all
env:
LIBBPF_VERSION: v1.2.2
- OPENSSL1_VERSION: 1_1_1v+quic
+ OPENSSL1_VERSION: 1_1_1w+quic
OPENSSL3_VERSION: 3.1.2+quic
BORINGSSL_VERSION: 6ca49385b168f47a50e7172d82a590b218f55e4d
NGHTTP3_VERSION: v0.15.0
- NGTCP2_VERSION: v0.19.0
+ NGTCP2_VERSION: v0.19.1
jobs:
build-cache:
@@ -21,7 +21,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Restore libbpf cache
id: cache-libbpf
uses: actions/cache@v3
@@ -200,7 +200,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Linux setup
if: runner.os == 'Linux'
run: |
@@ -394,7 +394,7 @@ jobs:
cd $NGHTTP2_CMAKE_DIR
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
- - uses: actions/setup-go@v3
+ - uses: actions/setup-go@v4
if: matrix.buildtool == 'cmake'
with:
go-version-file: go.mod
@@ -417,7 +417,7 @@ jobs:
HOST: ${{ matrix.host }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Linux setup
run: |
sudo dpkg --add-architecture i386
@@ -442,7 +442,8 @@ jobs:
run: |
autoreconf -i && \
./configure --enable-werror --enable-lib-only --with-cunit \
- --host="$HOST" PKG_CONFIG_PATH="$PWD/CUnit-2.1-3/build/lib/pkgconfig"
+ --host="$HOST" PKG_CONFIG_PATH="$PWD/CUnit-2.1-3/build/lib/pkgconfig" \
+ CFLAGS="-g -O2 -D_WIN32_WINNT=0x0600"
- name: Build nghttp2
run: |
make -j$(nproc)
@@ -466,7 +467,7 @@ jobs:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: microsoft/setup-msbuild@v1
- run: |
vcpkg --triplet=${{ matrix.arch }}-windows install cunit
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 611c64f..326cf46 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -17,7 +17,7 @@ jobs:
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
- uses: actions/upload-artifact@v1
+ uses: actions/upload-artifact@v3
if: failure()
with:
name: artifacts
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0149feb..42ec478 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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.56.0)
+project(nghttp2 VERSION 1.57.0)
# See versioning rule:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-set(LT_CURRENT 38)
-set(LT_REVISION 4)
-set(LT_AGE 24)
+set(LT_CURRENT 39)
+set(LT_REVISION 0)
+set(LT_AGE 25)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
include(Version)
@@ -277,6 +277,7 @@ check_include_file("netinet/ip.h" HAVE_NETINET_IP_H)
check_include_file("pwd.h" HAVE_PWD_H)
check_include_file("sys/socket.h" HAVE_SYS_SOCKET_H)
check_include_file("sys/time.h" HAVE_SYS_TIME_H)
+check_include_file("sysinfoapi.h" HAVE_SYSINFOAPI_H)
check_include_file("syslog.h" HAVE_SYSLOG_H)
check_include_file("time.h" HAVE_TIME_H)
check_include_file("unistd.h" HAVE_UNISTD_H)
@@ -317,8 +318,11 @@ check_type_size("time_t" SIZEOF_TIME_T)
include(CheckFunctionExists)
check_function_exists(_Exit HAVE__EXIT)
check_function_exists(accept4 HAVE_ACCEPT4)
+check_function_exists(clock_gettime HAVE_CLOCK_GETTIME)
check_function_exists(mkostemp HAVE_MKOSTEMP)
+check_symbol_exists(GetTickCount64 sysinfoapi.h HAVE_GETTICKCOUNT64)
+
include(CheckSymbolExists)
# XXX does this correctly detect initgroups (un)availability on cygwin?
check_symbol_exists(initgroups grp.h HAVE_DECL_INITGROUPS)
diff --git a/Makefile.am b/Makefile.am
index df1c3e5..894165b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,5 +55,5 @@ 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} examples/*.{c,cc} \
+ src/*.{c,cc,h} examples/*.c \
tests/*.{c,h} bpf/*.c fuzz/*.cc
diff --git a/README.rst b/README.rst
index 990af82..a513759 100644
--- a/README.rst
+++ b/README.rst
@@ -127,7 +127,7 @@ 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_1v+quic>`_; or
+ <https://github.com/quictls/openssl/tree/OpenSSL_1_1_1w+quic>`_; or
`BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit
6ca49385b168f47a50e7172d82a590b218f55e4d)
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ 0.19.x
@@ -343,7 +343,7 @@ Build custom OpenSSL:
.. code-block:: text
- $ git clone --depth 1 -b OpenSSL_1_1_1v+quic https://github.com/quictls/openssl
+ $ git clone --depth 1 -b OpenSSL_1_1_1w+quic https://github.com/quictls/openssl
$ cd openssl
$ ./config --prefix=$PWD/build --openssldir=/etc/ssl
$ make -j$(nproc)
@@ -366,7 +366,7 @@ Build ngtcp2:
.. code-block:: text
- $ git clone --depth 1 -b v0.19.0 https://github.com/ngtcp2/ngtcp2
+ $ git clone --depth 1 -b v0.19.1 https://github.com/ngtcp2/ngtcp2
$ cd ngtcp2
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only \
diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in
index 8e7583e..d93fed6 100644
--- a/cmakeconfig.h.in
+++ b/cmakeconfig.h.in
@@ -31,9 +31,15 @@
/* Define to 1 if you have the `accept4` function. */
#cmakedefine HAVE_ACCEPT4 1
+/* Define to 1 if you have the `clock_gettime` function. */
+#cmakedefine HAVE_CLOCK_GETTIME 1
+
/* Define to 1 if you have the `mkostemp` function. */
#cmakedefine HAVE_MKOSTEMP 1
+/* Define to 1 if you have the `GetTickCount64` function. */
+#cmakedefine HAVE_GETTICKCOUNT64 1
+
/* Define to 1 if you have the `initgroups` function. */
#cmakedefine01 HAVE_DECL_INITGROUPS
@@ -73,6 +79,9 @@
/* Define to 1 if you have the <sys/time.h> header file. */
#cmakedefine HAVE_SYS_TIME_H 1
+/* Define to 1 if you have the <sysinfoapi.h> header file. */
+#cmakedefine HAVE_SYSINFOAPI_H 1
+
/* Define to 1 if you have the <syslog.h> header file. */
#cmakedefine HAVE_SYSLOG_H 1
diff --git a/configure.ac b/configure.ac
index fcb7d2d..e3e743a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
-AC_INIT([nghttp2], [1.56.0], [t-tujikawa@users.sourceforge.net])
+AC_INIT([nghttp2], [1.57.0], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
@@ -44,9 +44,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl See versioning rule:
dnl https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-AC_SUBST(LT_CURRENT, 38)
-AC_SUBST(LT_REVISION, 4)
-AC_SUBST(LT_AGE, 24)
+AC_SUBST(LT_CURRENT, 39)
+AC_SUBST(LT_REVISION, 0)
+AC_SUBST(LT_AGE, 25)
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"`
@@ -855,6 +855,7 @@ AC_CHECK_HEADERS([ \
string.h \
sys/socket.h \
sys/time.h \
+ sysinfoapi.h \
syslog.h \
time.h \
unistd.h \
@@ -929,6 +930,7 @@ AC_FUNC_STRNLEN
AC_CHECK_FUNCS([ \
_Exit \
accept4 \
+ clock_gettime \
dup2 \
getcwd \
getpwnam \
@@ -954,6 +956,25 @@ AC_CHECK_FUNCS([ \
AC_CHECK_FUNC([timerfd_create],
[have_timerfd_create=yes], [have_timerfd_create=no])
+AC_MSG_CHECKING([checking for GetTickCount64])
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <sysinfoapi.h>
+]],
+[[
+GetTickCount64();
+]])],
+[have_gettickcount64=yes],
+[have_gettickcount64=no])
+
+if test "x${have_gettickcount64}" = "xyes"; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_GETTICKCOUNT64], [1],
+ [Define to 1 if you have `GetTickCount64` function.])
+else
+ AC_MSG_RESULT([no])
+fi
+
# For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but
# cygwin disables initgroups due to feature test macro magic with our
# configuration. FreeBSD declares initgroups() in unistd.h.
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 53f63c3..96f449f 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -75,6 +75,7 @@ APIDOCS= \
nghttp2_option_set_user_recv_extension_type.rst \
nghttp2_option_set_max_outbound_ack.rst \
nghttp2_option_set_max_settings.rst \
+ nghttp2_option_set_stream_reset_rate_limit.rst \
nghttp2_pack_settings_payload.rst \
nghttp2_priority_spec_check_default.rst \
nghttp2_priority_spec_default_init.rst \
diff --git a/doc/h2load.1 b/doc/h2load.1
index 2f85b06..58d826a 100644
--- a/doc/h2load.1
+++ b/doc/h2load.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "H2LOAD" "1" "Sep 04, 2023" "1.56.0" "nghttp2"
+.TH "H2LOAD" "1" "Oct 10, 2023" "1.57.0" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.SH SYNOPSIS
diff --git a/doc/nghttp.1 b/doc/nghttp.1
index 013be87..025ff99 100644
--- a/doc/nghttp.1
+++ b/doc/nghttp.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "NGHTTP" "1" "Sep 04, 2023" "1.56.0" "nghttp2"
+.TH "NGHTTP" "1" "Oct 10, 2023" "1.57.0" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.SH SYNOPSIS
diff --git a/doc/nghttpd.1 b/doc/nghttpd.1
index 86895b4..847af30 100644
--- a/doc/nghttpd.1
+++ b/doc/nghttpd.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "NGHTTPD" "1" "Sep 04, 2023" "1.56.0" "nghttp2"
+.TH "NGHTTPD" "1" "Oct 10, 2023" "1.57.0" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 server
.SH SYNOPSIS
diff --git a/doc/nghttpx.1 b/doc/nghttpx.1
index 7004e0e..1542a26 100644
--- a/doc/nghttpx.1
+++ b/doc/nghttpx.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "NGHTTPX" "1" "Sep 04, 2023" "1.56.0" "nghttp2"
+.TH "NGHTTPX" "1" "Oct 10, 2023" "1.57.0" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.SH SYNOPSIS
@@ -1546,18 +1546,20 @@ in HTTP/2 frontend.
.TP
.B \-\-add\-request\-header=<HEADER>
Specify additional header field to add to request header
-set. This option just appends header field and won\(aqt
-replace anything already set. This option can be used
-several times to specify multiple header fields.
+set. The field name must be lowercase. This option
+just appends header field and won\(aqt replace anything
+already set. This option can be used several times to
+specify multiple header fields.
Example: \fI\%\-\-add\-request\-header\fP=\(dqfoo: bar\(dq
.UNINDENT
.INDENT 0.0
.TP
.B \-\-add\-response\-header=<HEADER>
Specify additional header field to add to response
-header set. This option just appends header field and
-won\(aqt replace anything already set. This option can be
-used several times to specify multiple header fields.
+header set. The field name must be lowercase. This
+option just appends header field and won\(aqt replace
+anything already set. This option can be used several
+times to specify multiple header fields.
Example: \fI\%\-\-add\-response\-header\fP=\(dqfoo: bar\(dq
.UNINDENT
.INDENT 0.0
diff --git a/doc/nghttpx.1.rst b/doc/nghttpx.1.rst
index a19b922..a103745 100644
--- a/doc/nghttpx.1.rst
+++ b/doc/nghttpx.1.rst
@@ -1402,17 +1402,19 @@ HTTP
.. option:: --add-request-header=<HEADER>
Specify additional header field to add to request header
- set. This option just appends header field and won't
- replace anything already set. This option can be used
- several times to specify multiple header fields.
+ set. The field name must be lowercase. This option
+ just appends header field and won't replace anything
+ already set. This option can be used several times to
+ specify multiple header fields.
Example: :option:`--add-request-header`\="foo: bar"
.. option:: --add-response-header=<HEADER>
Specify additional header field to add to response
- header set. This option just appends header field and
- won't replace anything already set. This option can be
- used several times to specify multiple header fields.
+ header set. The field name must be lowercase. This
+ option just appends header field and won't replace
+ anything already set. This option can be used several
+ times to specify multiple header fields.
Example: :option:`--add-response-header`\="foo: bar"
.. option:: --request-header-field-buffer=<SIZE>
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 682287a..0c51708 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM debian:11 as build
+FROM debian:12 as build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
@@ -7,7 +7,7 @@ RUN apt-get update && \
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
libelf-dev
-RUN git clone --depth 1 -b OpenSSL_1_1_1v+quic https://github.com/quictls/openssl && \
+RUN git clone --depth 1 -b OpenSSL_1_1_1w+quic https://github.com/quictls/openssl && \
cd openssl && \
./config --openssldir=/etc/ssl && \
make -j$(nproc) && \
@@ -24,7 +24,7 @@ RUN git clone --depth 1 -b v0.15.0 https://github.com/ngtcp2/nghttp3 && \
cd .. && \
rm -rf nghttp3
-RUN git clone --depth 1 -b v0.19.0 https://github.com/ngtcp2/ngtcp2 && \
+RUN git clone --depth 1 -b v0.19.1 https://github.com/ngtcp2/ngtcp2 && \
cd ngtcp2 && \
autoreconf -i && \
./configure --enable-lib-only \
@@ -63,7 +63,7 @@ RUN git clone --depth 1 https://github.com/nghttp2/nghttp2.git && \
cd .. && \
rm -rf nghttp2
-FROM gcr.io/distroless/base-debian11
+FROM gcr.io/distroless/base-debian12
COPY --from=build \
/usr/local/share/nghttp2/ \
diff --git a/go.mod b/go.mod
index f540e19..62582c7 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746
github.com/quic-go/quic-go v0.35.1
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20150408091349-4742878d9c90
- golang.org/x/net v0.10.0
+ golang.org/x/net v0.15.0
)
require (
@@ -17,10 +17,10 @@ require (
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
- golang.org/x/crypto v0.4.0 // indirect
+ golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.8.0 // indirect
- golang.org/x/sys v0.8.0 // indirect
- golang.org/x/text v0.9.0 // indirect
+ golang.org/x/sys v0.12.0 // indirect
+ golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.6.0 // indirect
)
diff --git a/go.sum b/go.sum
index 1d95030..240b266 100644
--- a/go.sum
+++ b/go.sum
@@ -36,8 +36,8 @@ github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20150408091349-4742878d9c90/go.mod h1:Y
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
-golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
+golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -46,8 +46,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -56,13 +56,13 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index fad0471..7adba3a 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -24,6 +24,8 @@ set(NGHTTP2_SOURCES
nghttp2_http.c
nghttp2_rcbuf.c
nghttp2_extpri.c
+ nghttp2_ratelim.c
+ nghttp2_time.c
nghttp2_debug.c
sfparse.c
)
diff --git a/lib/Makefile.am b/lib/Makefile.am
index cd928ae..c3ace40 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -51,6 +51,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_http.c \
nghttp2_rcbuf.c \
nghttp2_extpri.c \
+ nghttp2_ratelim.c \
+ nghttp2_time.c \
nghttp2_debug.c \
sfparse.c
@@ -69,6 +71,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_http.h \
nghttp2_rcbuf.h \
nghttp2_extpri.h \
+ nghttp2_ratelim.h \
+ nghttp2_time.h \
nghttp2_debug.h \
sfparse.h
diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h
index 65077dd..fa22081 100644
--- a/lib/includes/nghttp2/nghttp2.h
+++ b/lib/includes/nghttp2/nghttp2.h
@@ -2759,6 +2759,23 @@ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(
/**
* @function
*
+ * This function sets the rate limit for the incoming stream reset
+ * (RST_STREAM frame). It is server use only. It is a token-bucket
+ * based rate limiter. |burst| specifies the number of tokens that is
+ * initially available. The maximum number of tokens is capped to
+ * this value. |rate| specifies the number of tokens that are
+ * regenerated per second. An incoming RST_STREAM consumes one token.
+ * If there is no token available, GOAWAY is sent to tear down the
+ * connection. |burst| and |rate| default to 1000 and 33
+ * respectively.
+ */
+NGHTTP2_EXTERN void
+nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option,
+ uint64_t burst, uint64_t rate);
+
+/**
+ * @function
+ *
* Initializes |*session_ptr| for client use. The all members of
* |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr|
* does not store |callbacks|. The |user_data| is an arbitrary user
diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c
index ee0cd0f..43d4e95 100644
--- a/lib/nghttp2_option.c
+++ b/lib/nghttp2_option.c
@@ -143,3 +143,10 @@ void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(
NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
option->no_rfc9113_leading_and_trailing_ws_validation = val;
}
+
+void nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option,
+ uint64_t burst, uint64_t rate) {
+ option->opt_set_mask |= NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT;
+ option->stream_reset_burst = burst;
+ option->stream_reset_rate = rate;
+}
diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h
index b228a07..2259e18 100644
--- a/lib/nghttp2_option.h
+++ b/lib/nghttp2_option.h
@@ -70,6 +70,7 @@ typedef enum {
NGHTTP2_OPT_MAX_SETTINGS = 1 << 12,
NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 13,
NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14,
+ NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT = 1 << 15,
} nghttp2_option_flag;
/**
@@ -77,6 +78,11 @@ typedef enum {
*/
struct nghttp2_option {
/**
+ * NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT
+ */
+ uint64_t stream_reset_burst;
+ uint64_t stream_reset_rate;
+ /**
* NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH
*/
size_t max_send_header_block_length;
diff --git a/lib/nghttp2_ratelim.c b/lib/nghttp2_ratelim.c
new file mode 100644
index 0000000..7011655
--- /dev/null
+++ b/lib/nghttp2_ratelim.c
@@ -0,0 +1,75 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2023 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 "nghttp2_ratelim.h"
+#include "nghttp2_helper.h"
+
+void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate) {
+ rl->val = rl->burst = burst;
+ rl->rate = rate;
+ rl->tstamp = 0;
+}
+
+void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp) {
+ uint64_t d, gain;
+
+ if (tstamp == rl->tstamp) {
+ return;
+ }
+
+ if (tstamp > rl->tstamp) {
+ d = tstamp - rl->tstamp;
+ } else {
+ d = 1;
+ }
+
+ rl->tstamp = tstamp;
+
+ if (UINT64_MAX / d < rl->rate) {
+ rl->val = rl->burst;
+
+ return;
+ }
+
+ gain = rl->rate * d;
+
+ if (UINT64_MAX - gain < rl->val) {
+ rl->val = rl->burst;
+
+ return;
+ }
+
+ rl->val += gain;
+ rl->val = nghttp2_min(rl->val, rl->burst);
+}
+
+int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n) {
+ if (rl->val < n) {
+ return -1;
+ }
+
+ rl->val -= n;
+
+ return 0;
+}
diff --git a/lib/nghttp2_ratelim.h b/lib/nghttp2_ratelim.h
new file mode 100644
index 0000000..866ed3f
--- /dev/null
+++ b/lib/nghttp2_ratelim.h
@@ -0,0 +1,57 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2023 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 NGHTTP2_RATELIM_H
+#define NGHTTP2_RATELIM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp2/nghttp2.h>
+
+typedef struct nghttp2_ratelim {
+ /* burst is the maximum value of val. */
+ uint64_t burst;
+ /* rate is the amount of value that is regenerated per 1 tstamp. */
+ uint64_t rate;
+ /* val is the amount of value available to drain. */
+ uint64_t val;
+ /* tstamp is the last timestamp in second resolution that is known
+ to this object. */
+ uint64_t tstamp;
+} nghttp2_ratelim;
+
+/* nghttp2_ratelim_init initializes |rl| with the given parameters. */
+void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate);
+
+/* nghttp2_ratelim_update updates rl->val with the current |tstamp|
+ given in second resolution. */
+void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp);
+
+/* nghttp2_ratelim_drain drains |n| from rl->val. It returns 0 if it
+ succeeds, or -1. */
+int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n);
+
+#endif /* NGHTTP2_RATELIM_H */
diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c
index a45cbee..ec5024d 100644
--- a/lib/nghttp2_session.c
+++ b/lib/nghttp2_session.c
@@ -37,6 +37,7 @@
#include "nghttp2_http.h"
#include "nghttp2_pq.h"
#include "nghttp2_extpri.h"
+#include "nghttp2_time.h"
#include "nghttp2_debug.h"
/*
@@ -475,6 +476,10 @@ static int session_new(nghttp2_session **session_ptr,
(*session_ptr)->pending_enable_push = 1;
(*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX;
+ nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim,
+ NGHTTP2_DEFAULT_STREAM_RESET_BURST,
+ NGHTTP2_DEFAULT_STREAM_RESET_RATE);
+
if (server) {
(*session_ptr)->server = 1;
}
@@ -573,6 +578,12 @@ static int session_new(nghttp2_session **session_ptr,
(*session_ptr)->opt_flags |=
NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
}
+
+ if (option->opt_set_mask & NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT) {
+ nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim,
+ option->stream_reset_burst,
+ option->stream_reset_rate);
+ }
}
rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
@@ -4449,6 +4460,23 @@ static int session_process_priority_frame(nghttp2_session *session) {
return nghttp2_session_on_priority_received(session, frame);
}
+static int session_update_stream_reset_ratelim(nghttp2_session *session) {
+ if (!session->server || (session->goaway_flags & NGHTTP2_GOAWAY_SUBMITTED)) {
+ return 0;
+ }
+
+ nghttp2_ratelim_update(&session->stream_reset_ratelim,
+ nghttp2_time_now_sec());
+
+ if (nghttp2_ratelim_drain(&session->stream_reset_ratelim, 1) == 0) {
+ return 0;
+ }
+
+ return nghttp2_session_add_goaway(session, session->last_recv_stream_id,
+ NGHTTP2_INTERNAL_ERROR, NULL, 0,
+ NGHTTP2_GOAWAY_AUX_NONE);
+}
+
int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
nghttp2_frame *frame) {
int rv;
@@ -4478,7 +4506,8 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
if (nghttp2_is_fatal(rv)) {
return rv;
}
- return 0;
+
+ return session_update_stream_reset_ratelim(session);
}
static int session_process_rst_stream_frame(nghttp2_session *session) {
@@ -7434,6 +7463,9 @@ int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
nghttp2_mem_free(mem, item);
return rv;
}
+
+ session->goaway_flags |= NGHTTP2_GOAWAY_SUBMITTED;
+
return 0;
}
diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h
index 34d2d58..b119329 100644
--- a/lib/nghttp2_session.h
+++ b/lib/nghttp2_session.h
@@ -39,6 +39,7 @@
#include "nghttp2_buf.h"
#include "nghttp2_callbacks.h"
#include "nghttp2_mem.h"
+#include "nghttp2_ratelim.h"
/* The global variable for tests where we want to disable strict
preface handling. */
@@ -105,6 +106,10 @@ typedef struct {
/* The default value of maximum number of concurrent streams. */
#define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu
+/* The default values for stream reset rate limiter. */
+#define NGHTTP2_DEFAULT_STREAM_RESET_BURST 1000
+#define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33
+
/* Internal state when receiving incoming frame */
typedef enum {
/* Receiving frame header */
@@ -178,7 +183,9 @@ typedef enum {
/* Flag means GOAWAY was sent */
NGHTTP2_GOAWAY_SENT = 0x4,
/* Flag means GOAWAY was received */
- NGHTTP2_GOAWAY_RECV = 0x8
+ NGHTTP2_GOAWAY_RECV = 0x8,
+ /* Flag means GOAWAY has been submitted at least once */
+ NGHTTP2_GOAWAY_SUBMITTED = 0x10
} nghttp2_goaway_flag;
/* nghttp2_inflight_settings stores the SETTINGS entries which local
@@ -235,6 +242,9 @@ struct nghttp2_session {
/* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not
considered as in-flight. */
nghttp2_inflight_settings *inflight_settings_head;
+ /* Stream reset rate limiter. If receiving excessive amount of
+ stream resets, GOAWAY will be sent. */
+ nghttp2_ratelim stream_reset_ratelim;
/* Sequential number across all streams to process streams in
FIFO. */
uint64_t stream_seq;
diff --git a/lib/nghttp2_time.c b/lib/nghttp2_time.c
new file mode 100644
index 0000000..2a5f1a6
--- /dev/null
+++ b/lib/nghttp2_time.c
@@ -0,0 +1,62 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2023 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 "nghttp2_time.h"
+
+#ifdef HAVE_TIME_H
+# include <time.h>
+#endif /* HAVE_TIME_H */
+
+#ifdef HAVE_SYSINFOAPI_H
+# include <sysinfoapi.h>
+#endif /* HAVE_SYSINFOAPI_H */
+
+#ifndef HAVE_GETTICKCOUNT64
+static uint64_t time_now_sec(void) {
+ time_t t = time(NULL);
+
+ if (t == -1) {
+ return 0;
+ }
+
+ return (uint64_t)t;
+}
+#endif /* HAVE_GETTICKCOUNT64 */
+
+#ifdef HAVE_CLOCK_GETTIME
+uint64_t nghttp2_time_now_sec(void) {
+ struct timespec tp;
+ int rv = clock_gettime(CLOCK_MONOTONIC, &tp);
+
+ if (rv == -1) {
+ return time_now_sec();
+ }
+
+ return (uint64_t)tp.tv_sec;
+}
+#elif defined(HAVE_GETTICKCOUNT64)
+uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; }
+#else /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */
+uint64_t nghttp2_time_now_sec(void) { return time_now_sec(); }
+#endif /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */
diff --git a/lib/nghttp2_time.h b/lib/nghttp2_time.h
new file mode 100644
index 0000000..03c0bbe
--- /dev/null
+++ b/lib/nghttp2_time.h
@@ -0,0 +1,38 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2023 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 NGHTTP2_TIME_H
+#define NGHTTP2_TIME_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp2/nghttp2.h>
+
+/* nghttp2_time_now_sec returns seconds from implementation-specific
+ timepoint. If it is unable to get seconds, it returns 0. */
+uint64_t nghttp2_time_now_sec(void);
+
+#endif /* NGHTTP2_TIME_H */
diff --git a/src/HttpServer.cc b/src/HttpServer.cc
index dac18a8..34b7444 100644
--- a/src/HttpServer.cc
+++ b/src/HttpServer.cc
@@ -754,37 +754,37 @@ int Http2Handler::read_tls() {
ERR_clear_error();
- for (;;) {
- auto rv = SSL_read(ssl_, buf.data(), buf.size());
-
- if (rv <= 0) {
- auto err = SSL_get_error(ssl_, rv);
- switch (err) {
- case SSL_ERROR_WANT_READ:
- return write_(*this);
- case SSL_ERROR_WANT_WRITE:
- // renegotiation started
- return -1;
- default:
- return -1;
- }
+ auto rv = SSL_read(ssl_, buf.data(), buf.size());
+
+ if (rv <= 0) {
+ auto err = SSL_get_error(ssl_, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ return write_(*this);
+ case SSL_ERROR_WANT_WRITE:
+ // renegotiation started
+ return -1;
+ default:
+ return -1;
}
+ }
- auto nread = rv;
+ auto nread = rv;
- if (get_config()->hexdump) {
- util::hexdump(stdout, buf.data(), nread);
- }
+ if (get_config()->hexdump) {
+ util::hexdump(stdout, buf.data(), nread);
+ }
- rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
- if (rv < 0) {
- if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
- std::cerr << "nghttp2_session_mem_recv() returned error: "
- << nghttp2_strerror(rv) << std::endl;
- }
- return -1;
+ rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
+ if (rv < 0) {
+ if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
+ std::cerr << "nghttp2_session_mem_recv() returned error: "
+ << nghttp2_strerror(rv) << std::endl;
}
+ return -1;
}
+
+ return write_(*this);
}
int Http2Handler::write_tls() {
diff --git a/src/shrpx.cc b/src/shrpx.cc
index b4407f2..d510b7e 100644
--- a/src/shrpx.cc
+++ b/src/shrpx.cc
@@ -1675,10 +1675,10 @@ pid_t fork_worker_process(
}
#endif // ENABLE_HTTP3
- close(worker_process_ready_ipc_fd[0]);
- shutdown_worker_process_ready_ipc_watcher(EV_DEFAULT);
-
if (!config->single_process) {
+ close(worker_process_ready_ipc_fd[0]);
+ shutdown_worker_process_ready_ipc_watcher(EV_DEFAULT);
+
shutdown_signal_watchers(EV_DEFAULT);
}
@@ -3324,15 +3324,17 @@ HTTP:
in HTTP/2 frontend.
--add-request-header=<HEADER>
Specify additional header field to add to request header
- set. This option just appends header field and won't
- replace anything already set. This option can be used
- several times to specify multiple header fields.
+ set. The field name must be lowercase. This option
+ just appends header field and won't replace anything
+ already set. This option can be used several times to
+ specify multiple header fields.
Example: --add-request-header="foo: bar"
--add-response-header=<HEADER>
Specify additional header field to add to response
- header set. This option just appends header field and
- won't replace anything already set. This option can be
- used several times to specify multiple header fields.
+ header set. The field name must be lowercase. This
+ option just appends header field and won't replace
+ anything already set. This option can be used several
+ times to specify multiple header fields.
Example: --add-response-header="foo: bar"
--request-header-field-buffer=<SIZE>
Set maximum buffer size for incoming HTTP request header
diff --git a/src/shrpx_connection.cc b/src/shrpx_connection.cc
index f0a9dc1..273fba3 100644
--- a/src/shrpx_connection.cc
+++ b/src/shrpx_connection.cc
@@ -1019,6 +1019,10 @@ ssize_t Connection::read_tls(void *data, size_t len) {
tls.last_readlen = 0;
}
+ auto &tlsconf = get_config()->tls;
+ auto via_bio =
+ tls.server_handshake && !tlsconf.session_cache.memcached.host.empty();
+
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
if (!tls.early_data_finish) {
// TLSv1.3 handshake is still going on.
@@ -1056,6 +1060,11 @@ ssize_t Connection::read_tls(void *data, size_t len) {
// We may have stopped write watcher in write_tls.
wlimit.startw();
}
+
+ if (!via_bio) {
+ rlimit.drain(nread);
+ }
+
return nread;
}
#endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
@@ -1088,6 +1097,10 @@ ssize_t Connection::read_tls(void *data, size_t len) {
}
}
+ if (!via_bio) {
+ rlimit.drain(rv);
+ }
+
return rv;
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e8a5adb..0e05235 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -22,6 +22,7 @@ if(HAVE_CUNIT)
nghttp2_buf_test.c
nghttp2_http_test.c
nghttp2_extpri_test.c
+ nghttp2_ratelim_test.c
)
add_executable(main EXCLUDE_FROM_ALL
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 162e082..98d72b6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -42,7 +42,8 @@ OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \
nghttp2_helper_test.c \
nghttp2_buf_test.c \
nghttp2_http_test.c \
- nghttp2_extpri_test.c
+ nghttp2_extpri_test.c \
+ nghttp2_ratelim_test.c
HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \
nghttp2_session_test.h \
@@ -51,7 +52,8 @@ HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \
nghttp2_test_helper.h \
nghttp2_buf_test.h \
nghttp2_http_test.h \
- nghttp2_extpri_test.h
+ nghttp2_extpri_test.h \
+ nghttp2_ratelim_test.h
main_SOURCES = $(HFILES) $(OBJECTS)
diff --git a/tests/main.c b/tests/main.c
index 590f059..fc3c28d 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -42,6 +42,7 @@
#include "nghttp2_buf_test.h"
#include "nghttp2_http_test.h"
#include "nghttp2_extpri_test.h"
+#include "nghttp2_ratelim_test.h"
extern int nghttp2_enable_strict_preface;
@@ -343,6 +344,8 @@ int main(void) {
test_nghttp2_session_no_rfc7540_priorities) ||
!CU_add_test(pSuite, "session_server_fallback_rfc7540_priorities",
test_nghttp2_session_server_fallback_rfc7540_priorities) ||
+ !CU_add_test(pSuite, "session_stream_reset_ratelim",
+ test_nghttp2_session_stream_reset_ratelim) ||
!CU_add_test(pSuite, "http_mandatory_headers",
test_nghttp2_http_mandatory_headers) ||
!CU_add_test(pSuite, "http_content_length",
@@ -449,7 +452,9 @@ int main(void) {
!CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc) ||
!CU_add_test(pSuite, "http_parse_priority",
test_nghttp2_http_parse_priority) ||
- !CU_add_test(pSuite, "extpri_to_uint8", test_nghttp2_extpri_to_uint8)) {
+ !CU_add_test(pSuite, "extpri_to_uint8", test_nghttp2_extpri_to_uint8) ||
+ !CU_add_test(pSuite, "ratelim_update", test_nghttp2_ratelim_update) ||
+ !CU_add_test(pSuite, "ratelim_drain", test_nghttp2_ratelim_drain)) {
CU_cleanup_registry();
return (int)CU_get_error();
}
diff --git a/tests/nghttp2_ratelim_test.c b/tests/nghttp2_ratelim_test.c
new file mode 100644
index 0000000..6abece9
--- /dev/null
+++ b/tests/nghttp2_ratelim_test.c
@@ -0,0 +1,101 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2023 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 "nghttp2_ratelim_test.h"
+
+#include <stdio.h>
+
+#include <CUnit/CUnit.h>
+
+#include "nghttp2_ratelim.h"
+
+void test_nghttp2_ratelim_update(void) {
+ nghttp2_ratelim rl;
+
+ nghttp2_ratelim_init(&rl, 1000, 21);
+
+ CU_ASSERT(1000 == rl.val);
+ CU_ASSERT(1000 == rl.burst);
+ CU_ASSERT(21 == rl.rate);
+ CU_ASSERT(0 == rl.tstamp);
+
+ nghttp2_ratelim_update(&rl, 999);
+
+ CU_ASSERT(1000 == rl.val);
+ CU_ASSERT(999 == rl.tstamp);
+
+ nghttp2_ratelim_drain(&rl, 100);
+
+ CU_ASSERT(900 == rl.val);
+
+ nghttp2_ratelim_update(&rl, 1000);
+
+ CU_ASSERT(921 == rl.val);
+
+ nghttp2_ratelim_update(&rl, 1002);
+
+ CU_ASSERT(963 == rl.val);
+
+ nghttp2_ratelim_update(&rl, 1004);
+
+ CU_ASSERT(1000 == rl.val);
+ CU_ASSERT(1004 == rl.tstamp);
+
+ /* timer skew */
+ nghttp2_ratelim_init(&rl, 1000, 21);
+ nghttp2_ratelim_update(&rl, 1);
+
+ CU_ASSERT(1000 == rl.val);
+
+ nghttp2_ratelim_update(&rl, 0);
+
+ CU_ASSERT(1000 == rl.val);
+
+ /* rate * duration overflow */
+ nghttp2_ratelim_init(&rl, 1000, 100);
+ nghttp2_ratelim_drain(&rl, 999);
+
+ CU_ASSERT(1 == rl.val);
+
+ nghttp2_ratelim_update(&rl, UINT64_MAX);
+
+ CU_ASSERT(1000 == rl.val);
+
+ /* val + rate * duration overflow */
+ nghttp2_ratelim_init(&rl, UINT64_MAX - 1, 2);
+ nghttp2_ratelim_update(&rl, 1);
+
+ CU_ASSERT(UINT64_MAX - 1 == rl.val);
+}
+
+void test_nghttp2_ratelim_drain(void) {
+ nghttp2_ratelim rl;
+
+ nghttp2_ratelim_init(&rl, 100, 7);
+
+ CU_ASSERT(-1 == nghttp2_ratelim_drain(&rl, 101));
+ CU_ASSERT(0 == nghttp2_ratelim_drain(&rl, 51));
+ CU_ASSERT(0 == nghttp2_ratelim_drain(&rl, 49));
+ CU_ASSERT(-1 == nghttp2_ratelim_drain(&rl, 1));
+}
diff --git a/tests/nghttp2_ratelim_test.h b/tests/nghttp2_ratelim_test.h
new file mode 100644
index 0000000..02b2f2b
--- /dev/null
+++ b/tests/nghttp2_ratelim_test.h
@@ -0,0 +1,35 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2023 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 NGHTTP2_RATELIM_TEST_H
+#define NGHTTP2_RATELIM_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_nghttp2_ratelim_update(void);
+void test_nghttp2_ratelim_drain(void);
+
+#endif /* NGHTTP2_RATELIM_TEST_H */
diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c
index af4e224..b1043be 100644
--- a/tests/nghttp2_session_test.c
+++ b/tests/nghttp2_session_test.c
@@ -11968,6 +11968,109 @@ void test_nghttp2_session_server_fallback_rfc7540_priorities(void) {
nghttp2_bufs_free(&bufs);
}
+void test_nghttp2_session_stream_reset_ratelim(void) {
+ nghttp2_session *session;
+ nghttp2_session_callbacks callbacks;
+ nghttp2_frame frame;
+ ssize_t rv;
+ nghttp2_bufs bufs;
+ nghttp2_buf *buf;
+ nghttp2_mem *mem;
+ size_t i;
+ nghttp2_hd_deflater deflater;
+ size_t nvlen;
+ nghttp2_nv *nva;
+ int32_t stream_id;
+ nghttp2_outbound_item *item;
+ nghttp2_option *option;
+
+ mem = nghttp2_mem_default();
+ frame_pack_bufs_init(&bufs);
+
+ memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
+ callbacks.send_callback = null_send_callback;
+
+ nghttp2_option_new(&option);
+ nghttp2_option_set_stream_reset_rate_limit(
+ option, NGHTTP2_DEFAULT_STREAM_RESET_BURST, 0);
+
+ nghttp2_session_server_new2(&session, &callbacks, NULL, option);
+
+ nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, NULL, 0);
+ rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp2_frame_settings_free(&frame.settings, mem);
+
+ buf = &bufs.head->buf;
+ rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
+
+ CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
+
+ /* Send SETTINGS ACK */
+ rv = nghttp2_session_send(session);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp2_hd_deflate_init(&deflater, mem);
+
+ for (i = 0; i < NGHTTP2_DEFAULT_STREAM_RESET_BURST + 2; ++i) {
+ stream_id = (int32_t)(i * 2 + 1);
+
+ nghttp2_bufs_reset(&bufs);
+
+ /* HEADERS */
+ nvlen = ARRLEN(reqnv);
+ nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
+ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
+ stream_id, NGHTTP2_HCAT_HEADERS, NULL, nva,
+ nvlen);
+ rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp2_frame_headers_free(&frame.headers, mem);
+
+ buf = &bufs.head->buf;
+ rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
+
+ CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
+
+ nghttp2_bufs_reset(&bufs);
+
+ /* RST_STREAM */
+ nghttp2_frame_rst_stream_init(&frame.rst_stream, stream_id,
+ NGHTTP2_NO_ERROR);
+ nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream);
+ nghttp2_frame_rst_stream_free(&frame.rst_stream);
+
+ buf = &bufs.head->buf;
+ rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
+
+ CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
+
+ if (i < NGHTTP2_DEFAULT_STREAM_RESET_BURST) {
+ CU_ASSERT(0 == nghttp2_outbound_queue_size(&session->ob_reg));
+
+ continue;
+ }
+
+ CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_reg));
+
+ item = nghttp2_session_get_next_ob_item(session);
+
+ CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
+ CU_ASSERT(NGHTTP2_DEFAULT_STREAM_RESET_BURST * 2 + 1 ==
+ item->frame.goaway.last_stream_id);
+ }
+
+ nghttp2_hd_deflate_free(&deflater);
+ nghttp2_session_del(session);
+ nghttp2_bufs_free(&bufs);
+ nghttp2_option_del(option);
+}
+
static void check_nghttp2_http_recv_headers_fail(
nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
int stream_state, const nghttp2_nv *nva, size_t nvlen) {
diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h
index e776813..be4fdf8 100644
--- a/tests/nghttp2_session_test.h
+++ b/tests/nghttp2_session_test.h
@@ -168,6 +168,7 @@ void test_nghttp2_session_no_closed_streams(void);
void test_nghttp2_session_set_stream_user_data(void);
void test_nghttp2_session_no_rfc7540_priorities(void);
void test_nghttp2_session_server_fallback_rfc7540_priorities(void);
+void test_nghttp2_session_stream_reset_ratelim(void);
void test_nghttp2_http_mandatory_headers(void);
void test_nghttp2_http_content_length(void);
void test_nghttp2_http_content_length_mismatch(void);