nghttp2 v1.58.0

-----BEGIN PGP SIGNATURE-----
 
 iF0EABECAB0WIQT087kUdNHrKYib0O9+hAPV1nPDZgUCZTutEwAKCRB+hAPV1nPD
 ZnYfAJ4ohvI3xRfVb45kyJzCL9APVEgGkwCgzg11hSTNQ1CfeWuhDvsC1REBqm0=
 =aYJq
 -----END PGP SIGNATURE-----

Nghttp2 upgrade to version 1.58.0

Signed-off-by: Aurora <liuxiyao223@huawei.com>
This commit is contained in:
Aurora 2024-04-07 16:46:29 +08:00
commit d64f959920
64 changed files with 1700 additions and 10165 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: v1.0.0
NGTCP2_VERSION: v1.0.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
@ -467,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

@ -17,6 +17,7 @@ github issues [2].
Adam Gołębiowski
Alek Storm
Alex Nalivko
Alexandr Vlasov
Alexandros Konstantinakis-Karmis
Alexis La Goutte
Amir Livneh

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.58.0)
# 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 1)
set(LT_AGE 25)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
include(Version)
@ -273,6 +273,7 @@ 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)
@ -342,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)
@ -505,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

@ -3,7 +3,7 @@
"Name" : "nghttp2",
"License" : "The MIT License",
"License File" : "COPYING",
"Version Number" : "1.55.0",
"Version Number" : "1.58.0",
"Owner" : "zhuwenchao@huawei.com",
"Upstream URL" : "https://nghttp2.org",
"Description" : "nghttp2 is an implementation of HTTP/2 and its header compression algorithm HPACK in C."

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>`_ >= 1.0.0
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 1.0.0
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 v1.0.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 v1.0.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 ..
@ -1456,12 +1456,10 @@ released, or mitigation is worked out.
In the future, we may setup a dedicated mail address for this purpose.
Release schedule
----------------
Versioning
----------
In general, we follow `Semantic Versioning <http://semver.org/>`_. We
release MINOR version update every month, and usually we ship it
around 25th day of every month.
In general, we follow `Semantic Versioning <http://semver.org/>`_.
We may release PATCH releases between the regular releases, mainly for
severe security bug fixes.

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

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

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], [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, 1)
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"`
@ -233,7 +233,7 @@ fi
save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS=
AX_CXX_COMPILE_STDCXX([14], [noext], [optional])
AX_CXX_COMPILE_STDCXX([14], [], [optional])
CXX1XCXXFLAGS="$CXXFLAGS"
CXXFLAGS="$save_CXXFLAGS"
@ -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 >= 1.0.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 >= 1.0.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 >= 1.0.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 \

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 27, 2023" "1.58.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 27, 2023" "1.58.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 27, 2023" "1.58.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 27, 2023" "1.58.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
@ -1832,8 +1834,8 @@ NEW_TOKEN frame in the previous connection.
.TP
.B \-\-frontend\-quic\-congestion\-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be one of \(dqcubic\(dq, \(dqbbr\(dq,
and \(dqbbr2\(dq.
QUIC connection. <CC> should be either \(dqcubic\(dq or
\(dqbbr\(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>
@ -1673,8 +1675,8 @@ HTTP/3 and QUIC
.. option:: --frontend-quic-congestion-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be one of "cubic", "bbr",
and "bbr2".
QUIC connection. <CC> should be either "cubic" or
"bbr".
Default: ``cubic``

View File

@ -20,19 +20,14 @@ privately. We also discuss the disclosure date to the public.
We make a new release with the fix at the same time when the
vulnerability is disclosed to public.
At least 7 days before the public disclosure date, we will post
security advisory (which includes all the details of the vulnerability
and the possible mitigation strategies) and the patches to fix the
issue to `distros@openwall
<https://oss-security.openwall.org/wiki/mailing-lists/distros>`_
mailing list. We also open a new issue on `nghttp2 issue tracker
At least 7 days before the public disclosure date, we open a new issue
on `nghttp2 issue tracker
<https://github.com/nghttp2/nghttp2/issues>`_ which notifies that the
upcoming release will have a security fix. The ``SECURITY`` label is
attached to this kind of issue.
attached to this kind of issue. The issue is not opened if a
vulnerability is already disclosed, and it is publicly known that
nghttp2 is affected by that.
Before few hours of new release, we merge the fixes to the master
branch (and/or a release branch if necessary) and make a new release.
Security advisory is disclosed on GitHub. We also post the
vulnerability information to `oss-security
<https://oss-security.openwall.org/wiki/mailing-lists/oss-security>`_
mailing list.
Security advisory is disclosed on GitHub.

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 v1.0.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 v1.0.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

@ -2,6 +2,7 @@ set(GO_FILES
nghttpx_http1_test.go
nghttpx_http2_test.go
server_tester.go
server_tester_http3.go
)
# XXX unused
@ -40,7 +41,11 @@ if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
endforeach()
endif()
if(ENABLE_HTTP3)
set(GO_TEST_TAGS quic)
endif()
add_custom_target(it
COMMAND sh setenv go test -v
COMMAND sh setenv go test -v --tags=${GO_TEST_TAGS}
DEPENDS ${GO_BUILD_FILES}
)

View File

@ -25,7 +25,8 @@ GO_FILES = \
nghttpx_http1_test.go \
nghttpx_http2_test.go \
nghttpx_http3_test.go \
server_tester.go
server_tester.go \
server_tester_http3.go
EXTRA_DIST = \
CMakeLists.txt \

View File

@ -1490,3 +1490,142 @@ func TestH1H1ChunkedEndsPrematurely(t *testing.T) {
t.Fatal("st.http1() should fail")
}
}
// TestH1H1RequestMalformedTransferEncoding tests that server rejects
// request which contains malformed transfer-encoding.
func TestH1H1RequestMalformedTransferEncoding(t *testing.T) {
opts := options{
handler: func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1H1RequestMalformedTransferEncoding\r\nTransfer-Encoding: ,chunked\r\n\r\n",
st.authority)); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H1ResponseMalformedTransferEncoding tests a request fails if
// its response contains malformed transfer-encoding.
func TestH1H1ResponseMalformedTransferEncoding(t *testing.T) {
opts := options{
handler: func(w http.ResponseWriter, r *http.Request) {
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer conn.Close()
if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: ,chunked\r\n\r\n"); err != nil {
t.Fatalf("Error bufrw.WriteString() = %v", err)
}
bufrw.Flush()
},
}
st := newServerTester(t, opts)
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1ResponseMalformedTransferEncoding",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, http.StatusBadGateway; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}
// TestH1H1ResponseUnknownTransferEncoding tests a request succeeds if
// its response contains unknown transfer-encoding.
func TestH1H1ResponseUnknownTransferEncoding(t *testing.T) {
opts := options{
handler: func(w http.ResponseWriter, r *http.Request) {
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer conn.Close()
if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: foo\r\n\r\n"); err != nil {
t.Fatalf("Error bufrw.WriteString() = %v", err)
}
bufrw.Flush()
},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1H1ResponseUnknownTransferEncoding\r\n\r\n",
st.authority)); err != nil {
t.Fatalf("Error: io.WriteString() = %v", err)
}
r := bufio.NewReader(st.conn)
resp := make([]byte, 4096)
resplen, err := r.Read(resp)
if err != nil {
t.Fatalf("Error: r.Read() = %v", err)
}
resp = resp[:resplen]
const expect = "HTTP/1.1 200 OK\r\nTransfer-Encoding: foo\r\nConnection: close\r\nServer: nghttpx\r\nVia: 1.1 nghttpx\r\n\r\n"
if got, want := string(resp), expect; got != want {
t.Errorf("resp = %v, want %v", got, want)
}
}
// TestH1H1RequestHTTP10TransferEncoding tests that server rejects
// HTTP/1.0 request which contains transfer-encoding.
func TestH1H1RequestHTTP10TransferEncoding(t *testing.T) {
opts := options{
handler: func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1RequestHTTP10TransferEncoding\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}

View File

@ -22,7 +22,6 @@ import (
"testing"
"time"
"github.com/quic-go/quic-go/http3"
"github.com/tatsuhiro-t/go-nghttp2"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
@ -390,81 +389,6 @@ func (st *serverTester) websocket(rp requestParam) *serverResponse {
return res
}
func (st *serverTester) http3(rp requestParam) (*serverResponse, error) {
rt := &http3.RoundTripper{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
defer rt.Close()
c := &http.Client{
Transport: rt,
}
method := "GET"
if rp.method != "" {
method = rp.method
}
var body io.Reader
if rp.body != nil {
body = bytes.NewBuffer(rp.body)
}
reqURL := st.url
if rp.path != "" {
u, err := url.Parse(st.url)
if err != nil {
st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
}
u.Path = ""
u.RawQuery = ""
reqURL = u.String() + rp.path
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, method, reqURL, body)
if err != nil {
return nil, err
}
for _, h := range rp.header {
req.Header.Add(h.Name, h.Value)
}
req.Header.Add("Test-Case", rp.name)
// TODO http3 package does not support trailer at the time of
// this writing.
resp, err := c.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
res := &serverResponse{
status: resp.StatusCode,
header: resp.Header,
body: respBody,
connClose: resp.Close,
}
return res, nil
}
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
method := "GET"
if rp.method != "" {

View File

@ -0,0 +1,90 @@
//go:build quic
package nghttp2
import (
"bytes"
"context"
"crypto/tls"
"io"
"net/http"
"net/url"
"time"
"github.com/quic-go/quic-go/http3"
)
func (st *serverTester) http3(rp requestParam) (*serverResponse, error) {
rt := &http3.RoundTripper{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
defer rt.Close()
c := &http.Client{
Transport: rt,
}
method := "GET"
if rp.method != "" {
method = rp.method
}
var body io.Reader
if rp.body != nil {
body = bytes.NewBuffer(rp.body)
}
reqURL := st.url
if rp.path != "" {
u, err := url.Parse(st.url)
if err != nil {
st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
}
u.Path = ""
u.RawQuery = ""
reqURL = u.String() + rp.path
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, method, reqURL, body)
if err != nil {
return nil, err
}
for _, h := range rp.header {
req.Header.Add(h.Name, h.Value)
}
req.Header.Add("Test-Case", rp.name)
// TODO http3 package does not support trailer at the time of
// this writing.
resp, err := c.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
res := &serverResponse{
status: resp.StatusCode,
header: resp.Header,
body: respBody,
connClose: resp.Close,
}
return res, nil
}

View File

@ -29,7 +29,7 @@
* @macro
* Version number of the nghttp2 library release
*/
#define NGHTTP2_VERSION "1.55.0"
#define NGHTTP2_VERSION "1.58.0"
/**
* @macro
@ -37,6 +37,6 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
#define NGHTTP2_VERSION_NUM 0x013700
#define NGHTTP2_VERSION_NUM 0x013A00
#endif /* NGHTTP2VER_H */

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

@ -126,6 +126,7 @@ static void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash,
bkt->data = data;
}
#ifndef WIN32
void nghttp2_map_print_distance(nghttp2_map *map) {
uint32_t i;
size_t idx;
@ -145,6 +146,7 @@ void nghttp2_map_print_distance(nghttp2_map *map) {
distance(map->tablelen, map->tablelenbits, bkt, idx));
}
}
#endif /* !WIN32 */
static int insert(nghttp2_map_bucket *table, uint32_t tablelen,
uint32_t tablelenbits, uint32_t hash,

View File

@ -131,6 +131,8 @@ size_t nghttp2_map_size(nghttp2_map *map);
int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
void *ptr);
#ifndef WIN32
void nghttp2_map_print_distance(nghttp2_map *map);
#endif /* !WIN32 */
#endif /* NGHTTP2_MAP_H */

View File

@ -948,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);
@ -962,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,
@ -983,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,
@ -1487,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
@ -2232,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;
@ -2257,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;
@ -2344,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;
@ -2365,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);
@ -2384,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);
@ -2410,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;
}
@ -2918,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
@ -3154,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;
@ -3187,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 */
@ -3207,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. */
@ -3219,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,
@ -3307,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));
@ -3349,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. */
@ -3465,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) {
@ -3473,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;
@ -3517,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);
@ -3545,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 */
@ -4435,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;
@ -5126,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);
}
@ -7840,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

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

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

@ -1044,12 +1044,39 @@ InputIt skip_to_right_dquote(InputIt first, InputIt last) {
switch (*first) {
case '"':
return first;
// quoted-pair
case '\\':
++first;
if (first == last) {
return first;
}
switch (*first) {
case '\t':
case ' ':
break;
default:
if ((0x21 <= *first && *first <= 0x7e) /* VCHAR */ ||
(0x80 <= *first && *first <= 0xff) /* obs-text */) {
break;
}
return last;
}
break;
// qdtext
case '\t':
case ' ':
case '!':
break;
default:
if ((0x23 <= *first && *first <= 0x5b) ||
(0x5d <= *first && *first <= 0x7e)) {
break;
}
return last;
}
++first;
}
@ -1957,6 +1984,108 @@ bool legacy_http1(int major, int minor) {
return major <= 0 || (major == 1 && minor == 0);
}
bool check_transfer_encoding(const StringRef &s) {
if (s.empty()) {
return false;
}
auto it = std::begin(s);
for (;;) {
// token
if (!util::in_token(*it)) {
return false;
}
++it;
for (; it != std::end(s) && util::in_token(*it); ++it)
;
if (it == std::end(s)) {
return true;
}
for (;;) {
// OWS
it = skip_lws(it, std::end(s));
if (it == std::end(s)) {
return false;
}
if (*it == ',') {
++it;
it = skip_lws(it, std::end(s));
if (it == std::end(s)) {
return false;
}
break;
}
if (*it != ';') {
return false;
}
++it;
// transfer-parameter follows
// OWS
it = skip_lws(it, std::end(s));
if (it == std::end(s)) {
return false;
}
// token
if (!util::in_token(*it)) {
return false;
}
++it;
for (; it != std::end(s) && util::in_token(*it); ++it)
;
if (it == std::end(s)) {
return false;
}
// No BWS allowed
if (*it != '=') {
return false;
}
++it;
if (util::in_token(*it)) {
// token
++it;
for (; it != std::end(s) && util::in_token(*it); ++it)
;
} else if (*it == '"') {
// quoted-string
++it;
it = skip_to_right_dquote(it, std::end(s));
if (it == std::end(s)) {
return false;
}
++it;
} else {
return false;
}
if (it == std::end(s)) {
return true;
}
}
}
}
} // namespace http2
} // namespace nghttp2

