Compare commits

..

69 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
a166099ff4 Replace the deprecated std::result_of with std::invoke_result 2023-10-14 23:07:56 +09:00
Tatsuhiro Tsujikawa
bd92902560 Merge pull request #1967 from vszakats/cmake-fast-warnopt-detection
cmake: speed up warning option detection
2023-10-14 22:40:46 +09:00
Viktor Szakats
10a77ce888 cmake: display WARNCXXFLAGS flags in summary 2023-10-14 10:27:17 +00:00
Viktor Szakats
81c199dd9b cmake: speed up warning option detection 2023-10-14 10:27:17 +00:00
Tatsuhiro Tsujikawa
3b82572b9e Merge pull request #1965 from nghttp2/dependabot/go_modules/golang.org/x/net-0.17.0
Bump golang.org/x/net from 0.15.0 to 0.17.0
2023-10-14 12:14:08 +09:00
dependabot[bot]
9d85ab415f Bump golang.org/x/net from 0.15.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-11 23:10:59 +00:00
Tatsuhiro Tsujikawa
d0358c0063 Merge pull request #1964 from nghttp2/nghttpx-h3-reset-stream
nghttpx: Shutdown h3 stream write if reset by a remote endpoint
2023-10-11 19:48:29 +09:00
Tatsuhiro Tsujikawa
e103726f4d nghttpx: Shutdown h3 stream write if reset by a remote endpoint 2023-10-11 19:13:04 +09:00
Tatsuhiro Tsujikawa
4262c90148 Merge pull request #1963 from nghttp2/fix-build-error
Fix build error when both clock_gettime and GetTickCount64 are available
2023-10-11 17:49:46 +09:00
Tatsuhiro Tsujikawa
bf8f419ca9 Fix build error when both clock_gettime and GetTickCount64 are available 2023-10-11 17:19:05 +09:00
Tatsuhiro Tsujikawa
cccb6ad2c9 Bump package version 2023-10-10 23:10:21 +09:00
Tatsuhiro Tsujikawa
6f41cc7271 Update manual pages 2023-10-10 22:57:01 +09:00
Tatsuhiro Tsujikawa
9b781ca734 Bump package and library versions 2023-10-10 22:57:01 +09:00
Tatsuhiro Tsujikawa
aa0973caf5 Merge pull request #1961 from nghttp2/rework-session-mgmt
Rework session management
2023-10-10 22:55:23 +09:00
Tatsuhiro Tsujikawa
72b4af6143 Rework session management 2023-10-10 22:41:58 +09:00
Tatsuhiro Tsujikawa
d40829b133 Merge pull request #1959 from nghttp2/fix-clang-format
Fix clang-format
2023-10-05 21:37:07 +09:00
Tatsuhiro Tsujikawa
939506b68b Fix clang-format 2023-10-05 20:54:00 +09:00
Tatsuhiro Tsujikawa
e62ef924fb Merge pull request #1958 from nghttp2/nghttpx-fix
nghttpx: Fix bug that --single-process does not work
2023-10-01 15:39:23 +09:00
Tatsuhiro Tsujikawa
8bad736d48 nghttpx: Fix bug that --single-process does not work 2023-10-01 15:18:39 +09:00
Tatsuhiro Tsujikawa
e761014ca5 Merge pull request #1957 from nghttp2/apps-fix
Apps fix
2023-10-01 12:30:44 +09:00
Tatsuhiro Tsujikawa
e6ca4ff173 nghttpd: Do not read too greedy for TLS connection 2023-10-01 11:59:53 +09:00
Tatsuhiro Tsujikawa
62d58bdabc nghttpx: Fix the bug that TLS connection is not rate limited 2023-09-30 22:53:31 +09:00
Tatsuhiro Tsujikawa
e37fe5543f Merge pull request #1945 from nghttp2/bump-quictls
Bump quictls
2023-09-23 16:25:54 +09:00
Tatsuhiro Tsujikawa
d54750b365 Merge pull request #1953 from nghttp2/nghttpx-field-name-lowercase
nghttpx: Header field name must be lowercase
2023-09-22 11:02:01 +09:00
Tatsuhiro Tsujikawa
8ebe7b10fc nghttpx: Header field name must be lowercase 2023-09-22 10:09:54 +09:00
Tatsuhiro Tsujikawa
5c1b0f261e Merge pull request #1951 from nghttp2/docker-debian12
docker: Bump base image to debian 12
2023-09-15 21:41:10 +09:00
Tatsuhiro Tsujikawa
0866ceffc7 Merge pull request #1947 from nghttp2/dependabot/github_actions/actions/upload-artifact-3
Bump actions/upload-artifact from 1 to 3
2023-09-15 20:41:41 +09:00
Tatsuhiro Tsujikawa
3c6f94258f Merge pull request #1949 from nghttp2/dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2023-09-15 19:36:46 +09:00
Tatsuhiro Tsujikawa
959a55f95f Merge pull request #1948 from nghttp2/dependabot/github_actions/actions/setup-go-4
Bump actions/setup-go from 3 to 4
2023-09-15 19:12:09 +09:00
dependabot[bot]
7d0d8775c9 Bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-15 09:50:01 +00:00
Tatsuhiro Tsujikawa
52cb8c35c8 Merge pull request #1950 from nghttp2/bump-golang.org/x/net
Bump golang.org/x/net to v0.15.0
2023-09-15 18:48:49 +09:00
Tatsuhiro Tsujikawa
d19bc4bb67 Bump golang.org/x/net to v0.15.0 2023-09-15 18:28:01 +09:00
Tatsuhiro Tsujikawa
1ee16fdeca docker: Bump base image to debian 12 2023-09-15 18:13:39 +09:00
dependabot[bot]
b809beacab Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-15 08:55:19 +00:00
dependabot[bot]
8cafb048be Bump actions/upload-artifact from 1 to 3
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 1 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v1...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-15 08:55:00 +00:00
Tatsuhiro Tsujikawa
627b2408e5 Merge pull request #1946 from nghttp2/dependabot-actions
Add dependabot to update actions
2023-09-15 17:44:06 +09:00
Tatsuhiro Tsujikawa
0d36a2a050 Add dependabot to update actions 2023-09-15 17:13:09 +09:00
Tatsuhiro Tsujikawa
06da18089f Bump quictls 2023-09-12 18:52:06 +09:00
Tatsuhiro Tsujikawa
19697bd44a Merge pull request #1944 from nghttp2/bump-ngtcp2
Bump ngtcp2
2023-09-12 18:50:02 +09:00
Tatsuhiro Tsujikawa
2e6c8ddd15 Bump ngtcp2 2023-09-06 23:23:57 +09:00
Tatsuhiro Tsujikawa
b7f9853968 Bump package version 2023-09-04 18:32:43 +09:00
Tatsuhiro Tsujikawa
80d05fe917 Merge pull request #1943 from nghttp2/update-dockerfile
Update Dockerfile
2023-09-04 18:27:04 +09:00
Tatsuhiro Tsujikawa
08c472230a Update Dockerfile 2023-09-04 18:11:18 +09:00
Tatsuhiro Tsujikawa
47ecf35737 Update manual pages 2023-09-04 18:04:09 +09:00
Tatsuhiro Tsujikawa
b48355b67e Bump package and library versions 2023-09-03 19:45:03 +09:00
Tatsuhiro Tsujikawa
0f768bdaab Merge pull request #1942 from nghttp2/bump-libbpf
Bump libbpf to v1.2.2
2023-09-03 19:44:37 +09:00
Tatsuhiro Tsujikawa
05f93ca729 Bump libbpf to v1.2.2 2023-09-03 19:26:17 +09:00
Tatsuhiro Tsujikawa
d70f0013d6 Merge pull request #1941 from nghttp2/bump-llhttp
Bump llhttp to 926c982942eb53a13f01c1e9e6b19bd3b196e7dd
2023-09-03 19:25:50 +09:00
Tatsuhiro Tsujikawa
d5cb882e62 Bump llhttp to 926c982942eb53a13f01c1e9e6b19bd3b196e7dd 2023-09-03 19:06:33 +09:00
Tatsuhiro Tsujikawa
2a382a2ce9 Merge pull request #1940 from nghttp2/simplify-unique_ptr-release
nghttpx: Simplify std::unique_ptr get and release
2023-09-03 18:29:37 +09:00
Tatsuhiro Tsujikawa
c13b08f861 nghttpx: Simplify std::unique_ptr get and release 2023-09-03 18:11:53 +09:00
Tatsuhiro Tsujikawa
298f71ce4e Merge pull request #1939 from nghttp2/bump-ngtcp2
Bump ngtcp2 and its dependencies
2023-09-03 15:02:54 +09:00
Tatsuhiro Tsujikawa
4114507587 Bump ngtcp2 and its dependencies 2023-09-03 13:17:39 +09:00
Tatsuhiro Tsujikawa
060d0ac455 Merge pull request #1937 from nghttp2/bump-quictls
Bump quictls
2023-08-05 19:35:12 +09:00
Tatsuhiro Tsujikawa
9a3c345f60 Bump quictls 2023-08-05 18:58:27 +09:00
Tatsuhiro Tsujikawa
98eb01aa3f Merge pull request #1935 from nghttp2/cmsg-data
CMSG_DATA does not necessarily return an aligned pointer
2023-08-02 21:06:45 +09:00
Tatsuhiro Tsujikawa
de47400a48 CMSG_DATA does not necessarily return an aligned pointer 2023-08-02 18:46:39 +09:00
Tatsuhiro Tsujikawa
871cf8e5a2 Merge pull request #1934 from nghttp2/ecn
nghttpx: Rework sending and receiving ECN bits
2023-08-02 18:46:07 +09:00
Tatsuhiro Tsujikawa
6ace578e5b nghttpx: Rework sending and receiving ECN bits
Now ECN is sent along with sendmsg ancillary message.  With this
commit, ECN should work fine with the recent Mac OS.
2023-08-02 17:47:51 +09:00
Tatsuhiro Tsujikawa
7273ec17d3 Merge pull request #1931 from nghttp2/return-void
Return void
2023-07-16 16:15:49 +09:00
Tatsuhiro Tsujikawa
07b69bb06f Make session_after_frame_sent2 return void 2023-07-15 16:27:38 +09:00
Tatsuhiro Tsujikawa
98df5b59e5 frame: Make functions that always succeed return void 2023-07-15 16:15:25 +09:00
Tatsuhiro Tsujikawa
cdfb517528 Make functions that always succeed return void 2023-07-15 15:36:50 +09:00
Tatsuhiro Tsujikawa
b16d4e951e Bump library version due to the patch release 2023-07-14 22:53:52 +09:00
Tatsuhiro Tsujikawa
feb5ff560a Merge pull request #1930 from nghttp2/fix-memory-leak
Fix memory leak
2023-07-14 21:56:30 +09:00
Tatsuhiro Tsujikawa
ce385d3f55 Fix memory leak
This commit fixes memory leak that happens when PUSH_PROMISE or
HEADERS frame cannot be sent, and nghttp2_on_stream_close_callback
fails with a fatal error.  For example, if GOAWAY frame has been
received, a HEADERS frame that opens new stream cannot be sent.

