diff --git a/0001-CVE-2026-25547-braces-expansion.patch b/0001-CVE-2026-25547-braces-expansion.patch new file mode 100644 index 0000000..726ec71 --- /dev/null +++ b/0001-CVE-2026-25547-braces-expansion.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: tjuhasz +Date: Tue, 25 Feb 2026 14:21:26 +0100 +Subject: [PATCH] CVE-2026-25547: Fix brace expansion vulnerability + +Add expansion limit to prevent DoS attacks through excessive +brace expansion in the brace-expansion module. + +--- + deps/npm/node_modules/brace-expansion/index.js | 20 ++++++++++++-------- + 1 file changed, 12 insertions(+), 8 deletions(-) + +diff --git a/deps/npm/node_modules/brace-expansion/index.js b/deps/npm/node_modules/brace-expansion/index.js +--- a/deps/npm/node_modules/brace-expansion/index.js 2026-01-12 23:55:24.000000000 +0100 ++++ b/deps/npm/node_modules/brace-expansion/index.js 2026-02-25 14:21:26.829483831 +0100 +@@ -8,6 +8,8 @@ + var escComma = '\0COMMA'+Math.random()+'\0'; + var escPeriod = '\0PERIOD'+Math.random()+'\0'; + ++const EXPANSION_MAX = 100_000; ++ + function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) +@@ -61,9 +63,11 @@ + return parts; + } + +-function expandTop(str) { ++function expandTop(str, options = {}) { + if (!str) + return []; ++ ++ const { max = EXPANSION_MAX } = options; + + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved +@@ -75,7 +79,7 @@ + str = '\\{\\}' + str.substr(2); + } + +- return expand(escapeBraces(str), true).map(unescapeBraces); ++ return expand(escapeBraces(str), max, true).map(unescapeBraces); + } + + function embrace(str) { +@@ -92,7 +96,7 @@ + return i >= y; + } + +-function expand(str, isTop) { ++function expand(str, max, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); +@@ -101,11 +105,11 @@ + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length +- ? expand(m.post, false) ++ ? expand(m.post, max, false) + : ['']; + + if (/\$$/.test(m.pre)) { +- for (var k = 0; k < post.length; k++) { ++ for (var k = 0; k < post.length && k < max; k++) { + var expansion = pre+ '{' + m.body + '}' + post[k]; + expansions.push(expansion); + } +@@ -118,7 +122,7 @@ + // {a},b} + if (m.post.match(/,(?!,).*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; +- return expand(str); ++ return expand(str, max, true); + } + return [str]; + } +@@ -130,7 +134,7 @@ + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y +- n = expand(n[0], false).map(embrace); ++ n = expand(n[0], max, false).map(embrace); + if (n.length === 1) { + return post.map(function(p) { + return m.pre + n[0] + p; +@@ -185,12 +189,12 @@ + N = []; + + for (var j = 0; j < n.length; j++) { +- N.push.apply(N, expand(n[j], false)); ++ N.push.apply(N, expand(n[j], max, false)); + } + } + + for (var j = 0; j < N.length; j++) { +- for (var k = 0; k < post.length; k++) { ++ for (var k = 0; k < post.length && expansions.length < max; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); diff --git a/0001-deps-update-nghttp2-to-1.68.1.patch b/0001-deps-update-nghttp2-to-1.68.1.patch new file mode 100644 index 0000000..31843ed --- /dev/null +++ b/0001-deps-update-nghttp2-to-1.68.1.patch @@ -0,0 +1,7712 @@ +From 6c2d85acee9efe29348068eadb38c8fec4f04ed0 Mon Sep 17 00:00:00 2001 +From: Andrei Radchenko +Date: Thu, 26 Mar 2026 08:58:20 +0100 +Subject: [PATCH] deps: update nghttp2 to 1.68.1 + +--- + deps/nghttp2/lib/CMakeLists.txt | 40 +- + deps/nghttp2/lib/Makefile.am | 2 +- + deps/nghttp2/lib/Makefile.in | 6 +- + deps/nghttp2/lib/config.cmake.in | 3 + + deps/nghttp2/lib/includes/Makefile.in | 4 + + deps/nghttp2/lib/includes/nghttp2/nghttp2.h | 315 ++--- + .../nghttp2/lib/includes/nghttp2/nghttp2ver.h | 4 +- + deps/nghttp2/lib/nghttp2_alpn.h | 4 +- + deps/nghttp2/lib/nghttp2_buf.h | 4 +- + deps/nghttp2/lib/nghttp2_callbacks.c | 5 + + deps/nghttp2/lib/nghttp2_callbacks.h | 5 +- + deps/nghttp2/lib/nghttp2_debug.c | 4 +- + deps/nghttp2/lib/nghttp2_debug.h | 8 +- + deps/nghttp2/lib/nghttp2_extpri.h | 4 +- + deps/nghttp2/lib/nghttp2_frame.c | 10 + + deps/nghttp2/lib/nghttp2_frame.h | 4 +- + deps/nghttp2/lib/nghttp2_hd.c | 11 + + deps/nghttp2/lib/nghttp2_hd.h | 4 +- + deps/nghttp2/lib/nghttp2_hd_huffman.h | 4 +- + deps/nghttp2/lib/nghttp2_helper.h | 4 +- + deps/nghttp2/lib/nghttp2_http.c | 17 +- + deps/nghttp2/lib/nghttp2_http.h | 4 +- + deps/nghttp2/lib/nghttp2_int.h | 10 +- + deps/nghttp2/lib/nghttp2_map.c | 70 +- + deps/nghttp2/lib/nghttp2_map.h | 9 +- + deps/nghttp2/lib/nghttp2_mem.h | 4 +- + deps/nghttp2/lib/nghttp2_net.h | 16 +- + deps/nghttp2/lib/nghttp2_option.c | 7 + + deps/nghttp2/lib/nghttp2_option.h | 10 +- + deps/nghttp2/lib/nghttp2_outbound_item.h | 4 +- + deps/nghttp2/lib/nghttp2_pq.h | 4 +- + deps/nghttp2/lib/nghttp2_priority_spec.h | 4 +- + deps/nghttp2/lib/nghttp2_queue.h | 4 +- + deps/nghttp2/lib/nghttp2_ratelim.h | 4 +- + deps/nghttp2/lib/nghttp2_rcbuf.h | 4 +- + deps/nghttp2/lib/nghttp2_session.c | 1163 ++++++----------- + deps/nghttp2/lib/nghttp2_session.h | 147 +-- + deps/nghttp2/lib/nghttp2_stream.c | 847 +----------- + deps/nghttp2/lib/nghttp2_stream.h | 161 +-- + deps/nghttp2/lib/nghttp2_submit.c | 164 +-- + deps/nghttp2/lib/nghttp2_submit.h | 4 +- + deps/nghttp2/lib/nghttp2_time.c | 15 +- + deps/nghttp2/lib/nghttp2_time.h | 4 +- + deps/nghttp2/lib/nghttp2_version.c | 2 +- + deps/nghttp2/lib/sfparse.c | 1140 ++++++++++++---- + deps/nghttp2/lib/sfparse.h | 313 +++-- + 46 files changed, 1834 insertions(+), 2742 deletions(-) + create mode 100644 deps/nghttp2/lib/config.cmake.in + +diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt +index fda8dcb7..98e3dbe7 100644 +--- a/deps/nghttp2/lib/CMakeLists.txt ++++ b/deps/nghttp2/lib/CMakeLists.txt +@@ -47,7 +47,29 @@ if(WIN32) + set(NGHTTP2_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + endif() + +-set(EXPORT_SET "${PROJECT_NAME}-targets") ++set(NGHTTP2_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") ++set(NGHTTP2_VERSION_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") ++set(NGHTTP2_PROJECT_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}Config.cmake") ++set(NGHTTP2_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") ++set(NGHTTP2_CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") ++set(NGHTTP2_NAMESPACE "${PROJECT_NAME}::") ++set(NGHTTP2_VERSION ${PROJECT_VERSION}) ++ ++include(CMakePackageConfigHelpers) ++write_basic_package_version_file( ++ "${NGHTTP2_VERSION_CONFIG}" VERSION ${NGHTTP2_VERSION} COMPATIBILITY SameMajorVersion ++) ++configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.cmake.in" "${NGHTTP2_PROJECT_CONFIG}" @ONLY) ++ ++# Install cmake config files ++install( ++ FILES "${NGHTTP2_PROJECT_CONFIG}" "${NGHTTP2_VERSION_CONFIG}" ++ DESTINATION "${NGHTTP2_CONFIG_INSTALL_DIR}") ++ ++install( ++ EXPORT "${NGHTTP2_TARGETS_EXPORT_NAME}" ++ NAMESPACE "${NGHTTP2_NAMESPACE}" ++ DESTINATION "${NGHTTP2_CONFIG_INSTALL_DIR}") + + # Public shared library + if(BUILD_SHARED_LIBS) +@@ -65,7 +87,11 @@ if(BUILD_SHARED_LIBS) + $ + ) + +- install(TARGETS ${SHARED_LIB} EXPORT ${EXPORT_SET}) ++ install(TARGETS ${SHARED_LIB} ++ EXPORT ${NGHTTP2_TARGETS_EXPORT_NAME} ++ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" ++ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ++ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + list(APPEND nghttp2_exports ${SHARED_LIB}) + endif() + +@@ -87,7 +113,9 @@ if(BUILD_STATIC_LIBS) + + target_compile_definitions(${STATIC_LIB} PUBLIC "-DNGHTTP2_STATICLIB") + +- install(TARGETS ${STATIC_LIB} EXPORT ${EXPORT_SET}) ++ install(TARGETS ${STATIC_LIB} ++ EXPORT ${NGHTTP2_TARGETS_EXPORT_NAME} ++ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") + list(APPEND nghttp2_exports ${STATIC_LIB}) + endif() + +@@ -97,11 +125,7 @@ else() + set(LIB_SELECTED ${STATIC_LIB}) + endif() + +-add_library(${PROJECT_NAME}::nghttp2 ALIAS ${LIB_SELECTED}) ++add_library(nghttp2 ALIAS ${LIB_SELECTED}) + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +- +-install(EXPORT ${EXPORT_SET} +- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +- NAMESPACE ${PROJECT_NAME}::) +diff --git a/deps/nghttp2/lib/Makefile.am b/deps/nghttp2/lib/Makefile.am +index 1168c1e6..3a743df6 100644 +--- a/deps/nghttp2/lib/Makefile.am ++++ b/deps/nghttp2/lib/Makefile.am +@@ -22,7 +22,7 @@ + # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + SUBDIRS = includes + +-EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in ++EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in config.cmake.in + + AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG) + AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \ +diff --git a/deps/nghttp2/lib/Makefile.in b/deps/nghttp2/lib/Makefile.in +index 14686ef1..bb458a18 100644 +--- a/deps/nghttp2/lib/Makefile.in ++++ b/deps/nghttp2/lib/Makefile.in +@@ -360,6 +360,10 @@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ + LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ + LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ + LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ ++LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ ++LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ ++LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ ++LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ + LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ + LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ + LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ +@@ -492,7 +496,7 @@ top_srcdir = @top_srcdir@ + # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + SUBDIRS = includes +-EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in ++EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in config.cmake.in + AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG) + AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \ + @DEFS@ +diff --git a/deps/nghttp2/lib/config.cmake.in b/deps/nghttp2/lib/config.cmake.in +new file mode 100644 +index 00000000..0b677718 +--- /dev/null ++++ b/deps/nghttp2/lib/config.cmake.in +@@ -0,0 +1,3 @@ ++include(CMakeFindDependencyMacro) ++ ++include("${CMAKE_CURRENT_LIST_DIR}/@NGHTTP2_TARGETS_EXPORT_NAME@.cmake") +diff --git a/deps/nghttp2/lib/includes/Makefile.in b/deps/nghttp2/lib/includes/Makefile.in +index 778dcb87..a4687390 100644 +--- a/deps/nghttp2/lib/includes/Makefile.in ++++ b/deps/nghttp2/lib/includes/Makefile.in +@@ -265,6 +265,10 @@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ + LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ + LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ + LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ ++LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ ++LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ ++LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ ++LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ + LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ + LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ + LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ +diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +index 2ef49b8d..19c1874f 100644 +--- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h ++++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +@@ -2039,18 +2039,19 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, + /** + * @functypedef + * +- * Callback function invoked when a invalid header name/value pair is ++ * Callback function invoked when an invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback`. The difference is that this +- * callback is only invoked when a invalid header name/value pair is +- * received which is treated as stream error if this callback is not +- * set. Only invalid regular header field are passed to this +- * callback. In other words, invalid pseudo header field is not +- * passed to this callback. Also header fields which includes upper +- * cased latter are also treated as error without passing them to this +- * callback. ++ * callback is only invoked when an invalid header name/value pair is ++ * received which is treated as stream error if this callback returns ++ * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` and ++ * :type:`nghttp2_on_invalid_header_callback2` is not set. Only ++ * invalid regular header field are passed to this callback. In other ++ * words, invalid pseudo header field is not passed to this callback. ++ * Also header fields which includes upper cased latter are also ++ * treated as error without passing them to this callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see +@@ -2076,17 +2077,18 @@ typedef int (*nghttp2_on_invalid_header_callback)( + /** + * @functypedef + * +- * Callback function invoked when a invalid header name/value pair is ++ * Callback function invoked when an invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback2`. The difference is that this +- * callback is only invoked when a invalid header name/value pair is +- * received which is silently ignored if this callback is not set. +- * Only invalid regular header field are passed to this callback. In +- * other words, invalid pseudo header field is not passed to this +- * callback. Also header fields which includes upper cased latter are +- * also treated as error without passing them to this callback. ++ * callback is only invoked when an invalid header name/value pair is ++ * received which is silently ignored if neither this callback nor ++ * :type:`nghttp2_on_invalid_header_callback` is set. Only invalid ++ * regular header field are passed to this callback. In other words, ++ * invalid pseudo header field is not passed to this callback. Also ++ * header fields which includes upper cased latter are also treated as ++ * error without passing them to this callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see +@@ -2445,6 +2447,15 @@ typedef int (*nghttp2_error_callback2)(nghttp2_session *session, + int lib_error_code, const char *msg, + size_t len, void *user_data); + ++/** ++ * @functypedef ++ * ++ * Callback function invoked when unpredictable data of |destlen| ++ * bytes are needed. The implementation must write unpredictable data ++ * of |destlen| bytes into the buffer pointed by |dest|. ++ */ ++typedef void (*nghttp2_rand_callback)(uint8_t *dest, size_t destlen); ++ + struct nghttp2_session_callbacks; + + /** +@@ -2649,7 +2660,7 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( + /** + * @function + * +- * Sets callback function invoked when a invalid header name/value ++ * Sets callback function invoked when an invalid header name/value + * pair is received. If both + * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and + * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are +@@ -2662,7 +2673,7 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( + /** + * @function + * +- * Sets callback function invoked when a invalid header name/value ++ * Sets callback function invoked when an invalid header name/value + * pair is received. + */ + NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( +@@ -2833,6 +2844,18 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( + NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); + ++/** ++ * @function ++ * ++ * Sets callback function invoked when unpredictable data is needed. ++ * Although this callback is optional due to the backward ++ * compatibility, it is recommended to specify it to harden the ++ * runtime behavior against suspicious activities of a remote ++ * endpoint. ++ */ ++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_rand_callback( ++ nghttp2_session_callbacks *cbs, nghttp2_rand_callback rand_callback); ++ + /** + * @functypedef + * +@@ -3133,14 +3156,12 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + /** + * @function + * +- * This option prevents the library from retaining closed streams to +- * maintain the priority tree. If this option is set to nonzero, +- * applications can discard closed stream completely to save memory. ++ * .. warning:: + * +- * If +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * of value of 1 is submitted via `nghttp2_submit_settings()`, any +- * closed streams are not retained regardless of this option. ++ * Deprecated. Closed streams are not retained anymore. ++ * ++ * This function works as before, but it does not take any effect ++ * against :type:`nghttp2_session`. + */ + NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, + int val); +@@ -3170,16 +3191,11 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, + /** + * @function + * +- * This option, if set to nonzero, allows server to fallback to +- * :rfc:`7540` priorities if SETTINGS_NO_RFC7540_PRIORITIES was not +- * received from client, and server submitted +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * = 1 via `nghttp2_submit_settings()`. Most of the advanced +- * functionality for RFC 7540 priorities are still disabled. This +- * fallback only enables the minimal feature set of RFC 7540 +- * priorities to deal with priority signaling from client. ++ * .. warning:: ++ * Deprecated. :rfc:`7540` priorities have been removed. + * +- * Client session ignores this option. ++ * This function works as before, but it does not take any effect ++ * against :type:`nghttp2_session`. + */ + NGHTTP2_EXTERN void + nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, +@@ -3225,6 +3241,23 @@ nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, + NGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option, + size_t val); + ++/** ++ * @function ++ * ++ * This function sets the rate limit for the "glitches", the ++ * suspicious activities from a remote endpoint. It is a token-bucket ++ * based rate limiter. |burst| specifies the number of tokens that is ++ * initially available. The maximum number of tokens is capped to ++ * this value. |rate| specifies the number of tokens that are ++ * regenerated per second. When a suspicious activity is detected, ++ * some amount of tokens are consumed. If there is no token ++ * available, GOAWAY is sent to tear down the connection. |burst| and ++ * |rate| default to 1000 and 33 respectively. ++ */ ++NGHTTP2_EXTERN void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, ++ uint64_t burst, ++ uint64_t rate); ++ + /** + * @function + * +@@ -4179,39 +4212,9 @@ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return 0 without doing anything. +- * +- * Changes priority of existing stream denoted by |stream_id|. The +- * new priority specification is |pri_spec|. +- * +- * The priority is changed silently and instantly, and no PRIORITY +- * frame will be sent to notify the peer of this change. This +- * function may be useful for server to change the priority of pushed +- * stream. +- * +- * If |session| is initialized as server, and ``pri_spec->stream_id`` +- * points to the idle stream, the idle stream is created if it does +- * not exist. The created idle stream will depend on root stream +- * (stream 0) with weight 16. +- * +- * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not +- * found, we use default priority instead of given |pri_spec|. That +- * is make stream depend on root stream with weight 16. +- * +- * If +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * of value of 1 is submitted via `nghttp2_submit_settings()`, this +- * function does nothing and returns 0. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: ++ * prioritization scheme. + * +- * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` +- * Out of memory. +- * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` +- * Attempted to depend on itself; or no stream exist for the given +- * |stream_id|; or |stream_id| is 0 ++ * This function is noop. It always returns 0. + */ + NGHTTP2_EXTERN int + nghttp2_session_change_stream_priority(nghttp2_session *session, +@@ -4225,51 +4228,9 @@ nghttp2_session_change_stream_priority(nghttp2_session *session, + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return 0 without doing anything. +- * +- * Creates idle stream with the given |stream_id|, and priority +- * |pri_spec|. +- * +- * The stream creation is done without sending PRIORITY frame, which +- * means that peer does not know about the existence of this idle +- * stream in the local endpoint. +- * +- * RFC 7540 does not disallow the use of creation of idle stream with +- * odd or even stream ID regardless of client or server. So this +- * function can create odd or even stream ID regardless of client or +- * server. But probably it is a bit safer to use the stream ID the +- * local endpoint can initiate (in other words, use odd stream ID for +- * client, and even stream ID for server), to avoid potential +- * collision from peer's instruction. Also we can use +- * `nghttp2_session_set_next_stream_id()` to avoid to open created +- * idle streams accidentally if we follow this recommendation. +- * +- * If |session| is initialized as server, and ``pri_spec->stream_id`` +- * points to the idle stream, the idle stream is created if it does +- * not exist. The created idle stream will depend on root stream +- * (stream 0) with weight 16. +- * +- * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not +- * found, we use default priority instead of given |pri_spec|. That +- * is make stream depend on root stream with weight 16. +- * +- * If +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * of value of 1 is submitted via `nghttp2_submit_settings()`, this +- * function does nothing and returns 0. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: ++ * prioritization scheme. + * +- * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` +- * Out of memory. +- * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` +- * Attempted to depend on itself; or stream denoted by |stream_id| +- * already exists; or |stream_id| cannot be used to create idle +- * stream (in other words, local endpoint has already opened +- * stream ID greater than or equal to the given stream ID; or +- * |stream_id| is 0 ++ * This function is noop. It always returns 0. + */ + NGHTTP2_EXTERN int + nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, +@@ -4505,23 +4466,7 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + * + * Submits HEADERS frame and optionally one or more DATA frames. + * +- * The |pri_spec| is a deprecated priority specification of this +- * request. ``NULL`` means the default priority (see +- * `nghttp2_priority_spec_default_init()`). To specify the priority, +- * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, +- * this function will copy its data members. +- * +- * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, +- * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` +- * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes +- * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than +- * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes +- * :macro:`NGHTTP2_MAX_WEIGHT`. +- * +- * If +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * of value of 1 is received by a remote endpoint, |pri_spec| is +- * ignored, and treated as if ``NULL`` is specified. ++ * The |pri_spec| is ignored. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include +@@ -4564,9 +4509,6 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. +- * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` +- * Trying to depend on itself (new stream ID equals +- * ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is server session. + * +@@ -4594,25 +4536,7 @@ NGHTTP2_EXTERN int32_t nghttp2_submit_request( + * + * Submits HEADERS frame and optionally one or more DATA frames. + * +- * The |pri_spec| is a deprecated priority specification of this +- * request. ``NULL`` means the default priority (see +- * `nghttp2_priority_spec_default_init()`). To specify the priority, +- * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, +- * this function will copy its data members. In the future release +- * after the end of 2024, this function will ignore |pri_spec| and +- * behave as if ``NULL`` is given. +- * +- * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, +- * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` +- * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes +- * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than +- * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes +- * :macro:`NGHTTP2_MAX_WEIGHT`. +- * +- * If +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * of value of 1 is received by a remote endpoint, |pri_spec| is +- * ignored, and treated as if ``NULL`` is specified. ++ * The |pri_spec| is ignored. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include +@@ -4655,9 +4579,6 @@ NGHTTP2_EXTERN int32_t nghttp2_submit_request( + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. +- * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` +- * Trying to depend on itself (new stream ID equals +- * ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is server session. + * +@@ -4899,24 +4820,7 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + * assigned stream ID will be returned. Otherwise, specify stream ID + * in |stream_id|. + * +- * The |pri_spec| is a deprecated priority specification of this +- * request. ``NULL`` means the default priority (see +- * `nghttp2_priority_spec_default_init()`). To specify the priority, +- * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, +- * this function will copy its data members. In the future release +- * after the end of 2024, this function will ignore |pri_spec| and +- * behave as if ``NULL`` is given. +- * +- * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, +- * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` +- * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes +- * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than +- * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. +- * +- * If +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * of value of 1 is received by a remote endpoint, |pri_spec| is +- * ignored, and treated as if ``NULL`` is specified. ++ * The |pri_spec| is ignored. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include +@@ -4956,8 +4860,7 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` +- * The |stream_id| is 0; or trying to depend on itself (stream ID +- * equals ``pri_spec->stream_id``). ++ * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. This happens if stream denoted by |stream_id| +@@ -5083,40 +4986,9 @@ NGHTTP2_EXTERN int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags, + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return 0 without doing anything. +- * +- * Submits PRIORITY frame to change the priority of stream |stream_id| +- * to the priority specification |pri_spec|. +- * +- * The |flags| is currently ignored and should be +- * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. +- * +- * The |pri_spec| is a deprecated priority specification of this +- * request. ``NULL`` is not allowed for this function. To specify the +- * priority, use `nghttp2_priority_spec_init()`. This function will +- * copy its data members. +- * +- * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, +- * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` +- * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes +- * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than +- * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes +- * :macro:`NGHTTP2_MAX_WEIGHT`. +- * +- * If +- * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` +- * of value of 1 is received by a remote endpoint, this function does +- * nothing and returns 0. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: ++ * prioritization scheme. + * +- * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` +- * Out of memory. +- * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` +- * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to +- * depend on itself. ++ * This function is noop. It always returns 0. + */ + NGHTTP2_EXTERN int + nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, +@@ -6885,11 +6757,9 @@ nghttp2_session_get_root_stream(nghttp2_session *session); + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return NULL. ++ * prioritization scheme. + * +- * Returns the parent stream of |stream| in dependency tree. Returns +- * NULL if there is no such stream. ++ * This function always returns NULL. + */ + NGHTTP2_EXTERN nghttp2_stream * + nghttp2_stream_get_parent(nghttp2_stream *stream); +@@ -6903,11 +6773,9 @@ NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream); + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return NULL. ++ * prioritization scheme. + * +- * Returns the next sibling stream of |stream| in dependency tree. +- * Returns NULL if there is no such stream. ++ * This function always returns NULL. + */ + NGHTTP2_EXTERN nghttp2_stream * + nghttp2_stream_get_next_sibling(nghttp2_stream *stream); +@@ -6919,11 +6787,9 @@ nghttp2_stream_get_next_sibling(nghttp2_stream *stream); + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return NULL. ++ * prioritization scheme. + * +- * Returns the previous sibling stream of |stream| in dependency tree. +- * Returns NULL if there is no such stream. ++ * This function always returns NULL. + */ + NGHTTP2_EXTERN nghttp2_stream * + nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); +@@ -6935,11 +6801,9 @@ nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return NULL. ++ * prioritization scheme. + * +- * Returns the first child stream of |stream| in dependency tree. +- * Returns NULL if there is no such stream. ++ * This function always returns NULL. + */ + NGHTTP2_EXTERN nghttp2_stream * + nghttp2_stream_get_first_child(nghttp2_stream *stream); +@@ -6951,11 +6815,9 @@ nghttp2_stream_get_first_child(nghttp2_stream *stream); + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return +- * :macro:`NGHTTP2_DEFAULT_WEIGHT`. ++ * prioritization scheme. + * +- * Returns dependency weight to the parent stream of |stream|. ++ * This function always returns :macro:`NGHTTP2_DEFAULT_WEIGHT`. + */ + NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); + +@@ -6966,10 +6828,9 @@ NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible +- * prioritization scheme. In the future release after the end of +- * 2024, this function will always return 0. ++ * prioritization scheme. + * +- * Returns the sum of the weight for |stream|'s children. ++ * This function always returns 0. + */ + NGHTTP2_EXTERN int32_t + nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream); +diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +index 827c9989..95acef22 100644 +--- a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h ++++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +@@ -29,7 +29,7 @@ + * @macro + * Version number of the nghttp2 library release + */ +-#define NGHTTP2_VERSION "1.64.0" ++#define NGHTTP2_VERSION "1.68.1" + + /** + * @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 0x014000 ++#define NGHTTP2_VERSION_NUM 0x014401 + + #endif /* NGHTTP2VER_H */ +diff --git a/deps/nghttp2/lib/nghttp2_alpn.h b/deps/nghttp2/lib/nghttp2_alpn.h +index 09810fd8..ecc9ed20 100644 +--- a/deps/nghttp2/lib/nghttp2_alpn.h ++++ b/deps/nghttp2/lib/nghttp2_alpn.h +@@ -27,8 +27,8 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +-#endif /* NGHTTP2_ALPN_H */ ++#endif /* !defined(NGHTTP2_ALPN_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_buf.h b/deps/nghttp2/lib/nghttp2_buf.h +index 95ff3706..636aa619 100644 +--- a/deps/nghttp2/lib/nghttp2_buf.h ++++ b/deps/nghttp2/lib/nghttp2_buf.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -409,4 +409,4 @@ int nghttp2_bufs_next_present(nghttp2_bufs *bufs); + */ + size_t nghttp2_bufs_len(nghttp2_bufs *bufs); + +-#endif /* NGHTTP2_BUF_H */ ++#endif /* !defined(NGHTTP2_BUF_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_callbacks.c b/deps/nghttp2/lib/nghttp2_callbacks.c +index 32fedd52..162ca6bd 100644 +--- a/deps/nghttp2/lib/nghttp2_callbacks.c ++++ b/deps/nghttp2/lib/nghttp2_callbacks.c +@@ -201,3 +201,8 @@ void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) { + cbs->error_callback2 = error_callback2; + } ++ ++void nghttp2_session_callbacks_set_rand_callback( ++ nghttp2_session_callbacks *cbs, nghttp2_rand_callback rand_callback) { ++ cbs->rand_callback = rand_callback; ++} +diff --git a/deps/nghttp2/lib/nghttp2_callbacks.h b/deps/nghttp2/lib/nghttp2_callbacks.h +index a611f485..4b45e578 100644 +--- a/deps/nghttp2/lib/nghttp2_callbacks.h ++++ b/deps/nghttp2/lib/nghttp2_callbacks.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -151,6 +151,7 @@ struct nghttp2_session_callbacks { + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback; + nghttp2_error_callback error_callback; + nghttp2_error_callback2 error_callback2; ++ nghttp2_rand_callback rand_callback; + }; + +-#endif /* NGHTTP2_CALLBACKS_H */ ++#endif /* !defined(NGHTTP2_CALLBACKS_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_debug.c b/deps/nghttp2/lib/nghttp2_debug.c +index 09dd2b28..b5f33ef3 100644 +--- a/deps/nghttp2/lib/nghttp2_debug.c ++++ b/deps/nghttp2/lib/nghttp2_debug.c +@@ -50,11 +50,11 @@ void nghttp2_set_debug_vprintf_callback( + static_debug_vprintf_callback = debug_vprintf_callback; + } + +-#else /* !DEBUGBUILD */ ++#else /* !defined(DEBUGBUILD) */ + + void nghttp2_set_debug_vprintf_callback( + nghttp2_debug_vprintf_callback debug_vprintf_callback) { + (void)debug_vprintf_callback; + } + +-#endif /* !DEBUGBUILD */ ++#endif /* !defined(DEBUGBUILD) */ +diff --git a/deps/nghttp2/lib/nghttp2_debug.h b/deps/nghttp2/lib/nghttp2_debug.h +index cbb4dd57..26566a62 100644 +--- a/deps/nghttp2/lib/nghttp2_debug.h ++++ b/deps/nghttp2/lib/nghttp2_debug.h +@@ -27,17 +27,17 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + + #ifdef DEBUGBUILD + # define DEBUGF(...) nghttp2_debug_vprintf(__VA_ARGS__) + void nghttp2_debug_vprintf(const char *format, ...); +-#else ++#else /* !defined(DEBUGBUILD) */ + # define DEBUGF(...) \ + do { \ + } while (0) +-#endif ++#endif /* !defined(DEBUGBUILD) */ + +-#endif /* NGHTTP2_DEBUG_H */ ++#endif /* !defined(NGHTTP2_DEBUG_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_extpri.h b/deps/nghttp2/lib/nghttp2_extpri.h +index db911972..2e039165 100644 +--- a/deps/nghttp2/lib/nghttp2_extpri.h ++++ b/deps/nghttp2/lib/nghttp2_extpri.h +@@ -28,7 +28,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -62,4 +62,4 @@ void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri); + */ + #define nghttp2_extpri_uint8_inc(PRI) (((PRI) & NGHTTP2_EXTPRI_INC_MASK) != 0) + +-#endif /* NGHTTP2_EXTPRI_H */ ++#endif /* !defined(NGHTTP2_EXTPRI_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_frame.c b/deps/nghttp2/lib/nghttp2_frame.c +index edc2aaaa..264ae9d3 100644 +--- a/deps/nghttp2/lib/nghttp2_frame.c ++++ b/deps/nghttp2/lib/nghttp2_frame.c +@@ -750,6 +750,16 @@ void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, + uint8_t *p; + + altsvc = frame->payload; ++ ++ if (payloadlen == 0) { ++ altsvc->origin = NULL; ++ altsvc->origin_len = 0; ++ altsvc->field_value = NULL; ++ altsvc->field_value_len = 0; ++ ++ return; ++ } ++ + p = payload; + + altsvc->origin = p; +diff --git a/deps/nghttp2/lib/nghttp2_frame.h b/deps/nghttp2/lib/nghttp2_frame.h +index d5866880..ed4ab2c6 100644 +--- a/deps/nghttp2/lib/nghttp2_frame.h ++++ b/deps/nghttp2/lib/nghttp2_frame.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + #include "nghttp2_hd.h" +@@ -634,4 +634,4 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); + void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only); + +-#endif /* NGHTTP2_FRAME_H */ ++#endif /* !defined(NGHTTP2_FRAME_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_hd.c b/deps/nghttp2/lib/nghttp2_hd.c +index 55fc2cc6..ad85eed1 100644 +--- a/deps/nghttp2/lib/nghttp2_hd.c ++++ b/deps/nghttp2/lib/nghttp2_hd.c +@@ -594,8 +594,19 @@ static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { + static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t size; ++ const size_t max_size = SIZE_MAX / sizeof(nghttp2_hd_entry *); ++ ++ if (bufsize > max_size) { ++ return NGHTTP2_ERR_NOMEM; ++ } ++ + for (size = 1; size < bufsize; size <<= 1) + ; ++ ++ if (size > max_size) { ++ return NGHTTP2_ERR_NOMEM; ++ } ++ + ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (ringbuf->buffer == NULL) { + return NGHTTP2_ERR_NOMEM; +diff --git a/deps/nghttp2/lib/nghttp2_hd.h b/deps/nghttp2/lib/nghttp2_hd.h +index 38a31a83..bdc7fad5 100644 +--- a/deps/nghttp2/lib/nghttp2_hd.h ++++ b/deps/nghttp2/lib/nghttp2_hd.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -439,4 +439,4 @@ nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + */ + int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx); + +-#endif /* NGHTTP2_HD_H */ ++#endif /* !defined(NGHTTP2_HD_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_hd_huffman.h b/deps/nghttp2/lib/nghttp2_hd_huffman.h +index 2bfd5318..df037a8b 100644 +--- a/deps/nghttp2/lib/nghttp2_hd_huffman.h ++++ b/deps/nghttp2/lib/nghttp2_hd_huffman.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -69,4 +69,4 @@ typedef struct { + extern const nghttp2_huff_sym huff_sym_table[]; + extern const nghttp2_huff_decode huff_decode_table[][16]; + +-#endif /* NGHTTP2_HD_HUFFMAN_H */ ++#endif /* !defined(NGHTTP2_HD_HUFFMAN_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_helper.h b/deps/nghttp2/lib/nghttp2_helper.h +index f5de6290..7d640761 100644 +--- a/deps/nghttp2/lib/nghttp2_helper.h ++++ b/deps/nghttp2/lib/nghttp2_helper.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + #include +@@ -142,4 +142,4 @@ int nghttp2_should_send_window_update(int32_t local_window_size, + */ + uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); + +-#endif /* NGHTTP2_HELPER_H */ ++#endif /* !defined(NGHTTP2_HELPER_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_http.c b/deps/nghttp2/lib/nghttp2_http.c +index f222fe5e..60a0b01e 100644 +--- a/deps/nghttp2/lib/nghttp2_http.c ++++ b/deps/nghttp2/lib/nghttp2_http.c +@@ -207,7 +207,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, + if (!trailer && + /* Do not parse the header field in PUSH_PROMISE. */ + (stream->stream_id & 1) && +- (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) && + !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) { + nghttp2_extpri_from_uint8(&extpri, stream->http_extpri); + if (nghttp2_http_parse_priority(&extpri, nv->value->base, +@@ -660,17 +659,17 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream, + int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + size_t valuelen) { + nghttp2_extpri pri = *dest; +- sf_parser sfp; +- sf_vec key; +- sf_value val; ++ sfparse_parser sfp; ++ sfparse_vec key; ++ sfparse_value val; + int rv; + +- sf_parser_init(&sfp, value, valuelen); ++ sfparse_parser_init(&sfp, value, valuelen); + + for (;;) { +- rv = sf_parser_dict(&sfp, &key, &val); ++ rv = sfparse_parser_dict(&sfp, &key, &val); + if (rv != 0) { +- if (rv == SF_ERR_EOF) { ++ if (rv == SFPARSE_ERR_EOF) { + break; + } + +@@ -683,7 +682,7 @@ int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + + switch (key.base[0]) { + case 'i': +- if (val.type != SF_TYPE_BOOLEAN) { ++ if (val.type != SFPARSE_TYPE_BOOLEAN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + +@@ -691,7 +690,7 @@ int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + + break; + case 'u': +- if (val.type != SF_TYPE_INTEGER || ++ if (val.type != SFPARSE_TYPE_INTEGER || + val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH || + NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) { + return NGHTTP2_ERR_INVALID_ARGUMENT; +diff --git a/deps/nghttp2/lib/nghttp2_http.h b/deps/nghttp2/lib/nghttp2_http.h +index d9992fe6..2925334c 100644 +--- a/deps/nghttp2/lib/nghttp2_http.h ++++ b/deps/nghttp2/lib/nghttp2_http.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + #include "nghttp2_session.h" +@@ -97,4 +97,4 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream, + int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + size_t valuelen); + +-#endif /* NGHTTP2_HTTP_H */ ++#endif /* !defined(NGHTTP2_HTTP_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_int.h b/deps/nghttp2/lib/nghttp2_int.h +index b23585cc..d89cf154 100644 +--- a/deps/nghttp2/lib/nghttp2_int.h ++++ b/deps/nghttp2/lib/nghttp2_int.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -52,7 +52,11 @@ typedef enum { + * Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke + * nghttp2_on_invalid_header_callback. + */ +- NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106 ++ NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106, ++ /* ++ * Cancel pushed stream. ++ */ ++ NGHTTP2_ERR_PUSH_CANCEL = -107, + } nghttp2_internal_error; + +-#endif /* NGHTTP2_INT_H */ ++#endif /* !defined(NGHTTP2_INT_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_map.c b/deps/nghttp2/lib/nghttp2_map.c +index ee6bb196..f89f3103 100644 +--- a/deps/nghttp2/lib/nghttp2_map.c ++++ b/deps/nghttp2/lib/nghttp2_map.c +@@ -31,12 +31,13 @@ + + #include "nghttp2_helper.h" + +-#define NGHTTP2_INITIAL_TABLE_LENBITS 4 ++#define NGHTTP2_INITIAL_HASHBITS 4 + +-void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { ++void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem) { + map->mem = mem; + map->hashbits = 0; + map->table = NULL; ++ map->seed = seed; + map->size = 0; + } + +@@ -77,8 +78,13 @@ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr), + return 0; + } + +-static size_t hash(nghttp2_map_key_type key, size_t bits) { +- return (size_t)(((uint32_t)key * 2654435769u) >> (32 - bits)); ++static size_t map_hash(const nghttp2_map *map, nghttp2_map_key_type key) { ++ /* hasher from ++ https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs ++ We do not perform finalization here because we use top bits ++ anyway. */ ++ uint32_t h = ((uint32_t)key + map->seed) * 0x93d765dd; ++ return (size_t)((h * 2654435769u) >> (32 - map->hashbits)); + } + + static void map_bucket_swap(nghttp2_map_bucket *a, nghttp2_map_bucket *b) { +@@ -109,24 +115,28 @@ void nghttp2_map_print_distance(const nghttp2_map *map) { + continue; + } + +- idx = hash(bkt->key, map->hashbits); ++ idx = map_hash(map, bkt->key); + fprintf(stderr, "@%zu hash=%zu key=%d base=%zu distance=%u\n", i, +- hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl); ++ map_hash(map, bkt->key), bkt->key, idx, bkt->psl); + } + } +-#endif /* !WIN32 */ +- +-static int insert(nghttp2_map_bucket *table, size_t hashbits, +- nghttp2_map_key_type key, void *data) { +- size_t idx = hash(key, hashbits); +- nghttp2_map_bucket b = {0, key, data}, *bkt; +- size_t mask = (1u << hashbits) - 1; ++#endif /* !defined(WIN32) */ ++ ++static int map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { ++ size_t idx = map_hash(map, key); ++ nghttp2_map_bucket b = { ++ .key = key, ++ .data = data, ++ }; ++ nghttp2_map_bucket *bkt; ++ size_t mask = (1u << map->hashbits) - 1; + + for (;;) { +- bkt = &table[idx]; ++ bkt = &map->table[idx]; + + if (bkt->data == NULL) { + *bkt = b; ++ ++map->size; + return 0; + } + +@@ -147,15 +157,19 @@ static int insert(nghttp2_map_bucket *table, size_t hashbits, + + static int map_resize(nghttp2_map *map, size_t new_hashbits) { + size_t i; +- nghttp2_map_bucket *new_table; + nghttp2_map_bucket *bkt; + size_t tablelen; + int rv; ++ nghttp2_map new_map = { ++ .table = nghttp2_mem_calloc(map->mem, 1u << new_hashbits, ++ sizeof(nghttp2_map_bucket)), ++ .mem = map->mem, ++ .seed = map->seed, ++ .hashbits = new_hashbits, ++ }; + (void)rv; + +- new_table = nghttp2_mem_calloc(map->mem, 1u << new_hashbits, +- sizeof(nghttp2_map_bucket)); +- if (new_table == NULL) { ++ if (new_map.table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + +@@ -168,15 +182,15 @@ static int map_resize(nghttp2_map *map, size_t new_hashbits) { + continue; + } + +- rv = insert(new_table, new_hashbits, bkt->key, bkt->data); ++ rv = map_insert(&new_map, bkt->key, bkt->data); + + assert(0 == rv); + } + } + + nghttp2_mem_free(map->mem, map->table); ++ map->table = new_map.table; + map->hashbits = new_hashbits; +- map->table = new_table; + + return 0; + } +@@ -186,30 +200,28 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { + + assert(data); + +- /* Load factor is 0.75 */ ++ /* Load factor is 7/8 */ + /* Under the very initial condition, that is map->size == 0 and +- map->hashbits == 0, 4 > 3 still holds nicely. */ +- if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) { ++ map->hashbits == 0, 8 > 7 still holds nicely. */ ++ if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) { + if (map->hashbits) { + rv = map_resize(map, map->hashbits + 1); + if (rv != 0) { + return rv; + } + } else { +- rv = map_resize(map, NGHTTP2_INITIAL_TABLE_LENBITS); ++ rv = map_resize(map, NGHTTP2_INITIAL_HASHBITS); + if (rv != 0) { + return rv; + } + } + } + +- rv = insert(map->table, map->hashbits, key, data); ++ rv = map_insert(map, key, data); + if (rv != 0) { + return rv; + } + +- ++map->size; +- + return 0; + } + +@@ -223,7 +235,7 @@ void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) { + return NULL; + } + +- idx = hash(key, map->hashbits); ++ idx = map_hash(map, key); + mask = (1u << map->hashbits) - 1; + + for (;;) { +@@ -252,7 +264,7 @@ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + +- idx = hash(key, map->hashbits); ++ idx = map_hash(map, key); + mask = (1u << map->hashbits) - 1; + + for (;;) { +diff --git a/deps/nghttp2/lib/nghttp2_map.h b/deps/nghttp2/lib/nghttp2_map.h +index 5adfb78d..e45685bc 100644 +--- a/deps/nghttp2/lib/nghttp2_map.h ++++ b/deps/nghttp2/lib/nghttp2_map.h +@@ -28,7 +28,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -47,6 +47,7 @@ typedef struct nghttp2_map_bucket { + typedef struct nghttp2_map { + nghttp2_map_bucket *table; + nghttp2_mem *mem; ++ uint32_t seed; + size_t size; + size_t hashbits; + } nghttp2_map; +@@ -54,7 +55,7 @@ typedef struct nghttp2_map { + /* + * nghttp2_map_init initializes the map |map|. + */ +-void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); ++void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem); + + /* + * nghttp2_map_free deallocates any resources allocated for |map|. +@@ -123,6 +124,6 @@ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr), + + #ifndef WIN32 + void nghttp2_map_print_distance(const nghttp2_map *map); +-#endif /* !WIN32 */ ++#endif /* !defined(WIN32) */ + +-#endif /* NGHTTP2_MAP_H */ ++#endif /* !defined(NGHTTP2_MAP_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_mem.h b/deps/nghttp2/lib/nghttp2_mem.h +index f83dbcb8..10af7c69 100644 +--- a/deps/nghttp2/lib/nghttp2_mem.h ++++ b/deps/nghttp2/lib/nghttp2_mem.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -42,4 +42,4 @@ void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data); + void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); + void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); + +-#endif /* NGHTTP2_MEM_H */ ++#endif /* !defined(NGHTTP2_MEM_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_net.h b/deps/nghttp2/lib/nghttp2_net.h +index 521f9814..0c57d947 100644 +--- a/deps/nghttp2/lib/nghttp2_net.h ++++ b/deps/nghttp2/lib/nghttp2_net.h +@@ -27,28 +27,28 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #ifdef HAVE_ARPA_INET_H + # include +-#endif /* HAVE_ARPA_INET_H */ ++#endif /* defined(HAVE_ARPA_INET_H) */ + + #ifdef HAVE_NETINET_IN_H + # include +-#endif /* HAVE_NETINET_IN_H */ ++#endif /* defined(HAVE_NETINET_IN_H) */ + + #include + +-#if defined(WIN32) ++#ifdef WIN32 + /* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependency on that lib. */ + + # ifdef _MSC_VER + # define STIN static __inline +-# else ++# else /* !defined(_MSC_VER) */ + # define STIN static inline +-# endif ++# endif /* !defined(_MSC_VER) */ + + STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; +@@ -86,6 +86,6 @@ STIN uint16_t ntohs(uint16_t netshort) { + return res; + } + +-#endif /* WIN32 */ ++#endif /* defined(WIN32) */ + +-#endif /* NGHTTP2_NET_H */ ++#endif /* !defined(NGHTTP2_NET_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_option.c b/deps/nghttp2/lib/nghttp2_option.c +index 02a24eee..7e44a241 100644 +--- a/deps/nghttp2/lib/nghttp2_option.c ++++ b/deps/nghttp2/lib/nghttp2_option.c +@@ -155,3 +155,10 @@ void nghttp2_option_set_max_continuations(nghttp2_option *option, size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_CONTINUATIONS; + option->max_continuations = val; + } ++ ++void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, ++ uint64_t burst, uint64_t rate) { ++ option->opt_set_mask |= NGHTTP2_OPT_GLITCH_RATE_LIMIT; ++ option->glitch_burst = burst; ++ option->glitch_rate = rate; ++} +diff --git a/deps/nghttp2/lib/nghttp2_option.h b/deps/nghttp2/lib/nghttp2_option.h +index c89cb97f..711185ba 100644 +--- a/deps/nghttp2/lib/nghttp2_option.h ++++ b/deps/nghttp2/lib/nghttp2_option.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -72,6 +72,7 @@ typedef enum { + NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14, + NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT = 1 << 15, + NGHTTP2_OPT_MAX_CONTINUATIONS = 1 << 16, ++ NGHTTP2_OPT_GLITCH_RATE_LIMIT = 1 << 17, + } nghttp2_option_flag; + + /** +@@ -83,6 +84,11 @@ struct nghttp2_option { + */ + uint64_t stream_reset_burst; + uint64_t stream_reset_rate; ++ /** ++ * NGHTTP2_OPT_GLITCH_RATE_LIMIT ++ */ ++ uint64_t glitch_burst; ++ uint64_t glitch_rate; + /** + * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH + */ +@@ -154,4 +160,4 @@ struct nghttp2_option { + uint8_t user_recv_ext_types[32]; + }; + +-#endif /* NGHTTP2_OPTION_H */ ++#endif /* !defined(NGHTTP2_OPTION_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_outbound_item.h b/deps/nghttp2/lib/nghttp2_outbound_item.h +index 4e917500..6e8e310c 100644 +--- a/deps/nghttp2/lib/nghttp2_outbound_item.h ++++ b/deps/nghttp2/lib/nghttp2_outbound_item.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + #include "nghttp2_frame.h" +@@ -186,4 +186,4 @@ void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); + /* Returns the size of the queue */ + #define nghttp2_outbound_queue_size(Q) ((Q)->n) + +-#endif /* NGHTTP2_OUTBOUND_ITEM_H */ ++#endif /* !defined(NGHTTP2_OUTBOUND_ITEM_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_pq.h b/deps/nghttp2/lib/nghttp2_pq.h +index c8d90ef2..5dd863f8 100644 +--- a/deps/nghttp2/lib/nghttp2_pq.h ++++ b/deps/nghttp2/lib/nghttp2_pq.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + #include "nghttp2_int.h" +@@ -121,4 +121,4 @@ int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + */ + void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item); + +-#endif /* NGHTTP2_PQ_H */ ++#endif /* !defined(NGHTTP2_PQ_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_priority_spec.h b/deps/nghttp2/lib/nghttp2_priority_spec.h +index 92ece822..158e0dcd 100644 +--- a/deps/nghttp2/lib/nghttp2_priority_spec.h ++++ b/deps/nghttp2/lib/nghttp2_priority_spec.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -39,4 +39,4 @@ + */ + void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec); + +-#endif /* NGHTTP2_PRIORITY_SPEC_H */ ++#endif /* !defined(NGHTTP2_PRIORITY_SPEC_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_queue.h b/deps/nghttp2/lib/nghttp2_queue.h +index a06fa6c7..c63897c0 100644 +--- a/deps/nghttp2/lib/nghttp2_queue.h ++++ b/deps/nghttp2/lib/nghttp2_queue.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include "config.h" +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -48,4 +48,4 @@ void *nghttp2_queue_front(nghttp2_queue *queue); + void *nghttp2_queue_back(nghttp2_queue *queue); + int nghttp2_queue_empty(nghttp2_queue *queue); + +-#endif /* NGHTTP2_QUEUE_H */ ++#endif /* !defined(NGHTTP2_QUEUE_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_ratelim.h b/deps/nghttp2/lib/nghttp2_ratelim.h +index 866ed3f0..d1097f2f 100644 +--- a/deps/nghttp2/lib/nghttp2_ratelim.h ++++ b/deps/nghttp2/lib/nghttp2_ratelim.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -54,4 +54,4 @@ void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp); + succeeds, or -1. */ + int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n); + +-#endif /* NGHTTP2_RATELIM_H */ ++#endif /* !defined(NGHTTP2_RATELIM_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_rcbuf.h b/deps/nghttp2/lib/nghttp2_rcbuf.h +index 6814e709..ef82e1b3 100644 +--- a/deps/nghttp2/lib/nghttp2_rcbuf.h ++++ b/deps/nghttp2/lib/nghttp2_rcbuf.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -77,4 +77,4 @@ int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, + */ + void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf); + +-#endif /* NGHTTP2_RCBUF_H */ ++#endif /* !defined(NGHTTP2_RCBUF_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c +index df33a89e..0fbcc930 100644 +--- a/deps/nghttp2/lib/nghttp2_session.c ++++ b/deps/nghttp2/lib/nghttp2_session.c +@@ -41,6 +41,8 @@ + #include "nghttp2_debug.h" + #include "nghttp2_submit.h" + ++nghttp2_stream nghttp2_stream_root; ++ + /* + * Returns non-zero if the number of outgoing opened streams is larger + * than or equal to +@@ -146,11 +148,6 @@ static int session_detect_idle_stream(nghttp2_session *session, + return 0; + } + +-static int session_no_rfc7540_pri_no_fallback(nghttp2_session *session) { +- return session->pending_no_rfc7540_priorities == 1 && +- !session->fallback_rfc7540_priorities; +-} +- + static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) { + return (ext_types[type / 8] & (1 << (type & 0x7))) > 0; + } +@@ -441,6 +438,7 @@ static int session_new(nghttp2_session **session_ptr, + size_t max_deflate_dynamic_table_size = + NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; + size_t i; ++ uint32_t map_seed; + + if (mem == NULL) { + mem = nghttp2_mem_default(); +@@ -458,10 +456,6 @@ static int session_new(nghttp2_session **session_ptr, + /* next_stream_id is initialized in either + nghttp2_session_client_new2 or nghttp2_session_server_new2 */ + +- nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE, +- NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL, +- mem); +- + (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + (*session_ptr)->recv_window_size = 0; + (*session_ptr)->consumed_size = 0; +@@ -481,6 +475,10 @@ static int session_new(nghttp2_session **session_ptr, + NGHTTP2_DEFAULT_STREAM_RESET_BURST, + NGHTTP2_DEFAULT_STREAM_RESET_RATE); + ++ nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim, ++ NGHTTP2_DEFAULT_GLITCH_BURST, ++ NGHTTP2_DEFAULT_GLITCH_RATE); ++ + if (server) { + (*session_ptr)->server = 1; + } +@@ -548,11 +546,6 @@ static int session_new(nghttp2_session **session_ptr, + max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size; + } + +- if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) && +- option->no_closed_streams) { +- (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS; +- } +- + if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) { + (*session_ptr)->max_outbound_ack = option->max_outbound_ack; + } +@@ -562,13 +555,6 @@ static int session_new(nghttp2_session **session_ptr, + (*session_ptr)->max_settings = option->max_settings; + } + +- if ((option->opt_set_mask & +- NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES) && +- option->server_fallback_rfc7540_priorities) { +- (*session_ptr)->opt_flags |= +- NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES; +- } +- + if ((option->opt_set_mask & + NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) && + option->no_rfc9113_leading_and_trailing_ws_validation) { +@@ -585,6 +571,11 @@ static int session_new(nghttp2_session **session_ptr, + if (option->opt_set_mask & NGHTTP2_OPT_MAX_CONTINUATIONS) { + (*session_ptr)->max_continuations = option->max_continuations; + } ++ ++ if (option->opt_set_mask & NGHTTP2_OPT_GLITCH_RATE_LIMIT) { ++ nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim, ++ option->glitch_burst, option->glitch_rate); ++ } + } + + rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, +@@ -613,7 +604,13 @@ static int session_new(nghttp2_session **session_ptr, + goto fail_aob_framebuf; + } + +- nghttp2_map_init(&(*session_ptr)->streams, mem); ++ if (callbacks->rand_callback) { ++ callbacks->rand_callback((uint8_t *)&map_seed, sizeof(map_seed)); ++ } else { ++ map_seed = 0; ++ } ++ ++ nghttp2_map_init(&(*session_ptr)->streams, map_seed, mem); + + active_outbound_item_reset(&(*session_ptr)->aob, mem); + +@@ -810,7 +807,6 @@ void nghttp2_session_del(nghttp2_session *session) { + for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { + nghttp2_pq_free(&session->sched[i].ob_data); + } +- nghttp2_stream_free(&session->root); + + /* Have to free streams first, so that we can check + stream->item->queued */ +@@ -829,82 +825,6 @@ void nghttp2_session_del(nghttp2_session *session) { + nghttp2_mem_free(mem, session); + } + +-int nghttp2_session_reprioritize_stream( +- nghttp2_session *session, nghttp2_stream *stream, +- const nghttp2_priority_spec *pri_spec_in) { +- int rv; +- nghttp2_stream *dep_stream = NULL; +- nghttp2_priority_spec pri_spec_default; +- const nghttp2_priority_spec *pri_spec = pri_spec_in; +- +- assert((!session->server && session->pending_no_rfc7540_priorities != 1) || +- (session->server && !session_no_rfc7540_pri_no_fallback(session))); +- assert(pri_spec->stream_id != stream->stream_id); +- +- if (!nghttp2_stream_in_dep_tree(stream)) { +- return 0; +- } +- +- if (pri_spec->stream_id != 0) { +- dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); +- +- if (!dep_stream && +- session_detect_idle_stream(session, pri_spec->stream_id)) { +- nghttp2_priority_spec_default_init(&pri_spec_default); +- +- dep_stream = nghttp2_session_open_stream( +- session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, +- NGHTTP2_STREAM_IDLE, NULL); +- +- if (dep_stream == NULL) { +- return NGHTTP2_ERR_NOMEM; +- } +- } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { +- nghttp2_priority_spec_default_init(&pri_spec_default); +- pri_spec = &pri_spec_default; +- } +- } +- +- if (pri_spec->stream_id == 0) { +- dep_stream = &session->root; +- } else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) { +- DEBUGF("stream: cycle detected, dep_stream(%p)=%d stream(%p)=%d\n", +- dep_stream, dep_stream->stream_id, stream, stream->stream_id); +- +- nghttp2_stream_dep_remove_subtree(dep_stream); +- rv = nghttp2_stream_dep_add_subtree(stream->dep_prev, dep_stream); +- if (rv != 0) { +- return rv; +- } +- } +- +- assert(dep_stream); +- +- if (dep_stream == stream->dep_prev && !pri_spec->exclusive) { +- /* This is minor optimization when just weight is changed. */ +- nghttp2_stream_change_weight(stream, pri_spec->weight); +- +- return 0; +- } +- +- nghttp2_stream_dep_remove_subtree(stream); +- +- /* We have to update weight after removing stream from tree */ +- stream->weight = pri_spec->weight; +- +- if (pri_spec->exclusive) { +- rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream); +- } else { +- rv = nghttp2_stream_dep_add_subtree(dep_stream, stream); +- } +- +- if (rv != 0) { +- return rv; +- } +- +- return 0; +-} +- + static uint64_t pq_get_first_cycle(nghttp2_pq *pq) { + nghttp2_stream *stream; + +@@ -923,7 +843,6 @@ static int session_ob_data_push(nghttp2_session *session, + int inc; + nghttp2_pq *pq; + +- assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES); + assert(stream->queued == 0); + + urgency = nghttp2_extpri_uint8_urgency(stream->extpri); +@@ -952,7 +871,6 @@ static void session_ob_data_remove(nghttp2_session *session, + nghttp2_stream *stream) { + uint32_t urgency; + +- assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES); + assert(stream->queued == 1); + + urgency = nghttp2_extpri_uint8_urgency(stream->extpri); +@@ -969,14 +887,7 @@ static int session_attach_stream_item(nghttp2_session *session, + nghttp2_outbound_item *item) { + int rv; + +- rv = nghttp2_stream_attach_item(stream, item); +- if (rv != 0) { +- return rv; +- } +- +- if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) { +- return 0; +- } ++ nghttp2_stream_attach_item(stream, item); + + rv = session_ob_data_push(session, stream); + if (rv != 0) { +@@ -992,8 +903,7 @@ 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) { ++ if (!stream->queued) { + return; + } + +@@ -1004,8 +914,7 @@ 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) { ++ if (!stream->queued) { + return; + } + +@@ -1015,15 +924,9 @@ static void session_defer_stream_item(nghttp2_session *session, + static int session_resume_deferred_stream_item(nghttp2_session *session, + nghttp2_stream *stream, + uint8_t flags) { +- int rv; ++ nghttp2_stream_resume_deferred_item(stream, flags); + +- rv = nghttp2_stream_resume_deferred_item(stream, flags); +- if (rv != 0) { +- return rv; +- } +- +- if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || +- (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)) { ++ if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { + return 0; + } + +@@ -1168,7 +1071,6 @@ int nghttp2_session_add_item(nghttp2_session *session, + return 0; + case NGHTTP2_PUSH_PROMISE: { + nghttp2_headers_aux_data *aux_data; +- nghttp2_priority_spec pri_spec; + + aux_data = &item->aux_data.headers; + +@@ -1176,20 +1078,13 @@ int nghttp2_session_add_item(nghttp2_session *session, + return NGHTTP2_ERR_STREAM_CLOSED; + } + +- nghttp2_priority_spec_init(&pri_spec, stream->stream_id, +- NGHTTP2_DEFAULT_WEIGHT, 0); +- + if (!nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, +- NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, ++ NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_RESERVED, + aux_data->stream_user_data)) { + return NGHTTP2_ERR_NOMEM; + } + +- /* We don't have to call nghttp2_session_adjust_closed_stream() +- here, since stream->stream_id is local stream_id, and it does +- not affect closed stream count. */ +- + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + +@@ -1213,6 +1108,15 @@ int nghttp2_session_add_item(nghttp2_session *session, + + int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { ++ return nghttp2_session_add_rst_stream_continue( ++ session, stream_id, error_code, ++ /* continue_without_stream = */ 1); ++} ++ ++int nghttp2_session_add_rst_stream_continue(nghttp2_session *session, ++ int32_t stream_id, ++ uint32_t error_code, ++ int continue_without_stream) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; +@@ -1269,6 +1173,12 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + } + } + ++ /* To keep the old behaviour, do not fail if stream was not ++ found. */ ++ if (!continue_without_stream && !stream) { ++ return 0; ++ } ++ + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; +@@ -1290,15 +1200,11 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + + nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, +- nghttp2_priority_spec *pri_spec_in, + nghttp2_stream_state initial_state, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; +- nghttp2_stream *dep_stream = NULL; + int stream_alloc = 0; +- nghttp2_priority_spec pri_spec_default; +- nghttp2_priority_spec *pri_spec = pri_spec_in; + nghttp2_mem *mem; + + mem = &session->mem; +@@ -1311,23 +1217,9 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + + if (stream) { + assert(stream->state == NGHTTP2_STREAM_IDLE); +- assert((stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || +- nghttp2_stream_in_dep_tree(stream)); +- +- nghttp2_session_detach_idle_stream(session, stream); +- +- if (nghttp2_stream_in_dep_tree(stream)) { +- assert(!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)); ++ assert(initial_state != NGHTTP2_STREAM_IDLE); + +- rv = nghttp2_stream_dep_remove(stream); +- if (rv != 0) { +- return NULL; +- } +- +- if (session_no_rfc7540_pri_no_fallback(session)) { +- stream->flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES; +- } +- } ++ --session->num_idle_streams; + } else { + stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); + if (stream == NULL) { +@@ -1337,69 +1229,16 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + stream_alloc = 1; + } + +- if (session_no_rfc7540_pri_no_fallback(session) || +- session->remote_settings.no_rfc7540_priorities == 1) { +- /* For client which has not received server +- SETTINGS_NO_RFC7540_PRIORITIES = 1, send a priority signal +- opportunistically. */ +- if (session->server || +- session->remote_settings.no_rfc7540_priorities == 1) { +- nghttp2_priority_spec_default_init(&pri_spec_default); +- pri_spec = &pri_spec_default; +- } +- +- if (session->pending_no_rfc7540_priorities == 1) { +- flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES; +- } +- } else if (pri_spec->stream_id != 0) { +- dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); +- +- if (!dep_stream && +- session_detect_idle_stream(session, pri_spec->stream_id)) { +- /* Depends on idle stream, which does not exist in memory. +- Assign default priority for it. */ +- nghttp2_priority_spec_default_init(&pri_spec_default); +- +- dep_stream = nghttp2_session_open_stream( +- session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, +- NGHTTP2_STREAM_IDLE, NULL); +- +- if (dep_stream == NULL) { +- if (stream_alloc) { +- nghttp2_mem_free(mem, stream); +- } +- +- return NULL; +- } +- } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { +- /* If dep_stream is not part of dependency tree, stream will get +- default priority. This handles the case when +- pri_spec->stream_id == stream_id. This happens because we +- don't check pri_spec->stream_id against new stream ID in +- nghttp2_submit_request. This also handles the case when idle +- stream created by PRIORITY frame was opened. Somehow we +- first remove the idle stream from dependency tree. This is +- done to simplify code base, but ideally we should retain old +- dependency. But I'm not sure this adds values. */ +- nghttp2_priority_spec_default_init(&pri_spec_default); +- pri_spec = &pri_spec_default; +- } +- } +- + if (initial_state == NGHTTP2_STREAM_RESERVED) { + flags |= NGHTTP2_STREAM_FLAG_PUSH; + } + + if (stream_alloc) { + nghttp2_stream_init(stream, stream_id, flags, initial_state, +- pri_spec->weight, + (int32_t)session->remote_settings.initial_window_size, + (int32_t)session->local_settings.initial_window_size, +- stream_user_data, mem); +- +- if (session_no_rfc7540_pri_no_fallback(session)) { +- stream->seq = session->stream_seq++; +- } ++ stream_user_data); ++ stream->seq = session->stream_seq++; + + rv = nghttp2_map_insert(&session->streams, stream_id, stream); + if (rv != 0) { +@@ -1410,7 +1249,6 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + } else { + stream->flags = flags; + stream->state = initial_state; +- stream->weight = pri_spec->weight; + stream->stream_user_data = stream_user_data; + } + +@@ -1428,9 +1266,7 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + limit. That is one of the DOS vector. */ + break; + case NGHTTP2_STREAM_IDLE: +- /* Idle stream does not count toward the concurrent streams limit. +- This is used as anchor node in dependency tree. */ +- nghttp2_session_keep_idle_stream(session, stream); ++ ++session->num_idle_streams; + break; + default: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { +@@ -1440,31 +1276,11 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + } + } + +- if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { +- return stream; +- } +- +- if (pri_spec->stream_id == 0) { +- dep_stream = &session->root; +- } +- +- assert(dep_stream); +- +- if (pri_spec->exclusive) { +- rv = nghttp2_stream_dep_insert(dep_stream, stream); +- if (rv != 0) { +- return NULL; +- } +- } else { +- nghttp2_stream_dep_add(dep_stream, stream); +- } +- + return stream; + } + + int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { +- int rv; + nghttp2_stream *stream; + nghttp2_mem *mem; + int is_my_stream_id; +@@ -1528,207 +1344,26 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + /* Closes both directions just in case they are not closed yet */ + stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; + +- if (session->pending_no_rfc7540_priorities == 1) { +- return nghttp2_session_destroy_stream(session, stream); +- } +- +- if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 && +- session->server && !is_my_stream_id && +- nghttp2_stream_in_dep_tree(stream)) { +- /* On server side, retain stream at most MAX_CONCURRENT_STREAMS +- combined with the current active incoming streams to make +- dependency tree work better. */ +- nghttp2_session_keep_closed_stream(session, stream); +- } else { +- rv = nghttp2_session_destroy_stream(session, stream); +- if (rv != 0) { +- return rv; +- } +- } ++ nghttp2_session_destroy_stream(session, stream); + + return 0; + } + +-int nghttp2_session_destroy_stream(nghttp2_session *session, +- nghttp2_stream *stream) { ++void nghttp2_session_destroy_stream(nghttp2_session *session, ++ nghttp2_stream *stream) { + nghttp2_mem *mem; +- int rv; + + DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id); + + mem = &session->mem; + +- if (nghttp2_stream_in_dep_tree(stream)) { +- rv = nghttp2_stream_dep_remove(stream); +- if (rv != 0) { +- return rv; +- } +- } +- +- if (stream->queued && +- (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) { ++ if (stream->queued) { + session_ob_data_remove(session, stream); + } + + nghttp2_map_remove(&session->streams, stream->stream_id); + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); +- +- return 0; +-} +- +-void nghttp2_session_keep_closed_stream(nghttp2_session *session, +- nghttp2_stream *stream) { +- DEBUGF("stream: keep closed stream(%p)=%d, state=%d\n", stream, +- stream->stream_id, stream->state); +- +- if (session->closed_stream_tail) { +- session->closed_stream_tail->closed_next = stream; +- stream->closed_prev = session->closed_stream_tail; +- } else { +- session->closed_stream_head = stream; +- } +- session->closed_stream_tail = stream; +- +- ++session->num_closed_streams; +-} +- +-void nghttp2_session_keep_idle_stream(nghttp2_session *session, +- nghttp2_stream *stream) { +- DEBUGF("stream: keep idle stream(%p)=%d, state=%d\n", stream, +- stream->stream_id, stream->state); +- +- if (session->idle_stream_tail) { +- session->idle_stream_tail->closed_next = stream; +- stream->closed_prev = session->idle_stream_tail; +- } else { +- session->idle_stream_head = stream; +- } +- session->idle_stream_tail = stream; +- +- ++session->num_idle_streams; +-} +- +-void nghttp2_session_detach_idle_stream(nghttp2_session *session, +- nghttp2_stream *stream) { +- nghttp2_stream *prev_stream, *next_stream; +- +- DEBUGF("stream: detach idle stream(%p)=%d, state=%d\n", stream, +- stream->stream_id, stream->state); +- +- prev_stream = stream->closed_prev; +- next_stream = stream->closed_next; +- +- if (prev_stream) { +- prev_stream->closed_next = next_stream; +- } else { +- session->idle_stream_head = next_stream; +- } +- +- if (next_stream) { +- next_stream->closed_prev = prev_stream; +- } else { +- session->idle_stream_tail = prev_stream; +- } +- +- stream->closed_prev = NULL; +- stream->closed_next = NULL; +- +- --session->num_idle_streams; +-} +- +-int nghttp2_session_adjust_closed_stream(nghttp2_session *session) { +- size_t num_stream_max; +- int rv; +- +- if (session->local_settings.max_concurrent_streams == +- NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS) { +- num_stream_max = session->pending_local_max_concurrent_stream; +- } else { +- num_stream_max = session->local_settings.max_concurrent_streams; +- } +- +- DEBUGF("stream: adjusting kept closed streams num_closed_streams=%zu, " +- "num_incoming_streams=%zu, max_concurrent_streams=%zu\n", +- session->num_closed_streams, session->num_incoming_streams, +- num_stream_max); +- +- while (session->num_closed_streams > 0 && +- session->num_closed_streams + session->num_incoming_streams > +- num_stream_max) { +- nghttp2_stream *head_stream; +- nghttp2_stream *next; +- +- head_stream = session->closed_stream_head; +- +- assert(head_stream); +- +- next = head_stream->closed_next; +- +- rv = nghttp2_session_destroy_stream(session, head_stream); +- if (rv != 0) { +- return rv; +- } +- +- /* head_stream is now freed */ +- +- session->closed_stream_head = next; +- +- if (session->closed_stream_head) { +- session->closed_stream_head->closed_prev = NULL; +- } else { +- session->closed_stream_tail = NULL; +- } +- +- --session->num_closed_streams; +- } +- +- return 0; +-} +- +-int nghttp2_session_adjust_idle_stream(nghttp2_session *session) { +- size_t max; +- int rv; +- +- /* Make minimum number of idle streams 16, and maximum 100, which +- are arbitrary chosen numbers. */ +- max = nghttp2_min_uint32( +- 100, +- nghttp2_max_uint32( +- 16, nghttp2_min_uint32(session->local_settings.max_concurrent_streams, +- session->pending_local_max_concurrent_stream))); +- +- DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n", +- session->num_idle_streams, max); +- +- while (session->num_idle_streams > max) { +- nghttp2_stream *head; +- nghttp2_stream *next; +- +- head = session->idle_stream_head; +- assert(head); +- +- next = head->closed_next; +- +- rv = nghttp2_session_destroy_stream(session, head); +- if (rv != 0) { +- return rv; +- } +- +- /* head is now destroyed */ +- +- session->idle_stream_head = next; +- +- if (session->idle_stream_head) { +- session->idle_stream_head->closed_prev = NULL; +- } else { +- session->idle_stream_tail = NULL; +- } +- +- --session->num_idle_streams; +- } +- +- return 0; + } + + /* +@@ -2411,16 +2046,12 @@ static int session_prep_frame(nghttp2_session *session, + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, +- &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, +- aux_data->stream_user_data); ++ NGHTTP2_STREAM_INITIAL, aux_data->stream_user_data); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + +- /* We don't call nghttp2_session_adjust_closed_stream() here, +- since we don't keep closed stream in client side */ +- + rv = session_predicate_request_headers_send(session, item); + if (rv != 0) { + return rv; +@@ -2662,8 +2293,6 @@ static int session_prep_frame(nghttp2_session *session, + + nghttp2_outbound_item * + nghttp2_session_get_next_ob_item(nghttp2_session *session) { +- nghttp2_outbound_item *item; +- + if (nghttp2_outbound_queue_top(&session->ob_urgent)) { + return nghttp2_outbound_queue_top(&session->ob_urgent); + } +@@ -2679,11 +2308,6 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) { + } + + if (session->remote_window_size > 0) { +- item = nghttp2_stream_next_outbound_item(&session->root); +- if (item) { +- return item; +- } +- + return session_sched_get_next_outbound_item(session); + } + +@@ -2718,11 +2342,6 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) { + } + + if (session->remote_window_size > 0) { +- item = nghttp2_stream_next_outbound_item(&session->root); +- if (item) { +- return item; +- } +- + return session_sched_get_next_outbound_item(session); + } + +@@ -2781,7 +2400,6 @@ static int find_stream_on_goaway_func(void *entry, void *ptr) { + nghttp2_session_close_stream() inside nghttp2_map_each(). + Reuse closed_next member.. bad choice? */ + assert(stream->closed_next == NULL); +- assert(stream->closed_prev == NULL); + + if (arg->head) { + stream->closed_next = arg->head; +@@ -2837,11 +2455,6 @@ static void session_reschedule_stream(nghttp2_session *session, + nghttp2_stream *stream) { + stream->last_writelen = stream->item->frame.hd.length; + +- if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) { +- nghttp2_stream_reschedule(stream); +- return; +- } +- + if (!session->server) { + return; + } +@@ -3020,37 +2633,6 @@ static int session_after_frame_sent1(nghttp2_session *session) { + } + } + case NGHTTP2_PRIORITY: +- if (session->server || session->pending_no_rfc7540_priorities == 1) { +- return 0; +- } +- +- stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); +- +- if (!stream) { +- if (!session_detect_idle_stream(session, frame->hd.stream_id)) { +- return 0; +- } +- +- stream = nghttp2_session_open_stream( +- session, frame->hd.stream_id, NGHTTP2_FLAG_NONE, +- &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); +- if (!stream) { +- return NGHTTP2_ERR_NOMEM; +- } +- } else { +- rv = nghttp2_session_reprioritize_stream(session, stream, +- &frame->priority.pri_spec); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- } +- +- rv = nghttp2_session_adjust_idle_stream(session); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- + return 0; + case NGHTTP2_RST_STREAM: + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, +@@ -3239,14 +2821,6 @@ static nghttp2_ssize nghttp2_session_mem_send_internal(nghttp2_session *session, + aob = &session->aob; + framebufs = &aob->framebufs; + +- /* We may have idle streams more than we expect (e.g., +- nghttp2_session_change_stream_priority() or +- nghttp2_session_create_idle_stream()). Adjust them here. */ +- rv = nghttp2_session_adjust_idle_stream(session); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- + for (;;) { + switch (aob->state) { + case NGHTTP2_OB_POP_ITEM: { +@@ -3698,7 +3272,9 @@ static int session_call_on_invalid_header(nghttp2_session *session, + session, frame, nv->name->base, nv->name->len, nv->value->base, + nv->value->len, nv->flags, session->user_data); + } else { +- return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; ++ /* If both callbacks are not set, the invalid field nv is ++ ignored. */ ++ return 0; + } + + if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +@@ -3783,6 +3359,10 @@ static uint32_t get_error_code_from_lib_error_code(int lib_error_code) { + case NGHTTP2_ERR_HTTP_HEADER: + case NGHTTP2_ERR_HTTP_MESSAGING: + return NGHTTP2_PROTOCOL_ERROR; ++ case NGHTTP2_ERR_INTERNAL: ++ return NGHTTP2_INTERNAL_ERROR; ++ case NGHTTP2_ERR_PUSH_CANCEL: ++ return NGHTTP2_CANCEL; + default: + return NGHTTP2_INTERNAL_ERROR; + } +@@ -3809,17 +3389,32 @@ static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session, + return 0; + } + ++static int session_update_glitch_ratelim(nghttp2_session *session) { ++ if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { ++ return 0; ++ } ++ ++ nghttp2_ratelim_update(&session->glitch_ratelim, nghttp2_time_now_sec()); ++ ++ if (nghttp2_ratelim_drain(&session->glitch_ratelim, 1) == 0) { ++ return 0; ++ } ++ ++ return nghttp2_session_terminate_session(session, NGHTTP2_ENHANCE_YOUR_CALM); ++} ++ + static int session_handle_invalid_stream2(nghttp2_session *session, + int32_t stream_id, + nghttp2_frame *frame, + int lib_error_code) { + int rv; ++ + rv = nghttp2_session_add_rst_stream( + session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); + if (rv != 0) { + return rv; + } +- if (session->callbacks.on_invalid_frame_recv_callback) { ++ if (frame && session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; +@@ -3974,7 +3569,29 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, + + rv2 = session_call_on_invalid_header(session, frame, &nv); + if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +- rv = NGHTTP2_ERR_HTTP_HEADER; ++ DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n", ++ frame->hd.type, frame->hd.stream_id, (int)nv.name->len, ++ nv.name->base, (int)nv.value->len, nv.value->base); ++ ++ rv = session_call_error_callback( ++ session, NGHTTP2_ERR_HTTP_HEADER, ++ "Invalid HTTP header field was received: frame type: " ++ "%u, stream: %d, name: [%.*s], value: [%.*s]", ++ frame->hd.type, frame->hd.stream_id, (int)nv.name->len, ++ nv.name->base, (int)nv.value->len, nv.value->base); ++ ++ if (nghttp2_is_fatal(rv)) { ++ return rv; ++ } ++ ++ rv = session_handle_invalid_stream2( ++ session, subject_stream->stream_id, frame, ++ NGHTTP2_ERR_HTTP_HEADER); ++ if (nghttp2_is_fatal(rv)) { ++ return rv; ++ } ++ ++ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } else { + if (rv2 != 0) { + return rv2; +@@ -4014,13 +3631,8 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, + return rv; + } + +- rv = +- session_handle_invalid_stream2(session, subject_stream->stream_id, +- frame, NGHTTP2_ERR_HTTP_HEADER); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; ++ return nghttp2_session_terminate_session(session, ++ NGHTTP2_PROTOCOL_ERROR); + } + } + if (rv == 0) { +@@ -4064,7 +3676,6 @@ static int session_end_stream_headers_received(nghttp2_session *session, + + if (session->server && session_enforce_http_messaging(session) && + frame->headers.cat == NGHTTP2_HCAT_REQUEST && +- (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) && + !(stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) && + (stream->http_flags & NGHTTP2_HTTP_FLAG_PRIORITY)) { + rv = session_update_stream_priority(session, stream, stream->http_extpri); +@@ -4134,27 +3745,7 @@ static int session_after_header_block_received(nghttp2_session *session) { + } + } + if (rv != 0) { +- int32_t stream_id; +- +- if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { +- stream_id = frame->push_promise.promised_stream_id; +- } else { +- stream_id = frame->hd.stream_id; +- } +- +- rv = session_handle_invalid_stream2(session, stream_id, frame, +- NGHTTP2_ERR_HTTP_MESSAGING); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- +- if (frame->hd.type == NGHTTP2_HEADERS && +- (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { +- nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); +- /* Don't call nghttp2_session_close_stream_if_shut_rdwr +- because RST_STREAM has been submitted. */ +- } +- return 0; ++ return nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); + } + } + +@@ -4253,18 +3844,13 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session, + NGHTTP2_ERR_REFUSED_STREAM); + } + +- stream = nghttp2_session_open_stream( +- session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, +- &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL); ++ stream = nghttp2_session_open_stream(session, frame->hd.stream_id, ++ NGHTTP2_STREAM_FLAG_NONE, ++ NGHTTP2_STREAM_OPENING, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + +- rv = nghttp2_session_adjust_closed_stream(session); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- + session->last_proc_stream_id = session->last_recv_stream_id; + + rv = session_call_on_begin_headers(session, frame); +@@ -4396,104 +3982,33 @@ int nghttp2_session_on_headers_received(nghttp2_session *session, + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + +-static int session_process_headers_frame(nghttp2_session *session) { +- nghttp2_inbound_frame *iframe = &session->iframe; +- nghttp2_frame *frame = &iframe->frame; +- nghttp2_stream *stream; +- +- nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); +- +- stream = nghttp2_session_get_stream(session, frame->hd.stream_id); +- if (!stream) { +- frame->headers.cat = NGHTTP2_HCAT_REQUEST; +- return nghttp2_session_on_request_headers_received(session, frame); +- } +- +- if (stream->state == NGHTTP2_STREAM_RESERVED) { +- frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; +- return nghttp2_session_on_push_response_headers_received(session, frame, +- stream); +- } +- +- if (stream->state == NGHTTP2_STREAM_OPENING && +- nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { +- frame->headers.cat = NGHTTP2_HCAT_RESPONSE; +- return nghttp2_session_on_response_headers_received(session, frame, stream); +- } +- +- frame->headers.cat = NGHTTP2_HCAT_HEADERS; +- return nghttp2_session_on_headers_received(session, frame, stream); +-} +- +-int nghttp2_session_on_priority_received(nghttp2_session *session, +- nghttp2_frame *frame) { +- int rv; +- nghttp2_stream *stream; +- +- assert(!session_no_rfc7540_pri_no_fallback(session)); +- +- if (frame->hd.stream_id == 0) { +- return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, +- "PRIORITY: stream_id == 0"); +- } +- +- if (frame->priority.pri_spec.stream_id == frame->hd.stream_id) { +- return nghttp2_session_terminate_session_with_reason( +- session, NGHTTP2_PROTOCOL_ERROR, "depend on itself"); +- } +- +- if (!session->server) { +- /* Re-prioritization works only in server */ +- return session_call_on_frame_received(session, frame); +- } +- +- stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); +- +- if (!stream) { +- /* PRIORITY against idle stream can create anchor node in +- dependency tree. */ +- if (!session_detect_idle_stream(session, frame->hd.stream_id)) { +- return 0; +- } +- +- stream = nghttp2_session_open_stream( +- session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, +- &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); +- +- if (stream == NULL) { +- return NGHTTP2_ERR_NOMEM; +- } +- +- rv = nghttp2_session_adjust_idle_stream(session); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- } else { +- rv = nghttp2_session_reprioritize_stream(session, stream, +- &frame->priority.pri_spec); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- +- rv = nghttp2_session_adjust_idle_stream(session); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- } +- +- return session_call_on_frame_received(session, frame); +-} +- +-static int session_process_priority_frame(nghttp2_session *session) { ++static int session_process_headers_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; ++ nghttp2_stream *stream; ++ ++ nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); ++ ++ stream = nghttp2_session_get_stream(session, frame->hd.stream_id); ++ if (!stream) { ++ frame->headers.cat = NGHTTP2_HCAT_REQUEST; ++ return nghttp2_session_on_request_headers_received(session, frame); ++ } + +- assert(!session_no_rfc7540_pri_no_fallback(session)); ++ if (stream->state == NGHTTP2_STREAM_RESERVED) { ++ frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; ++ return nghttp2_session_on_push_response_headers_received(session, frame, ++ stream); ++ } + +- nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos); ++ if (stream->state == NGHTTP2_STREAM_OPENING && ++ nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { ++ frame->headers.cat = NGHTTP2_HCAT_RESPONSE; ++ return nghttp2_session_on_response_headers_received(session, frame, stream); ++ } + +- return nghttp2_session_on_priority_received(session, frame); ++ frame->headers.cat = NGHTTP2_HCAT_HEADERS; ++ return nghttp2_session_on_headers_received(session, frame, stream); + } + + static int session_update_stream_reset_ratelim(nghttp2_session *session) { +@@ -4566,8 +4081,7 @@ static int update_remote_initial_window_size_func(void *entry, void *ptr) { + rv = nghttp2_stream_update_remote_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { +- return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, +- NGHTTP2_FLOW_CONTROL_ERROR); ++ return NGHTTP2_ERR_FLOW_CONTROL; + } + + /* If window size gets positive, push deferred DATA frame to +@@ -4593,6 +4107,8 @@ static int update_remote_initial_window_size_func(void *entry, void *ptr) { + * + * NGHTTP2_ERR_NOMEM + * Out of memory. ++ * NGHTTP2_ERR_FLOW_CONTROL ++ * Window size gets out of range. + */ + static int + session_update_remote_initial_window_size(nghttp2_session *session, +@@ -4616,8 +4132,7 @@ static int update_local_initial_window_size_func(void *entry, void *ptr) { + rv = nghttp2_stream_update_local_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { +- return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, +- NGHTTP2_FLOW_CONTROL_ERROR); ++ return NGHTTP2_ERR_FLOW_CONTROL; + } + + if (stream->window_update_queued) { +@@ -4651,6 +4166,8 @@ static int update_local_initial_window_size_func(void *entry, void *ptr) { + * + * NGHTTP2_ERR_NOMEM + * Out of memory. ++ * NGHTTP2_ERR_FLOW_CONTROL ++ * Window size gets out of range. + */ + static int + session_update_local_initial_window_size(nghttp2_session *session, +@@ -4934,12 +4451,6 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, + + if (session->remote_settings.no_rfc7540_priorities == UINT32_MAX) { + session->remote_settings.no_rfc7540_priorities = 0; +- +- if (session->server && session->pending_no_rfc7540_priorities && +- (session->opt_flags & +- NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES)) { +- session->fallback_rfc7540_priorities = 1; +- } + } + + if (!noack && !session_is_closing(session)) { +@@ -5000,7 +4511,6 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session, + int rv; + nghttp2_stream *stream; + nghttp2_stream *promised_stream; +- nghttp2_priority_spec pri_spec; + + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( +@@ -5044,9 +4554,9 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session, + session->max_incoming_reserved_streams) { + /* Currently, client does not retain closed stream, so we don't + check NGHTTP2_SHUT_RD condition here. */ +- +- rv = nghttp2_session_add_rst_stream( +- session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); ++ rv = session_handle_invalid_stream2(session, ++ frame->push_promise.promised_stream_id, ++ NULL, NGHTTP2_ERR_PUSH_CANCEL); + if (rv != 0) { + return rv; + } +@@ -5058,20 +4568,14 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session, + session, frame, NGHTTP2_ERR_STREAM_CLOSED, "PUSH_PROMISE: stream closed"); + } + +- nghttp2_priority_spec_init(&pri_spec, stream->stream_id, +- NGHTTP2_DEFAULT_WEIGHT, 0); +- + promised_stream = nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, +- &pri_spec, NGHTTP2_STREAM_RESERVED, NULL); ++ NGHTTP2_STREAM_RESERVED, NULL); + + if (!promised_stream) { + return NGHTTP2_ERR_NOMEM; + } + +- /* We don't call nghttp2_session_adjust_closed_stream(), since we +- don't keep closed stream in client side */ +- + session->last_proc_stream_id = session->last_recv_stream_id; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { +@@ -5209,8 +4713,9 @@ static int session_on_stream_window_update_received(nghttp2_session *session, + } + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + stream->remote_window_size) { +- return session_handle_invalid_stream(session, frame, +- NGHTTP2_ERR_FLOW_CONTROL); ++ return session_handle_invalid_connection( ++ session, frame, NGHTTP2_ERR_FLOW_CONTROL, ++ "WINDOW_UPDATE: window size overflow"); + } + stream->remote_window_size += frame->window_update.window_size_increment; + +@@ -5292,7 +4797,6 @@ int nghttp2_session_on_priority_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + nghttp2_ext_priority_update *priority_update; + nghttp2_stream *stream; +- nghttp2_priority_spec pri_spec; + nghttp2_extpri extpri; + int rv; + +@@ -5330,10 +4834,9 @@ int nghttp2_session_on_priority_update_received(nghttp2_session *session, + "PRIORITY_UPDATE: max concurrent streams exceeded"); + } + +- nghttp2_priority_spec_default_init(&pri_spec); +- stream = nghttp2_session_open_stream(session, priority_update->stream_id, +- NGHTTP2_FLAG_NONE, &pri_spec, +- NGHTTP2_STREAM_IDLE, NULL); ++ stream = ++ nghttp2_session_open_stream(session, priority_update->stream_id, ++ NGHTTP2_FLAG_NONE, NGHTTP2_STREAM_IDLE, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } +@@ -5442,16 +4945,7 @@ int nghttp2_session_on_data_received(nghttp2_session *session, + if (session_enforce_http_messaging(session) && + (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + if (nghttp2_http_on_remote_end_stream(stream) != 0) { +- rv = nghttp2_session_add_rst_stream(session, stream->stream_id, +- NGHTTP2_PROTOCOL_ERROR); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- +- nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); +- /* Don't call nghttp2_session_close_stream_if_shut_rdwr because +- RST_STREAM has been submitted. */ +- return 0; ++ return nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); + } + } + +@@ -5509,8 +5003,8 @@ int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session, + rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, + stream->local_window_size); + if (rv != 0) { +- return nghttp2_session_add_rst_stream(session, stream->stream_id, +- NGHTTP2_FLOW_CONTROL_ERROR); ++ return nghttp2_session_terminate_session(session, ++ NGHTTP2_FLOW_CONTROL_ERROR); + } + /* We don't have to send WINDOW_UPDATE if the data received is the + last chunk in the incoming stream. */ +@@ -5869,14 +5363,6 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + + mem = &session->mem; + +- /* We may have idle streams more than we expect (e.g., +- nghttp2_session_change_stream_priority() or +- nghttp2_session_create_idle_stream()). Adjust them here. */ +- rv = nghttp2_session_adjust_idle_stream(session); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- + if (!nghttp2_session_want_read(session)) { + return (nghttp2_ssize)inlen; + } +@@ -5980,20 +5466,30 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + busy = 1; + + rv = session_on_data_received_fail_fast(session); ++ if (nghttp2_is_fatal(rv)) { ++ return rv; ++ } ++ + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { + DEBUGF("recv: DATA not allowed stream_id=%d\n", + iframe->frame.hd.stream_id); ++ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + rv = nghttp2_session_terminate_session_with_reason( +@@ -6011,6 +5507,20 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + break; + } + ++ /* Empty DATA frame without END_STREAM flag set is ++ suspicious. */ ++ if (iframe->payloadleft == 0 && ++ (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ } ++ + iframe->state = NGHTTP2_IB_READ_DATA; + break; + } +@@ -6063,6 +5573,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + return rv; + } + ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + on_begin_frame_called = 1; + + rv = session_process_headers_frame(session); +@@ -6077,8 +5591,8 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +- rv = nghttp2_session_add_rst_stream( +- session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); ++ rv = session_handle_invalid_stream2( ++ session, iframe->frame.hd.stream_id, NULL, NGHTTP2_ERR_INTERNAL); + if (nghttp2_is_fatal(rv)) { + return rv; + } +@@ -6087,6 +5601,15 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } +@@ -6107,6 +5630,18 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + break; + } + ++ /* This is deprecated RFC 7540 priorities mechanism which is ++ very unpopular. We do not expect it is received so ++ frequently. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); +@@ -6123,7 +5658,7 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + DEBUGF("recv: WINDOW_UPDATE\n"); + break; + } +-#endif /* DEBUGBUILD */ ++#endif /* defined(DEBUGBUILD) */ + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + +@@ -6279,8 +5814,17 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + if (check_ext_type_set(session->user_recv_ext_types, + iframe->frame.hd.type)) { + if (!session->callbacks.unpack_extension_callback) { +- /* Silently ignore unknown frame type. */ ++ /* Receiving too frequent unknown frames is suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } + ++ /* Silently ignore unknown frame type. */ + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; +@@ -6298,6 +5842,16 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + case NGHTTP2_ALTSVC: + if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == + 0) { ++ /* Receiving too frequent unknown frames is suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; +@@ -6309,6 +5863,17 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc; + + if (session->server) { ++ /* Receiving too frequent ALTSVC from client is ++ suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; +@@ -6328,6 +5893,16 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + break; + case NGHTTP2_ORIGIN: + if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) { ++ /* Receiving too frequent unknown frames is suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; +@@ -6339,6 +5914,17 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + + if (session->server || iframe->frame.hd.stream_id || + (iframe->frame.hd.flags & 0xf0)) { ++ /* Receiving too frequent invalid frames is ++ suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; +@@ -6365,6 +5951,16 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + case NGHTTP2_PRIORITY_UPDATE: + if ((session->builtin_recv_ext_types & + NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) { ++ /* Receiving too frequent unknown frames is suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; +@@ -6392,8 +5988,18 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + break; + } + +- if (!session_no_rfc7540_pri_no_fallback(session) || +- iframe->payloadleft > sizeof(iframe->raw_sbuf)) { ++ /* Receiving too frequent PRIORITY_UPDATE is ++ suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ ++ if (iframe->payloadleft > sizeof(iframe->raw_sbuf)) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; +@@ -6406,6 +6012,16 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + + break; + default: ++ /* Receiving too frequent unknown frames is suspicious. */ ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; +@@ -6429,6 +6045,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + if (nghttp2_is_fatal(rv)) { + return rv; + } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } + } + } + +@@ -6492,8 +6112,8 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +- rv = nghttp2_session_add_rst_stream( +- session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); ++ rv = session_handle_invalid_stream2( ++ session, iframe->frame.hd.stream_id, NULL, NGHTTP2_ERR_INTERNAL); + if (nghttp2_is_fatal(rv)) { + return rv; + } +@@ -6502,26 +6122,23 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { +- iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; +- break; +- } +- +- iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; +- +- break; +- case NGHTTP2_PRIORITY: +- if (!session_no_rfc7540_pri_no_fallback(session) && +- session->remote_settings.no_rfc7540_priorities != 1) { +- rv = session_process_priority_frame(session); +- if (nghttp2_is_fatal(rv)) { ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } ++ ++ iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; ++ break; + } + ++ iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; ++ ++ break; ++ case NGHTTP2_PRIORITY: + session_inbound_frame_reset(session); + + break; +@@ -6579,9 +6196,9 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +- rv = nghttp2_session_add_rst_stream( +- session, iframe->frame.push_promise.promised_stream_id, +- NGHTTP2_INTERNAL_ERROR); ++ rv = session_handle_invalid_stream2( ++ session, iframe->frame.push_promise.promised_stream_id, NULL, ++ NGHTTP2_ERR_INTERNAL); + if (nghttp2_is_fatal(rv)) { + return rv; + } +@@ -6684,6 +6301,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + return rv; + } + ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + session_inbound_frame_reset(session); + + break; +@@ -6706,7 +6327,7 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + } else { + DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n"); + } +-#endif /* DEBUGBUILD */ ++#endif /* defined(DEBUGBUILD) */ + + readlen = inbound_frame_payload_readlen(iframe, in, last); + +@@ -6759,12 +6380,12 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + iframe->payloadleft -= hd_proclen; + + /* Use promised stream ID for PUSH_PROMISE */ +- rv = nghttp2_session_add_rst_stream( ++ rv = session_handle_invalid_stream2( + session, + iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE + ? iframe->frame.push_promise.promised_stream_id + : iframe->frame.hd.stream_id, +- NGHTTP2_INTERNAL_ERROR); ++ NULL, NGHTTP2_ERR_INTERNAL); + if (nghttp2_is_fatal(rv)) { + return rv; + } +@@ -6811,6 +6432,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + if (nghttp2_is_fatal(rv)) { + return rv; + } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } + } + session_inbound_frame_reset(session); + +@@ -6934,7 +6559,7 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + } else { + fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); + } +-#endif /* DEBUGBUILD */ ++#endif /* defined(DEBUGBUILD) */ + + if (++session->num_continuations > session->max_continuations) { + return NGHTTP2_ERR_TOO_MANY_CONTINUATIONS; +@@ -6986,6 +6611,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + if (nghttp2_is_fatal(rv)) { + return rv; + } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } + } else { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + } +@@ -7036,6 +6665,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + if (nghttp2_is_fatal(rv)) { + return rv; + } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } + } + + busy = 1; +@@ -7052,6 +6685,20 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + + iframe->frame.data.padlen = (size_t)padlen; + ++ /* Empty DATA frame without END_STREAM flag set is ++ suspicious. */ ++ if (iframe->payloadleft == 0 && ++ (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { ++ rv = session_update_glitch_ratelim(session); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ } ++ + iframe->state = NGHTTP2_IB_READ_DATA; + + break; +@@ -7094,6 +6741,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + return rv; + } + ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + data_readlen = + inbound_frame_effective_readlen(iframe, iframe->payloadleft, readlen); + +@@ -7123,41 +6774,30 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + if (data_readlen > 0) { + if (session_enforce_http_messaging(session)) { + if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) { +- if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { +- /* Consume all data for connection immediately here */ +- rv = session_update_connection_consumed_size( +- session, (size_t)data_readlen); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } +- +- if (iframe->state == NGHTTP2_IB_IGN_DATA) { +- return (nghttp2_ssize)inlen; +- } +- } +- +- rv = nghttp2_session_add_rst_stream( +- session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR); ++ rv = nghttp2_session_terminate_session(session, ++ NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } +- busy = 1; +- iframe->state = NGHTTP2_IB_IGN_DATA; +- break; ++ ++ return (nghttp2_ssize)inlen; + } + } + if (session->callbacks.on_data_chunk_recv_callback) { + rv = session->callbacks.on_data_chunk_recv_callback( + session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, + in - readlen, (size_t)data_readlen, session->user_data); +- if (rv == NGHTTP2_ERR_PAUSE) { +- return (nghttp2_ssize)(in - first); +- } +- + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } ++ ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ ++ if (rv == NGHTTP2_ERR_PAUSE) { ++ return (nghttp2_ssize)(in - first); ++ } + } + } + } +@@ -7171,6 +6811,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + return rv; + } + ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + session_inbound_frame_reset(session); + + break; +@@ -7237,6 +6881,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + return rv; + } + ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + if (rv != 0) { + busy = 1; + +@@ -7255,6 +6903,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + return rv; + } + ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + session_inbound_frame_reset(session); + + break; +@@ -7283,6 +6935,10 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + return rv; + } + ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (nghttp2_ssize)inlen; ++ } ++ + session_inbound_frame_reset(session); + + break; +@@ -7404,9 +7060,7 @@ int nghttp2_session_want_write(nghttp2_session *session) { + */ + return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) || + nghttp2_outbound_queue_top(&session->ob_reg) || +- ((!nghttp2_pq_empty(&session->root.obq) || +- !session_sched_empty(session)) && +- session->remote_window_size > 0) || ++ (!session_sched_empty(session) && session->remote_window_size > 0) || + (nghttp2_outbound_queue_top(&session->ob_syn) && + !session_is_outgoing_concurrent_streams_max(session)); + } +@@ -8046,7 +7700,6 @@ static int nghttp2_session_upgrade_internal(nghttp2_session *session, + nghttp2_settings_entry *iv; + size_t niv; + int rv; +- nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; +@@ -8083,18 +7736,13 @@ static int nghttp2_session_upgrade_internal(nghttp2_session *session, + return rv; + } + +- nghttp2_priority_spec_default_init(&pri_spec); +- + stream = nghttp2_session_open_stream( +- session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING, ++ session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENING, + session->server ? NULL : stream_user_data); + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + +- /* We don't call nghttp2_session_adjust_closed_stream(), since this +- should be the first stream open. */ +- + if (session->server) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + session->last_recv_stream_id = 1; +@@ -8293,14 +7941,16 @@ int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { + nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session, + int32_t stream_id) { + if (stream_id == 0) { +- return &session->root; ++ return &nghttp2_stream_root; + } + + return nghttp2_session_get_stream_raw(session, stream_id); + } + + nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) { +- return &session->root; ++ (void)session; ++ ++ return &nghttp2_stream_root; + } + + int nghttp2_session_check_server_session(nghttp2_session *session) { +@@ -8310,75 +7960,20 @@ int nghttp2_session_check_server_session(nghttp2_session *session) { + int nghttp2_session_change_stream_priority( + nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { +- int rv; +- nghttp2_stream *stream; +- nghttp2_priority_spec pri_spec_copy; +- +- if (session->pending_no_rfc7540_priorities == 1) { +- return 0; +- } +- +- if (stream_id == 0 || stream_id == pri_spec->stream_id) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- +- stream = nghttp2_session_get_stream_raw(session, stream_id); +- if (!stream) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- +- pri_spec_copy = *pri_spec; +- nghttp2_priority_spec_normalize_weight(&pri_spec_copy); +- +- rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ (void)session; ++ (void)stream_id; ++ (void)pri_spec; + +- /* We don't intentionally call nghttp2_session_adjust_idle_stream() +- so that idle stream created by this function, and existing ones +- are kept for application. We will adjust number of idle stream +- in nghttp2_session_mem_send2 or nghttp2_session_mem_recv2 is +- called. */ + return 0; + } + + int nghttp2_session_create_idle_stream(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { +- nghttp2_stream *stream; +- nghttp2_priority_spec pri_spec_copy; +- +- if (session->pending_no_rfc7540_priorities == 1) { +- return 0; +- } +- +- if (stream_id == 0 || stream_id == pri_spec->stream_id || +- !session_detect_idle_stream(session, stream_id)) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- +- stream = nghttp2_session_get_stream_raw(session, stream_id); +- if (stream) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- +- pri_spec_copy = *pri_spec; +- nghttp2_priority_spec_normalize_weight(&pri_spec_copy); +- +- stream = +- nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, +- &pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL); +- if (!stream) { +- return NGHTTP2_ERR_NOMEM; +- } ++ (void)session; ++ (void)stream_id; ++ (void)pri_spec; + +- /* We don't intentionally call nghttp2_session_adjust_idle_stream() +- so that idle stream created by this function, and existing ones +- are kept for application. We will adjust number of idle stream +- in nghttp2_session_mem_send2 or nghttp2_session_mem_recv2 is +- called. */ + return 0; + } + +diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h +index ef8f7b27..edbef80d 100644 +--- a/deps/nghttp2/lib/nghttp2_session.h ++++ b/deps/nghttp2/lib/nghttp2_session.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + #include "nghttp2_map.h" +@@ -45,6 +45,8 @@ + preface handling. */ + extern int nghttp2_enable_strict_preface; + ++extern nghttp2_stream nghttp2_stream_root; ++ + /* + * Option flags. + */ +@@ -53,8 +55,6 @@ typedef enum { + NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, + NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2, + NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3, +- NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4, +- NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 5, + NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 6, + } nghttp2_optmask; + +@@ -89,10 +89,6 @@ typedef struct { + /* The default maximum number of incoming reserved streams */ + #define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200 + +-/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this +- number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */ +-#define NGHTTP2_MIN_IDLE_STREAMS 16 +- + /* The maximum number of items in outbound queue, which is considered + as flooding caused by peer. All frames are not considered here. + We only consider PING + ACK and SETTINGS + ACK. This is because +@@ -110,6 +106,10 @@ typedef struct { + #define NGHTTP2_DEFAULT_STREAM_RESET_BURST 1000 + #define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33 + ++/* The default values for glitch rate limiter. */ ++#define NGHTTP2_DEFAULT_GLITCH_BURST 1000 ++#define NGHTTP2_DEFAULT_GLITCH_RATE 33 ++ + /* The default max number of CONTINUATION frames following an incoming + HEADER frame. */ + #define NGHTTP2_DEFAULT_MAX_CONTINUATIONS 8 +@@ -205,8 +205,6 @@ typedef struct nghttp2_inflight_settings nghttp2_inflight_settings; + + struct nghttp2_session { + nghttp2_map /* */ streams; +- /* root of dependency tree*/ +- nghttp2_stream root; + /* Queue for outbound urgent frames (PING and SETTINGS) */ + nghttp2_outbound_queue ob_urgent; + /* Queue for non-DATA frames */ +@@ -229,26 +227,14 @@ struct nghttp2_session { + /* Memory allocator */ + nghttp2_mem mem; + void *user_data; +- /* Points to the latest incoming closed stream. NULL if there is no +- closed stream. Only used when session is initialized as +- server. */ +- nghttp2_stream *closed_stream_head; +- /* Points to the oldest incoming closed stream. NULL if there is no +- closed stream. Only used when session is initialized as +- server. */ +- nghttp2_stream *closed_stream_tail; +- /* Points to the latest idle stream. NULL if there is no idle +- stream. Only used when session is initialized as server .*/ +- nghttp2_stream *idle_stream_head; +- /* Points to the oldest idle stream. NULL if there is no idle +- stream. Only used when session is initialized as erver. */ +- nghttp2_stream *idle_stream_tail; + /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not + considered as in-flight. */ + nghttp2_inflight_settings *inflight_settings_head; + /* Stream reset rate limiter. If receiving excessive amount of + stream resets, GOAWAY will be sent. */ + nghttp2_ratelim stream_reset_ratelim; ++ /* Rate limiter for all kinds of glitches. */ ++ nghttp2_ratelim glitch_ratelim; + /* Sequential number across all streams to process streams in + FIFO. */ + uint64_t stream_seq; +@@ -276,10 +262,9 @@ struct nghttp2_session { + |closed_stream_head|. The current implementation only keeps + incoming streams and session is initialized as server. */ + size_t num_closed_streams; +- /* The number of idle streams kept in |streams| hash. The idle +- streams can be accessed through doubly linked list +- |idle_stream_head|. The current implementation only keeps idle +- streams if session is initialized as server. */ ++ /* The number of idle streams kept in |streams| hash. The current ++ implementation only keeps idle streams if session is initialized ++ as server. */ + size_t num_idle_streams; + /* The number of bytes allocated for nvbuf */ + size_t nvbuflen; +@@ -362,8 +347,6 @@ struct nghttp2_session { + /* Unacked local SETTINGS_NO_RFC7540_PRIORITIES value, which is + effective before it is acknowledged. */ + uint8_t pending_no_rfc7540_priorities; +- /* Turn on fallback to RFC 7540 priorities; for server use only. */ +- uint8_t fallback_rfc7540_priorities; + /* Nonzero if the session is server side. */ + uint8_t server; + /* Flags indicating GOAWAY is sent and/or received. The flags are +@@ -425,6 +408,13 @@ int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item); + ++/* ++ * This function wraps around nghttp2_session_add_rst_stream_continue ++ * with continue_without_stream = 1. ++ */ ++int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, ++ uint32_t error_code); ++ + /* + * Adds RST_STREAM frame for the stream |stream_id| with the error + * code |error_code|. This is a convenient function built on top of +@@ -432,7 +422,9 @@ int nghttp2_session_add_item(nghttp2_session *session, + * + * This function simply returns 0 without adding RST_STREAM frame if + * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple +- * RST_STREAM for a stream is redundant. ++ * RST_STREAM for a stream is redundant. It also returns 0 without ++ * adding the frame if |continue_without_stream| is nonzero, and ++ * stream was already gone. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: +@@ -440,8 +432,10 @@ int nghttp2_session_add_item(nghttp2_session *session, + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +-int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, +- uint32_t error_code); ++int nghttp2_session_add_rst_stream_continue(nghttp2_session *session, ++ int32_t stream_id, ++ uint32_t error_code, ++ int continue_without_stream); + + /* + * Adds PING frame. This is a convenient function built on top of +@@ -527,15 +521,9 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + * + * This function returns a pointer to created new stream object, or + * NULL. +- * +- * This function adjusts neither the number of closed streams or idle +- * streams. The caller should manually call +- * nghttp2_session_adjust_closed_stream() or +- * nghttp2_session_adjust_idle_stream() respectively. + */ + nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, +- nghttp2_priority_spec *pri_spec, + nghttp2_stream_state initial_state, + void *stream_user_data); + +@@ -544,11 +532,6 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + * is indicated by the |error_code|. When closing the stream, + * on_stream_close_callback will be called. + * +- * If the session is initialized as server and |stream| is incoming +- * stream, stream is just marked closed and this function calls +- * nghttp2_session_keep_closed_stream() with |stream|. Otherwise, +- * |stream| will be deleted from memory. +- * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * +@@ -565,63 +548,9 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + /* + * Deletes |stream| from memory. After this function returns, stream + * cannot be accessed. +- * +- * This function returns 0 if it succeeds, or one the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-int nghttp2_session_destroy_stream(nghttp2_session *session, +- nghttp2_stream *stream); +- +-/* +- * Tries to keep incoming closed stream |stream|. Due to the +- * limitation of maximum number of streams in memory, |stream| is not +- * closed and just deleted from memory (see +- * nghttp2_session_destroy_stream). +- */ +-void nghttp2_session_keep_closed_stream(nghttp2_session *session, +- nghttp2_stream *stream); +- +-/* +- * Appends |stream| to linked list |session->idle_stream_head|. We +- * apply fixed limit for list size. To fit into that limit, one or +- * more oldest streams are removed from list as necessary. + */ +-void nghttp2_session_keep_idle_stream(nghttp2_session *session, +- nghttp2_stream *stream); +- +-/* +- * Detaches |stream| from idle streams linked list. +- */ +-void nghttp2_session_detach_idle_stream(nghttp2_session *session, +- nghttp2_stream *stream); +- +-/* +- * Deletes closed stream to ensure that number of incoming streams +- * including active and closed is in the maximum number of allowed +- * stream. +- * +- * This function returns 0 if it succeeds, or one the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-int nghttp2_session_adjust_closed_stream(nghttp2_session *session); +- +-/* +- * Deletes idle stream to ensure that number of idle streams is in +- * certain limit. +- * +- * This function returns 0 if it succeeds, or one the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-int nghttp2_session_adjust_idle_stream(nghttp2_session *session); ++void nghttp2_session_destroy_stream(nghttp2_session *session, ++ nghttp2_stream *stream); + + /* + * If further receptions and transmissions over the stream |stream_id| +@@ -915,24 +844,6 @@ int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv); + +-/* +- * Re-prioritize |stream|. The new priority specification is +- * |pri_spec|. Caller must ensure that stream->hd.stream_id != +- * pri_spec->stream_id. +- * +- * This function does not adjust the number of idle streams. The +- * caller should call nghttp2_session_adjust_idle_stream() later. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-int nghttp2_session_reprioritize_stream(nghttp2_session *session, +- nghttp2_stream *stream, +- const nghttp2_priority_spec *pri_spec); +- + /* + * Terminates current |session| with the |error_code|. The |reason| + * is NULL-terminated debug string. +@@ -981,4 +892,4 @@ int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session, + size_t delta_size, + int send_window_update); + +-#endif /* NGHTTP2_SESSION_H */ ++#endif /* !defined(NGHTTP2_SESSION_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_stream.c b/deps/nghttp2/lib/nghttp2_stream.c +index f2362447..0f2d3658 100644 +--- a/deps/nghttp2/lib/nghttp2_stream.c ++++ b/deps/nghttp2/lib/nghttp2_stream.c +@@ -25,45 +25,17 @@ + #include "nghttp2_stream.h" + + #include +-#include + + #include "nghttp2_session.h" + #include "nghttp2_helper.h" + #include "nghttp2_debug.h" + #include "nghttp2_frame.h" + +-/* Maximum distance between any two stream's cycle in the same +- priority queue. Imagine stream A's cycle is A, and stream B's +- cycle is B, and A < B. The cycle is unsigned 32 bit integer, it +- may get overflow. Because of how we calculate the next cycle +- value, if B - A is less than or equals to +- NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other +- words, B is really greater than or equal to A. Otherwise, A is a +- result of overflow, and it is actually A > B if we consider that +- fact. */ +-#define NGHTTP2_MAX_CYCLE_DISTANCE \ +- ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255) +- +-static int stream_less(const void *lhsx, const void *rhsx) { +- const nghttp2_stream *lhs, *rhs; +- +- lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); +- rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); +- +- if (lhs->cycle == rhs->cycle) { +- return lhs->seq < rhs->seq; +- } +- +- return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE; +-} +- + void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, +- int32_t weight, int32_t remote_initial_window_size, ++ int32_t remote_initial_window_size, + int32_t local_initial_window_size, +- void *stream_user_data, nghttp2_mem *mem) { +- nghttp2_pq_init(&stream->obq, stream_less, mem); +- ++ void *stream_user_data) { + stream->stream_id = stream_id; + stream->flags = flags; + stream->state = initial_state; +@@ -77,428 +49,36 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + stream->recv_reduction = 0; + stream->window_update_queued = 0; + +- stream->dep_prev = NULL; +- stream->dep_next = NULL; +- stream->sib_prev = NULL; +- stream->sib_next = NULL; +- +- stream->closed_prev = NULL; + stream->closed_next = NULL; + +- stream->weight = weight; +- stream->sum_dep_weight = 0; +- + stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; + stream->content_length = -1; + stream->recv_content_length = 0; + stream->status_code = -1; + + stream->queued = 0; +- stream->descendant_last_cycle = 0; + stream->cycle = 0; + stream->pending_penalty = 0; +- stream->descendant_next_seq = 0; + stream->seq = 0; + stream->last_writelen = 0; + + stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY; + } + +-void nghttp2_stream_free(nghttp2_stream *stream) { +- nghttp2_pq_free(&stream->obq); +- /* We don't free stream->item. If it is assigned to aob, then +- active_outbound_item_reset() will delete it. Otherwise, +- nghttp2_stream_close() or session_del() will delete it. */ +-} ++void nghttp2_stream_free(nghttp2_stream *stream) { (void)stream; } + + void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { + stream->shut_flags = (uint8_t)(stream->shut_flags | flag); + } + +-/* +- * Returns nonzero if |stream| is active. This function does not take +- * into account its descendants. +- */ +-static int stream_active(nghttp2_stream *stream) { +- return stream->item && +- (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0; +-} +- +-/* +- * Returns nonzero if |stream| or one of its descendants is active +- */ +-static int stream_subtree_active(nghttp2_stream *stream) { +- return stream_active(stream) || !nghttp2_pq_empty(&stream->obq); +-} +- +-/* +- * Returns next cycle for |stream|. +- */ +-static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) { +- uint64_t penalty; +- +- penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT + +- stream->pending_penalty; +- +- stream->cycle = last_cycle + penalty / (uint32_t)stream->weight; +- stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight); +-} +- +-static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { +- int rv; +- +- for (; dep_stream && !stream->queued; +- stream = dep_stream, dep_stream = dep_stream->dep_prev) { +- stream_next_cycle(stream, dep_stream->descendant_last_cycle); +- stream->seq = dep_stream->descendant_next_seq++; +- +- DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id, +- stream->cycle); +- +- DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id, +- dep_stream->stream_id); +- +- rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); +- if (rv != 0) { +- return rv; +- } +- stream->queued = 1; +- } +- +- return 0; +-} +- +-/* +- * Removes |stream| from parent's obq. If removal of |stream| makes +- * parent's obq empty, and parent is not active, then parent is also +- * removed. This process is repeated recursively. +- */ +-static void stream_obq_remove(nghttp2_stream *stream) { +- nghttp2_stream *dep_stream; +- +- dep_stream = stream->dep_prev; +- +- if (!stream->queued) { +- return; +- } +- +- for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { +- DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id, +- dep_stream->stream_id); +- +- nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); +- +- assert(stream->queued); +- +- stream->queued = 0; +- stream->cycle = 0; +- stream->pending_penalty = 0; +- stream->descendant_last_cycle = 0; +- stream->last_writelen = 0; +- +- if (stream_subtree_active(dep_stream)) { +- return; +- } +- } +-} +- +-/* +- * Moves |stream| from |src|'s obq to |dest|'s obq. Removal from +- * |src|'s obq is just done calling nghttp2_pq_remove(), so it does +- * not recursively remove |src| and ancestors, like +- * stream_obq_remove(). +- */ +-static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src, +- nghttp2_stream *stream) { +- if (!stream->queued) { +- return 0; +- } +- +- DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id, +- src->stream_id); +- +- nghttp2_pq_remove(&src->obq, &stream->pq_entry); +- stream->queued = 0; +- +- return stream_obq_push(dest, stream); +-} +- +-void nghttp2_stream_reschedule(nghttp2_stream *stream) { +- nghttp2_stream *dep_stream; +- +- assert(stream->queued); +- +- dep_stream = stream->dep_prev; +- +- for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { +- nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); +- +- stream_next_cycle(stream, dep_stream->descendant_last_cycle); +- stream->seq = dep_stream->descendant_next_seq++; +- +- nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); +- +- DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id, +- stream->cycle); +- +- dep_stream->last_writelen = stream->last_writelen; +- } +-} +- +-void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) { +- nghttp2_stream *dep_stream; +- uint64_t last_cycle; +- int32_t old_weight; +- uint64_t wlen_penalty; +- +- if (stream->weight == weight) { +- return; +- } +- +- old_weight = stream->weight; +- stream->weight = weight; +- +- dep_stream = stream->dep_prev; +- +- if (!dep_stream) { +- return; +- } +- +- dep_stream->sum_dep_weight += weight - old_weight; +- +- if (!stream->queued) { +- return; +- } +- +- nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); +- +- wlen_penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT; +- +- /* Compute old stream->pending_penalty we used to calculate +- stream->cycle */ +- stream->pending_penalty = +- (uint32_t)((stream->pending_penalty + (uint32_t)old_weight - +- (wlen_penalty % (uint32_t)old_weight)) % +- (uint32_t)old_weight); +- +- last_cycle = stream->cycle - +- (wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight; +- +- /* Now we have old stream->pending_penalty and new stream->weight in +- place */ +- stream_next_cycle(stream, last_cycle); +- +- if (dep_stream->descendant_last_cycle - stream->cycle <= +- NGHTTP2_MAX_CYCLE_DISTANCE) { +- stream->cycle = dep_stream->descendant_last_cycle; +- } +- +- /* Continue to use same stream->seq */ +- +- nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); +- +- DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id, +- stream->cycle); +-} +- +-static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { +- for (; stream->sib_next; stream = stream->sib_next) +- ; +- +- return stream; +-} +- +-int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, +- int32_t weight) { +- weight = stream->weight * weight / stream->sum_dep_weight; +- +- return nghttp2_max_int32(1, weight); +-} +- +-#ifdef STREAM_DEP_DEBUG +- +-static void ensure_inactive(nghttp2_stream *stream) { +- nghttp2_stream *si; +- +- if (stream->queued) { +- fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream, +- stream->stream_id); +- assert(0); +- } +- +- if (stream_active(stream)) { +- fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n", +- stream, stream->stream_id); +- assert(0); +- } +- +- if (!nghttp2_pq_empty(&stream->obq)) { +- fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream, +- stream->stream_id, nghttp2_pq_size(&stream->obq)); +- assert(0); +- } +- +- for (si = stream->dep_next; si; si = si->sib_next) { +- ensure_inactive(si); +- } +-} +- +-static void check_queued(nghttp2_stream *stream) { +- nghttp2_stream *si; +- int queued; +- +- if (stream->queued) { +- if (!stream_subtree_active(stream)) { +- fprintf(stderr, +- "stream(%p)=%d, stream->queued == 1, but " +- "stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n", +- stream, stream->stream_id, stream_active(stream), +- nghttp2_pq_size(&stream->obq)); +- assert(0); +- } +- if (!stream_active(stream)) { +- queued = 0; +- for (si = stream->dep_next; si; si = si->sib_next) { +- if (si->queued) { +- ++queued; +- } +- } +- if (queued == 0) { +- fprintf(stderr, +- "stream(%p)=%d, stream->queued == 1, and " +- "!stream_active(), but no descendants is queued\n", +- stream, stream->stream_id); +- assert(0); +- } +- } +- +- for (si = stream->dep_next; si; si = si->sib_next) { +- check_queued(si); +- } +- } else { +- if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) { +- fprintf(stderr, +- "stream(%p) = %d, stream->queued == 0, but " +- "stream_active(stream) == %d and " +- "nghttp2_pq_size(&stream->obq) = %zu\n", +- stream, stream->stream_id, stream_active(stream), +- nghttp2_pq_size(&stream->obq)); +- assert(0); +- } +- for (si = stream->dep_next; si; si = si->sib_next) { +- ensure_inactive(si); +- } +- } +-} +- +-static void check_sum_dep(nghttp2_stream *stream) { +- nghttp2_stream *si; +- int32_t n = 0; +- for (si = stream->dep_next; si; si = si->sib_next) { +- n += si->weight; +- } +- if (n != stream->sum_dep_weight) { +- fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream, +- stream->stream_id, n, stream->sum_dep_weight); +- assert(0); +- } +- for (si = stream->dep_next; si; si = si->sib_next) { +- check_sum_dep(si); +- } +-} +- +-static void check_dep_prev(nghttp2_stream *stream) { +- nghttp2_stream *si; +- for (si = stream->dep_next; si; si = si->sib_next) { +- if (si->dep_prev != stream) { +- fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream); +- assert(0); +- } +- check_dep_prev(si); +- } +-} +- +-#endif /* STREAM_DEP_DEBUG */ +- +-#ifdef STREAM_DEP_DEBUG +-static void validate_tree(nghttp2_stream *stream) { +- nghttp2_stream *si; +- +- if (!stream) { +- return; +- } +- +- for (; stream->dep_prev; stream = stream->dep_prev) +- ; +- +- assert(stream->stream_id == 0); +- assert(!stream->queued); +- +- fprintf(stderr, "checking...\n"); +- if (nghttp2_pq_empty(&stream->obq)) { +- fprintf(stderr, "root obq empty\n"); +- for (si = stream->dep_next; si; si = si->sib_next) { +- ensure_inactive(si); +- } +- } else { +- for (si = stream->dep_next; si; si = si->sib_next) { +- check_queued(si); +- } +- } +- +- check_sum_dep(stream); +- check_dep_prev(stream); +-} +-#else /* !STREAM_DEP_DEBUG */ +-static void validate_tree(nghttp2_stream *stream) { (void)stream; } +-#endif /* !STREAM_DEP_DEBUG*/ +- +-static int stream_update_dep_on_attach_item(nghttp2_stream *stream) { +- int rv; +- +- rv = stream_obq_push(stream->dep_prev, stream); +- if (rv != 0) { +- return rv; +- } +- +- validate_tree(stream); +- return 0; +-} +- +-static void stream_update_dep_on_detach_item(nghttp2_stream *stream) { +- if (nghttp2_pq_empty(&stream->obq)) { +- stream_obq_remove(stream); +- } +- +- validate_tree(stream); +-} +- +-int nghttp2_stream_attach_item(nghttp2_stream *stream, +- nghttp2_outbound_item *item) { +- int rv; +- ++void nghttp2_stream_attach_item(nghttp2_stream *stream, ++ nghttp2_outbound_item *item) { + assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); + assert(stream->item == NULL); + + DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item); + + stream->item = item; +- +- if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { +- return 0; +- } +- +- rv = stream_update_dep_on_attach_item(stream); +- if (rv != 0) { +- /* This may relave stream->queued == 1, but stream->item == NULL. +- But only consequence of this error is fatal one, and session +- destruction. In that execution path, these inconsistency does +- not matter. */ +- stream->item = NULL; +- return rv; +- } +- +- return 0; + } + + void nghttp2_stream_detach_item(nghttp2_stream *stream) { +@@ -506,12 +86,6 @@ void nghttp2_stream_detach_item(nghttp2_stream *stream) { + + stream->item = NULL; + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); +- +- if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { +- return; +- } +- +- stream_update_dep_on_detach_item(stream); + } + + void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { +@@ -521,31 +95,16 @@ void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { + stream->item, flags); + + stream->flags |= flags; +- +- if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { +- return; +- } +- +- stream_update_dep_on_detach_item(stream); + } + +-int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) { ++void nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, ++ uint8_t flags) { + assert(stream->item); + + DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id, + stream->item, flags); + + stream->flags = (uint8_t)(stream->flags & ~flags); +- +- if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { +- return 0; +- } +- +- if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { +- return 0; +- } +- +- return stream_update_dep_on_attach_item(stream); + } + + int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { +@@ -591,373 +150,11 @@ void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); + } + +-int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, +- nghttp2_stream *target) { +- for (; stream; stream = stream->dep_prev) { +- if (stream == target) { +- return 1; +- } +- } +- return 0; +-} +- +-int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, +- nghttp2_stream *stream) { +- nghttp2_stream *si; +- int rv; +- +- DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, +- dep_stream->stream_id, stream, stream->stream_id); +- +- stream->sum_dep_weight = dep_stream->sum_dep_weight; +- dep_stream->sum_dep_weight = stream->weight; +- +- if (dep_stream->dep_next) { +- for (si = dep_stream->dep_next; si; si = si->sib_next) { +- si->dep_prev = stream; +- if (si->queued) { +- rv = stream_obq_move(stream, dep_stream, si); +- if (rv != 0) { +- return rv; +- } +- } +- } +- +- if (stream_subtree_active(stream)) { +- rv = stream_obq_push(dep_stream, stream); +- if (rv != 0) { +- return rv; +- } +- } +- +- stream->dep_next = dep_stream->dep_next; +- } +- +- dep_stream->dep_next = stream; +- stream->dep_prev = dep_stream; +- +- validate_tree(stream); +- +- return 0; +-} +- +-static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) { +- for (; stream; stream = stream->sib_next) { +- stream->dep_prev = dep; +- } +-} +- +-static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) { +- dep_stream->dep_next = stream; +- if (stream) { +- stream->dep_prev = dep_stream; +- } +-} +- +-static void link_sib(nghttp2_stream *a, nghttp2_stream *b) { +- a->sib_next = b; +- if (b) { +- b->sib_prev = a; +- } +-} +- +-static void insert_link_dep(nghttp2_stream *dep_stream, +- nghttp2_stream *stream) { +- nghttp2_stream *sib_next; +- +- assert(stream->sib_prev == NULL); +- +- sib_next = dep_stream->dep_next; +- +- link_sib(stream, sib_next); +- +- link_dep(dep_stream, stream); +-} +- +-static void unlink_sib(nghttp2_stream *stream) { +- nghttp2_stream *prev, *next, *dep_next; +- +- prev = stream->sib_prev; +- dep_next = stream->dep_next; +- +- assert(prev); +- +- if (dep_next) { +- /* +- * prev--stream(--sib_next--...) +- * | +- * dep_next +- */ +- +- link_sib(prev, dep_next); +- +- set_dep_prev(dep_next, stream->dep_prev); +- +- if (stream->sib_next) { +- link_sib(stream_last_sib(dep_next), stream->sib_next); +- } +- } else { +- /* +- * prev--stream(--sib_next--...) +- */ +- next = stream->sib_next; +- +- prev->sib_next = next; +- +- if (next) { +- next->sib_prev = prev; +- } +- } +-} +- +-static void unlink_dep(nghttp2_stream *stream) { +- nghttp2_stream *prev, *next, *dep_next; +- +- prev = stream->dep_prev; +- dep_next = stream->dep_next; +- +- assert(prev); +- +- if (dep_next) { +- /* +- * prev +- * | +- * stream(--sib_next--...) +- * | +- * dep_next +- */ +- link_dep(prev, dep_next); +- +- set_dep_prev(dep_next, stream->dep_prev); +- +- if (stream->sib_next) { +- link_sib(stream_last_sib(dep_next), stream->sib_next); +- } +- +- } else if (stream->sib_next) { +- /* +- * prev +- * | +- * stream--sib_next +- */ +- next = stream->sib_next; +- +- next->sib_prev = NULL; +- +- link_dep(prev, next); +- } else { +- prev->dep_next = NULL; +- } +-} +- +-void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, +- nghttp2_stream *stream) { +- DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, +- dep_stream->stream_id, stream, stream->stream_id); +- +- dep_stream->sum_dep_weight += stream->weight; +- +- if (dep_stream->dep_next == NULL) { +- link_dep(dep_stream, stream); +- } else { +- insert_link_dep(dep_stream, stream); +- } +- +- validate_tree(stream); +-} +- +-int nghttp2_stream_dep_remove(nghttp2_stream *stream) { +- nghttp2_stream *dep_prev, *si; +- int32_t sum_dep_weight_delta; +- int rv; +- +- DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id); +- +- /* Distribute weight of |stream| to direct descendants */ +- sum_dep_weight_delta = -stream->weight; +- +- for (si = stream->dep_next; si; si = si->sib_next) { +- si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight); +- +- sum_dep_weight_delta += si->weight; +- +- if (si->queued) { +- rv = stream_obq_move(stream->dep_prev, stream, si); +- if (rv != 0) { +- return rv; +- } +- } +- } +- +- assert(stream->dep_prev); +- +- dep_prev = stream->dep_prev; +- +- dep_prev->sum_dep_weight += sum_dep_weight_delta; +- +- if (stream->queued) { +- stream_obq_remove(stream); +- } +- +- if (stream->sib_prev) { +- unlink_sib(stream); +- } else { +- unlink_dep(stream); +- } +- +- stream->sum_dep_weight = 0; +- +- stream->dep_prev = NULL; +- stream->dep_next = NULL; +- stream->sib_prev = NULL; +- stream->sib_next = NULL; +- +- validate_tree(dep_prev); +- +- return 0; +-} +- +-int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, +- nghttp2_stream *stream) { +- nghttp2_stream *last_sib; +- nghttp2_stream *dep_next; +- nghttp2_stream *si; +- int rv; +- +- DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n", +- dep_stream, dep_stream->stream_id, stream, stream->stream_id); +- +- stream->sum_dep_weight += dep_stream->sum_dep_weight; +- dep_stream->sum_dep_weight = stream->weight; +- +- if (dep_stream->dep_next) { +- dep_next = dep_stream->dep_next; +- +- link_dep(dep_stream, stream); +- +- if (stream->dep_next) { +- last_sib = stream_last_sib(stream->dep_next); +- +- link_sib(last_sib, dep_next); +- } else { +- link_dep(stream, dep_next); +- } +- +- for (si = dep_next; si; si = si->sib_next) { +- si->dep_prev = stream; +- if (si->queued) { +- rv = stream_obq_move(stream, dep_stream, si); +- if (rv != 0) { +- return rv; +- } +- } +- } +- } else { +- link_dep(dep_stream, stream); +- } +- +- if (stream_subtree_active(stream)) { +- rv = stream_obq_push(dep_stream, stream); +- if (rv != 0) { +- return rv; +- } +- } +- +- validate_tree(dep_stream); +- +- return 0; +-} +- +-int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, +- nghttp2_stream *stream) { +- int rv; +- +- DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n", +- dep_stream, dep_stream->stream_id, stream, stream->stream_id); +- +- dep_stream->sum_dep_weight += stream->weight; +- +- if (dep_stream->dep_next) { +- insert_link_dep(dep_stream, stream); +- } else { +- link_dep(dep_stream, stream); +- } +- +- if (stream_subtree_active(stream)) { +- rv = stream_obq_push(dep_stream, stream); +- if (rv != 0) { +- return rv; +- } +- } +- +- validate_tree(dep_stream); +- +- return 0; +-} +- +-void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) { +- nghttp2_stream *next, *dep_prev; +- +- DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream, +- stream->stream_id); +- +- assert(stream->dep_prev); +- +- dep_prev = stream->dep_prev; +- +- if (stream->sib_prev) { +- link_sib(stream->sib_prev, stream->sib_next); +- } else { +- next = stream->sib_next; +- +- link_dep(dep_prev, next); +- +- if (next) { +- next->sib_prev = NULL; +- } +- } +- +- dep_prev->sum_dep_weight -= stream->weight; +- +- if (stream->queued) { +- stream_obq_remove(stream); +- } +- +- validate_tree(dep_prev); +- +- stream->sib_prev = NULL; +- stream->sib_next = NULL; +- stream->dep_prev = NULL; +-} +- +-int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { +- return stream->dep_prev || stream->dep_next || stream->sib_prev || +- stream->sib_next; +-} +- +-nghttp2_outbound_item * +-nghttp2_stream_next_outbound_item(nghttp2_stream *stream) { +- nghttp2_pq_entry *ent; +- nghttp2_stream *si; +- +- for (;;) { +- if (stream_active(stream)) { +- /* Update ascendant's descendant_last_cycle here, so that we can +- assure that new stream is scheduled based on it. */ +- for (si = stream; si->dep_prev; si = si->dep_prev) { +- si->dep_prev->descendant_last_cycle = si->cycle; +- } +- return stream->item; +- } +- ent = nghttp2_pq_top(&stream->obq); +- if (!ent) { +- return NULL; +- } +- stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry); ++nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) { ++ if (stream == &nghttp2_stream_root) { ++ return NGHTTP2_STREAM_STATE_IDLE; + } +-} + +-nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) { + if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) { + return NGHTTP2_STREAM_STATE_CLOSED; + } +@@ -988,27 +185,39 @@ nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) { + } + + nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) { +- return stream->dep_prev; ++ (void)stream; ++ ++ return NULL; + } + + nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) { +- return stream->sib_next; ++ (void)stream; ++ ++ return NULL; + } + + nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) { +- return stream->sib_prev; ++ (void)stream; ++ ++ return NULL; + } + + nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) { +- return stream->dep_next; ++ (void)stream; ++ ++ return NULL; + } + + int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) { +- return stream->weight; ++ (void)stream; ++ ++ return NGHTTP2_DEFAULT_WEIGHT; + } + + int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) { +- return stream->sum_dep_weight; ++ (void)stream; ++ ++ return 0; + } + + int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) { +diff --git a/deps/nghttp2/lib/nghttp2_stream.h b/deps/nghttp2/lib/nghttp2_stream.h +index 28add165..603209ec 100644 +--- a/deps/nghttp2/lib/nghttp2_stream.h ++++ b/deps/nghttp2/lib/nghttp2_stream.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + #include "nghttp2_outbound_item.h" +@@ -91,9 +91,6 @@ typedef enum { + /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and + NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ + NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c, +- /* Indicates that this stream is not subject to RFC7540 +- priorities scheme. */ +- NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10, + /* Ignore client RFC 9218 priority signal. */ + NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20, + /* Indicates that RFC 9113 leading and trailing white spaces +@@ -146,39 +143,18 @@ typedef enum { + } nghttp2_http_flag; + + struct nghttp2_stream { +- /* Entry for dep_prev->obq */ ++ nghttp2_stream_state state; + nghttp2_pq_entry pq_entry; +- /* Priority Queue storing direct descendant (nghttp2_stream). Only +- streams which itself has some data to send, or has a descendant +- which has some data to sent. */ +- nghttp2_pq obq; + /* Content-Length of request/response body. -1 if unknown. */ + int64_t content_length; + /* Received body so far */ + int64_t recv_content_length; +- /* Base last_cycle for direct descendent streams. */ +- uint64_t descendant_last_cycle; + /* Next scheduled time to sent item */ + uint64_t cycle; +- /* Next seq used for direct descendant streams */ +- uint64_t descendant_next_seq; + /* Secondary key for prioritization to break a tie for cycle. This + value is monotonically increased for single parent stream. */ + uint64_t seq; +- /* pointers to form dependency tree. If multiple streams depend on +- a stream, only one stream (left most) has non-NULL dep_prev which +- points to the stream it depends on. The remaining streams are +- linked using sib_prev and sib_next. The stream which has +- non-NULL dep_prev always NULL sib_prev. The right most stream +- has NULL sib_next. If this stream is a root of dependency tree, +- dep_prev and sib_prev are NULL. */ +- nghttp2_stream *dep_prev, *dep_next; +- nghttp2_stream *sib_prev, *sib_next; +- /* When stream is kept after closure, it may be kept in doubly +- linked list pointed by nghttp2_session closed_stream_head. +- closed_next points to the next stream object if it is the element +- of the list. */ +- nghttp2_stream *closed_prev, *closed_next; ++ nghttp2_stream *closed_next; + /* The arbitrary data provided by user for this stream. */ + void *stream_user_data; + /* Item to send */ +@@ -205,13 +181,8 @@ struct nghttp2_stream { + NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by + submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ + int32_t local_window_size; +- /* weight of this stream */ +- int32_t weight; + /* This is unpaid penalty (offset) when calculating cycle. */ + uint32_t pending_penalty; +- /* sum of weight of direct descendants */ +- int32_t sum_dep_weight; +- nghttp2_stream_state state; + /* status code from remote server */ + int16_t status_code; + /* Bitwise OR of zero or more nghttp2_http_flag values */ +@@ -239,9 +210,9 @@ struct nghttp2_stream { + + void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, +- int32_t weight, int32_t remote_initial_window_size, ++ int32_t remote_initial_window_size, + int32_t local_initial_window_size, +- void *stream_user_data, nghttp2_mem *mem); ++ void *stream_user_data); + + void nghttp2_stream_free(nghttp2_stream *stream); + +@@ -267,14 +238,8 @@ void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are + * cleared if they are set. So even if this function is called, if + * one of flag is still set, data does not become active. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory + */ +-int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags); ++void nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags); + + /* + * Returns nonzero if item is deferred by whatever reason. +@@ -317,57 +282,11 @@ int nghttp2_stream_update_local_initial_window_size( + */ + void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); + +-/* +- * Returns nonzero if |target| is an ancestor of |stream|. +- */ +-int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, +- nghttp2_stream *target); +- +-/* +- * Computes distributed weight of a stream of the |weight| under the +- * |stream| if |stream| is removed from a dependency tree. +- */ +-int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, +- int32_t weight); +- +-/* +- * Makes the |stream| depend on the |dep_stream|. This dependency is +- * exclusive. All existing direct descendants of |dep_stream| become +- * the descendants of the |stream|. This function assumes +- * |stream->item| is NULL. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, +- nghttp2_stream *stream); +- +-/* +- * Makes the |stream| depend on the |dep_stream|. This dependency is +- * not exclusive. This function assumes |stream->item| is NULL. +- */ +-void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); +- +-/* +- * Removes the |stream| from the current dependency tree. This +- * function assumes |stream->item| is NULL. +- */ +-int nghttp2_stream_dep_remove(nghttp2_stream *stream); +- + /* + * Attaches |item| to |stream|. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory + */ +-int nghttp2_stream_attach_item(nghttp2_stream *stream, +- nghttp2_outbound_item *item); ++void nghttp2_stream_attach_item(nghttp2_stream *stream, ++ nghttp2_outbound_item *item); + + /* + * Detaches |stream->item|. This function does not free +@@ -375,66 +294,4 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream, + */ + void nghttp2_stream_detach_item(nghttp2_stream *stream); + +-/* +- * Makes the |stream| depend on the |dep_stream|. This dependency is +- * exclusive. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, +- nghttp2_stream *stream); +- +-/* +- * Makes the |stream| depend on the |dep_stream|. This dependency is +- * not exclusive. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, +- nghttp2_stream *stream); +- +-/* +- * Removes subtree whose root stream is |stream|. The +- * effective_weight of streams in removed subtree is not updated. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory +- */ +-void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); +- +-/* +- * Returns nonzero if |stream| is in any dependency tree. +- */ +-int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); +- +-/* +- * Schedules transmission of |stream|'s item, assuming stream->item is +- * attached, and stream->last_writelen was updated. +- */ +-void nghttp2_stream_reschedule(nghttp2_stream *stream); +- +-/* +- * Changes |stream|'s weight to |weight|. If |stream| is queued, it +- * will be rescheduled based on new weight. +- */ +-void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight); +- +-/* +- * Returns a stream which has highest priority, updating +- * descendant_last_cycle of selected stream's ancestors. +- */ +-nghttp2_outbound_item * +-nghttp2_stream_next_outbound_item(nghttp2_stream *stream); +- +-#endif /* NGHTTP2_STREAM */ ++#endif /* !defined(NGHTTP2_STREAM_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_submit.c b/deps/nghttp2/lib/nghttp2_submit.c +index 81c1ab70..8a90f714 100644 +--- a/deps/nghttp2/lib/nghttp2_submit.c ++++ b/deps/nghttp2/lib/nghttp2_submit.c +@@ -32,42 +32,12 @@ + #include "nghttp2_helper.h" + #include "nghttp2_priority_spec.h" + +-/* +- * Detects the dependency error, that is stream attempted to depend on +- * itself. If |stream_id| is -1, we use session->next_stream_id as +- * stream ID. +- * +- * This function returns 0 if it succeeds, or one of the following +- * error codes: +- * +- * NGHTTP2_ERR_INVALID_ARGUMENT +- * Stream attempted to depend on itself. +- */ +-static int detect_self_dependency(nghttp2_session *session, int32_t stream_id, +- const nghttp2_priority_spec *pri_spec) { +- assert(pri_spec); +- +- if (stream_id == -1) { +- if ((int32_t)session->next_stream_id == pri_spec->stream_id) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- return 0; +- } +- +- if (stream_id == pri_spec->stream_id) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- +- return 0; +-} +- + /* This function takes ownership of |nva_copy|. Regardless of the + return value, the caller must not free |nva_copy| after this + function returns. */ + static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, +- int32_t stream_id, +- const nghttp2_priority_spec *pri_spec, +- nghttp2_nv *nva_copy, size_t nvlen, ++ int32_t stream_id, nghttp2_nv *nva_copy, ++ size_t nvlen, + const nghttp2_data_provider_wrap *dpw, + void *stream_user_data) { + int rv; +@@ -114,8 +84,8 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, + + frame = &item->frame; + +- nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, +- pri_spec, nva_copy, nvlen); ++ nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, NULL, ++ nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + +@@ -141,31 +111,22 @@ fail2: + + static int32_t submit_headers_shared_nva(nghttp2_session *session, + uint8_t flags, int32_t stream_id, +- const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider_wrap *dpw, + void *stream_user_data) { + int rv; + nghttp2_nv *nva_copy; +- nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + +- if (pri_spec) { +- copy_pri_spec = *pri_spec; +- nghttp2_priority_spec_normalize_weight(©_pri_spec); +- } else { +- nghttp2_priority_spec_default_init(©_pri_spec); +- } +- + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + return rv; + } + +- return submit_headers_shared(session, flags, stream_id, ©_pri_spec, +- nva_copy, nvlen, dpw, stream_user_data); ++ return submit_headers_shared(session, flags, stream_id, nva_copy, nvlen, dpw, ++ stream_user_data); + } + + int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, +@@ -174,8 +135,8 @@ int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + +- return (int)submit_headers_shared_nva( +- session, NGHTTP2_FLAG_END_STREAM, stream_id, NULL, nva, nvlen, NULL, NULL); ++ return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, ++ stream_id, nva, nvlen, NULL, NULL); + } + + int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, +@@ -183,7 +144,7 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data) { +- int rv; ++ (void)pri_spec; + + if (stream_id == -1) { + if (session->server) { +@@ -195,20 +156,8 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + + flags &= NGHTTP2_FLAG_END_STREAM; + +- if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) && +- session->remote_settings.no_rfc7540_priorities != 1) { +- rv = detect_self_dependency(session, stream_id, pri_spec); +- if (rv != 0) { +- return rv; +- } +- +- flags |= NGHTTP2_FLAG_PRIORITY; +- } else { +- pri_spec = NULL; +- } +- +- return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, +- nvlen, NULL, stream_user_data); ++ return submit_headers_shared_nva(session, flags, stream_id, nva, nvlen, NULL, ++ stream_user_data); + } + + int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, +@@ -220,51 +169,10 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { +- int rv; +- nghttp2_outbound_item *item; +- nghttp2_frame *frame; +- nghttp2_priority_spec copy_pri_spec; +- nghttp2_mem *mem; ++ (void)session; + (void)flags; +- +- mem = &session->mem; +- +- if (session->remote_settings.no_rfc7540_priorities == 1) { +- return 0; +- } +- +- if (stream_id == 0 || pri_spec == NULL) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- +- if (stream_id == pri_spec->stream_id) { +- return NGHTTP2_ERR_INVALID_ARGUMENT; +- } +- +- copy_pri_spec = *pri_spec; +- +- nghttp2_priority_spec_normalize_weight(©_pri_spec); +- +- item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); +- +- if (item == NULL) { +- return NGHTTP2_ERR_NOMEM; +- } +- +- nghttp2_outbound_item_init(item); +- +- frame = &item->frame; +- +- nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec); +- +- rv = nghttp2_session_add_item(session, item); +- +- if (rv != 0) { +- nghttp2_frame_priority_free(&frame->priority); +- nghttp2_mem_free(mem, item); +- +- return rv; +- } ++ (void)stream_id; ++ (void)pri_spec; + + return 0; + } +@@ -277,7 +185,8 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + +- return nghttp2_session_add_rst_stream(session, stream_id, error_code); ++ return nghttp2_session_add_rst_stream_continue( ++ session, stream_id, error_code, /* continue_without_stream = */ 0); + } + + int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, +@@ -578,7 +487,7 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, + return 0; + + fail_item_malloc: +- free(buf); ++ nghttp2_mem_free(mem, buf); + + return rv; + } +@@ -661,7 +570,7 @@ int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, + return 0; + + fail_item_malloc: +- free(ov_copy); ++ nghttp2_mem_free(mem, ov_copy); + + return rv; + } +@@ -733,51 +642,34 @@ int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags, + return 0; + + fail_item_malloc: +- free(buf); ++ nghttp2_mem_free(mem, buf); + + return rv; + } + +-static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, +- const nghttp2_data_provider_wrap *dpw) { ++static uint8_t set_request_flags(const nghttp2_data_provider_wrap *dpw) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (dpw == NULL || dpw->data_prd.read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + +- if (pri_spec) { +- flags |= NGHTTP2_FLAG_PRIORITY; +- } +- + return flags; + } + + static int32_t submit_request_shared(nghttp2_session *session, +- const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider_wrap *dpw, + void *stream_user_data) { + uint8_t flags; +- int rv; + + if (session->server) { + return NGHTTP2_ERR_PROTO; + } + +- if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) && +- session->remote_settings.no_rfc7540_priorities != 1) { +- rv = detect_self_dependency(session, -1, pri_spec); +- if (rv != 0) { +- return rv; +- } +- } else { +- pri_spec = NULL; +- } +- +- flags = set_request_flags(pri_spec, dpw); ++ flags = set_request_flags(dpw); + +- return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, +- dpw, stream_user_data); ++ return submit_headers_shared_nva(session, flags, -1, nva, nvlen, dpw, ++ stream_user_data); + } + + int32_t nghttp2_submit_request(nghttp2_session *session, +@@ -786,8 +678,9 @@ int32_t nghttp2_submit_request(nghttp2_session *session, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + nghttp2_data_provider_wrap dpw; ++ (void)pri_spec; + +- return submit_request_shared(session, pri_spec, nva, nvlen, ++ return submit_request_shared(session, nva, nvlen, + nghttp2_data_provider_wrap_v1(&dpw, data_prd), + stream_user_data); + } +@@ -798,8 +691,9 @@ int32_t nghttp2_submit_request2(nghttp2_session *session, + const nghttp2_data_provider2 *data_prd, + void *stream_user_data) { + nghttp2_data_provider_wrap dpw; ++ (void)pri_spec; + +- return submit_request_shared(session, pri_spec, nva, nvlen, ++ return submit_request_shared(session, nva, nvlen, + nghttp2_data_provider_wrap_v2(&dpw, data_prd), + stream_user_data); + } +@@ -826,8 +720,8 @@ static int submit_response_shared(nghttp2_session *session, int32_t stream_id, + } + + flags = set_response_flags(dpw); +- return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, +- dpw, NULL); ++ return submit_headers_shared_nva(session, flags, stream_id, nva, nvlen, dpw, ++ NULL); + } + + int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, +diff --git a/deps/nghttp2/lib/nghttp2_submit.h b/deps/nghttp2/lib/nghttp2_submit.h +index 350ee022..decf803d 100644 +--- a/deps/nghttp2/lib/nghttp2_submit.h ++++ b/deps/nghttp2/lib/nghttp2_submit.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -37,4 +37,4 @@ int nghttp2_submit_data_shared(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider_wrap *dpw); + +-#endif /* NGHTTP2_SUBMIT_H */ ++#endif /* !defined(NGHTTP2_SUBMIT_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_time.c b/deps/nghttp2/lib/nghttp2_time.c +index 148ccfdc..18fd7bca 100644 +--- a/deps/nghttp2/lib/nghttp2_time.c ++++ b/deps/nghttp2/lib/nghttp2_time.c +@@ -26,7 +26,7 @@ + + #ifdef HAVE_WINDOWS_H + # include +-#endif /* HAVE_WINDOWS_H */ ++#endif /* defined(HAVE_WINDOWS_H) */ + + #include + +@@ -40,12 +40,11 @@ static uint64_t time_now_sec(void) { + + return (uint64_t)t; + } +-#endif /* !HAVE_GETTICKCOUNT64 || __CYGWIN__ */ ++#endif /* !defined(HAVE_GETTICKCOUNT64) || defined(__CYGWIN__) */ + + #if defined(HAVE_GETTICKCOUNT64) && !defined(__CYGWIN__) + uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; } +-#elif defined(HAVE_CLOCK_GETTIME) && defined(HAVE_DECL_CLOCK_MONOTONIC) && \ +- HAVE_DECL_CLOCK_MONOTONIC ++#elif defined(HAVE_CLOCK_GETTIME) && HAVE_DECL_CLOCK_MONOTONIC + uint64_t nghttp2_time_now_sec(void) { + struct timespec tp; + int rv = clock_gettime(CLOCK_MONOTONIC, &tp); +@@ -56,8 +55,8 @@ uint64_t nghttp2_time_now_sec(void) { + + return (uint64_t)tp.tv_sec; + } +-#else /* (!HAVE_CLOCK_GETTIME || !HAVE_DECL_CLOCK_MONOTONIC) && \ +- (!HAVE_GETTICKCOUNT64 || __CYGWIN__)) */ ++#else /* (!defined(HAVE_GETTICKCOUNT64) || !defined(__CYGWIN__)) && \ ++ (!defined(HAVE_CLOCK_GETTIME) || !HAVE_DECL_CLOCK_MONOTONIC) */ + uint64_t nghttp2_time_now_sec(void) { return time_now_sec(); } +-#endif /* (!HAVE_CLOCK_GETTIME || !HAVE_DECL_CLOCK_MONOTONIC) && \ +- (!HAVE_GETTICKCOUNT64 || __CYGWIN__)) */ ++#endif /* (!defined(HAVE_GETTICKCOUNT64) || !defined(__CYGWIN__)) && \ ++ (!defined(HAVE_CLOCK_GETTIME) || !HAVE_DECL_CLOCK_MONOTONIC) */ +diff --git a/deps/nghttp2/lib/nghttp2_time.h b/deps/nghttp2/lib/nghttp2_time.h +index 03c0bbe9..a91e62ad 100644 +--- a/deps/nghttp2/lib/nghttp2_time.h ++++ b/deps/nghttp2/lib/nghttp2_time.h +@@ -27,7 +27,7 @@ + + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +@@ -35,4 +35,4 @@ + timepoint. If it is unable to get seconds, it returns 0. */ + uint64_t nghttp2_time_now_sec(void); + +-#endif /* NGHTTP2_TIME_H */ ++#endif /* !defined(NGHTTP2_TIME_H) */ +diff --git a/deps/nghttp2/lib/nghttp2_version.c b/deps/nghttp2/lib/nghttp2_version.c +index 4211f2cf..39a49ff3 100644 +--- a/deps/nghttp2/lib/nghttp2_version.c ++++ b/deps/nghttp2/lib/nghttp2_version.c +@@ -24,7 +24,7 @@ + */ + #ifdef HAVE_CONFIG_H + # include +-#endif /* HAVE_CONFIG_H */ ++#endif /* defined(HAVE_CONFIG_H) */ + + #include + +diff --git a/deps/nghttp2/lib/sfparse.c b/deps/nghttp2/lib/sfparse.c +index b5e94cc2..cee089d3 100644 +--- a/deps/nghttp2/lib/sfparse.c ++++ b/deps/nghttp2/lib/sfparse.c +@@ -30,38 +30,46 @@ + #include + #include + +-#define SF_STATE_DICT 0x08u +-#define SF_STATE_LIST 0x10u +-#define SF_STATE_ITEM 0x18u ++#ifdef __AVX2__ ++# include ++#endif /* __AVX2__ */ + +-#define SF_STATE_INNER_LIST 0x04u ++#define SFPARSE_STATE_DICT 0x08u ++#define SFPARSE_STATE_LIST 0x10u ++#define SFPARSE_STATE_ITEM 0x18u + +-#define SF_STATE_BEFORE 0x00u +-#define SF_STATE_BEFORE_PARAMS 0x01u +-#define SF_STATE_PARAMS 0x02u +-#define SF_STATE_AFTER 0x03u ++#define SFPARSE_STATE_INNER_LIST 0x04u + +-#define SF_STATE_OP_MASK 0x03u ++#define SFPARSE_STATE_BEFORE 0x00u ++#define SFPARSE_STATE_BEFORE_PARAMS 0x01u ++#define SFPARSE_STATE_PARAMS 0x02u ++#define SFPARSE_STATE_AFTER 0x03u + +-#define SF_SET_STATE_AFTER(NAME) (SF_STATE_##NAME | SF_STATE_AFTER) +-#define SF_SET_STATE_BEFORE_PARAMS(NAME) \ +- (SF_STATE_##NAME | SF_STATE_BEFORE_PARAMS) +-#define SF_SET_STATE_INNER_LIST_BEFORE(NAME) \ +- (SF_STATE_##NAME | SF_STATE_INNER_LIST | SF_STATE_BEFORE) ++#define SFPARSE_STATE_OP_MASK 0x03u + +-#define SF_STATE_DICT_AFTER SF_SET_STATE_AFTER(DICT) +-#define SF_STATE_DICT_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(DICT) +-#define SF_STATE_DICT_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(DICT) ++#define SFPARSE_SET_STATE_AFTER(NAME) \ ++ (SFPARSE_STATE_##NAME | SFPARSE_STATE_AFTER) ++#define SFPARSE_SET_STATE_BEFORE_PARAMS(NAME) \ ++ (SFPARSE_STATE_##NAME | SFPARSE_STATE_BEFORE_PARAMS) ++#define SFPARSE_SET_STATE_INNER_LIST_BEFORE(NAME) \ ++ (SFPARSE_STATE_##NAME | SFPARSE_STATE_INNER_LIST | SFPARSE_STATE_BEFORE) + +-#define SF_STATE_LIST_AFTER SF_SET_STATE_AFTER(LIST) +-#define SF_STATE_LIST_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(LIST) +-#define SF_STATE_LIST_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(LIST) ++#define SFPARSE_STATE_DICT_AFTER SFPARSE_SET_STATE_AFTER(DICT) ++#define SFPARSE_STATE_DICT_BEFORE_PARAMS SFPARSE_SET_STATE_BEFORE_PARAMS(DICT) ++#define SFPARSE_STATE_DICT_INNER_LIST_BEFORE \ ++ SFPARSE_SET_STATE_INNER_LIST_BEFORE(DICT) + +-#define SF_STATE_ITEM_AFTER SF_SET_STATE_AFTER(ITEM) +-#define SF_STATE_ITEM_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(ITEM) +-#define SF_STATE_ITEM_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(ITEM) ++#define SFPARSE_STATE_LIST_AFTER SFPARSE_SET_STATE_AFTER(LIST) ++#define SFPARSE_STATE_LIST_BEFORE_PARAMS SFPARSE_SET_STATE_BEFORE_PARAMS(LIST) ++#define SFPARSE_STATE_LIST_INNER_LIST_BEFORE \ ++ SFPARSE_SET_STATE_INNER_LIST_BEFORE(LIST) + +-#define SF_STATE_INITIAL 0x00u ++#define SFPARSE_STATE_ITEM_AFTER SFPARSE_SET_STATE_AFTER(ITEM) ++#define SFPARSE_STATE_ITEM_BEFORE_PARAMS SFPARSE_SET_STATE_BEFORE_PARAMS(ITEM) ++#define SFPARSE_STATE_ITEM_INNER_LIST_BEFORE \ ++ SFPARSE_SET_STATE_INNER_LIST_BEFORE(ITEM) ++ ++#define SFPARSE_STATE_INITIAL 0x00u + + #define DIGIT_CASES \ + case '0': \ +@@ -135,6 +143,70 @@ + UCALPHA_CASES: \ + LCALPHA_CASES + ++#define TOKEN_CASES \ ++ case '!': \ ++ case '#': \ ++ case '$': \ ++ case '%': \ ++ case '&': \ ++ case '\'': \ ++ case '*': \ ++ case '+': \ ++ case '-': \ ++ case '.': \ ++ case '/': \ ++ DIGIT_CASES: \ ++ case ':': \ ++ UCALPHA_CASES: \ ++ case '^': \ ++ case '_': \ ++ case '`': \ ++ LCALPHA_CASES: \ ++ case '|': \ ++ case '~' ++ ++#define LCHEXALPHA_CASES \ ++ case 'a': \ ++ case 'b': \ ++ case 'c': \ ++ case 'd': \ ++ case 'e': \ ++ case 'f' ++ ++#define X00_1F_CASES \ ++ case 0x00: \ ++ case 0x01: \ ++ case 0x02: \ ++ case 0x03: \ ++ case 0x04: \ ++ case 0x05: \ ++ case 0x06: \ ++ case 0x07: \ ++ case 0x08: \ ++ case 0x09: \ ++ case 0x0a: \ ++ case 0x0b: \ ++ case 0x0c: \ ++ case 0x0d: \ ++ case 0x0e: \ ++ case 0x0f: \ ++ case 0x10: \ ++ case 0x11: \ ++ case 0x12: \ ++ case 0x13: \ ++ case 0x14: \ ++ case 0x15: \ ++ case 0x16: \ ++ case 0x17: \ ++ case 0x18: \ ++ case 0x19: \ ++ case 0x1a: \ ++ case 0x1b: \ ++ case 0x1c: \ ++ case 0x1d: \ ++ case 0x1e: \ ++ case 0x1f ++ + #define X20_21_CASES \ + case ' ': \ + case '!' +@@ -175,6 +247,137 @@ + case '}': \ + case '~' + ++#define X7F_FF_CASES \ ++ case 0x7f: \ ++ case 0x80: \ ++ case 0x81: \ ++ case 0x82: \ ++ case 0x83: \ ++ case 0x84: \ ++ case 0x85: \ ++ case 0x86: \ ++ case 0x87: \ ++ case 0x88: \ ++ case 0x89: \ ++ case 0x8a: \ ++ case 0x8b: \ ++ case 0x8c: \ ++ case 0x8d: \ ++ case 0x8e: \ ++ case 0x8f: \ ++ case 0x90: \ ++ case 0x91: \ ++ case 0x92: \ ++ case 0x93: \ ++ case 0x94: \ ++ case 0x95: \ ++ case 0x96: \ ++ case 0x97: \ ++ case 0x98: \ ++ case 0x99: \ ++ case 0x9a: \ ++ case 0x9b: \ ++ case 0x9c: \ ++ case 0x9d: \ ++ case 0x9e: \ ++ case 0x9f: \ ++ case 0xa0: \ ++ case 0xa1: \ ++ case 0xa2: \ ++ case 0xa3: \ ++ case 0xa4: \ ++ case 0xa5: \ ++ case 0xa6: \ ++ case 0xa7: \ ++ case 0xa8: \ ++ case 0xa9: \ ++ case 0xaa: \ ++ case 0xab: \ ++ case 0xac: \ ++ case 0xad: \ ++ case 0xae: \ ++ case 0xaf: \ ++ case 0xb0: \ ++ case 0xb1: \ ++ case 0xb2: \ ++ case 0xb3: \ ++ case 0xb4: \ ++ case 0xb5: \ ++ case 0xb6: \ ++ case 0xb7: \ ++ case 0xb8: \ ++ case 0xb9: \ ++ case 0xba: \ ++ case 0xbb: \ ++ case 0xbc: \ ++ case 0xbd: \ ++ case 0xbe: \ ++ case 0xbf: \ ++ case 0xc0: \ ++ case 0xc1: \ ++ case 0xc2: \ ++ case 0xc3: \ ++ case 0xc4: \ ++ case 0xc5: \ ++ case 0xc6: \ ++ case 0xc7: \ ++ case 0xc8: \ ++ case 0xc9: \ ++ case 0xca: \ ++ case 0xcb: \ ++ case 0xcc: \ ++ case 0xcd: \ ++ case 0xce: \ ++ case 0xcf: \ ++ case 0xd0: \ ++ case 0xd1: \ ++ case 0xd2: \ ++ case 0xd3: \ ++ case 0xd4: \ ++ case 0xd5: \ ++ case 0xd6: \ ++ case 0xd7: \ ++ case 0xd8: \ ++ case 0xd9: \ ++ case 0xda: \ ++ case 0xdb: \ ++ case 0xdc: \ ++ case 0xdd: \ ++ case 0xde: \ ++ case 0xdf: \ ++ case 0xe0: \ ++ case 0xe1: \ ++ case 0xe2: \ ++ case 0xe3: \ ++ case 0xe4: \ ++ case 0xe5: \ ++ case 0xe6: \ ++ case 0xe7: \ ++ case 0xe8: \ ++ case 0xe9: \ ++ case 0xea: \ ++ case 0xeb: \ ++ case 0xec: \ ++ case 0xed: \ ++ case 0xee: \ ++ case 0xef: \ ++ case 0xf0: \ ++ case 0xf1: \ ++ case 0xf2: \ ++ case 0xf3: \ ++ case 0xf4: \ ++ case 0xf5: \ ++ case 0xf6: \ ++ case 0xf7: \ ++ case 0xf8: \ ++ case 0xf9: \ ++ case 0xfa: \ ++ case 0xfb: \ ++ case 0xfc: \ ++ case 0xfd: \ ++ case 0xfe: \ ++ case 0xff ++ + static int is_ws(uint8_t c) { + switch (c) { + case ' ': +@@ -185,40 +388,108 @@ static int is_ws(uint8_t c) { + } + } + +-static int parser_eof(sf_parser *sfp) { return sfp->pos == sfp->end; } ++#ifdef __AVX2__ ++# ifdef _MSC_VER ++# include ++ ++static int ctz(unsigned int v) { ++ unsigned long n; ++ ++ /* Assume that v is not 0. */ ++ _BitScanForward(&n, v); ++ ++ return (int)n; ++} ++# else /* !_MSC_VER */ ++# define ctz __builtin_ctz ++# endif /* !_MSC_VER */ ++#endif /* __AVX2__ */ ++ ++static int parser_eof(sfparse_parser *sfp) { return sfp->pos == sfp->end; } + +-static void parser_discard_ows(sf_parser *sfp) { ++static void parser_discard_ows(sfparse_parser *sfp) { + for (; !parser_eof(sfp) && is_ws(*sfp->pos); ++sfp->pos) + ; + } + +-static void parser_discard_sp(sf_parser *sfp) { ++static void parser_discard_sp(sfparse_parser *sfp) { + for (; !parser_eof(sfp) && *sfp->pos == ' '; ++sfp->pos) + ; + } + +-static void parser_set_op_state(sf_parser *sfp, uint32_t op) { +- sfp->state &= ~SF_STATE_OP_MASK; ++static void parser_set_op_state(sfparse_parser *sfp, uint32_t op) { ++ sfp->state &= ~SFPARSE_STATE_OP_MASK; + sfp->state |= op; + } + +-static void parser_unset_inner_list_state(sf_parser *sfp) { +- sfp->state &= ~SF_STATE_INNER_LIST; ++static void parser_unset_inner_list_state(sfparse_parser *sfp) { ++ sfp->state &= ~SFPARSE_STATE_INNER_LIST; + } + +-static int parser_key(sf_parser *sfp, sf_vec *dest) { ++#ifdef __AVX2__ ++static const uint8_t *find_char_key(const uint8_t *first, const uint8_t *last) { ++ const __m256i us = _mm256_set1_epi8('_'); ++ const __m256i ds = _mm256_set1_epi8('-'); ++ const __m256i dot = _mm256_set1_epi8('.'); ++ const __m256i ast = _mm256_set1_epi8('*'); ++ const __m256i r0l = _mm256_set1_epi8('0' - 1); ++ const __m256i r0r = _mm256_set1_epi8('9' + 1); ++ const __m256i r1l = _mm256_set1_epi8('a' - 1); ++ const __m256i r1r = _mm256_set1_epi8('z' + 1); ++ __m256i s, x; ++ uint32_t m; ++ ++ for (; first != last; first += 32) { ++ s = _mm256_loadu_si256((void *)first); ++ ++ x = _mm256_cmpeq_epi8(s, us); ++ x = _mm256_or_si256(_mm256_cmpeq_epi8(s, ds), x); ++ x = _mm256_or_si256(_mm256_cmpeq_epi8(s, dot), x); ++ x = _mm256_or_si256(_mm256_cmpeq_epi8(s, ast), x); ++ x = _mm256_or_si256( ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r0l), _mm256_cmpgt_epi8(r0r, s)), ++ x); ++ x = _mm256_or_si256( ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r1l), _mm256_cmpgt_epi8(r1r, s)), ++ x); ++ ++ m = ~(uint32_t)_mm256_movemask_epi8(x); ++ if (m) { ++ return first + ctz(m); ++ } ++ } ++ ++ return last; ++} ++#endif /* __AVX2__ */ ++ ++static int parser_key(sfparse_parser *sfp, sfparse_vec *dest) { + const uint8_t *base; ++#ifdef __AVX2__ ++ const uint8_t *last; ++#endif /* __AVX2__ */ + + switch (*sfp->pos) { + case '*': + LCALPHA_CASES: + break; + default: +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + base = sfp->pos++; + ++#ifdef __AVX2__ ++ if (sfp->end - sfp->pos >= 32) { ++ last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); ++ ++ sfp->pos = find_char_key(sfp->pos, last); ++ if (sfp->pos != last) { ++ goto fin; ++ } ++ } ++#endif /* __AVX2__ */ ++ + for (; !parser_eof(sfp); ++sfp->pos) { + switch (*sfp->pos) { + case '_': +@@ -233,6 +504,9 @@ static int parser_key(sf_parser *sfp, sf_vec *dest) { + break; + } + ++#ifdef __AVX2__ ++fin: ++#endif /* __AVX2__ */ + if (dest) { + dest->base = (uint8_t *)base; + dest->len = (size_t)(sfp->pos - dest->base); +@@ -241,7 +515,7 @@ static int parser_key(sf_parser *sfp, sf_vec *dest) { + return 0; + } + +-static int parser_number(sf_parser *sfp, sf_value *dest) { ++static int parser_number(sfparse_parser *sfp, sfparse_value *dest) { + int sign = 1; + int64_t value = 0; + size_t len = 0; +@@ -250,7 +524,7 @@ static int parser_number(sf_parser *sfp, sf_value *dest) { + if (*sfp->pos == '-') { + ++sfp->pos; + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + sign = -1; +@@ -262,7 +536,7 @@ static int parser_number(sf_parser *sfp, sf_value *dest) { + switch (*sfp->pos) { + DIGIT_CASES: + if (++len > 15) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + value *= 10; +@@ -275,13 +549,13 @@ static int parser_number(sf_parser *sfp, sf_value *dest) { + } + + if (len == 0) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + if (parser_eof(sfp) || *sfp->pos != '.') { + if (dest) { +- dest->type = SF_TYPE_INTEGER; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_INTEGER; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + dest->integer = value * sign; + } + +@@ -291,7 +565,7 @@ static int parser_number(sf_parser *sfp, sf_value *dest) { + /* decimal */ + + if (len > 12) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + fpos = len; +@@ -302,7 +576,7 @@ static int parser_number(sf_parser *sfp, sf_value *dest) { + switch (*sfp->pos) { + DIGIT_CASES: + if (++len > 15) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + value *= 10; +@@ -315,12 +589,12 @@ static int parser_number(sf_parser *sfp, sf_value *dest) { + } + + if (fpos == len || len - fpos > 3) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + if (dest) { +- dest->type = SF_TYPE_DECIMAL; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_DECIMAL; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + dest->decimal.numer = value * sign; + + switch (len - fpos) { +@@ -342,9 +616,9 @@ static int parser_number(sf_parser *sfp, sf_value *dest) { + return 0; + } + +-static int parser_date(sf_parser *sfp, sf_value *dest) { ++static int parser_date(sfparse_parser *sfp, sfparse_value *dest) { + int rv; +- sf_value val; ++ sfparse_value val; + + /* The first byte has already been validated by the caller. */ + assert('@' == *sfp->pos); +@@ -352,7 +626,7 @@ static int parser_date(sf_parser *sfp, sf_value *dest) { + ++sfp->pos; + + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + rv = parser_number(sfp, &val); +@@ -360,27 +634,93 @@ static int parser_date(sf_parser *sfp, sf_value *dest) { + return rv; + } + +- if (val.type != SF_TYPE_INTEGER) { +- return SF_ERR_PARSE_ERROR; ++ if (val.type != SFPARSE_TYPE_INTEGER) { ++ return SFPARSE_ERR_PARSE; + } + + if (dest) { + *dest = val; +- dest->type = SF_TYPE_DATE; ++ dest->type = SFPARSE_TYPE_DATE; + } + + return 0; + } + +-static int parser_string(sf_parser *sfp, sf_value *dest) { ++#ifdef __AVX2__ ++static const uint8_t *find_char_string(const uint8_t *first, ++ const uint8_t *last) { ++ const __m256i bs = _mm256_set1_epi8('\\'); ++ const __m256i dq = _mm256_set1_epi8('"'); ++ const __m256i del = _mm256_set1_epi8(0x7f); ++ const __m256i sp = _mm256_set1_epi8(' '); ++ __m256i s, x; ++ uint32_t m; ++ ++ for (; first != last; first += 32) { ++ s = _mm256_loadu_si256((void *)first); ++ ++ x = _mm256_cmpgt_epi8(sp, s); ++ x = _mm256_or_si256(_mm256_cmpeq_epi8(s, bs), x); ++ x = _mm256_or_si256(_mm256_cmpeq_epi8(s, dq), x); ++ x = _mm256_or_si256(_mm256_cmpeq_epi8(s, del), x); ++ ++ m = (uint32_t)_mm256_movemask_epi8(x); ++ if (m) { ++ return first + ctz(m); ++ } ++ } ++ ++ return last; ++} ++#endif /* __AVX2__ */ ++ ++static int parser_string(sfparse_parser *sfp, sfparse_value *dest) { + const uint8_t *base; +- uint32_t flags = SF_VALUE_FLAG_NONE; ++#ifdef __AVX2__ ++ const uint8_t *last; ++#endif /* __AVX2__ */ ++ uint32_t flags = SFPARSE_VALUE_FLAG_NONE; + + /* The first byte has already been validated by the caller. */ + assert('"' == *sfp->pos); + + base = ++sfp->pos; + ++#ifdef __AVX2__ ++ for (; sfp->end - sfp->pos >= 32; ++sfp->pos) { ++ last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); ++ ++ sfp->pos = find_char_string(sfp->pos, last); ++ if (sfp->pos == last) { ++ break; ++ } ++ ++ switch (*sfp->pos) { ++ case '\\': ++ ++sfp->pos; ++ if (parser_eof(sfp)) { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ switch (*sfp->pos) { ++ case '"': ++ case '\\': ++ flags = SFPARSE_VALUE_FLAG_ESCAPED_STRING; ++ ++ break; ++ default: ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ break; ++ case '"': ++ goto fin; ++ default: ++ return SFPARSE_ERR_PARSE; ++ } ++ } ++#endif /* __AVX2__ */ ++ + for (; !parser_eof(sfp); ++sfp->pos) { + switch (*sfp->pos) { + X20_21_CASES: +@@ -390,75 +730,131 @@ static int parser_string(sf_parser *sfp, sf_value *dest) { + case '\\': + ++sfp->pos; + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + switch (*sfp->pos) { + case '"': + case '\\': +- flags = SF_VALUE_FLAG_ESCAPED_STRING; ++ flags = SFPARSE_VALUE_FLAG_ESCAPED_STRING; + + break; + default: +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + break; + case '"': +- if (dest) { +- dest->type = SF_TYPE_STRING; +- dest->flags = flags; +- dest->vec.len = (size_t)(sfp->pos - base); +- dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; +- } ++ goto fin; ++ default: ++ return SFPARSE_ERR_PARSE; ++ } ++ } + +- ++sfp->pos; ++ return SFPARSE_ERR_PARSE; + +- return 0; +- default: +- return SF_ERR_PARSE_ERROR; ++fin: ++ if (dest) { ++ dest->type = SFPARSE_TYPE_STRING; ++ dest->flags = flags; ++ dest->vec.len = (size_t)(sfp->pos - base); ++ dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; ++ } ++ ++ ++sfp->pos; ++ ++ return 0; ++} ++ ++#ifdef __AVX2__ ++static const uint8_t *find_char_token(const uint8_t *first, ++ const uint8_t *last) { ++ /* r0: !..:, excluding "(), ++ r1: A..Z ++ r2: ^..~, excluding {} */ ++ const __m256i r0l = _mm256_set1_epi8('!' - 1); ++ const __m256i r0r = _mm256_set1_epi8(':' + 1); ++ const __m256i dq = _mm256_set1_epi8('"'); ++ const __m256i prl = _mm256_set1_epi8('('); ++ const __m256i prr = _mm256_set1_epi8(')'); ++ const __m256i comma = _mm256_set1_epi8(','); ++ const __m256i r1l = _mm256_set1_epi8('A' - 1); ++ const __m256i r1r = _mm256_set1_epi8('Z' + 1); ++ const __m256i r2l = _mm256_set1_epi8('^' - 1); ++ const __m256i r2r = _mm256_set1_epi8('~' + 1); ++ const __m256i cbl = _mm256_set1_epi8('{'); ++ const __m256i cbr = _mm256_set1_epi8('}'); ++ __m256i s, x; ++ uint32_t m; ++ ++ for (; first != last; first += 32) { ++ s = _mm256_loadu_si256((void *)first); ++ ++ x = _mm256_andnot_si256( ++ _mm256_cmpeq_epi8(s, comma), ++ _mm256_andnot_si256( ++ _mm256_cmpeq_epi8(s, prr), ++ _mm256_andnot_si256( ++ _mm256_cmpeq_epi8(s, prl), ++ _mm256_andnot_si256(_mm256_cmpeq_epi8(s, dq), ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r0l), ++ _mm256_cmpgt_epi8(r0r, s)))))); ++ x = _mm256_or_si256( ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r1l), _mm256_cmpgt_epi8(r1r, s)), ++ x); ++ x = _mm256_or_si256( ++ _mm256_andnot_si256( ++ _mm256_cmpeq_epi8(s, cbr), ++ _mm256_andnot_si256(_mm256_cmpeq_epi8(s, cbl), ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r2l), ++ _mm256_cmpgt_epi8(r2r, s)))), ++ x); ++ ++ m = ~(uint32_t)_mm256_movemask_epi8(x); ++ if (m) { ++ return first + ctz(m); + } + } + +- return SF_ERR_PARSE_ERROR; ++ return last; + } ++#endif /* __AVX2__ */ + +-static int parser_token(sf_parser *sfp, sf_value *dest) { ++static int parser_token(sfparse_parser *sfp, sfparse_value *dest) { + const uint8_t *base; ++#ifdef __AVX2__ ++ const uint8_t *last; ++#endif /* __AVX2__ */ + + /* The first byte has already been validated by the caller. */ + base = sfp->pos++; + ++#ifdef __AVX2__ ++ if (sfp->end - sfp->pos >= 32) { ++ last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); ++ ++ sfp->pos = find_char_token(sfp->pos, last); ++ if (sfp->pos != last) { ++ goto fin; ++ } ++ } ++#endif /* __AVX2__ */ ++ + for (; !parser_eof(sfp); ++sfp->pos) { + switch (*sfp->pos) { +- case '!': +- case '#': +- case '$': +- case '%': +- case '&': +- case '\'': +- case '*': +- case '+': +- case '-': +- case '.': +- case '^': +- case '_': +- case '`': +- case '|': +- case '~': +- case ':': +- case '/': +- DIGIT_CASES: +- ALPHA_CASES: ++ TOKEN_CASES: + continue; + } + + break; + } + ++#ifdef __AVX2__ ++fin: ++#endif /* __AVX2__ */ + if (dest) { +- dest->type = SF_TYPE_TOKEN; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_TOKEN; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + dest->vec.base = (uint8_t *)base; + dest->vec.len = (size_t)(sfp->pos - base); + } +@@ -466,14 +862,63 @@ static int parser_token(sf_parser *sfp, sf_value *dest) { + return 0; + } + +-static int parser_byteseq(sf_parser *sfp, sf_value *dest) { ++#ifdef __AVX2__ ++static const uint8_t *find_char_byteseq(const uint8_t *first, ++ const uint8_t *last) { ++ const __m256i pls = _mm256_set1_epi8('+'); ++ const __m256i fs = _mm256_set1_epi8('/'); ++ const __m256i r0l = _mm256_set1_epi8('0' - 1); ++ const __m256i r0r = _mm256_set1_epi8('9' + 1); ++ const __m256i r1l = _mm256_set1_epi8('A' - 1); ++ const __m256i r1r = _mm256_set1_epi8('Z' + 1); ++ const __m256i r2l = _mm256_set1_epi8('a' - 1); ++ const __m256i r2r = _mm256_set1_epi8('z' + 1); ++ __m256i s, x; ++ uint32_t m; ++ ++ for (; first != last; first += 32) { ++ s = _mm256_loadu_si256((void *)first); ++ ++ x = _mm256_cmpeq_epi8(s, pls); ++ x = _mm256_or_si256(_mm256_cmpeq_epi8(s, fs), x); ++ x = _mm256_or_si256( ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r0l), _mm256_cmpgt_epi8(r0r, s)), ++ x); ++ x = _mm256_or_si256( ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r1l), _mm256_cmpgt_epi8(r1r, s)), ++ x); ++ x = _mm256_or_si256( ++ _mm256_and_si256(_mm256_cmpgt_epi8(s, r2l), _mm256_cmpgt_epi8(r2r, s)), ++ x); ++ ++ m = ~(uint32_t)_mm256_movemask_epi8(x); ++ if (m) { ++ return first + ctz(m); ++ } ++ } ++ ++ return last; ++} ++#endif /* __AVX2__ */ ++ ++static int parser_byteseq(sfparse_parser *sfp, sfparse_value *dest) { + const uint8_t *base; ++#ifdef __AVX2__ ++ const uint8_t *last; ++#endif /* __AVX2__ */ + + /* The first byte has already been validated by the caller. */ + assert(':' == *sfp->pos); + + base = ++sfp->pos; + ++#ifdef __AVX2__ ++ if (sfp->end - sfp->pos >= 32) { ++ last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); ++ sfp->pos = find_char_byteseq(sfp->pos, last); ++ } ++#endif /* __AVX2__ */ ++ + for (; !parser_eof(sfp); ++sfp->pos) { + switch (*sfp->pos) { + case '+': +@@ -485,75 +930,47 @@ static int parser_byteseq(sf_parser *sfp, sf_value *dest) { + switch ((sfp->pos - base) & 0x3) { + case 0: + case 1: +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + case 2: +- switch (*(sfp->pos - 1)) { +- case 'A': +- case 'Q': +- case 'g': +- case 'w': +- break; +- default: +- return SF_ERR_PARSE_ERROR; +- } +- + ++sfp->pos; + +- if (parser_eof(sfp) || *sfp->pos != '=') { +- return SF_ERR_PARSE_ERROR; ++ if (parser_eof(sfp)) { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ if (*sfp->pos == '=') { ++ ++sfp->pos; + } + + break; + case 3: +- switch (*(sfp->pos - 1)) { +- case 'A': +- case 'E': +- case 'I': +- case 'M': +- case 'Q': +- case 'U': +- case 'Y': +- case 'c': +- case 'g': +- case 'k': +- case 'o': +- case 's': +- case 'w': +- case '0': +- case '4': +- case '8': +- break; +- default: +- return SF_ERR_PARSE_ERROR; +- } ++ ++sfp->pos; + + break; + } + +- ++sfp->pos; +- + if (parser_eof(sfp) || *sfp->pos != ':') { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + goto fin; + case ':': +- if ((sfp->pos - base) & 0x3) { +- return SF_ERR_PARSE_ERROR; ++ if (((sfp->pos - base) & 0x3) == 1) { ++ return SFPARSE_ERR_PARSE; + } + + goto fin; + default: +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + } + +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + + fin: + if (dest) { +- dest->type = SF_TYPE_BYTESEQ; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_BYTESEQ; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + dest->vec.len = (size_t)(sfp->pos - base); + dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; + } +@@ -563,7 +980,7 @@ fin: + return 0; + } + +-static int parser_boolean(sf_parser *sfp, sf_value *dest) { ++static int parser_boolean(sfparse_parser *sfp, sfparse_value *dest) { + int b; + + /* The first byte has already been validated by the caller. */ +@@ -572,7 +989,7 @@ static int parser_boolean(sf_parser *sfp, sf_value *dest) { + ++sfp->pos; + + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + switch (*sfp->pos) { +@@ -585,21 +1002,184 @@ static int parser_boolean(sf_parser *sfp, sf_value *dest) { + + break; + default: +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + ++sfp->pos; + + if (dest) { +- dest->type = SF_TYPE_BOOLEAN; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_BOOLEAN; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + dest->boolean = b; + } + + return 0; + } + +-static int parser_bare_item(sf_parser *sfp, sf_value *dest) { ++static int pctdecode(uint8_t *pc, const uint8_t **ppos) { ++ uint8_t c, b = **ppos; ++ ++ switch (b) { ++ DIGIT_CASES: ++ c = (uint8_t)((b - '0') << 4); ++ ++ break; ++ LCHEXALPHA_CASES: ++ c = (uint8_t)((b - 'a' + 10) << 4); ++ ++ break; ++ default: ++ return -1; ++ } ++ ++ b = *++*ppos; ++ ++ switch (b) { ++ DIGIT_CASES: ++ c |= (uint8_t)(b - '0'); ++ ++ break; ++ LCHEXALPHA_CASES: ++ c |= (uint8_t)(b - 'a' + 10); ++ ++ break; ++ default: ++ return -1; ++ } ++ ++ *pc = c; ++ ++*ppos; ++ ++ return 0; ++} ++ ++/* Start of utf8 dfa */ ++/* Copyright (c) 2008-2010 Bjoern Hoehrmann ++ * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. ++ * ++ * Copyright (c) 2008-2009 Bjoern Hoehrmann ++ * ++ * 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. ++ */ ++#define UTF8_ACCEPT 0 ++#define UTF8_REJECT 12 ++ ++/* clang-format off */ ++static const uint8_t utf8d[] = { ++ /* ++ * The first part of the table maps bytes to character classes that ++ * to reduce the size of the transition table and create bitmasks. ++ */ ++ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ++ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ++ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ++ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, ++ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, ++ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, ++ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, ++ ++ /* ++ * The second part is a transition table that maps a combination ++ * of a state of the automaton and a character class to a state. ++ */ ++ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, ++ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, ++ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, ++ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, ++ 12,36,12,12,12,12,12,12,12,12,12,12, ++}; ++/* clang-format on */ ++ ++static void utf8_decode(uint32_t *state, uint8_t byte) { ++ *state = utf8d[256 + *state + utf8d[byte]]; ++} ++ ++/* End of utf8 dfa */ ++ ++static int parser_dispstring(sfparse_parser *sfp, sfparse_value *dest) { ++ const uint8_t *base; ++ uint8_t c; ++ uint32_t utf8state = UTF8_ACCEPT; ++ ++ assert('%' == *sfp->pos); ++ ++ ++sfp->pos; ++ ++ if (parser_eof(sfp) || *sfp->pos != '"') { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ base = ++sfp->pos; ++ ++ for (; !parser_eof(sfp);) { ++ switch (*sfp->pos) { ++ X00_1F_CASES: ++ X7F_FF_CASES: ++ return SFPARSE_ERR_PARSE; ++ case '%': ++ ++sfp->pos; ++ ++ if (sfp->pos + 2 > sfp->end) { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ if (pctdecode(&c, &sfp->pos) != 0) { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ utf8_decode(&utf8state, c); ++ if (utf8state == UTF8_REJECT) { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ break; ++ case '"': ++ if (utf8state != UTF8_ACCEPT) { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ if (dest) { ++ dest->type = SFPARSE_TYPE_DISPSTRING; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; ++ dest->vec.len = (size_t)(sfp->pos - base); ++ dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; ++ } ++ ++ ++sfp->pos; ++ ++ return 0; ++ default: ++ if (utf8state != UTF8_ACCEPT) { ++ return SFPARSE_ERR_PARSE; ++ } ++ ++ ++sfp->pos; ++ } ++ } ++ ++ return SFPARSE_ERR_PARSE; ++} ++ ++static int parser_bare_item(sfparse_parser *sfp, sfparse_value *dest) { + switch (*sfp->pos) { + case '"': + return parser_string(sfp, dest); +@@ -615,29 +1195,32 @@ static int parser_bare_item(sf_parser *sfp, sf_value *dest) { + case '*': + ALPHA_CASES: + return parser_token(sfp, dest); ++ case '%': ++ return parser_dispstring(sfp, dest); + default: +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + } + +-static int parser_skip_inner_list(sf_parser *sfp); ++static int parser_skip_inner_list(sfparse_parser *sfp); + +-int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { ++int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, ++ sfparse_value *dest_value) { + int rv; + +- switch (sfp->state & SF_STATE_OP_MASK) { +- case SF_STATE_BEFORE: ++ switch (sfp->state & SFPARSE_STATE_OP_MASK) { ++ case SFPARSE_STATE_BEFORE: + rv = parser_skip_inner_list(sfp); + if (rv != 0) { + return rv; + } + + /* fall through */ +- case SF_STATE_BEFORE_PARAMS: +- parser_set_op_state(sfp, SF_STATE_PARAMS); ++ case SFPARSE_STATE_BEFORE_PARAMS: ++ parser_set_op_state(sfp, SFPARSE_STATE_PARAMS); + + break; +- case SF_STATE_PARAMS: ++ case SFPARSE_STATE_PARAMS: + break; + default: + assert(0); +@@ -645,16 +1228,16 @@ int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { + } + + if (parser_eof(sfp) || *sfp->pos != ';') { +- parser_set_op_state(sfp, SF_STATE_AFTER); ++ parser_set_op_state(sfp, SFPARSE_STATE_AFTER); + +- return SF_ERR_EOF; ++ return SFPARSE_ERR_EOF; + } + + ++sfp->pos; + + parser_discard_sp(sfp); + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + rv = parser_key(sfp, dest_key); +@@ -664,8 +1247,8 @@ int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { + + if (parser_eof(sfp) || *sfp->pos != '=') { + if (dest_value) { +- dest_value->type = SF_TYPE_BOOLEAN; +- dest_value->flags = SF_VALUE_FLAG_NONE; ++ dest_value->type = SFPARSE_TYPE_BOOLEAN; ++ dest_value->flags = SFPARSE_VALUE_FLAG_NONE; + dest_value->boolean = 1; + } + +@@ -675,23 +1258,23 @@ int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { + ++sfp->pos; + + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + return parser_bare_item(sfp, dest_value); + } + +-static int parser_skip_params(sf_parser *sfp) { ++static int parser_skip_params(sfparse_parser *sfp) { + int rv; + + for (;;) { +- rv = sf_parser_param(sfp, NULL, NULL); ++ rv = sfparse_parser_param(sfp, NULL, NULL); + switch (rv) { + case 0: + break; +- case SF_ERR_EOF: ++ case SFPARSE_ERR_EOF: + return 0; +- case SF_ERR_PARSE_ERROR: ++ case SFPARSE_ERR_PARSE: + return rv; + default: + assert(0); +@@ -700,45 +1283,45 @@ static int parser_skip_params(sf_parser *sfp) { + } + } + +-int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) { ++int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest) { + int rv; + +- switch (sfp->state & SF_STATE_OP_MASK) { +- case SF_STATE_BEFORE: ++ switch (sfp->state & SFPARSE_STATE_OP_MASK) { ++ case SFPARSE_STATE_BEFORE: + parser_discard_sp(sfp); + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + break; +- case SF_STATE_BEFORE_PARAMS: ++ case SFPARSE_STATE_BEFORE_PARAMS: + rv = parser_skip_params(sfp); + if (rv != 0) { + return rv; + } + +- /* Technically, we are entering SF_STATE_AFTER, but we will set ++ /* Technically, we are entering SFPARSE_STATE_AFTER, but we will set + another state without reading the state. */ +- /* parser_set_op_state(sfp, SF_STATE_AFTER); */ ++ /* parser_set_op_state(sfp, SFPARSE_STATE_AFTER); */ + + /* fall through */ +- case SF_STATE_AFTER: ++ case SFPARSE_STATE_AFTER: + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + switch (*sfp->pos) { + case ' ': + parser_discard_sp(sfp); + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + break; + case ')': + break; + default: +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + break; +@@ -751,9 +1334,9 @@ int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) { + ++sfp->pos; + + parser_unset_inner_list_state(sfp); +- parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); ++ parser_set_op_state(sfp, SFPARSE_STATE_BEFORE_PARAMS); + +- return SF_ERR_EOF; ++ return SFPARSE_ERR_EOF; + } + + rv = parser_bare_item(sfp, dest); +@@ -761,22 +1344,22 @@ int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) { + return rv; + } + +- parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); ++ parser_set_op_state(sfp, SFPARSE_STATE_BEFORE_PARAMS); + + return 0; + } + +-static int parser_skip_inner_list(sf_parser *sfp) { ++static int parser_skip_inner_list(sfparse_parser *sfp) { + int rv; + + for (;;) { +- rv = sf_parser_inner_list(sfp, NULL); ++ rv = sfparse_parser_inner_list(sfp, NULL); + switch (rv) { + case 0: + break; +- case SF_ERR_EOF: ++ case SFPARSE_ERR_EOF: + return 0; +- case SF_ERR_PARSE_ERROR: ++ case SFPARSE_ERR_PARSE: + return rv; + default: + assert(0); +@@ -785,39 +1368,39 @@ static int parser_skip_inner_list(sf_parser *sfp) { + } + } + +-static int parser_next_key_or_item(sf_parser *sfp) { ++static int parser_next_key_or_item(sfparse_parser *sfp) { + parser_discard_ows(sfp); + + if (parser_eof(sfp)) { +- return SF_ERR_EOF; ++ return SFPARSE_ERR_EOF; + } + + if (*sfp->pos != ',') { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + ++sfp->pos; + + parser_discard_ows(sfp); + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + return 0; + } + +-static int parser_dict_value(sf_parser *sfp, sf_value *dest) { ++static int parser_dict_value(sfparse_parser *sfp, sfparse_value *dest) { + int rv; + + if (parser_eof(sfp) || *(sfp->pos) != '=') { + /* Boolean true */ + if (dest) { +- dest->type = SF_TYPE_BOOLEAN; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_BOOLEAN; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + dest->boolean = 1; + } + +- sfp->state = SF_STATE_DICT_BEFORE_PARAMS; ++ sfp->state = SFPARSE_STATE_DICT_BEFORE_PARAMS; + + return 0; + } +@@ -825,18 +1408,18 @@ static int parser_dict_value(sf_parser *sfp, sf_value *dest) { + ++sfp->pos; + + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + if (*sfp->pos == '(') { + if (dest) { +- dest->type = SF_TYPE_INNER_LIST; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_INNER_LIST; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + } + + ++sfp->pos; + +- sfp->state = SF_STATE_DICT_INNER_LIST_BEFORE; ++ sfp->state = SFPARSE_STATE_DICT_INNER_LIST_BEFORE; + + return 0; + } +@@ -846,41 +1429,42 @@ static int parser_dict_value(sf_parser *sfp, sf_value *dest) { + return rv; + } + +- sfp->state = SF_STATE_DICT_BEFORE_PARAMS; ++ sfp->state = SFPARSE_STATE_DICT_BEFORE_PARAMS; + + return 0; + } + +-int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { ++int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, ++ sfparse_value *dest_value) { + int rv; + + switch (sfp->state) { +- case SF_STATE_DICT_INNER_LIST_BEFORE: ++ case SFPARSE_STATE_DICT_INNER_LIST_BEFORE: + rv = parser_skip_inner_list(sfp); + if (rv != 0) { + return rv; + } + + /* fall through */ +- case SF_STATE_DICT_BEFORE_PARAMS: ++ case SFPARSE_STATE_DICT_BEFORE_PARAMS: + rv = parser_skip_params(sfp); + if (rv != 0) { + return rv; + } + + /* fall through */ +- case SF_STATE_DICT_AFTER: ++ case SFPARSE_STATE_DICT_AFTER: + rv = parser_next_key_or_item(sfp); + if (rv != 0) { + return rv; + } + + break; +- case SF_STATE_INITIAL: ++ case SFPARSE_STATE_INITIAL: + parser_discard_sp(sfp); + + if (parser_eof(sfp)) { +- return SF_ERR_EOF; ++ return SFPARSE_ERR_EOF; + } + + break; +@@ -897,36 +1481,36 @@ int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { + return parser_dict_value(sfp, dest_value); + } + +-int sf_parser_list(sf_parser *sfp, sf_value *dest) { ++int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest) { + int rv; + + switch (sfp->state) { +- case SF_STATE_LIST_INNER_LIST_BEFORE: ++ case SFPARSE_STATE_LIST_INNER_LIST_BEFORE: + rv = parser_skip_inner_list(sfp); + if (rv != 0) { + return rv; + } + + /* fall through */ +- case SF_STATE_LIST_BEFORE_PARAMS: ++ case SFPARSE_STATE_LIST_BEFORE_PARAMS: + rv = parser_skip_params(sfp); + if (rv != 0) { + return rv; + } + + /* fall through */ +- case SF_STATE_LIST_AFTER: ++ case SFPARSE_STATE_LIST_AFTER: + rv = parser_next_key_or_item(sfp); + if (rv != 0) { + return rv; + } + + break; +- case SF_STATE_INITIAL: ++ case SFPARSE_STATE_INITIAL: + parser_discard_sp(sfp); + + if (parser_eof(sfp)) { +- return SF_ERR_EOF; ++ return SFPARSE_ERR_EOF; + } + + break; +@@ -937,13 +1521,13 @@ int sf_parser_list(sf_parser *sfp, sf_value *dest) { + + if (*sfp->pos == '(') { + if (dest) { +- dest->type = SF_TYPE_INNER_LIST; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_INNER_LIST; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + } + + ++sfp->pos; + +- sfp->state = SF_STATE_LIST_INNER_LIST_BEFORE; ++ sfp->state = SFPARSE_STATE_LIST_INNER_LIST_BEFORE; + + return 0; + } +@@ -953,45 +1537,45 @@ int sf_parser_list(sf_parser *sfp, sf_value *dest) { + return rv; + } + +- sfp->state = SF_STATE_LIST_BEFORE_PARAMS; ++ sfp->state = SFPARSE_STATE_LIST_BEFORE_PARAMS; + + return 0; + } + +-int sf_parser_item(sf_parser *sfp, sf_value *dest) { ++int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest) { + int rv; + + switch (sfp->state) { +- case SF_STATE_INITIAL: ++ case SFPARSE_STATE_INITIAL: + parser_discard_sp(sfp); + + if (parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + + break; +- case SF_STATE_ITEM_INNER_LIST_BEFORE: ++ case SFPARSE_STATE_ITEM_INNER_LIST_BEFORE: + rv = parser_skip_inner_list(sfp); + if (rv != 0) { + return rv; + } + + /* fall through */ +- case SF_STATE_ITEM_BEFORE_PARAMS: ++ case SFPARSE_STATE_ITEM_BEFORE_PARAMS: + rv = parser_skip_params(sfp); + if (rv != 0) { + return rv; + } + + /* fall through */ +- case SF_STATE_ITEM_AFTER: ++ case SFPARSE_STATE_ITEM_AFTER: + parser_discard_sp(sfp); + + if (!parser_eof(sfp)) { +- return SF_ERR_PARSE_ERROR; ++ return SFPARSE_ERR_PARSE; + } + +- return SF_ERR_EOF; ++ return SFPARSE_ERR_EOF; + default: + assert(0); + abort(); +@@ -999,13 +1583,13 @@ int sf_parser_item(sf_parser *sfp, sf_value *dest) { + + if (*sfp->pos == '(') { + if (dest) { +- dest->type = SF_TYPE_INNER_LIST; +- dest->flags = SF_VALUE_FLAG_NONE; ++ dest->type = SFPARSE_TYPE_INNER_LIST; ++ dest->flags = SFPARSE_VALUE_FLAG_NONE; + } + + ++sfp->pos; + +- sfp->state = SF_STATE_ITEM_INNER_LIST_BEFORE; ++ sfp->state = SFPARSE_STATE_ITEM_INNER_LIST_BEFORE; + + return 0; + } +@@ -1015,12 +1599,13 @@ int sf_parser_item(sf_parser *sfp, sf_value *dest) { + return rv; + } + +- sfp->state = SF_STATE_ITEM_BEFORE_PARAMS; ++ sfp->state = SFPARSE_STATE_ITEM_BEFORE_PARAMS; + + return 0; + } + +-void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen) { ++void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, ++ size_t datalen) { + if (datalen == 0) { + sfp->pos = sfp->end = NULL; + } else { +@@ -1028,16 +1613,16 @@ void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen) { + sfp->end = data + datalen; + } + +- sfp->state = SF_STATE_INITIAL; ++ sfp->state = SFPARSE_STATE_INITIAL; + } + +-void sf_unescape(sf_vec *dest, const sf_vec *src) { ++void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src) { + const uint8_t *p, *q; + uint8_t *o; + size_t len, slen; + + if (src->len == 0) { +- *dest = *src; ++ dest->len = 0; + + return; + } +@@ -1049,16 +1634,12 @@ void sf_unescape(sf_vec *dest, const sf_vec *src) { + for (;;) { + q = memchr(p, '\\', len); + if (q == NULL) { +- if (len == src->len) { +- *dest = *src; +- +- return; +- } +- + memcpy(o, p, len); + o += len; + +- break; ++ dest->len = (size_t)(o - dest->base); ++ ++ return; + } + + slen = (size_t)(q - p); +@@ -1069,11 +1650,9 @@ void sf_unescape(sf_vec *dest, const sf_vec *src) { + *o++ = *p++; + len -= slen + 2; + } +- +- dest->len = (size_t)(o - dest->base); + } + +-void sf_base64decode(sf_vec *dest, const sf_vec *src) { ++void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src) { + static const int index_tbl[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +@@ -1092,20 +1671,22 @@ void sf_base64decode(sf_vec *dest, const sf_vec *src) { + uint8_t *o; + const uint8_t *p, *end; + uint32_t n; +- size_t i; ++ size_t i, left; + int idx; + +- assert((src->len & 0x3) == 0); +- + if (src->len == 0) { +- *dest = *src; ++ dest->len = 0; + + return; + } + + o = dest->base; + p = src->base; +- end = src->base + src->len; ++ left = src->len & 0x3; ++ if (left == 0 && src->base[src->len - 1] == '=') { ++ left = 4; ++ } ++ end = src->base + src->len - left; + + for (; p != end;) { + n = 0; +@@ -1113,33 +1694,94 @@ void sf_base64decode(sf_vec *dest, const sf_vec *src) { + for (i = 1; i <= 4; ++i, ++p) { + idx = index_tbl[*p]; + +- if (idx == -1) { +- assert(i > 2); ++ assert(idx != -1); + +- if (i == 3) { +- assert(*p == '=' && *(p + 1) == '=' && p + 2 == end); ++ n += (uint32_t)(idx << (24 - i * 6)); ++ } + +- *o++ = (uint8_t)(n >> 16); ++ *o++ = (uint8_t)(n >> 16); ++ *o++ = (n >> 8) & 0xffu; ++ *o++ = n & 0xffu; ++ } + +- goto fin; +- } ++ switch (left) { ++ case 0: ++ goto fin; ++ case 1: ++ assert(0); ++ abort(); ++ case 3: ++ if (src->base[src->len - 1] == '=') { ++ left = 2; ++ } + +- assert(*p == '=' && p + 1 == end); ++ break; ++ case 4: ++ assert('=' == src->base[src->len - 1]); + +- *o++ = (uint8_t)(n >> 16); +- *o++ = (n >> 8) & 0xffu; ++ if (src->base[src->len - 2] == '=') { ++ left = 2; ++ } else { ++ left = 3; ++ } + +- goto fin; +- } ++ break; ++ } + +- n += (uint32_t)(idx << (24 - i * 6)); +- } ++ switch (left) { ++ case 2: ++ *o = (uint8_t)(index_tbl[*p++] << 2); ++ *o++ |= (uint8_t)(index_tbl[*p++] >> 4); + +- *o++ = (uint8_t)(n >> 16); ++ break; ++ case 3: ++ n = (uint32_t)(index_tbl[*p++] << 10); ++ n += (uint32_t)(index_tbl[*p++] << 4); ++ n += (uint32_t)(index_tbl[*p++] >> 2); + *o++ = (n >> 8) & 0xffu; + *o++ = n & 0xffu; ++ ++ break; + } + + fin: + dest->len = (size_t)(o - dest->base); + } ++ ++void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src) { ++ const uint8_t *p, *q; ++ uint8_t *o; ++ size_t len, slen; ++ ++ if (src->len == 0) { ++ dest->len = 0; ++ ++ return; ++ } ++ ++ o = dest->base; ++ p = src->base; ++ len = src->len; ++ ++ for (;;) { ++ q = memchr(p, '%', len); ++ if (q == NULL) { ++ memcpy(o, p, len); ++ o += len; ++ ++ dest->len = (size_t)(o - dest->base); ++ ++ return; ++ } ++ ++ slen = (size_t)(q - p); ++ memcpy(o, p, slen); ++ o += slen; ++ ++ p = q + 1; ++ ++ pctdecode(o++, &p); ++ ++ len -= slen + 3; ++ } ++} +diff --git a/deps/nghttp2/lib/sfparse.h b/deps/nghttp2/lib/sfparse.h +index 1474db14..9341221a 100644 +--- a/deps/nghttp2/lib/sfparse.h ++++ b/deps/nghttp2/lib/sfparse.h +@@ -31,86 +31,90 @@ + libcurl) */ + #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) + # define WIN32 +-#endif ++#endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ + + #ifdef __cplusplus + extern "C" { +-#endif ++#endif /* defined(__cplusplus) */ + + #if defined(_MSC_VER) && (_MSC_VER < 1800) + /* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ + # include +-#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ ++#else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ + # include +-#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ ++#endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ + #include + #include + + /** + * @enum + * +- * :type:`sf_type` defines value type. ++ * :type:`sfparse_type` defines value type. + */ +-typedef enum sf_type { ++typedef enum sfparse_type { + /** +- * :enum:`SF_TYPE_BOOLEAN` indicates boolean type. ++ * :enum:`SFPARSE_TYPE_BOOLEAN` indicates boolean type. + */ +- SF_TYPE_BOOLEAN, ++ SFPARSE_TYPE_BOOLEAN, + /** +- * :enum:`SF_TYPE_INTEGER` indicates integer type. ++ * :enum:`SFPARSE_TYPE_INTEGER` indicates integer type. + */ +- SF_TYPE_INTEGER, ++ SFPARSE_TYPE_INTEGER, + /** +- * :enum:`SF_TYPE_DECIMAL` indicates decimal type. ++ * :enum:`SFPARSE_TYPE_DECIMAL` indicates decimal type. + */ +- SF_TYPE_DECIMAL, ++ SFPARSE_TYPE_DECIMAL, + /** +- * :enum:`SF_TYPE_STRING` indicates string type. ++ * :enum:`SFPARSE_TYPE_STRING` indicates string type. + */ +- SF_TYPE_STRING, ++ SFPARSE_TYPE_STRING, + /** +- * :enum:`SF_TYPE_TOKEN` indicates token type. ++ * :enum:`SFPARSE_TYPE_TOKEN` indicates token type. + */ +- SF_TYPE_TOKEN, ++ SFPARSE_TYPE_TOKEN, + /** +- * :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type. ++ * :enum:`SFPARSE_TYPE_BYTESEQ` indicates byte sequence type. + */ +- SF_TYPE_BYTESEQ, ++ SFPARSE_TYPE_BYTESEQ, + /** +- * :enum:`SF_TYPE_INNER_LIST` indicates inner list type. ++ * :enum:`SFPARSE_TYPE_INNER_LIST` indicates inner list type. + */ +- SF_TYPE_INNER_LIST, ++ SFPARSE_TYPE_INNER_LIST, + /** +- * :enum:`SF_TYPE_DATE` indicates date type. ++ * :enum:`SFPARSE_TYPE_DATE` indicates date type. + */ +- SF_TYPE_DATE +-} sf_type; ++ SFPARSE_TYPE_DATE, ++ /** ++ * :enum:`SFPARSE_TYPE_DISPSTRING` indicates display string type. ++ */ ++ SFPARSE_TYPE_DISPSTRING ++} sfparse_type; + + /** + * @macro + * +- * :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has ++ * :macro:`SFPARSE_ERR_PARSE` indicates fatal parse error has + * occurred, and it is not possible to continue the processing. + */ +-#define SF_ERR_PARSE_ERROR -1 ++#define SFPARSE_ERR_PARSE -1 + + /** + * @macro + * +- * :macro:`SF_ERR_EOF` indicates that there is nothing left to read. +- * The context of this error varies depending on the function that +- * returns this error code. ++ * :macro:`SFPARSE_ERR_EOF` indicates that there is nothing left to ++ * read. The context of this error varies depending on the function ++ * that returns this error code. + */ +-#define SF_ERR_EOF -2 ++#define SFPARSE_ERR_EOF -2 + + /** + * @struct + * +- * :type:`sf_vec` stores sequence of bytes. ++ * :type:`sfparse_vec` stores sequence of bytes. + */ +-typedef struct sf_vec { ++typedef struct sfparse_vec { + /** + * :member:`base` points to the beginning of the sequence of bytes. + */ +@@ -119,29 +123,29 @@ typedef struct sf_vec { + * :member:`len` is the number of bytes contained in this sequence. + */ + size_t len; +-} sf_vec; ++} sfparse_vec; + + /** + * @macro + * +- * :macro:`SF_VALUE_FLAG_NONE` indicates no flag set. ++ * :macro:`SFPARSE_VALUE_FLAG_NONE` indicates no flag set. + */ +-#define SF_VALUE_FLAG_NONE 0x0u ++#define SFPARSE_VALUE_FLAG_NONE 0x0u + + /** + * @macro + * +- * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string ++ * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` indicates that a string + * contains escaped character(s). + */ +-#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u ++#define SFPARSE_VALUE_FLAG_ESCAPED_STRING 0x1u + + /** + * @struct + * +- * :type:`sf_decimal` contains decimal value. ++ * :type:`sfparse_decimal` contains decimal value. + */ +-typedef struct sf_decimal { ++typedef struct sfparse_decimal { + /** + * :member:`numer` contains numerator of the decimal value. + */ +@@ -150,260 +154,289 @@ typedef struct sf_decimal { + * :member:`denom` contains denominator of the decimal value. + */ + int64_t denom; +-} sf_decimal; ++} sfparse_decimal; + + /** + * @struct + * +- * :type:`sf_value` stores a Structured Field item. For Inner List, +- * only type is set to :enum:`sf_type.SF_TYPE_INNER_LIST`. In order +- * to read the items contained in an inner list, call +- * `sf_parser_inner_list`. ++ * :type:`sfparse_value` stores a Structured Field item. For Inner ++ * List, only type is set to ++ * :enum:`sfparse_type.SFPARSE_TYPE_INNER_LIST`. In order to read the ++ * items contained in an inner list, call `sfparse_parser_inner_list`. + */ +-typedef struct sf_value { ++typedef struct sfparse_value { + /** + * :member:`type` is the type of the value contained in this + * particular object. + */ +- sf_type type; ++ sfparse_type type; + /** + * :member:`flags` is bitwise OR of one or more of +- * :macro:`SF_VALUE_FLAG_* `. ++ * :macro:`SFPARSE_VALUE_FLAG_* `. + */ + uint32_t flags; + /** + * @anonunion_start + * +- * @sf_value_value ++ * @sfparse_value_value + */ + union { + /** + * :member:`boolean` contains boolean value if :member:`type` == +- * :enum:`sf_type.SF_TYPE_BOOLEAN`. 1 indicates true, and 0 +- * indicates false. ++ * :enum:`sfparse_type.SFPARSE_TYPE_BOOLEAN`. 1 indicates true, ++ * and 0 indicates false. + */ + int boolean; + /** + * :member:`integer` contains integer value if :member:`type` is +- * either :enum:`sf_type.SF_TYPE_INTEGER` or +- * :enum:`sf_type.SF_TYPE_DATE`. ++ * either :enum:`sfparse_type.SFPARSE_TYPE_INTEGER` or ++ * :enum:`sfparse_type.SFPARSE_TYPE_DATE`. + */ + int64_t integer; + /** + * :member:`decimal` contains decimal value if :member:`type` == +- * :enum:`sf_type.SF_TYPE_DECIMAL`. ++ * :enum:`sfparse_type.SFPARSE_TYPE_DECIMAL`. + */ +- sf_decimal decimal; ++ sfparse_decimal decimal; + /** + * :member:`vec` contains sequence of bytes if :member:`type` is +- * either :enum:`sf_type.SF_TYPE_STRING`, +- * :enum:`sf_type.SF_TYPE_TOKEN`, or +- * :enum:`sf_type.SF_TYPE_BYTESEQ`. ++ * either :enum:`sfparse_type.SFPARSE_TYPE_STRING`, ++ * :enum:`sfparse_type.SFPARSE_TYPE_TOKEN`, ++ * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, or ++ * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`. + * +- * For :enum:`sf_type.SF_TYPE_STRING`, this field contains one or +- * more escaped characters if :member:`flags` has +- * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` set. To unescape the +- * string, use `sf_unescape`. ++ * For :enum:`sfparse_type.SFPARSE_TYPE_STRING`, this field ++ * contains one or more escaped characters if :member:`flags` has ++ * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` set. To unescape ++ * the string, use `sfparse_unescape`. + * +- * For :enum:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64 +- * encoded string. To decode this byte string, use +- * `sf_base64decode`. ++ * For :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, this field ++ * contains base64 encoded string. To decode this byte string, ++ * use `sfparse_base64decode`. + * +- * If :member:`vec.len ` == 0, :member:`vec.base +- * ` is guaranteed to be NULL. ++ * For :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`, this field ++ * may contain percent-encoded UTF-8 byte sequences. To decode ++ * it, use `sfparse_pctdecode`. ++ * ++ * If :member:`vec.len ` == 0, :member:`vec.base ++ * ` is guaranteed to be NULL. + */ +- sf_vec vec; ++ sfparse_vec vec; + /** + * @anonunion_end + */ + }; +-} sf_value; ++} sfparse_value; + + /** + * @struct + * +- * :type:`sf_parser` is the Structured Field Values parser. Use +- * `sf_parser_init` to initialize it. ++ * :type:`sfparse_parser` is the Structured Field Values parser. Use ++ * `sfparse_parser_init` to initialize it. + */ +-typedef struct sf_parser { ++typedef struct sfparse_parser { + /* all fields are private */ + const uint8_t *pos; + const uint8_t *end; + uint32_t state; +-} sf_parser; ++} sfparse_parser; + + /** + * @function + * +- * `sf_parser_init` initializes |sfp| with the given buffer pointed by +- * |data| of length |datalen|. ++ * `sfparse_parser_init` initializes |sfp| with the given data encoded ++ * in Structured Field Values pointed by |data| of length |datalen|. + */ +-void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen); ++void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, ++ size_t datalen); + + /** + * @function + * +- * `sf_parser_param` reads a parameter. If this function returns 0, +- * it stores parameter key and value in |dest_key| and |dest_value| ++ * `sfparse_parser_param` reads a parameter. If this function returns ++ * 0, it stores parameter key and value in |dest_key| and |dest_value| + * respectively, if they are not NULL. + * + * This function does no effort to find duplicated keys. Same key may + * be reported more than once. + * + * Caller should keep calling this function until it returns negative +- * error code. If it returns :macro:`SF_ERR_EOF`, all parameters have +- * read, and caller can continue to read rest of the values. If it +- * returns :macro:`SF_ERR_PARSE_ERROR`, it encountered fatal error ++ * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all parameters ++ * have read, and caller can continue to read rest of the values. If ++ * it returns :macro:`SFPARSE_ERR_PARSE`, it encountered fatal error + * while parsing field value. + */ +-int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); ++int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, ++ sfparse_value *dest_value); + + /** + * @function + * +- * `sf_parser_dict` reads the next dictionary key and value pair. If +- * this function returns 0, it stores the key and value in |dest_key| +- * and |dest_value| respectively, if they are not NULL. ++ * `sfparse_parser_dict` reads the next dictionary key and value pair. ++ * If this function returns 0, it stores the key and value in ++ * |dest_key| and |dest_value| respectively, if they are not NULL. + * + * Caller can optionally read parameters attached to the pair by +- * calling `sf_parser_param`. ++ * calling `sfparse_parser_param`. + * + * This function does no effort to find duplicated keys. Same key may + * be reported more than once. + * + * Caller should keep calling this function until it returns negative +- * error code. If it returns :macro:`SF_ERR_EOF`, all key and value +- * pairs have been read, and there is nothing left to read. ++ * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all key and ++ * value pairs have been read, and there is nothing left to read. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * +- * :macro:`SF_ERR_EOF` ++ * :macro:`SFPARSE_ERR_EOF` + * All values in the dictionary have read. +- * :macro:`SF_ERR_PARSE_ERROR` ++ * :macro:`SFPARSE_ERR_PARSE` + * It encountered fatal error while parsing field value. + */ +-int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); ++int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, ++ sfparse_value *dest_value); + + /** + * @function + * +- * `sf_parser_list` reads the next list item. If this function ++ * `sfparse_parser_list` reads the next list item. If this function + * returns 0, it stores the item in |dest| if it is not NULL. + * + * Caller can optionally read parameters attached to the item by +- * calling `sf_parser_param`. ++ * calling `sfparse_parser_param`. + * + * Caller should keep calling this function until it returns negative +- * error code. If it returns :macro:`SF_ERR_EOF`, all values in the +- * list have been read, and there is nothing left to read. ++ * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in ++ * the list have been read, and there is nothing left to read. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * +- * :macro:`SF_ERR_EOF` ++ * :macro:`SFPARSE_ERR_EOF` + * All values in the list have read. +- * :macro:`SF_ERR_PARSE_ERROR` ++ * :macro:`SFPARSE_ERR_PARSE` + * It encountered fatal error while parsing field value. + */ +-int sf_parser_list(sf_parser *sfp, sf_value *dest); ++int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest); + + /** + * @function + * +- * `sf_parser_item` reads a single item. If this function returns 0, +- * it stores the item in |dest| if it is not NULL. ++ * `sfparse_parser_item` reads a single item. If this function ++ * returns 0, it stores the item in |dest| if it is not NULL. + * + * This function is only used for the field value that consists of a + * single item. + * + * Caller can optionally read parameters attached to the item by +- * calling `sf_parser_param`. ++ * calling `sfparse_parser_param`. + * + * Caller should call this function again to make sure that there is + * nothing left to read. If this 2nd function call returns +- * :macro:`SF_ERR_EOF`, all data have been processed successfully. ++ * :macro:`SFPARSE_ERR_EOF`, all data have been processed ++ * successfully. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * +- * :macro:`SF_ERR_EOF` ++ * :macro:`SFPARSE_ERR_EOF` + * There is nothing left to read. +- * :macro:`SF_ERR_PARSE_ERROR` ++ * :macro:`SFPARSE_ERR_PARSE` + * It encountered fatal error while parsing field value. + */ +-int sf_parser_item(sf_parser *sfp, sf_value *dest); ++int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest); + + /** + * @function + * +- * `sf_parser_inner_list` reads the next inner list item. If this +- * function returns 0, it stores the item in |dest| if it is not NULL. ++ * `sfparse_parser_inner_list` reads the next inner list item. If ++ * this function returns 0, it stores the item in |dest| if it is not ++ * NULL. + * + * Caller can optionally read parameters attached to the item by +- * calling `sf_parser_param`. ++ * calling `sfparse_parser_param`. + * + * Caller should keep calling this function until it returns negative +- * error code. If it returns :macro:`SF_ERR_EOF`, all values in this +- * inner list have been read, and caller can optionally read ++ * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in ++ * this inner list have been read, and caller can optionally read + * parameters attached to this inner list by calling +- * `sf_parser_param`. Then caller can continue to read rest of the +- * values. ++ * `sfparse_parser_param`. Then caller can continue to read rest of ++ * the values. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * +- * :macro:`SF_ERR_EOF` ++ * :macro:`SFPARSE_ERR_EOF` + * All values in the inner list have read. +- * :macro:`SF_ERR_PARSE_ERROR` ++ * :macro:`SFPARSE_ERR_PARSE` + * It encountered fatal error while parsing field value. + */ +-int sf_parser_inner_list(sf_parser *sfp, sf_value *dest); ++int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest); + + /** + * @function + * +- * `sf_unescape` copies |src| to |dest| by removing escapes (``\``). +- * |src| should be the pointer to :member:`sf_value.vec` of type +- * :enum:`sf_type.SF_TYPE_STRING` produced by either `sf_parser_dict`, +- * `sf_parser_list`, `sf_parser_inner_list`, `sf_parser_item`, or +- * `sf_parser_param`, otherwise the behavior is undefined. ++ * `sfparse_unescape` copies |src| to |dest| by removing escapes ++ * (``\``). |src| should be the pointer to ++ * :member:`sfparse_value.vec` of type ++ * :enum:`sfparse_type.SFPARSE_TYPE_STRING` produced by either ++ * `sfparse_parser_dict`, `sfparse_parser_list`, ++ * `sfparse_parser_inner_list`, `sfparse_parser_item`, or ++ * `sfparse_parser_param`, otherwise the behavior is undefined. + * +- * :member:`dest->base ` must point to the buffer that +- * has sufficient space to store the unescaped string. +- * +- * If there is no escape character in |src|, |*src| is assigned to +- * |*dest|. This includes the case that :member:`src->len +- * ` == 0. ++ * :member:`dest->base ` must point to the buffer ++ * that has sufficient space to store the unescaped string. The ++ * memory areas pointed by :member:`dest->base ` and ++ * :member:`src->base ` must not overlap. + * + * This function sets the length of unescaped string to +- * :member:`dest->len `. ++ * :member:`dest->len `. + */ +-void sf_unescape(sf_vec *dest, const sf_vec *src); ++void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src); + + /** + * @function + * +- * `sf_base64decode` decodes Base64 encoded string |src| and writes +- * the result into |dest|. |src| should be the pointer to +- * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ` +- * produced by either `sf_parser_dict`, `sf_parser_list`, +- * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`, +- * otherwise the behavior is undefined. ++ * `sfparse_base64decode` decodes Base64 encoded string |src| and ++ * writes the result into |dest|. |src| should be the pointer to ++ * :member:`sfparse_value.vec` of type ++ * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ` produced by either ++ * `sfparse_parser_dict`, `sfparse_parser_list`, ++ * `sfparse_parser_inner_list`, `sfparse_parser_item`, or ++ * `sfparse_parser_param`, otherwise the behavior is undefined. ++ * ++ * :member:`dest->base ` must point to the buffer ++ * that has sufficient space to store the decoded byte string. + * +- * :member:`dest->base ` must point to the buffer that +- * has sufficient space to store the decoded byte string. ++ * This function sets the length of decoded byte string to ++ * :member:`dest->len `. ++ */ ++void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src); ++ ++/** ++ * @function ++ * ++ * `sfparse_pctdecode` decodes percent-encoded string |src| and writes ++ * the result into |dest|. |src| should be the pointer to ++ * :member:`sfparse_value.vec` of type ++ * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING` produced by either ++ * `sfparse_parser_dict`, `sfparse_parser_list`, ++ * `sfparse_parser_inner_list`, `sfparse_parser_item`, or ++ * `sfparse_parser_param`, otherwise the behavior is undefined. + * +- * If :member:`src->len ` == 0, |*src| is assigned to +- * |*dest|. ++ * :member:`dest->base ` must point to the buffer ++ * that has sufficient space to store the decoded byte string. The ++ * memory areas pointed by :member:`dest->base ` and ++ * :member:`src->base ` must not overlap. + * + * This function sets the length of decoded byte string to +- * :member:`dest->len `. ++ * :member:`dest->len `. + */ +-void sf_base64decode(sf_vec *dest, const sf_vec *src); ++void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src); + + #ifdef __cplusplus + } +-#endif ++#endif /* defined(__cplusplus) */ + +-#endif /* SFPARSE_H */ ++#endif /* !defined(SFPARSE_H) */ +-- +2.53.0 + diff --git a/nodejs22.spec b/nodejs22.spec index 6352f0e..7c50770 100644 --- a/nodejs22.spec +++ b/nodejs22.spec @@ -53,7 +53,7 @@ %global nodejs_epoch 1 %global nodejs_major 22 %global nodejs_minor 22 -%global nodejs_patch 0 +%global nodejs_patch 2 # nodejs_soversion - from NODE_MODULE_VERSION in src/node_version.h %global nodejs_soversion 127 %global nodejs_abi %{nodejs_soversion} @@ -89,7 +89,7 @@ %global libuv_version 1.51.0 # nghttp2 - from deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h -%global nghttp2_version 1.64.0 +%global nghttp2_version 1.68.1 # nghttp3 - from deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h %global nghttp3_version 1.6.0 @@ -98,8 +98,8 @@ %global ngtcp2_version 1.11.0 # ICU - from tools/icu/current_ver.dep -%global icu_major 77 -%global icu_minor 1 +%global icu_major 78 +%global icu_minor 2 %global icu_version %{icu_major}.%{icu_minor} %global icudatadir %{nodejs_datadir}/icudata @@ -120,7 +120,7 @@ # npm - from deps/npm/package.json %global npm_epoch 1 -%global npm_version 10.9.4 +%global npm_version 10.9.7 # In order to avoid needing to keep incrementing the release version for the # main package forever, we will just construct one for npm that is guaranteed @@ -137,7 +137,7 @@ %global histogram_version 0.11.9 # sqlite – from deps/sqlite/sqlite3.h -%global sqlite_version 3.50.4 +%global sqlite_version 3.51.2 Name: nodejs%{nodejs_pkg_major} @@ -172,6 +172,8 @@ Source301: test-should-pass.txt Patch: 0001-Remove-unused-OpenSSL-config.patch Patch: 0001-fips-disable-options.patch +Patch: 0001-deps-update-nghttp2-to-1.68.1.patch +Patch: 0001-CVE-2026-25547-braces-expansion.patch %if 0%{?nodejs_default} %global pkgname nodejs @@ -348,14 +350,14 @@ Provides: bundled(ada) = 2.9.2 # undici and cjs-module-lexer ship with pre-built WASM binaries. %if %{with bundled_cjs_module_lexer} -Provides: bundled(nodejs-cjs-module-lexer) = 1.4.1 +Provides: bundled(nodejs-cjs-module-lexer) = 2.2.0 %else BuildRequires: nodejs-cjs-module-lexer Requires: nodejs-cjs-module-lexer %endif %if %{with bundled_undici} -Provides: bundled(nodejs-undici) = 6.23.0 +Provides: bundled(nodejs-undici) = 6.24.1 %else BuildRequires: nodejs-undici Requires: nodejs-undici diff --git a/sources b/sources index 8eed132..d89160f 100644 --- a/sources +++ b/sources @@ -1,3 +1,3 @@ -SHA512 (node-v22.22.0-stripped.tar.gz) = 32049c569d90145c918dd4db7847ccf4d979a418a54a01ecf966d277607c7460d13e62334386d75d9854db4ec345dcc1abfda32bde4edbda18a61cbf484d0580 -SHA512 (icu4c-77_1-data-bin-b.zip) = 93b4c8228a059546e7c3e337f1f837db255c0046c15f50a31a7bd20daf361174edab05b01faaac1dd4f515ca3c1f1d7fb0f61e4177eb5631833ad1450e252c4e -SHA512 (icu4c-77_1-data-bin-l.zip) = 3de15bb5925956b8e51dc6724c2114a1009ec471a2241b09ae09127f1760f44d02cc29cfbeed6cbaac6ee880553ac8395c61c6043c00ddba3277233e19e6490e +SHA512 (node-v22.22.2-stripped.tar.gz) = 82c3357cce10a3fe89373ec4e3460af5992d853f28a7339358a3f910959e7b17987c8eb1748d9c3033d4c642701d321e2265cc0ac004a218860da4eda2971226 +SHA512 (icu4c-78.2-data-bin-b.zip) = 032a1e519bf92dfa7936ef85ebed697550dbcb4e32c6ecd28ffecb158a403eeff6c0a3545b2551eba73f288e31693be6880e202a38cd86c129dffa395e8ab625 +SHA512 (icu4c-78.2-data-bin-l.zip) = c0b46de115332940d3276763904caa6257eb516edce4382632f4b96a5b010fee4cb06a5e10ef5eee2f881515c1ee8277d9ae59015f6de6fe1d175b9d00dbb1ca diff --git a/test-should-pass.txt b/test-should-pass.txt index a638dc5..4ea6f21 100644 --- a/test-should-pass.txt +++ b/test-should-pass.txt @@ -1464,12 +1464,14 @@ parallel/test-http2-client-request-options-errors.js parallel/test-http2-client-rststream-before-connect.js parallel/test-http2-client-setLocalWindowSize.js parallel/test-http2-client-setNextStreamID-errors.js -parallel/test-http2-client-set-priority.js +# disabled on 25.03.26, see https://github.com/nodejs/node/issues/60661 +# parallel/test-http2-client-set-priority.js parallel/test-http2-client-settings-before-connect.js parallel/test-http2-client-shutdown-before-connect.js parallel/test-http2-client-socket-destroy.js parallel/test-http2-client-stream-destroy-before-connect.js -parallel/test-http2-client-unescaped-path.js +# disabled on 25.03.26, see https://github.com/nodejs/node/issues/60661 +# parallel/test-http2-client-unescaped-path.js parallel/test-http2-client-upload.js parallel/test-http2-client-upload-reject.js parallel/test-http2-client-write-before-connect.js @@ -1563,16 +1565,19 @@ parallel/test-http2-large-writes-session-memory-leak.js parallel/test-http2-malformed-altsvc.js parallel/test-http2-many-writes-and-destroy.js parallel/test-http2-max-concurrent-streams.js -parallel/test-http2-max-invalid-frames.js +# disabled on 25.03.26, see https://github.com/nodejs/node/issues/60661 +# parallel/test-http2-max-invalid-frames.js parallel/test-http2-max-session-memory-leak.js parallel/test-http2-max-settings.js parallel/test-http2-methods.js -parallel/test-http2-misbehaving-flow-control.js -parallel/test-http2-misbehaving-flow-control-paused.js +# disabled on 25.03.26, see https://github.com/nodejs/node/issues/60661 +# parallel/test-http2-misbehaving-flow-control.js +# parallel/test-http2-misbehaving-flow-control-paused.js parallel/test-http2-misbehaving-multiplex.js parallel/test-http2-misc-util.js parallel/test-http2-misused-pseudoheaders.js -parallel/test-http2-multi-content-length.js +# disabled on 25.03.26, see https://github.com/nodejs/node/issues/60661 +# parallel/test-http2-multi-content-length.js parallel/test-http2-multiheaders.js parallel/test-http2-multiheaders-raw.js parallel/test-http2-multiplex.js @@ -1596,13 +1601,15 @@ parallel/test-http2-ping-unsolicited-ack.js parallel/test-http2-pipe.js parallel/test-http2-pipe-named-pipe.js parallel/test-http2-priority-cycle-.js -parallel/test-http2-priority-event.js +# disabled on 25.03.26, see https://github.com/nodejs/node/issues/60661 +# parallel/test-http2-priority-event.js parallel/test-http2-propagate-session-destroy-code.js parallel/test-http2-removed-header-stays-removed.js parallel/test-http2-request-remove-connect-listener.js parallel/test-http2-request-response-proto.js parallel/test-http2-res-corked.js -parallel/test-http2-reset-flood.js +# disabled on 25.03.26, see https://github.com/nodejs/node/issues/60661 +# parallel/test-http2-reset-flood.js parallel/test-http2-respond-errors.js parallel/test-http2-respond-file-204.js parallel/test-http2-respond-file-304.js