View File

@ -444,6 +444,11 @@ StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key);
// HTTP/0.9 or HTTP/1.0).
bool legacy_http1(int major, int minor);
// Returns true if transfer-encoding field value |s| conforms RFC
// strictly. This function does not allow empty value, BWS, and empty
// list elements.
bool check_transfer_encoding(const StringRef &s);
} // namespace http2
} // namespace nghttp2

View File

@ -1189,4 +1189,61 @@ void test_http2_contains_trailers(void) {
CU_ASSERT(http2::contains_trailers(StringRef::from_lit(",trailers")));
}
void test_http2_check_transfer_encoding(void) {
CU_ASSERT(http2::check_transfer_encoding(StringRef::from_lit("chunked")));
CU_ASSERT(http2::check_transfer_encoding(StringRef::from_lit("foo,chunked")));
CU_ASSERT(
http2::check_transfer_encoding(StringRef::from_lit("foo, chunked")));
CU_ASSERT(
http2::check_transfer_encoding(StringRef::from_lit("foo , chunked")));
CU_ASSERT(
http2::check_transfer_encoding(StringRef::from_lit("chunked;foo=bar")));
CU_ASSERT(
http2::check_transfer_encoding(StringRef::from_lit("chunked ; foo=bar")));
CU_ASSERT(http2::check_transfer_encoding(
StringRef::from_lit(R"(chunked;foo="bar")")));
CU_ASSERT(http2::check_transfer_encoding(
StringRef::from_lit(R"(chunked;foo="\bar\"";FOO=BAR)")));
CU_ASSERT(
http2::check_transfer_encoding(StringRef::from_lit(R"(chunked;foo="")")));
CU_ASSERT(http2::check_transfer_encoding(
StringRef::from_lit(R"(chunked;foo="bar" , gzip)")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef{}));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit(",chunked")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit("chunked,")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit("chunked, ")));
CU_ASSERT(
!http2::check_transfer_encoding(StringRef::from_lit("foo,,chunked")));
CU_ASSERT(
!http2::check_transfer_encoding(StringRef::from_lit("chunked;foo")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit("chunked;")));
CU_ASSERT(
!http2::check_transfer_encoding(StringRef::from_lit("chunked;foo=bar;")));
CU_ASSERT(
!http2::check_transfer_encoding(StringRef::from_lit("chunked;?=bar")));
CU_ASSERT(
!http2::check_transfer_encoding(StringRef::from_lit("chunked;=bar")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit("chunked;;")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit("chunked?")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit(",")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit(" ")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit(";")));
CU_ASSERT(!http2::check_transfer_encoding(StringRef::from_lit("\"")));
CU_ASSERT(!http2::check_transfer_encoding(
StringRef::from_lit(R"(chunked;foo="bar)")));
CU_ASSERT(!http2::check_transfer_encoding(
StringRef::from_lit(R"(chunked;foo="bar\)")));
CU_ASSERT(
!http2::check_transfer_encoding(StringRef::from_lit(R"(chunked;foo="bar\)"
"\x0a"
R"(")")));
CU_ASSERT(
!http2::check_transfer_encoding(StringRef::from_lit(R"(chunked;foo=")"
"\x0a"
R"(")")));
CU_ASSERT(!http2::check_transfer_encoding(
StringRef::from_lit(R"(chunked;foo="bar",,gzip)")));
}
} // namespace shrpx

View File

@ -47,6 +47,7 @@ void test_http2_rewrite_clean_path(void);
void test_http2_get_pure_path_component(void);
void test_http2_construct_push_component(void);
void test_http2_contains_trailers(void);
void test_http2_check_transfer_encoding(void);
} // namespace shrpx

View File

@ -109,6 +109,8 @@ int main(int argc, char *argv[]) {
shrpx::test_http2_construct_push_component) ||
!CU_add_test(pSuite, "http2_contains_trailers",
shrpx::test_http2_contains_trailers) ||
!CU_add_test(pSuite, "http2_check_transfer_encoding",
shrpx::test_http2_check_transfer_encoding) ||
!CU_add_test(pSuite, "downstream_field_store_append_last_header",
shrpx::test_downstream_field_store_append_last_header) ||
!CU_add_test(pSuite, "downstream_field_store_header",

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
@ -3520,15 +3522,12 @@ HTTP/3 and QUIC:
NEW_TOKEN frame in the previous connection.
--frontend-quic-congestion-controller=<CC>
Specify a congestion controller algorithm for a frontend
QUIC connection. <CC> should be one of "cubic", "bbr",
and "bbr2".
QUIC connection. <CC> should be either "cubic" or
"bbr".
Default: )"
<< (config->quic.upstream.congestion_controller == NGTCP2_CC_ALGO_CUBIC
? "cubic"
: (config->quic.upstream.congestion_controller ==
NGTCP2_CC_ALGO_BBR
? "bbr"
: "bbr2"))
: "bbr")
<< R"(
--frontend-quic-secret-file=<PATH>
Path to file that contains secure random data to be used

View File

@ -4116,10 +4116,8 @@ 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 {
LOG(ERROR) << opt << ": must be one of cubic, bbr, and bbr2";
LOG(ERROR) << opt << ": must be either cubic or bbr";
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

@ -864,9 +864,6 @@ void Downstream::inspect_http1_request() {
auto transfer_encoding = req_.fs.header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding) {
req_.fs.content_length = -1;
if (util::iends_with_l(transfer_encoding->value, "chunked")) {
chunked_request_ = true;
}
}
auto expect = req_.fs.header(http2::HD_EXPECT);
@ -879,9 +876,6 @@ void Downstream::inspect_http1_response() {
auto transfer_encoding = resp_.fs.header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding) {
resp_.fs.content_length = -1;
if (util::iends_with_l(transfer_encoding->value, "chunked")) {
chunked_response_ = true;
}
}
}

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

@ -929,6 +929,11 @@ int htp_hdrs_completecb(llhttp_t *htp) {
for (auto &kv : resp.fs.headers()) {
kv.value = util::rstrip(balloc, kv.value);
if (kv.token == http2::HD_TRANSFER_ENCODING &&
!http2::check_transfer_encoding(kv.value)) {
return -1;
}
}
auto config = get_config();
@ -1004,6 +1009,16 @@ int htp_hdrs_completecb(llhttp_t *htp) {
resp.connection_close = !llhttp_should_keep_alive(htp);
downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
downstream->inspect_http1_response();
if (htp->flags & F_CHUNKED) {
downstream->set_chunked_response(true);
}
auto transfer_encoding = resp.fs.header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding && !downstream->get_chunked_response()) {
resp.connection_close = true;
}
if (downstream->get_upgraded()) {
// content-length must be ignored for upgraded connection.
resp.fs.content_length = -1;

View File

@ -345,6 +345,11 @@ int htp_hdrs_completecb(llhttp_t *htp) {
for (auto &kv : req.fs.headers()) {
kv.value = util::rstrip(balloc, kv.value);
if (kv.token == http2::HD_TRANSFER_ENCODING &&
!http2::check_transfer_encoding(kv.value)) {
return -1;
}
}
auto lgconf = log_config();
@ -414,6 +419,16 @@ int htp_hdrs_completecb(llhttp_t *htp) {
downstream->inspect_http1_request();
if (htp->flags & F_CHUNKED) {
downstream->set_chunked_request(true);
}
auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding &&
http2::legacy_http1(req.http_major, req.http_minor)) {
return -1;
}
auto faddr = handler->get_upstream_addr();
auto config = get_config();

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

@ -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>
@ -102,17 +105,34 @@ int nghttp2_inet_pton(int af, const char *src, void *dst) {
const char UPPER_XDIGITS[] = "0123456789ABCDEF";
bool in_rfc3986_unreserved_chars(const char c) {
static constexpr char unreserved[] = {'-', '.', '_', '~'};
return is_alpha(c) || is_digit(c) ||
std::find(std::begin(unreserved), std::end(unreserved), c) !=
std::end(unreserved);
switch (c) {
case '-':
case '.':
case '_':
case '~':
return true;
}
return is_alpha(c) || is_digit(c);
}
bool in_rfc3986_sub_delims(const char c) {
static constexpr char sub_delims[] = {'!', '$', '&', '\'', '(', ')',
'*', '+', ',', ';', '='};
return std::find(std::begin(sub_delims), std::end(sub_delims), c) !=
std::end(sub_delims);
switch (c) {
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
return true;
}
return false;
}
std::string percent_encode(const unsigned char *target, size_t len) {
@ -137,16 +157,37 @@ std::string percent_encode(const std::string &target) {
}
bool in_token(char c) {
static constexpr char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+',
'-', '.', '^', '_', '`', '|', '~'};
return is_alpha(c) || is_digit(c) ||
std::find(std::begin(extra), std::end(extra), c) != std::end(extra);
switch (c) {
case '!':
case '#':
case '$':
case '%':
case '&':
case '\'':
case '*':
case '+':
case '-':
case '.':
case '^':
case '_':
case '`':
case '|':
case '~':
return true;
}
return is_alpha(c) || is_digit(c);
}
bool in_attr_char(char c) {
static constexpr char bad[] = {'*', '\'', '%'};
return util::in_token(c) &&
std::find(std::begin(bad), std::end(bad), c) == std::end(bad);
switch (c) {
case '*':
case '\'':
case '%':
return false;
}
return util::in_token(c);
}
StringRef percent_encode_token(BlockAllocator &balloc,
@ -1680,11 +1721,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 +1736,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 +1752,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 +1772,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 +1801,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

@ -81,6 +81,7 @@ AM_CFLAGS = $(WARNCFLAGS) \
-I${top_srcdir}/lib/includes \
-I${top_builddir}/lib/includes \
-DBUILDING_NGHTTP2 \
-DNGHTTP2_STATICLIB \
@CUNIT_CFLAGS@ @DEFS@
TESTS = main

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

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

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

@ -1 +1 @@
Subproject commit ab9ac5ecb30188683cf29b45c0e66ca2c94394f9
Subproject commit 4cf9b993b2cb3d5fbf1a34b86119f298edd00f1d