This issue has already been made public via CVE-2023-35945 [1] issued
by envoyproxy/envoy project.  During embargo period, the patch to fix
this bug was accidentally submitted to nghttp2/nghttp2 repository [2].
And they decided to disclose CVE early.  I was notified just 1.5 hours
before disclosure.  I had no time to respond.

PoC described in [1] is quite simple, but I think it is not enough to
trigger this bug.  While it is true that receiving GOAWAY prevents a
client from opening new stream, and nghttp2 enters error handling
branch, in order to cause the memory leak,
nghttp2_session_close_stream function must return a fatal error.
nghttp2 defines 2 fatal error codes:

- NGHTTP2_ERR_NOMEM
- NGHTTP2_ERR_CALLBACK_FAILURE

NGHTTP2_ERR_NOMEM, as its name suggests, indicates out of memory.  It
is unlikely that a process gets short of memory with this simple PoC
scenario unless application does something memory heavy processing.

NGHTTP2_ERR_CALLBACK_FAILURE is returned from application defined
callback function (nghttp2_on_stream_close_callback, in this case),
which indicates something fatal happened inside a callback, and a
connection must be closed immediately without any further action.  As
nghttp2_on_stream_close_error_callback documentation says, any error
code other than 0 or NGHTTP2_ERR_CALLBACK_FAILURE is treated as fatal
error code.  More specifically, it is treated as if
NGHTTP2_ERR_CALLBACK_FAILURE is returned.  I guess that envoy returns
NGHTTP2_ERR_CALLBACK_FAILURE or other error code which is translated
into NGHTTP2_ERR_CALLBACK_FAILURE.

[1] https://github.com/envoyproxy/envoy/security/advisories/GHSA-jfxv-29pc-x22r
[2] https://github.com/nghttp2/nghttp2/pull/1929
2023-07-14 21:37:37 +09:00
Tatsuhiro Tsujikawa
82fc1163c6 Merge pull request #1928 from nghttp2/doc-bump-boringssl
doc: Bump boringssl
2023-07-12 18:06:28 +09:00
Tatsuhiro Tsujikawa
79d2b7f931 doc: Bump boringssl 2023-07-12 17:44:26 +09:00
Tatsuhiro Tsujikawa
4c1f1b0efc Bump package version 2023-07-12 17:25:14 +09:00
61 changed files with 1766 additions and 10047 deletions

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@@ -5,12 +5,12 @@ on: [push, pull_request]
permissions: read-all
env:
LIBBPF_VERSION: v1.2.0
OPENSSL1_VERSION: 1_1_1u+quic
OPENSSL3_VERSION: 3.1.0+quic
BORINGSSL_VERSION: 80dcb67d4481fb1194b9669917e35580c32dc388
NGHTTP3_VERSION: v0.13.0
NGTCP2_VERSION: v0.17.0
LIBBPF_VERSION: v1.2.2
OPENSSL1_VERSION: 1_1_1w+quic
OPENSSL3_VERSION: 3.1.2+quic
BORINGSSL_VERSION: 6ca49385b168f47a50e7172d82a590b218f55e4d
NGHTTP3_VERSION: v0.15.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

View File

@@ -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

View File

@@ -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.55.0)
project(nghttp2 VERSION 1.57.90)
# See versioning rule:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
set(LT_CURRENT 38)
set(LT_REVISION 2)
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)
@@ -273,9 +273,11 @@ check_include_file("inttypes.h" HAVE_INTTYPES_H)
check_include_file("limits.h" HAVE_LIMITS_H)
check_include_file("netdb.h" HAVE_NETDB_H)
check_include_file("netinet/in.h" HAVE_NETINET_IN_H)
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)
@@ -316,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)
@@ -338,74 +343,12 @@ if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
endif()
else()
if(ENABLE_WERROR)
extract_valid_c_flags(WARNCFLAGS -Werror)
extract_valid_c_flags(WARNCXXFLAGS -Werror)
set(WARNCFLAGS "-Werror")
set(WARNCXXFLAGS "-Werror")
endif()
# For C compiler
extract_valid_c_flags(WARNCFLAGS
-Wall
-Wextra
-Wmissing-prototypes
-Wstrict-prototypes
-Wmissing-declarations
-Wpointer-arith
-Wdeclaration-after-statement
-Wformat-security
-Wwrite-strings
-Wshadow
-Winline
-Wnested-externs
-Wfloat-equal
-Wundef
-Wendif-labels
-Wempty-body
-Wcast-align
-Wclobbered
-Wvla
-Wpragmas
-Wunreachable-code
-Waddress
-Wattributes
-Wdiv-by-zero
-Wshorten-64-to-32
-Wconversion
-Wextended-offsetof
-Wformat-nonliteral
-Wlanguage-extension-token
-Wmissing-field-initializers
-Wmissing-noreturn
-Wmissing-variable-declarations
# Not used because we cannot change public structs
# -Wpadded
-Wsign-conversion
# Not used because this basically disallows default case
# -Wswitch-enum
-Wunreachable-code-break
-Wunused-macros
-Wunused-parameter
-Wredundant-decls
# Only work with Clang for the moment
-Wheader-guard
# This is required because we pass format string as "const char*.
-Wno-format-nonliteral
)
extract_valid_cxx_flags(WARNCXXFLAGS
# For C++ compiler
-Wall
-Wformat-security
)
endif()
if(ENABLE_STATIC_CRT)
foreach(lang C CXX)
foreach(suffix "" _DEBUG _MINSIZEREL _RELEASE _RELWITHDEBINFO)
set(var "CMAKE_${lang}_FLAGS${suffix}")
string(REPLACE "/MD" "/MT" ${var} "${${var}}")
endforeach()
endforeach()
include(PickyWarningsC)
include(PickyWarningsCXX)
endif()
if(ENABLE_DEBUG)
@@ -501,6 +444,7 @@ message(STATUS "summary of build options:
CXXFLAGS: ${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS}
WARNCFLAGS: ${WARNCFLAGS}
CXX1XCXXFLAGS: ${CXX1XCXXFLAGS}
WARNCXXFLAGS: ${WARNCXXFLAGS}
Python:
Python: ${Python3_EXECUTABLE}
Python3_VERSION: ${Python3_VERSION}

View File

@@ -44,7 +44,9 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-env \
cmake/FindLibbpf.cmake \
cmake/FindLibnghttp3.cmake \
cmake/FindLibngtcp2.cmake \
cmake/FindLibngtcp2_crypto_quictls.cmake
cmake/FindLibngtcp2_crypto_quictls.cmake \
cmake/PickyWarningsC.cmake \
cmake/PickyWarningsCXX.cmake
.PHONY: clang-format
@@ -55,5 +57,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

View File

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

163
cmake/PickyWarningsC.cmake Normal file
View File

@@ -0,0 +1,163 @@
# nghttp2
#
# 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.
# C
include(CheckCCompilerFlag)
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang")
# https://clang.llvm.org/docs/DiagnosticsReference.html
# https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
# WPICKY_ENABLE = Options we want to enable as-is.
# WPICKY_DETECT = Options we want to test first and enable if available.
# Prefer the -Wextra alias with clang.
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(WPICKY_ENABLE "-Wextra")
else()
set(WPICKY_ENABLE "-W")
endif()
list(APPEND WPICKY_ENABLE
-Wall
)
# ----------------------------------
# Add new options here, if in doubt:
# ----------------------------------
set(WPICKY_DETECT
)
# Assume these options always exist with both clang and gcc.
# Require clang 3.0 / gcc 2.95 or later.
list(APPEND WPICKY_ENABLE
-Wconversion # clang 3.0 gcc 2.95
-Winline # clang 1.0 gcc 1.0
-Wmissing-declarations # clang 1.0 gcc 2.7
-Wmissing-prototypes # clang 1.0 gcc 1.0
-Wnested-externs # clang 1.0 gcc 2.7
-Wpointer-arith # clang 1.0 gcc 1.4
-Wshadow # clang 1.0 gcc 2.95
-Wundef # clang 1.0 gcc 2.95
-Wwrite-strings # clang 1.0 gcc 1.4
)
# Always enable with clang, version dependent with gcc
set(WPICKY_COMMON_OLD
-Waddress # clang 3.0 gcc 4.3
-Wattributes # clang 3.0 gcc 4.1
-Wcast-align # clang 1.0 gcc 4.2
-Wdeclaration-after-statement # clang 1.0 gcc 3.4
-Wdiv-by-zero # clang 3.0 gcc 4.1
-Wempty-body # clang 3.0 gcc 4.3
-Wendif-labels # clang 1.0 gcc 3.3
-Wfloat-equal # clang 1.0 gcc 2.96 (3.0)
-Wformat-nonliteral # clang 3.0 gcc 4.1
-Wformat-security # clang 3.0 gcc 4.1
-Wmissing-field-initializers # clang 3.0 gcc 4.1
-Wmissing-noreturn # clang 3.0 gcc 4.1
-Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) # This is required because we pass format string as "const char*"
# -Wpadded # clang 3.0 gcc 4.1 # Not used because we cannot change public structs
-Wredundant-decls # clang 3.0 gcc 4.1
-Wsign-conversion # clang 3.0 gcc 4.3
-Wstrict-prototypes # clang 1.0 gcc 3.3
# -Wswitch-enum # clang 3.0 gcc 4.1 # Not used because this basically disallows default case
-Wunreachable-code # clang 3.0 gcc 4.1
-Wunused-macros # clang 3.0 gcc 4.1
-Wunused-parameter # clang 3.0 gcc 4.1
-Wvla # clang 2.8 gcc 4.3
)
set(WPICKY_COMMON
-Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0
)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
list(APPEND WPICKY_ENABLE
${WPICKY_COMMON_OLD}
-Wshorten-64-to-32 # clang 1.0
-Wlanguage-extension-token # clang 3.0
)
# Enable based on compiler version
if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR
(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3))
list(APPEND WPICKY_ENABLE
${WPICKY_COMMON}
-Wunreachable-code-break # clang 3.5 appleclang 6.0
-Wheader-guard # clang 3.4 appleclang 5.1
)
endif()
if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR
(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.3))
list(APPEND WPICKY_ENABLE
-Wmissing-variable-declarations # clang 3.2 appleclang 4.6
)
endif()
if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0) OR
(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.4))
list(APPEND WPICKY_ENABLE
)
endif()
if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) OR
(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.3))
list(APPEND WPICKY_ENABLE
)
endif()
else() # gcc
list(APPEND WPICKY_DETECT
${WPICKY_COMMON}
)
# Enable based on compiler version
if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3)
list(APPEND WPICKY_ENABLE
${WPICKY_COMMON_OLD}
-Wclobbered # gcc 4.3
)
endif()
endif()
#
unset(_wpicky)
foreach(_CCOPT IN LISTS WPICKY_ENABLE)
set(_wpicky "${_wpicky} ${_CCOPT}")
endforeach()
foreach(_CCOPT IN LISTS WPICKY_DETECT)
# surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new
# test result in.
string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname)
# GCC only warns about unknown -Wno- options if there are also other diagnostic messages,
# so test for the positive form instead
string(REPLACE "-Wno-" "-W" _CCOPT_ON "${_CCOPT}")
check_c_compiler_flag(${_CCOPT_ON} ${_optvarname})
if(${_optvarname})
set(_wpicky "${_wpicky} ${_CCOPT}")
endif()
endforeach()
set(WARNCFLAGS "${WARNCFLAGS} ${_wpicky}")
endif()

