diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-11-05 10:02:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-11-05 10:02:47 +0000 |
commit | fc4232f93f2ed9958e8c7a76985d1ec69cddf9f4 (patch) | |
tree | 623d5210e2aeed4921cd21a85af0b530c1319c67 | |
parent | Adding upstream version 1.57.0. (diff) | |
download | nghttp2-upstream.tar.xz nghttp2-upstream.zip |
Adding upstream version 1.58.0.upstream/1.58.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
39 files changed, 881 insertions, 232 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 262c471..9d87efa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,8 +9,8 @@ env: OPENSSL1_VERSION: 1_1_1w+quic OPENSSL3_VERSION: 3.1.2+quic BORINGSSL_VERSION: 6ca49385b168f47a50e7172d82a590b218f55e4d - NGHTTP3_VERSION: v0.15.0 - NGTCP2_VERSION: v0.19.1 + NGHTTP3_VERSION: v1.0.0 + NGTCP2_VERSION: v1.0.1 jobs: build-cache: @@ -17,6 +17,7 @@ github issues [2]. Adam Gołębiowski Alek Storm Alex Nalivko +Alexandr Vlasov Alexandros Konstantinakis-Karmis Alexis La Goutte Amir Livneh diff --git a/CMakeLists.txt b/CMakeLists.txt index 42ec478..2c4139d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,12 +24,12 @@ cmake_minimum_required(VERSION 3.0) # XXX using 1.8.90 instead of 1.9.0-DEV -project(nghttp2 VERSION 1.57.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 39) -set(LT_REVISION 0) +set(LT_REVISION 1) set(LT_AGE 25) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) @@ -343,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) @@ -506,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} diff --git a/Makefile.am b/Makefile.am index 894165b..683b989 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 @@ -130,8 +130,8 @@ following libraries are required: <https://github.com/quictls/openssl/tree/OpenSSL_1_1_1w+quic>`_; or `BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit 6ca49385b168f47a50e7172d82a590b218f55e4d) -* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ 0.19.x -* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ 0.15.x +* `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. @@ -354,7 +354,7 @@ Build nghttp3: .. code-block:: text - $ git clone --depth 1 -b v0.15.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.19.1 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 \ @@ -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. diff --git a/cmake/PickyWarningsC.cmake b/cmake/PickyWarningsC.cmake new file mode 100644 index 0000000..50eb789 --- /dev/null +++ b/cmake/PickyWarningsC.cmake @@ -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() diff --git a/cmake/PickyWarningsCXX.cmake b/cmake/PickyWarningsCXX.cmake new file mode 100644 index 0000000..4699733 --- /dev/null +++ b/cmake/PickyWarningsCXX.cmake @@ -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() diff --git a/configure.ac b/configure.ac index e3e743a..ad9b316 100644 --- a/configure.ac +++ b/configure.ac @@ -25,7 +25,7 @@ dnl Do not change user variables! dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html AC_PREREQ(2.61) -AC_INIT([nghttp2], [1.57.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]) @@ -45,7 +45,7 @@ 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, 39) -AC_SUBST(LT_REVISION, 0) +AC_SUBST(LT_REVISION, 1) AC_SUBST(LT_AGE, 25) major=`echo $PACKAGE_VERSION |cut -d. -f1 | 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.19.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.19.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.15.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) diff --git a/doc/h2load.1 b/doc/h2load.1 index 58d826a..9f4cec5 100644 --- a/doc/h2load.1 +++ b/doc/h2load.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "H2LOAD" "1" "Oct 10, 2023" "1.57.0" "nghttp2" +.TH "H2LOAD" "1" "Oct 27, 2023" "1.58.0" "nghttp2" .SH NAME h2load \- HTTP/2 benchmarking tool .SH SYNOPSIS diff --git a/doc/nghttp.1 b/doc/nghttp.1 index 025ff99..4bf274f 100644 --- a/doc/nghttp.1 +++ b/doc/nghttp.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NGHTTP" "1" "Oct 10, 2023" "1.57.0" "nghttp2" +.TH "NGHTTP" "1" "Oct 27, 2023" "1.58.0" "nghttp2" .SH NAME nghttp \- HTTP/2 client .SH SYNOPSIS diff --git a/doc/nghttpd.1 b/doc/nghttpd.1 index 847af30..232757d 100644 --- a/doc/nghttpd.1 +++ b/doc/nghttpd.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NGHTTPD" "1" "Oct 10, 2023" "1.57.0" "nghttp2" +.TH "NGHTTPD" "1" "Oct 27, 2023" "1.58.0" "nghttp2" .SH NAME nghttpd \- HTTP/2 server .SH SYNOPSIS diff --git a/doc/nghttpx.1 b/doc/nghttpx.1 index 1542a26..1aaa47e 100644 --- a/doc/nghttpx.1 +++ b/doc/nghttpx.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NGHTTPX" "1" "Oct 10, 2023" "1.57.0" "nghttp2" +.TH "NGHTTPX" "1" "Oct 27, 2023" "1.58.0" "nghttp2" .SH NAME nghttpx \- HTTP/2 proxy .SH SYNOPSIS @@ -1834,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 \(dqbbrv2\(dq. +QUIC connection. <CC> should be either \(dqcubic\(dq or +\(dqbbr\(dq. .sp Default: \fBcubic\fP .UNINDENT diff --git a/doc/nghttpx.1.rst b/doc/nghttpx.1.rst index a103745..b70b163 100644 --- a/doc/nghttpx.1.rst +++ b/doc/nghttpx.1.rst @@ -1675,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 "bbrv2". + QUIC connection. <CC> should be either "cubic" or + "bbr". Default: ``cubic`` diff --git a/doc/sources/security.rst b/doc/sources/security.rst index ab27eab..5a8fcd0 100644 --- a/doc/sources/security.rst +++ b/doc/sources/security.rst @@ -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. diff --git a/docker/Dockerfile b/docker/Dockerfile index 0c51708..e7b5496 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,7 +15,7 @@ RUN git clone --depth 1 -b OpenSSL_1_1_1w+quic https://github.com/quictls/openss cd .. && \ rm -rf openssl -RUN git clone --depth 1 -b v0.15.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.15.0 https://github.com/ngtcp2/nghttp3 && \ cd .. && \ rm -rf nghttp3 -RUN git clone --depth 1 -b v0.19.1 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 \ @@ -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.15.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.13.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.12.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 ) @@ -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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +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,8 +56,8 @@ 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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.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= diff --git a/integration-tests/CMakeLists.txt b/integration-tests/CMakeLists.txt index d9385a5..0815cb3 100644 --- a/integration-tests/CMakeLists.txt +++ b/integration-tests/CMakeLists.txt @@ -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} ) diff --git a/integration-tests/Makefile.am b/integration-tests/Makefile.am index 38cd119..21166ed 100644 --- a/integration-tests/Makefile.am +++ b/integration-tests/Makefile.am @@ -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 \ diff --git a/integration-tests/nghttpx_http1_test.go b/integration-tests/nghttpx_http1_test.go index ca74a2e..740396d 100644 --- a/integration-tests/nghttpx_http1_test.go +++ b/integration-tests/nghttpx_http1_test.go @@ -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) + } +} diff --git a/integration-tests/server_tester.go b/integration-tests/server_tester.go index b341aaa..6a4ce65 100644 --- a/integration-tests/server_tester.go +++ b/integration-tests/server_tester.go @@ -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 != "" { diff --git a/integration-tests/server_tester_http3.go b/integration-tests/server_tester_http3.go new file mode 100644 index 0000000..7bbc4e7 --- /dev/null +++ b/integration-tests/server_tester_http3.go @@ -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 +} diff --git a/lib/nghttp2_map.c b/lib/nghttp2_map.c index 5f63fc2..0aaaf29 100644 --- a/lib/nghttp2_map.c +++ b/lib/nghttp2_map.c @@ -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, diff --git a/lib/nghttp2_map.h b/lib/nghttp2_map.h index d90245a..236d282 100644 --- a/lib/nghttp2_map.h +++ b/lib/nghttp2_map.h @@ -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 */ diff --git a/lib/nghttp2_time.c b/lib/nghttp2_time.c index 2a5f1a6..897556f 100644 --- a/lib/nghttp2_time.c +++ b/lib/nghttp2_time.c @@ -32,7 +32,7 @@ # include <sysinfoapi.h> #endif /* HAVE_SYSINFOAPI_H */ -#ifndef HAVE_GETTICKCOUNT64 +#if !defined(HAVE_GETTICKCOUNT64) || defined(__CYGWIN__) static uint64_t time_now_sec(void) { time_t t = time(NULL); @@ -42,9 +42,11 @@ static uint64_t time_now_sec(void) { return (uint64_t)t; } -#endif /* HAVE_GETTICKCOUNT64 */ +#endif /* !HAVE_GETTICKCOUNT64 || __CYGWIN__ */ -#ifdef HAVE_CLOCK_GETTIME +#if defined(HAVE_GETTICKCOUNT64) && !defined(__CYGWIN__) +uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; } +#elif defined(HAVE_CLOCK_GETTIME) uint64_t nghttp2_time_now_sec(void) { struct timespec tp; int rv = clock_gettime(CLOCK_MONOTONIC, &tp); @@ -55,8 +57,6 @@ uint64_t nghttp2_time_now_sec(void) { return (uint64_t)tp.tv_sec; } -#elif defined(HAVE_GETTICKCOUNT64) -uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; } -#else /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */ +#else /* (!HAVE_CLOCK_GETTIME || __CYGWIN__) && !HAVE_GETTICKCOUNT64 */ uint64_t nghttp2_time_now_sec(void) { return time_now_sec(); } -#endif /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */ +#endif /* (!HAVE_CLOCK_GETTIME || __CYGWIN__) && !HAVE_GETTICKCOUNT64 */ diff --git a/src/http2.cc b/src/http2.cc index 7685879..07d0144 100644 --- a/src/http2.cc +++ b/src/http2.cc @@ -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 diff --git a/src/http2.h b/src/http2.h index fc88e4d..e5b4f1b 100644 --- a/src/http2.h +++ b/src/http2.h @@ -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 diff --git a/src/http2_test.cc b/src/http2_test.cc index 5760726..f8be9f4 100644 --- a/src/http2_test.cc +++ b/src/http2_test.cc @@ -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 diff --git a/src/http2_test.h b/src/http2_test.h index 0d63020..382470d 100644 --- a/src/http2_test.h +++ b/src/http2_test.h @@ -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 diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index 417ed54..07373d5 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -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", diff --git a/src/shrpx.cc b/src/shrpx.cc index d510b7e..a6ed8f8 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -3522,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 "bbrv2". + 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" - : "bbrv2")) + : "bbr") << R"( --frontend-quic-secret-file=<PATH> Path to file that contains secure random data to be used diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 453261c..bcb48eb 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -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("bbrv2", optarg)) { - config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR_V2; } else { - LOG(ERROR) << opt << ": must be one of cubic, bbr, and bbrv2"; + LOG(ERROR) << opt << ": must be either cubic or bbr"; return -1; } #endif // ENABLE_HTTP3 diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index 245e00b..9ea52b4 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -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; - } } } diff --git a/src/shrpx_http3_upstream.cc b/src/shrpx_http3_upstream.cc index 5bc3bff..bc5c400 100644 --- a/src/shrpx_http3_upstream.cc +++ b/src/shrpx_http3_upstream.cc @@ -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; diff --git a/src/shrpx_http3_upstream.h b/src/shrpx_http3_upstream.h index 89dfc17..1807652 100644 --- a/src/shrpx_http3_upstream.h +++ b/src/shrpx_http3_upstream.h @@ -125,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); diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index e464471..bdb4294 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -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; diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 79cc22a..49d2088 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -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(); diff --git a/src/util.cc b/src/util.cc index 6abfc3e..9d0c1bd 100644 --- a/src/util.cc +++ b/src/util.cc @@ -105,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) { @@ -140,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, diff --git a/tests/Makefile.am b/tests/Makefile.am index 98d72b6..e6cdde1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 |