View File

@@ -0,0 +1,117 @@
# nghttp2
#
# 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.
# C++
include(CheckCXXCompilerFlag)
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# https://clang.llvm.org/docs/DiagnosticsReference.html
# https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
# WPICKY_ENABLE = Options we want to enable as-is.
# WPICKY_DETECT = Options we want to test first and enable if available.
set(WPICKY_ENABLE "-Wall")
# ----------------------------------
# Add new options here, if in doubt:
# ----------------------------------
set(WPICKY_DETECT
)
# Assume these options always exist with both clang and gcc.
# Require clang 3.0 / gcc 2.95 or later.
list(APPEND WPICKY_ENABLE
)
# Always enable with clang, version dependent with gcc
set(WPICKY_COMMON_OLD
-Wformat-security # clang 3.0 gcc 4.1
)
set(WPICKY_COMMON
)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND WPICKY_ENABLE
${WPICKY_COMMON_OLD}
)
# Enable based on compiler version
if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.6) OR
(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.3))
list(APPEND WPICKY_ENABLE
${WPICKY_COMMON}
)
endif()
if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.9) OR
(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.3))
list(APPEND WPICKY_ENABLE
)
endif()
if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) OR
(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.4))
list(APPEND WPICKY_ENABLE
)
endif()
if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) OR
(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.3))
list(APPEND WPICKY_ENABLE
)
endif()
else() # gcc
list(APPEND WPICKY_DETECT
${WPICKY_COMMON}
)
# Enable based on compiler version
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.3)
list(APPEND WPICKY_ENABLE
${WPICKY_COMMON_OLD}
)
endif()
endif()
#
unset(_wpicky)
foreach(_CCOPT IN LISTS WPICKY_ENABLE)
set(_wpicky "${_wpicky} ${_CCOPT}")
endforeach()
foreach(_CCOPT IN LISTS WPICKY_DETECT)
# surprisingly, CHECK_CXX_COMPILER_FLAG needs a new variable to store each new
# test result in.
string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname)
# GCC only warns about unknown -Wno- options if there are also other diagnostic messages,
# so test for the positive form instead
string(REPLACE "-Wno-" "-W" _CCOPT_ON "${_CCOPT}")
check_cxx_compiler_flag(${_CCOPT_ON} ${_optvarname})
if(${_optvarname})
set(_wpicky "${_wpicky} ${_CCOPT}")
endif()
endforeach()
set(WARNCXXFLAGS "${WARNCXXFLAGS} ${_wpicky}")
endif()

View File

@@ -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
@@ -61,6 +67,9 @@
/* Define to 1 if you have the <netinet/in.h> header file. */
#cmakedefine HAVE_NETINET_IN_H 1
/* Define to 1 if you have the <netinet/ip.h> header file. */
#cmakedefine HAVE_NETINET_IP_H 1
/* Define to 1 if you have the <pwd.h> header file. */
#cmakedefine HAVE_PWD_H 1
@@ -70,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

View File

@@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.55.0], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.58.0-DEV], [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, 2)
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"`
@@ -508,7 +508,7 @@ fi
# ngtcp2 (for src)
have_libngtcp2=no
if test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.17.0], [have_libngtcp2=yes],
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.19.0], [have_libngtcp2=yes],
[have_libngtcp2=no])
if test "x${have_libngtcp2}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS)
@@ -525,7 +525,7 @@ have_libngtcp2_crypto_quictls=no
if test "x${have_ssl_is_quic}" = "xyes" &&
test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_QUICTLS],
[libngtcp2_crypto_quictls >= 0.17.0],
[libngtcp2_crypto_quictls >= 0.19.0],
[have_libngtcp2_crypto_quictls=yes],
[have_libngtcp2_crypto_quictls=no])
if test "x${have_libngtcp2_crypto_quictls}" = "xno"; then
@@ -567,7 +567,7 @@ fi
# nghttp3 (for src)
have_libnghttp3=no
if test "x${request_libnghttp3}" != "xno"; then
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.13.0], [have_libnghttp3=yes],
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.15.0], [have_libnghttp3=yes],
[have_libnghttp3=no])
if test "x${have_libnghttp3}" = "xno"; then
AC_MSG_NOTICE($LIBNGHTTP3_PKG_ERRORS)
@@ -847,6 +847,7 @@ AC_CHECK_HEADERS([ \
limits.h \
netdb.h \
netinet/in.h \
netinet/ip.h \
pwd.h \
stddef.h \
stdint.h \
@@ -854,6 +855,7 @@ AC_CHECK_HEADERS([ \
string.h \
sys/socket.h \
sys/time.h \
sysinfoapi.h \
syslog.h \
time.h \
unistd.h \
@@ -928,6 +930,7 @@ AC_FUNC_STRNLEN
AC_CHECK_FUNCS([ \
_Exit \
accept4 \
clock_gettime \
dup2 \
getcwd \
getpwnam \
@@ -953,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.

View File

@@ -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 \

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "H2LOAD" "1" "Jul 12, 2023" "1.55.0" "nghttp2"
.TH "H2LOAD" "1" "Oct 10, 2023" "1.57.0" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTP" "1" "Jul 12, 2023" "1.55.0" "nghttp2"
.TH "NGHTTP" "1" "Oct 10, 2023" "1.57.0" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.SH SYNOPSIS

View File

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

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTPX" "1" "Jul 12, 2023" "1.55.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
@@ -1833,7 +1835,7 @@ NEW_TOKEN frame in the previous connection.
.B \-\-frontend\-quic\-congestion\-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be one of \(dqcubic\(dq, \(dqbbr\(dq,
and \(dqbbr2\(dq.
and \(dqbbrv2\(dq.
.sp
Default: \fBcubic\fP
.UNINDENT

View File

@@ -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>
@@ -1674,7 +1676,7 @@ HTTP/3 and QUIC
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be one of "cubic", "bbr",
and "bbr2".
and "bbrv2".
Default: ``cubic``

View File

@@ -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_1u+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) && \
@@ -15,7 +15,7 @@ RUN git clone --depth 1 -b OpenSSL_1_1_1u+quic https://github.com/quictls/openss
cd .. && \
rm -rf openssl
RUN git clone --depth 1 -b v0.13.0 https://github.com/ngtcp2/nghttp3 && \
RUN git clone --depth 1 -b v0.15.0 https://github.com/ngtcp2/nghttp3 && \
cd nghttp3 && \
autoreconf -i && \
./configure --enable-lib-only && \
@@ -24,7 +24,7 @@ RUN git clone --depth 1 -b v0.13.0 https://github.com/ngtcp2/nghttp3 && \
cd .. && \
rm -rf nghttp3
RUN git clone --depth 1 -b v0.17.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 \
@@ -36,7 +36,7 @@ RUN git clone --depth 1 -b v0.17.0 https://github.com/ngtcp2/ngtcp2 && \
cd .. && \
rm -rf ngtcp2
RUN git clone --depth 1 -b v1.2.0 https://github.com/libbpf/libbpf && \
RUN git clone --depth 1 -b v1.2.2 https://github.com/libbpf/libbpf && \
cd libbpf && \
PREFIX=/usr/local make -C src install && \
cd .. && \
@@ -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/ \

8
go.mod
View File

@@ -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.17.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.14.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.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.6.0 // indirect
)

16
go.sum
View File

@@ -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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.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=

View File

@@ -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
)

View File

@@ -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

View File

@@ -2756,6 +2756,23 @@ NGHTTP2_EXTERN void
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(
nghttp2_option *option, int val);
/**
* @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
*

View File

@@ -418,8 +418,8 @@ void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive);
}
int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
const uint8_t *payload) {
void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
const uint8_t *payload) {
if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
} else {
@@ -428,11 +428,9 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
frame->nva = NULL;
frame->nvlen = 0;
return 0;
}
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {
void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {
nghttp2_buf *buf;
assert(bufs->head == bufs->cur);
@@ -448,8 +446,6 @@ int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {
nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec);
buf->last += NGHTTP2_PRIORITY_SPECLEN;
return 0;
}
void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
@@ -457,8 +453,8 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
}
int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
nghttp2_rst_stream *frame) {
void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
nghttp2_rst_stream *frame) {
nghttp2_buf *buf;
assert(bufs->head == bufs->cur);
@@ -473,8 +469,6 @@ int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
nghttp2_put_uint32be(buf->last, frame->error_code);
buf->last += 4;
return 0;
}
void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
@@ -592,16 +586,15 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
return frame_pack_headers_shared(bufs, &frame->hd);
}
int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
const uint8_t *payload) {
void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
const uint8_t *payload) {
frame->promised_stream_id =
nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
frame->nva = NULL;
frame->nvlen = 0;
return 0;
}
int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {
void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {
nghttp2_buf *buf;
assert(bufs->head == bufs->cur);
@@ -616,8 +609,6 @@ int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {
buf->last =
nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data));
return 0;
}
void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
@@ -697,8 +688,8 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
return 0;
}
int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
nghttp2_window_update *frame) {
void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
nghttp2_window_update *frame) {
nghttp2_buf *buf;
assert(bufs->head == bufs->cur);
@@ -713,8 +704,6 @@ int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment);
buf->last += 4;
return 0;
}
void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
@@ -723,7 +712,7 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
}
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
int rv;
nghttp2_buf *buf;
nghttp2_ext_altsvc *altsvc;
@@ -752,8 +741,6 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len);
assert(rv == 0);
return 0;
}
void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
@@ -901,8 +888,8 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
return 0;
}
int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
nghttp2_extension *frame) {
void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
nghttp2_extension *frame) {
int rv;
nghttp2_buf *buf;
nghttp2_ext_priority_update *priority_update;
@@ -927,8 +914,6 @@ int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
priority_update->field_value_len);
assert(rv == 0);
return 0;
}
void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame,
@@ -1186,14 +1171,14 @@ static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) {
buf->last += trail_padlen;
}
int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
size_t padlen, int framehd_only) {
void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
size_t padlen, int framehd_only) {
nghttp2_buf *buf;
if (padlen == 0) {
DEBUGF("send: padlen = 0, nothing to do\n");
return 0;
return;
}
/*
@@ -1226,6 +1211,4 @@ int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
hd->flags |= NGHTTP2_FLAG_PADDED;
DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen);
return 0;
}

View File

@@ -143,11 +143,9 @@ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
* Unpacks HEADERS frame byte sequence into |frame|. This function
* only unapcks bytes that come before name/value header block and
* after possible Pad Length field.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
const uint8_t *payload);
void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
const uint8_t *payload);
/*
* Packs PRIORITY frame |frame| in wire format and store it in
@@ -155,10 +153,8 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame);
void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame);
/*
* Unpacks PRIORITY wire format into |frame|.
@@ -172,11 +168,9 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
nghttp2_rst_stream *frame);
void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
nghttp2_rst_stream *frame);
/*
* Unpacks RST_STREAM frame byte sequence into |frame|.
@@ -265,15 +259,9 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
* Unpacks PUSH_PROMISE frame byte sequence into |frame|. This
* function only unapcks bytes that come before name/value header
* block and after possible Pad Length field.
*
* This function returns 0 if it succeeds or one of the following
* negative error codes:
*
* NGHTTP2_ERR_PROTO
* TODO END_HEADERS flag is not set
*/
int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
const uint8_t *payload);
void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
const uint8_t *payload);
/*
* Packs PING frame |frame| in wire format and store it in
@@ -281,10 +269,8 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame);
void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame);
/*
* Unpacks PING wire format into |frame|.
@@ -343,11 +329,9 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
nghttp2_window_update *frame);
void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
nghttp2_window_update *frame);
/*
* Unpacks WINDOW_UPDATE frame byte sequence into |frame|.
@@ -361,17 +345,13 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext);
void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext);
/*
* Unpacks ALTSVC wire format into |frame|. The |payload| of
* |payloadlen| bytes contains frame payload. This function assumes
* that frame->payload points to the nghttp2_ext_altsvc object.
*
* This function always succeeds and returns 0.
*/
void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
size_t origin_len, uint8_t *payload,
@@ -431,19 +411,15 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
nghttp2_extension *ext);
void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
nghttp2_extension *ext);
/*
* Unpacks PRIORITY_UPDATE wire format into |frame|. The |payload| of
* |payloadlen| bytes contains frame payload. This function assumes
* that frame->payload points to the nghttp2_ext_priority_update
* object.
*
* This function always succeeds and returns 0.
*/
void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame,
uint8_t *payload,
@@ -654,16 +630,8 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv);
* |padlen| including Pad Length field. The |hd| is the frame header
* for the serialized data. This function fills zeros padding region
* unless framehd_only is nonzero.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
* NGHTTP2_ERR_FRAME_SIZE_ERROR
* The length of the resulting frame is too large.
*/
int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
size_t padlen, int framehd_only);
void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
size_t padlen, int framehd_only);
#endif /* NGHTTP2_FRAME_H */

View File

@@ -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;
}

View File

@@ -70,12 +70,18 @@ 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;
/**
* Struct to store option values for nghttp2_session.
*/
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
*/

75
lib/nghttp2_ratelim.c Normal file
View File

@@ -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;
}

57
lib/nghttp2_ratelim.h Normal file
View File

@@ -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 */

View File

@@ -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,
@@ -937,8 +948,8 @@ static int session_ob_data_push(nghttp2_session *session,
return 0;
}
static int session_ob_data_remove(nghttp2_session *session,
nghttp2_stream *stream) {
static void session_ob_data_remove(nghttp2_session *session,
nghttp2_stream *stream) {
uint32_t urgency;
assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);
@@ -951,8 +962,6 @@ static int session_ob_data_remove(nghttp2_session *session,
nghttp2_pq_remove(&session->sched[urgency].ob_data, &stream->pq_entry);
stream->queued = 0;
return 0;
}
static int session_attach_stream_item(nghttp2_session *session,
@@ -972,38 +981,28 @@ static int session_attach_stream_item(nghttp2_session *session,
return session_ob_data_push(session, stream);
}
static int session_detach_stream_item(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
rv = nghttp2_stream_detach_item(stream);
if (rv != 0) {
return rv;
}
static void session_detach_stream_item(nghttp2_session *session,
nghttp2_stream *stream) {
nghttp2_stream_detach_item(stream);
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
!stream->queued) {
return 0;
return;
}
return session_ob_data_remove(session, stream);
session_ob_data_remove(session, stream);
}
static int session_defer_stream_item(nghttp2_session *session,
nghttp2_stream *stream, uint8_t flags) {
int rv;
rv = nghttp2_stream_defer_item(stream, flags);
if (rv != 0) {
return rv;
}
static void session_defer_stream_item(nghttp2_session *session,
nghttp2_stream *stream, uint8_t flags) {
nghttp2_stream_defer_item(stream, flags);
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
!stream->queued) {
return 0;
return;
}
return session_ob_data_remove(session, stream);
session_ob_data_remove(session, stream);
}
static int session_resume_deferred_stream_item(nghttp2_session *session,
@@ -1476,11 +1475,7 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
item = stream->item;
rv = session_detach_stream_item(session, stream);
if (rv != 0) {
return rv;
}
session_detach_stream_item(session, stream);
/* If item is queued, it will be deleted when it is popped
(nghttp2_session_prep_frame() will fail). If session->aob.item
@@ -2221,7 +2216,6 @@ static ssize_t session_call_select_padding(nghttp2_session *session,
frame->push_promise has also padlen in the same position. */
static int session_headers_add_pad(nghttp2_session *session,
nghttp2_frame *frame) {
int rv;
ssize_t padded_payloadlen;
nghttp2_active_outbound_item *aob;
nghttp2_bufs *framebufs;
@@ -2246,11 +2240,7 @@ static int session_headers_add_pad(nghttp2_session *session,
DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n",
padded_payloadlen, padlen);
rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);
if (rv != 0) {
return rv;
}
nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);
frame->headers.padlen = padlen;
@@ -2333,13 +2323,7 @@ static int session_prep_frame(nghttp2_session *session,
// Search stream including closed again.
stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
if (stream) {
int rv2;
rv2 = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv2)) {
return rv2;
}
session_detach_stream_item(session, stream);
}
return rv;
@@ -2354,12 +2338,8 @@ static int session_prep_frame(nghttp2_session *session,
queue when session->remote_window_size > 0 */
assert(session->remote_window_size > 0);
rv = session_defer_stream_item(session, stream,
NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) {
return rv;
}
session_defer_stream_item(session, stream,
NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
session->aob.item = NULL;
active_outbound_item_reset(&session->aob, mem);
@@ -2373,23 +2353,15 @@ static int session_prep_frame(nghttp2_session *session,
return rv;
}
if (rv == NGHTTP2_ERR_DEFERRED) {
rv = session_defer_stream_item(session, stream,
NGHTTP2_STREAM_FLAG_DEFERRED_USER);
if (nghttp2_is_fatal(rv)) {
return rv;
}
session_defer_stream_item(session, stream,
NGHTTP2_STREAM_FLAG_DEFERRED_USER);
session->aob.item = NULL;
active_outbound_item_reset(&session->aob, mem);
return NGHTTP2_ERR_DEFERRED;
}
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) {
return rv;
}
session_detach_stream_item(session, stream);
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
@@ -2399,13 +2371,7 @@ static int session_prep_frame(nghttp2_session *session,
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (rv != 0) {
int rv2;
rv2 = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv2)) {
return rv2;
}
session_detach_stream_item(session, stream);
return rv;
}
@@ -2907,10 +2873,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
}
if (stream && aux_data->eof) {
rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) {
return rv;
}
session_detach_stream_item(session, stream);
/* Call on_frame_send_callback after
nghttp2_stream_detach_item(), so that application can issue
@@ -3143,17 +3106,8 @@ static int session_after_frame_sent1(nghttp2_session *session) {
/*
* Called after a frame is sent and session_after_frame_sent1. This
* function is responsible to reset session->aob.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
*/
static int session_after_frame_sent2(nghttp2_session *session) {
int rv;
static void session_after_frame_sent2(nghttp2_session *session) {
nghttp2_active_outbound_item *aob = &session->aob;
nghttp2_outbound_item *item = aob->item;
nghttp2_bufs *framebufs = &aob->framebufs;
@@ -3176,13 +3130,13 @@ static int session_after_frame_sent2(nghttp2_session *session) {
DEBUGF("send: next CONTINUATION frame, %zu bytes\n",
nghttp2_buf_len(&framebufs->cur->buf));
return 0;
return;
}
}
active_outbound_item_reset(&session->aob, mem);
return 0;
return;
}
/* DATA frame */
@@ -3196,7 +3150,7 @@ static int session_after_frame_sent2(nghttp2_session *session) {
if (aux_data->eof) {
active_outbound_item_reset(aob, mem);
return 0;
return;
}
/* Reset no_copy here because next write may not use this. */
@@ -3208,22 +3162,18 @@ static int session_after_frame_sent2(nghttp2_session *session) {
further data. */
if (nghttp2_session_predicate_data_send(session, stream) != 0) {
if (stream) {
rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) {
return rv;
}
session_detach_stream_item(session, stream);
}
active_outbound_item_reset(aob, mem);
return 0;
return;
}
aob->item = NULL;
active_outbound_item_reset(&session->aob, mem);
return 0;
return;
}
static int session_call_send_data(nghttp2_session *session,
@@ -3296,6 +3246,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
if (rv < 0) {
int32_t opened_stream_id = 0;
uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
int rv2 = 0;
DEBUGF("send: frame preparation failed with %s\n",
nghttp2_strerror(rv));
@@ -3338,19 +3289,18 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
}
if (opened_stream_id) {
/* careful not to override rv */
int rv2;
rv2 = nghttp2_session_close_stream(session, opened_stream_id,
error_code);
if (nghttp2_is_fatal(rv2)) {
return rv2;
}
}
nghttp2_outbound_item_free(item, mem);
nghttp2_mem_free(mem, item);
active_outbound_item_reset(aob, mem);
if (nghttp2_is_fatal(rv2)) {
return rv2;
}
if (rv == NGHTTP2_ERR_HEADER_COMP) {
/* If header compression error occurred, should terminiate
connection. */
@@ -3454,7 +3404,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
/* Frame has completely sent */
if (fast_cb) {
rv = session_after_frame_sent2(session);
session_after_frame_sent2(session);
} else {
rv = session_after_frame_sent1(session);
if (rv < 0) {
@@ -3462,12 +3412,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
assert(nghttp2_is_fatal(rv));
return rv;
}
rv = session_after_frame_sent2(session);
}
if (rv < 0) {
/* FATAL */
assert(nghttp2_is_fatal(rv));
return rv;
session_after_frame_sent2(session);
}
/* We have already adjusted the next state */
break;
@@ -3506,11 +3451,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
}
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) {
return rv;
}
session_detach_stream_item(session, stream);
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
@@ -3534,11 +3475,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
assert(nghttp2_is_fatal(rv));
return rv;
}
rv = session_after_frame_sent2(session);
if (rv < 0) {
assert(nghttp2_is_fatal(rv));
return rv;
}
session_after_frame_sent2(session);
/* We have already adjusted the next state */
@@ -4424,17 +4361,12 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
}
static int session_process_headers_frame(nghttp2_session *session) {
int rv;
nghttp2_inbound_frame *iframe = &session->iframe;
nghttp2_frame *frame = &iframe->frame;
nghttp2_stream *stream;
rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);
nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);
if (rv != 0) {
return nghttp2_session_terminate_session_with_reason(
session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack");
}
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if (!stream) {
frame->headers.cat = NGHTTP2_HCAT_REQUEST;
@@ -4528,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;
@@ -4557,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) {
@@ -5097,17 +5047,11 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
}
static int session_process_push_promise_frame(nghttp2_session *session) {
int rv;
nghttp2_inbound_frame *iframe = &session->iframe;
nghttp2_frame *frame = &iframe->frame;
rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
iframe->sbuf.pos);
if (rv != 0) {
return nghttp2_session_terminate_session_with_reason(
session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack");
}
nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
iframe->sbuf.pos);
return nghttp2_session_on_push_promise_received(session, frame);
}
@@ -7519,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;
}
@@ -7808,11 +7755,8 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,
aux_data->no_copy);
if (rv != 0) {
return rv;
}
nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,
aux_data->no_copy);
session_reschedule_stream(session, stream);

View File

@@ -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;

View File

@@ -465,14 +465,12 @@ static int stream_update_dep_on_attach_item(nghttp2_stream *stream) {
return 0;
}
static int stream_update_dep_on_detach_item(nghttp2_stream *stream) {
static void stream_update_dep_on_detach_item(nghttp2_stream *stream) {
if (nghttp2_pq_empty(&stream->obq)) {
stream_obq_remove(stream);
}
validate_tree(stream);
return 0;
}
int nghttp2_stream_attach_item(nghttp2_stream *stream,
@@ -503,20 +501,20 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
return 0;
}
int nghttp2_stream_detach_item(nghttp2_stream *stream) {
void nghttp2_stream_detach_item(nghttp2_stream *stream) {
DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item);
stream->item = NULL;
stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
return;
}
return stream_update_dep_on_detach_item(stream);
stream_update_dep_on_detach_item(stream);
}
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
assert(stream->item);
DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id,
@@ -525,10 +523,10 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
stream->flags |= flags;
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
return;
}
return stream_update_dep_on_detach_item(stream);
stream_update_dep_on_detach_item(stream);
}
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {

View File

@@ -258,14 +258,8 @@ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag);
* more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates
* the reason of this action.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags);
void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags);
/*
* Put back deferred data in this stream to active state. The |flags|
@@ -379,14 +373,8 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
/*
* Detaches |stream->item|. This function does not free
* |stream->item|. The caller must free it.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_detach_item(nghttp2_stream *stream);
void nghttp2_stream_detach_item(nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is

62
lib/nghttp2_time.c Normal file
View File

@@ -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_GETTICKCOUNT64
uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; }
#elif defined(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;
}
#else /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */
uint64_t nghttp2_time_now_sec(void) { return time_now_sec(); }
#endif /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */

38
lib/nghttp2_time.h Normal file
View File

@@ -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 */

View File

@@ -754,37 +754,37 @@ int Http2Handler::read_tls() {
ERR_clear_error();
for (;;) {
auto rv = SSL_read(ssl_, buf.data(), buf.size());
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;
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;
}
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;
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;
}
return write_(*this);
}
int Http2Handler::write_tls() {

View File

@@ -1469,7 +1469,8 @@ int Client::write_udp(const sockaddr *addr, socklen_t addrlen,
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;
uint16_t n = gso_size;
memcpy(CMSG_DATA(cm), &n, sizeof(n));
}
# endif // UDP_SEGMENT

View File

@@ -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
@@ -3521,14 +3523,14 @@ HTTP/3 and QUIC:
--frontend-quic-congestion-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be one of "cubic", "bbr",
and "bbr2".
and "bbrv2".
Default: )"
<< (config->quic.upstream.congestion_controller == NGTCP2_CC_ALGO_CUBIC
? "cubic"
: (config->quic.upstream.congestion_controller ==
NGTCP2_CC_ALGO_BBR
? "bbr"
: "bbr2"))
: "bbrv2"))
<< R"(
--frontend-quic-secret-file=<PATH>
Path to file that contains secure random data to be used

View File

@@ -4116,10 +4116,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
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 if (util::strieq_l("bbr2", optarg)) {
config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR2;
} else if (util::strieq_l("bbrv2", optarg)) {
config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR_V2;
} else {
LOG(ERROR) << opt << ": must be one of cubic, bbr, and bbr2";
LOG(ERROR) << opt << ": must be one of cubic, bbr, and bbrv2";
return -1;
}
#endif // ENABLE_HTTP3

View File

@@ -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;
}

View File

@@ -421,7 +421,7 @@ int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
void *stream_user_data) {
auto upstream = static_cast<Http3Upstream *>(user_data);
if (upstream->http_shutdown_stream_read(stream_id) != 0) {
if (upstream->stream_reset(stream_id) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -429,6 +429,24 @@ int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
}
} // namespace
int Http3Upstream::stream_reset(int64_t stream_id) {
if (http_shutdown_stream_read(stream_id) != 0) {
return -1;
}
if (ngtcp2_is_bidi_stream(stream_id)) {
auto rv = ngtcp2_conn_shutdown_stream_write(conn_, 0, stream_id,
NGHTTP3_H3_NO_ERROR);
if (rv != 0) {
ULOG(ERROR, this) << "ngtcp2_conn_shutdown_stream_write: "
<< ngtcp2_strerror(rv);
return -1;
}
}
return 0;
}
int Http3Upstream::http_shutdown_stream_read(int64_t stream_id) {
if (!httpconn_) {
return 0;
@@ -551,7 +569,7 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr,
const ngtcp2_pkt_hd &initial_hd,
const ngtcp2_cid *odcid, const uint8_t *token,
size_t tokenlen) {
size_t tokenlen, ngtcp2_token_type token_type) {
int rv;
auto worker = handler_->get_worker();
@@ -638,6 +656,7 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
settings.rand_ctx.native_handle = &worker->get_randgen();
settings.token = token;
settings.tokenlen = tokenlen;
settings.token_type = token_type;
settings.initial_pkt_num = std::uniform_int_distribution<uint32_t>(
0, std::numeric_limits<int32_t>::max())(worker->get_randgen());
@@ -1559,9 +1578,7 @@ void Http3Upstream::on_handler_delete() {
auto cw = std::make_unique<CloseWait>(worker, std::move(scids),
std::move(conn_close_), d);
quic_conn_handler->add_close_wait(cw.get());
cw.release();
quic_conn_handler->add_close_wait(cw.release());
}
int Http3Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {

View File

@@ -89,7 +89,8 @@ public:
int init(const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_hd &initial_hd,
const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen);
const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
ngtcp2_token_type token_type);
int on_read(const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_info &pi,
@@ -124,6 +125,7 @@ public:
void consume(int64_t stream_id, size_t nconsumed);
void remove_downstream(Downstream *downstream);
int stream_close(int64_t stream_id, uint64_t app_error_code);
int stream_reset(int64_t stream_id);
void log_response_headers(Downstream *downstream,
const std::vector<nghttp3_nv> &nva) const;
int http_acked_stream_data(Downstream *downstream, uint64_t datalen);

View File

@@ -66,11 +66,11 @@ int quic_send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa,
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
uint8_t msg_ctrl[
uint8_t msg_ctrl[CMSG_SPACE(sizeof(int)) +
#ifdef UDP_SEGMENT
CMSG_SPACE(sizeof(uint16_t)) +
CMSG_SPACE(sizeof(uint16_t)) +
#endif // UDP_SEGMENT
CMSG_SPACE(sizeof(in6_pktinfo))];
CMSG_SPACE(sizeof(in6_pktinfo))];
memset(msg_ctrl, 0, sizeof(msg_ctrl));
@@ -87,11 +87,12 @@ int quic_send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa,
cm->cmsg_level = IPPROTO_IP;
cm->cmsg_type = IP_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
auto pktinfo = reinterpret_cast<in_pktinfo *>(CMSG_DATA(cm));
memset(pktinfo, 0, sizeof(in_pktinfo));
in_pktinfo pktinfo{};
auto addrin =
reinterpret_cast<sockaddr_in *>(const_cast<sockaddr *>(local_sa));
pktinfo->ipi_spec_dst = addrin->sin_addr;
pktinfo.ipi_spec_dst = addrin->sin_addr;
memcpy(CMSG_DATA(cm), &pktinfo, sizeof(pktinfo));
break;
}
case AF_INET6: {
@@ -99,11 +100,12 @@ int quic_send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa,
cm->cmsg_level = IPPROTO_IPV6;
cm->cmsg_type = IPV6_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
auto pktinfo = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cm));
memset(pktinfo, 0, sizeof(in6_pktinfo));
in6_pktinfo pktinfo{};
auto addrin =
reinterpret_cast<sockaddr_in6 *>(const_cast<sockaddr *>(local_sa));
pktinfo->ipi6_addr = addrin->sin6_addr;
pktinfo.ipi6_addr = addrin->sin6_addr;
memcpy(CMSG_DATA(cm), &pktinfo, sizeof(pktinfo));
break;
}
default:
@@ -117,13 +119,33 @@ int quic_send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa,
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;
uint16_t n = gso_size;
memcpy(CMSG_DATA(cm), &n, sizeof(n));
}
#endif // UDP_SEGMENT
msg.msg_controllen = controllen;
controllen += CMSG_SPACE(sizeof(int));
cm = CMSG_NXTHDR(&msg, cm);
cm->cmsg_len = CMSG_LEN(sizeof(int));
unsigned int tos = pi.ecn;
memcpy(CMSG_DATA(cm), &tos, sizeof(tos));
util::fd_set_send_ecn(faddr->fd, local_sa->sa_family, pi.ecn);
switch (local_sa->sa_family) {
case AF_INET:
cm->cmsg_level = IPPROTO_IP;
cm->cmsg_type = IP_TOS;
break;
case AF_INET6:
cm->cmsg_level = IPPROTO_IPV6;
cm->cmsg_type = IPV6_TCLASS;
break;
default:
assert(0);
}
msg.msg_controllen = controllen;
ssize_t nwrite;

View File

@@ -172,6 +172,7 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
ngtcp2_cid odcid, *podcid = nullptr;
const uint8_t *token = nullptr;
size_t tokenlen = 0;
ngtcp2_token_type token_type = NGTCP2_TOKEN_TYPE_UNKNOWN;
switch (ngtcp2_accept(&hd, data, datalen)) {
case 0: {
@@ -249,6 +250,7 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
podcid = &odcid;
token = hd.token;
tokenlen = hd.tokenlen;
token_type = NGTCP2_TOKEN_TYPE_RETRY;
break;
}
@@ -303,6 +305,7 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
token = hd.token;
tokenlen = hd.tokenlen;
token_type = NGTCP2_TOKEN_TYPE_NEW_TOKEN;
break;
}
@@ -342,7 +345,7 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
}
handler = handle_new_connection(faddr, remote_addr, local_addr, hd, podcid,
token, tokenlen);
token, tokenlen, token_type);
if (handler == nullptr) {
return 0;
}
@@ -364,7 +367,7 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
ClientHandler *QUICConnectionHandler::handle_new_connection(
const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_hd &hd, const ngtcp2_cid *odcid,
const uint8_t *token, size_t tokenlen) {
const uint8_t *token, size_t tokenlen, ngtcp2_token_type token_type) {
std::array<char, NI_MAXHOST> host;
std::array<char, NI_MAXSERV> service;
int rv;
@@ -415,8 +418,8 @@ ClientHandler *QUICConnectionHandler::handle_new_connection(
StringRef{service.data()}, remote_addr.su.sa.sa_family, faddr);
auto upstream = std::make_unique<Http3Upstream>(handler.get());
if (upstream->init(faddr, remote_addr, local_addr, hd, odcid, token,
tokenlen) != 0) {
if (upstream->init(faddr, remote_addr, local_addr, hd, odcid, token, tokenlen,
token_type) != 0) {
return nullptr;
}
@@ -526,9 +529,7 @@ int QUICConnectionHandler::send_retry(
auto cw = std::make_unique<CloseWait>(worker_, std::vector<ngtcp2_cid>{idcid},
std::move(buf), d);
add_close_wait(cw.get());
cw.release();
add_close_wait(cw.release());
return 0;
}

View File

@@ -116,12 +116,11 @@ public:
const Address &remote_addr,
const Address &local_addr, uint64_t error_code,
size_t max_pktlen);
ClientHandler *handle_new_connection(const UpstreamAddr *faddr,
const Address &remote_addr,
const Address &local_addr,
const ngtcp2_pkt_hd &hd,
const ngtcp2_cid *odcid,
const uint8_t *token, size_t tokenlen);
ClientHandler *
handle_new_connection(const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const ngtcp2_pkt_hd &hd,
const ngtcp2_cid *odcid, const uint8_t *token,
size_t tokenlen, ngtcp2_token_type token_type);
void add_connection_id(const ngtcp2_cid &cid, ClientHandler *handler);
void remove_connection_id(const ngtcp2_cid &cid);

View File

@@ -59,8 +59,7 @@ void QUICListener::on_read() {
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint8_t)) +
CMSG_SPACE(sizeof(in6_pktinfo)) +
uint8_t msg_ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(in6_pktinfo)) +
CMSG_SPACE(sizeof(uint16_t))];
msg.msg_control = msg_ctrl;

View File

@@ -67,8 +67,7 @@ template <typename F, typename... T> struct Defer {
Defer(Defer &&o) noexcept : f(std::move(o.f)) {}
~Defer() { f(); }
using ResultType = typename std::result_of<typename std::decay<F>::type(
typename std::decay<T>::type...)>::type;
using ResultType = std::invoke_result_t<F, T...>;
std::function<ResultType()> f;
};

View File

@@ -41,6 +41,9 @@
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif // HAVE_NETINET_IN_H
#ifdef HAVE_NETINET_IP_H
# include <netinet/ip.h>
#endif // HAVE_NETINET_IP_H
#include <netinet/udp.h>
#ifdef _WIN32
# include <ws2tcpip.h>
@@ -1680,11 +1683,12 @@ int msghdr_get_local_addr(Address &dest, msghdr *msg, int family) {
case AF_INET:
for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
auto pktinfo = reinterpret_cast<in_pktinfo *>(CMSG_DATA(cmsg));
in_pktinfo pktinfo;
memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo));
dest.len = sizeof(dest.su.in);
auto &sa = dest.su.in;
sa.sin_family = AF_INET;
sa.sin_addr = pktinfo->ipi_addr;
sa.sin_addr = pktinfo.ipi_addr;
return 0;
}
@@ -1694,11 +1698,12 @@ int msghdr_get_local_addr(Address &dest, msghdr *msg, int family) {
case AF_INET6:
for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
auto pktinfo = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsg));
in6_pktinfo pktinfo;
memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo));
dest.len = sizeof(dest.su.in6);
auto &sa = dest.su.in6;
sa.sin6_family = AF_INET6;
sa.sin6_addr = pktinfo->ipi6_addr;
sa.sin6_addr = pktinfo.ipi6_addr;
return 0;
}
}
@@ -1709,13 +1714,18 @@ int msghdr_get_local_addr(Address &dest, msghdr *msg, int family) {
return -1;
}
unsigned int msghdr_get_ecn(msghdr *msg, int family) {
uint8_t msghdr_get_ecn(msghdr *msg, int family) {
switch (family) {
case AF_INET:
for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS &&
cmsg->cmsg_len) {
return *reinterpret_cast<uint8_t *>(CMSG_DATA(cmsg));
if (cmsg->cmsg_level == IPPROTO_IP &&
# ifdef __APPLE__
cmsg->cmsg_type == IP_RECVTOS
# else // !__APPLE__
cmsg->cmsg_type == IP_TOS
# endif // !__APPLE__
&& cmsg->cmsg_len) {
return *reinterpret_cast<uint8_t *>(CMSG_DATA(cmsg)) & IPTOS_ECN_MASK;
}
}
@@ -1724,7 +1734,11 @@ unsigned int msghdr_get_ecn(msghdr *msg, int family) {
for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS &&
cmsg->cmsg_len) {
return *reinterpret_cast<uint8_t *>(CMSG_DATA(cmsg));
unsigned int tos;
memcpy(&tos, CMSG_DATA(cmsg), sizeof(tos));
return tos & IPTOS_ECN_MASK;
}
}
@@ -1749,27 +1763,6 @@ size_t msghdr_get_udp_gro(msghdr *msg) {
return gso_size;
}
int fd_set_send_ecn(int fd, int family, unsigned int ecn) {
switch (family) {
case AF_INET:
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &ecn,
static_cast<socklen_t>(sizeof(ecn))) == -1) {
return -1;
}
return 0;
case AF_INET6:
if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &ecn,
static_cast<socklen_t>(sizeof(ecn))) == -1) {
return -1;
}
return 0;
}
return -1;
}
#endif // ENABLE_HTTP3
} // namespace util

View File

@@ -957,13 +957,11 @@ StringRef rstrip(BlockAllocator &balloc, const StringRef &s);
#ifdef ENABLE_HTTP3
int msghdr_get_local_addr(Address &dest, msghdr *msg, int family);
unsigned int msghdr_get_ecn(msghdr *msg, int family);
uint8_t msghdr_get_ecn(msghdr *msg, int family);
// msghdr_get_udp_gro returns UDP_GRO value from |msg|. If UDP_GRO is
// not found, or UDP_GRO is not supported, this function returns 0.
size_t msghdr_get_udp_gro(msghdr *msg);
int fd_set_send_ecn(int fd, int family, unsigned int ecn);
#endif // ENABLE_HTTP3
} // namespace util

View File

@@ -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

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -210,7 +210,6 @@ void test_nghttp2_frame_pack_priority(void) {
nghttp2_priority frame, oframe;
nghttp2_bufs bufs;
nghttp2_priority_spec pri_spec;
int rv;
frame_pack_bufs_init(&bufs);
@@ -218,9 +217,8 @@ void test_nghttp2_frame_pack_priority(void) {
nghttp2_priority_spec_init(&pri_spec, 1000000009, 12, 1);
nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec);
rv = nghttp2_frame_pack_priority(&bufs, &frame);
nghttp2_frame_pack_priority(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 5 == nghttp2_bufs_len(&bufs));
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
check_frame_header(5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007,
@@ -240,14 +238,12 @@ void test_nghttp2_frame_pack_priority(void) {
void test_nghttp2_frame_pack_rst_stream(void) {
nghttp2_rst_stream frame, oframe;
nghttp2_bufs bufs;
int rv;
frame_pack_bufs_init(&bufs);
nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR);
rv = nghttp2_frame_pack_rst_stream(&bufs, &frame);
nghttp2_frame_pack_rst_stream(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs));
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007,
@@ -259,9 +255,8 @@ void test_nghttp2_frame_pack_rst_stream(void) {
/* Unknown error code is passed to callback as is */
frame.error_code = 1000000009;
rv = nghttp2_frame_pack_rst_stream(&bufs, &frame);
nghttp2_frame_pack_rst_stream(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007,
@@ -365,14 +360,12 @@ void test_nghttp2_frame_pack_ping(void) {
nghttp2_ping frame, oframe;
nghttp2_bufs bufs;
const uint8_t opaque_data[] = "01234567";
int rv;
frame_pack_bufs_init(&bufs);
nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data);
rv = nghttp2_frame_pack_ping(&bufs, &frame);
nghttp2_frame_pack_ping(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 8 == nghttp2_bufs_len(&bufs));
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd);
@@ -435,14 +428,12 @@ void test_nghttp2_frame_pack_goaway(void) {
void test_nghttp2_frame_pack_window_update(void) {
nghttp2_window_update frame, oframe;
nghttp2_bufs bufs;
int rv;
frame_pack_bufs_init(&bufs);
nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096);
rv = nghttp2_frame_pack_window_update(&bufs, &frame);
nghttp2_frame_pack_window_update(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs));
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007,
@@ -485,9 +476,8 @@ void test_nghttp2_frame_pack_altsvc(void) {
payloadlen = 2 + sizeof(origin) - 1 + sizeof(field_value) - 1;
rv = nghttp2_frame_pack_altsvc(&bufs, &frame);
nghttp2_frame_pack_altsvc(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(NGHTTP2_FRAME_HDLEN + payloadlen == nghttp2_bufs_len(&bufs));
rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
@@ -618,9 +608,8 @@ void test_nghttp2_frame_pack_priority_update(void) {
payloadlen = 4 + sizeof(field_value) - 1;
rv = nghttp2_frame_pack_priority_update(&bufs, &frame);
nghttp2_frame_pack_priority_update(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(NGHTTP2_FRAME_HDLEN + payloadlen == nghttp2_bufs_len(&bufs));
rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);

View File

@@ -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));
}

View File

@@ -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 */

View File

@@ -584,6 +584,15 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
return 0;
}
static int fatal_error_on_stream_close_callback(nghttp2_session *session,
int32_t stream_id,
uint32_t error_code,
void *user_data) {
on_stream_close_callback(session, stream_id, error_code, user_data);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
static ssize_t pack_extension_callback(nghttp2_session *session, uint8_t *buf,
size_t len, const nghttp2_frame *frame,
void *user_data) {
@@ -720,9 +729,7 @@ void test_nghttp2_session_recv(void) {
/* Receive PRIORITY */
nghttp2_frame_priority_init(&frame.priority, 5, &pri_spec_default);
rv = nghttp2_frame_pack_priority(&bufs, &frame.priority);
CU_ASSERT(0 == rv);
nghttp2_frame_pack_priority(&bufs, &frame.priority);
nghttp2_frame_priority_free(&frame.priority);
@@ -746,9 +753,7 @@ void test_nghttp2_session_recv(void) {
/* Receive PING with too large payload */
nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
rv = nghttp2_frame_pack_ping(&bufs, &frame.ping);
CU_ASSERT(0 == rv);
nghttp2_frame_pack_ping(&bufs, &frame.ping);
/* Add extra 16 bytes */
nghttp2_bufs_seek_last_present(&bufs);
@@ -1401,9 +1406,8 @@ void test_nghttp2_session_recv_continuation(void) {
nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
nghttp2_bufs_reset(&bufs);
rv = nghttp2_frame_pack_priority(&bufs, &frame.priority);
nghttp2_frame_pack_priority(&bufs, &frame.priority);
CU_ASSERT(0 == rv);
CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
memcpy(data + datalen, buf->pos, nghttp2_buf_len(buf));
@@ -4296,6 +4300,8 @@ void test_nghttp2_session_on_goaway_received(void) {
nghttp2_frame frame;
int i;
nghttp2_mem *mem;
const uint8_t *data;
ssize_t datalen;
mem = nghttp2_mem_default();
user_data.frame_recv_cb_called = 0;
@@ -4337,6 +4343,29 @@ void test_nghttp2_session_on_goaway_received(void) {
nghttp2_frame_goaway_free(&frame.goaway, mem);
nghttp2_session_del(session);
/* Make sure that no memory leak when stream_close callback fails
with a fatal error */
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.on_stream_close_callback = fatal_error_on_stream_close_callback;
memset(&user_data, 0, sizeof(user_data));
nghttp2_session_client_new(&session, &callbacks, &user_data);
nghttp2_frame_goaway_init(&frame.goaway, 0, NGHTTP2_NO_ERROR, NULL, 0);
CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame));
nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL);
datalen = nghttp2_session_mem_send(session, &data);
CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == datalen);
CU_ASSERT(1 == user_data.stream_close_cb_called);
nghttp2_frame_goaway_free(&frame.goaway, mem);
nghttp2_session_del(session);
}
void test_nghttp2_session_on_window_update_received(void) {
@@ -4372,8 +4401,7 @@ void test_nghttp2_session_on_window_update_received(void) {
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 ==
stream->remote_window_size);
CU_ASSERT(0 == nghttp2_stream_defer_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL));
nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
CU_ASSERT(2 == user_data.frame_recv_cb_called);
@@ -9639,9 +9667,7 @@ void test_nghttp2_session_stream_get_state(void) {
/* Create idle stream by PRIORITY frame */
nghttp2_frame_priority_init(&frame.priority, 7, &pri_spec_default);
rv = nghttp2_frame_pack_priority(&bufs, &frame.priority);
CU_ASSERT(0 == rv);
nghttp2_frame_pack_priority(&bufs, &frame.priority);
nghttp2_frame_priority_free(&frame.priority);
@@ -11847,9 +11873,7 @@ void test_nghttp2_session_server_fallback_rfc7540_priorities(void) {
nghttp2_priority_spec_init(&pri_spec, 5, 1, 0);
nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
nghttp2_bufs_reset(&bufs);
rv = nghttp2_frame_pack_priority(&bufs, &frame.priority);
CU_ASSERT(0 == rv);
nghttp2_frame_pack_priority(&bufs, &frame.priority);
nghttp2_frame_priority_free(&frame.priority);
@@ -11944,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) {

View File

@@ -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);

View File

@@ -54,8 +54,7 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) {
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
payloadoff = ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0);
rv = nghttp2_frame_unpack_headers_payload(&frame->headers,
payload + payloadoff);
nghttp2_frame_unpack_headers_payload(&frame->headers, payload + payloadoff);
break;
case NGHTTP2_PRIORITY:
nghttp2_frame_unpack_priority_payload(&frame->priority, payload);
@@ -68,8 +67,7 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) {
&frame->settings.iv, &frame->settings.niv, payload, payloadlen, mem);
break;
case NGHTTP2_PUSH_PROMISE:
rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
payload);
nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, payload);
break;
case NGHTTP2_PING:
nghttp2_frame_unpack_ping_payload(&frame->ping, payload);

View File

@@ -287,7 +287,7 @@ protocol support to highly non-compliant clients/server.
No `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
lenient parsing is "on".
**USE AT YOUR OWN RISK!**
**Enabling this flag can pose a security issue since you will be exposed to request smuggling attacks. USE WITH CAUTION!**
### `void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled)`
@@ -300,23 +300,22 @@ conjunction with `Content-Length`.
This error is important to prevent HTTP request smuggling, but may be less desirable
for small number of cases involving legacy servers.
**USE AT YOUR OWN RISK!**
**Enabling this flag can pose a security issue since you will be exposed to request smuggling attacks. USE WITH CAUTION!**
### `void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled)`
Enables/disables lenient handling of `Connection: close` and HTTP/1.0
requests responses.
Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
the HTTP request/response after the request/response with `Connection: close`
and `Content-Length`.
Normally `llhttp` would error the HTTP request/response
after the request/response with `Connection: close` and `Content-Length`.
This is important to prevent cache poisoning attacks,
but might interact badly with outdated and insecure clients.
With this flag the extra request/response will be parsed normally.
**USE AT YOUR OWN RISK!**
**Enabling this flag can pose a security issue since you will be exposed to poisoning attacks. USE WITH CAUTION!**
### `void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled)`
@@ -331,7 +330,48 @@ avoid request smuggling.
With this flag the extra value will be parsed normally.
**USE AT YOUR OWN RISK!**
**Enabling this flag can pose a security issue since you will be exposed to request smuggling attacks. USE WITH CAUTION!**
### `void llhttp_set_lenient_version(llhttp_t* parser, int enabled)`
Enables/disables lenient handling of HTTP version.
Normally `llhttp` would error when the HTTP version in the request or status line
is not `0.9`, `1.0`, `1.1` or `2.0`.
With this flag the extra value will be parsed normally.
**Enabling this flag can pose a security issue since you will allow unsupported HTTP versions. USE WITH CAUTION!**
### `void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled)`
Enables/disables lenient handling of additional data received after a message ends
and keep-alive is disabled.
Normally `llhttp` would error when additional unexpected data is received if the message
contains the `Connection` header with `close` value.
With this flag the extra data will discarded without throwing an error.
**Enabling this flag can pose a security issue since you will be exposed to poisoning attacks. USE WITH CAUTION!**
### `void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled)`
Enables/disables lenient handling of incomplete CRLF sequences.
Normally `llhttp` would error when a CR is not followed by LF when terminating the
request line, the status line, the headers or a chunk header.
With this flag only a CR is required to terminate such sections.
**Enabling this flag can pose a security issue since you will be exposed to request smuggling attacks. USE WITH CAUTION!**
### `void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled)`
Enables/disables lenient handling of chunks not separated via CRLF.
Normally `llhttp` would error when after a chunk data a CRLF is missing before
starting a new chunk.
With this flag the new chunk can start immediately after the previous one.
**Enabling this flag can pose a security issue since you will be exposed to request smuggling attacks. USE WITH CAUTION!**
## Build Instructions

View File

@@ -1,14 +1,11 @@
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 8
#define LLHTTP_VERSION_MINOR 1
#define LLHTTP_VERSION_MAJOR 9
#define LLHTTP_VERSION_MINOR 0
#define LLHTTP_VERSION_PATCH 1
#ifndef LLHTTP_STRICT_MODE
# define LLHTTP_STRICT_MODE 0
#endif
#ifndef INCLUDE_LLHTTP_ITSELF_H_
#define INCLUDE_LLHTTP_ITSELF_H_
#ifdef __cplusplus
@@ -50,6 +47,7 @@ int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* e
#endif
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
#ifndef LLLLHTTP_C_HEADERS_
#define LLLLHTTP_C_HEADERS_
#ifdef __cplusplus
@@ -114,7 +112,10 @@ enum llhttp_lenient_flags {
LENIENT_CHUNKED_LENGTH = 0x2,
LENIENT_KEEP_ALIVE = 0x4,
LENIENT_TRANSFER_ENCODING = 0x8,
LENIENT_VERSION = 0x10
LENIENT_VERSION = 0x10,
LENIENT_DATA_AFTER_CLOSE = 0x20,
LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80
};
typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
@@ -534,6 +535,7 @@ typedef enum llhttp_status llhttp_status_t;
#endif
#endif /* LLLLHTTP_C_HEADERS_ */
#ifndef INCLUDE_LLHTTP_API_H_
#define INCLUDE_LLHTTP_API_H_
#ifdef __cplusplus
@@ -759,7 +761,8 @@ const char* llhttp_status_name(llhttp_status_t status);
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
* lenient parsing is "on".
*
* **(USE AT YOUR OWN RISK)**
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
@@ -773,7 +776,8 @@ void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
* request smuggling, but may be less desirable for small number of cases
* involving legacy servers.
*
* **(USE AT YOUR OWN RISK)**
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
@@ -788,7 +792,8 @@ void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
* but might interact badly with outdated and insecure clients. With this flag
* the extra request/response will be parsed normally.
*
* **(USE AT YOUR OWN RISK)**
* **Enabling this flag can pose a security issue since you will be exposed to
* poisoning attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
@@ -802,14 +807,65 @@ void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
* avoid request smuggling.
* With this flag the extra value will be parsed normally.
*
* **(USE AT YOUR OWN RISK)**
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of HTTP version.
*
* Normally `llhttp` would error when the HTTP version in the request or status line
* is not `0.9`, `1.0`, `1.1` or `2.0`.
* With this flag the invalid value will be parsed normally.
*
* **Enabling this flag can pose a security issue since you will allow unsupported
* HTTP versions. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_version(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of additional data received after a message ends
* and keep-alive is disabled.
*
* Normally `llhttp` would error when additional unexpected data is received if the message
* contains the `Connection` header with `close` value.
* With this flag the extra data will discarded without throwing an error.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* poisoning attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of incomplete CRLF sequences.
*
* Normally `llhttp` would error when a CR is not followed by LF when terminating the
* request line, the status line, the headers or a chunk header.
* With this flag only a CR is required to terminate such sections.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of chunks not separated via CRLF.
*
* Normally `llhttp` would error when after a chunk data a CRLF is missing before
* starting a new chunk.
* With this flag the new chunk can start immediately after the previous one.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */
#endif /* INCLUDE_LLHTTP_H_ */

View File

@@ -283,6 +283,38 @@ void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
}
}
void llhttp_set_lenient_version(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_VERSION;
} else {
parser->lenient_flags &= ~LENIENT_VERSION;
}
}
void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
} else {
parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
}
}
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
}
}
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
}
}
/* Callbacks */

File diff suppressed because it is too large Load Diff