diff --git a/SOURCES/0001-Disable-running-gyp-on-shared-deps.patch b/SOURCES/0001-Disable-running-gyp-on-shared-deps.patch index bfc20b4..33c2db1 100644 --- a/SOURCES/0001-Disable-running-gyp-on-shared-deps.patch +++ b/SOURCES/0001-Disable-running-gyp-on-shared-deps.patch @@ -1,4 +1,4 @@ -From 8a45f34d9d74d59879973210cf06f4383b9832b8 Mon Sep 17 00:00:00 2001 +From 39f761838b5fc10af995642bd44e6bb4c79085f1 Mon Sep 17 00:00:00 2001 From: rpm-build Date: Tue, 30 May 2023 13:12:35 +0200 Subject: [PATCH] Disable running gyp on shared deps @@ -9,7 +9,7 @@ Signed-off-by: rpm-build 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile -index 6d6f2e4..88e1a11 100644 +index ef3eda2..8b52a4f 100644 --- a/Makefile +++ b/Makefile @@ -148,7 +148,7 @@ with-code-cache test-code-cache: @@ -22,5 +22,5 @@ index 6d6f2e4..88e1a11 100644 tools/v8_gypfiles/inspector.gypi tools/v8_gypfiles/v8.gyp $(PYTHON) tools/gyp_node.py -f make -- -2.40.1 +2.41.0 diff --git a/SOURCES/nodejs-fips-disable-options.patch b/SOURCES/0002-disable-fips-options.patch similarity index 53% rename from SOURCES/nodejs-fips-disable-options.patch rename to SOURCES/0002-disable-fips-options.patch index 5338b36..67eeb96 100644 --- a/SOURCES/nodejs-fips-disable-options.patch +++ b/SOURCES/0002-disable-fips-options.patch @@ -1,14 +1,17 @@ -FIPS related options cause a segfault, let's end sooner +From b9370dcfba759c63e894f12abcf49699f1e8f0dc Mon Sep 17 00:00:00 2001 +From: Honza Horak +Date: Thu, 12 Oct 2023 13:52:59 +0200 +Subject: [PATCH] disable fips options -Upstream report: https://github.com/nodejs/node/pull/48950 -RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=2227796 +Signed-off-by: rpm-build +--- + src/crypto/crypto_util.cc | 2 ++ + 1 file changed, 2 insertions(+) -This patch makes the part of the code that processes cmd-line options for -FIPS to end sooner before the code gets to the problematic part of the code. - -diff -up node-v18.16.1/src/crypto/crypto_util.cc.origfips node-v18.16.1/src/crypto/crypto_util.cc ---- node-v18.16.1/src/crypto/crypto_util.cc.origfips 2023-07-31 12:09:46.603683081 +0200 -+++ node-v18.16.1/src/crypto/crypto_util.cc 2023-07-31 12:16:16.906617914 +0200 +diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc +index 59ae7f8..7343396 100644 +--- a/src/crypto/crypto_util.cc ++++ b/src/crypto/crypto_util.cc @@ -111,6 +111,8 @@ bool ProcessFipsOptions() { /* Override FIPS settings in configuration file, if needed. */ if (per_process::cli_options->enable_fips_crypto || @@ -18,3 +21,6 @@ diff -up node-v18.16.1/src/crypto/crypto_util.cc.origfips node-v18.16.1/src/cryp #if OPENSSL_VERSION_MAJOR >= 3 OSSL_PROVIDER* fips_provider = OSSL_PROVIDER_load(nullptr, "fips"); if (fips_provider == nullptr) +-- +2.41.0 + diff --git a/SOURCES/0003-deps-nghttp2-update-to-1.57.0.patch b/SOURCES/0003-deps-nghttp2-update-to-1.57.0.patch new file mode 100644 index 0000000..758caad --- /dev/null +++ b/SOURCES/0003-deps-nghttp2-update-to-1.57.0.patch @@ -0,0 +1,5802 @@ +From 3cdb8a61ff25e4d299d9d47284da5134bc5f1072 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Thu, 12 Oct 2023 14:18:12 +0200 +Subject: [PATCH] deps(nghttp2): update to 1.57.0 + +Resolves: CVE-2023-44487 +Signed-off-by: rpm-build +--- + deps/nghttp2/lib/CMakeLists.txt | 4 + + deps/nghttp2/lib/Makefile.am | 12 +- + deps/nghttp2/lib/Makefile.in | 66 +- + deps/nghttp2/lib/includes/Makefile.in | 26 +- + deps/nghttp2/lib/includes/config.h | 92 -- + deps/nghttp2/lib/includes/nghttp2/nghttp2.h | 266 +++- + .../nghttp2/lib/includes/nghttp2/nghttp2ver.h | 4 +- + deps/nghttp2/lib/nghttp2_extpri.c | 35 + + deps/nghttp2/lib/nghttp2_extpri.h | 65 + + deps/nghttp2/lib/nghttp2_frame.c | 122 +- + deps/nghttp2/lib/nghttp2_frame.h | 95 +- + deps/nghttp2/lib/nghttp2_hd.c | 5 + + deps/nghttp2/lib/nghttp2_hd.h | 1 + + deps/nghttp2/lib/nghttp2_helper.c | 13 + + deps/nghttp2/lib/nghttp2_http.c | 136 +- + deps/nghttp2/lib/nghttp2_http.h | 3 + + deps/nghttp2/lib/nghttp2_map.c | 61 +- + deps/nghttp2/lib/nghttp2_map.h | 8 +- + deps/nghttp2/lib/nghttp2_net.h | 12 +- + deps/nghttp2/lib/nghttp2_option.c | 24 + + deps/nghttp2/lib/nghttp2_option.h | 16 + + deps/nghttp2/lib/nghttp2_outbound_item.c | 3 + + deps/nghttp2/lib/nghttp2_pq.c | 3 +- + deps/nghttp2/lib/nghttp2_pq.h | 8 +- + deps/nghttp2/lib/nghttp2_ratelim.c | 75 ++ + deps/nghttp2/lib/nghttp2_ratelim.h | 57 + + deps/nghttp2/lib/nghttp2_session.c | 870 ++++++++++--- + deps/nghttp2/lib/nghttp2_session.h | 53 +- + deps/nghttp2/lib/nghttp2_stream.c | 30 +- + deps/nghttp2/lib/nghttp2_stream.h | 40 +- + deps/nghttp2/lib/nghttp2_submit.c | 82 +- + deps/nghttp2/lib/nghttp2_time.c | 62 + + deps/nghttp2/lib/nghttp2_time.h | 38 + + deps/nghttp2/lib/sfparse.c | 1146 +++++++++++++++++ + deps/nghttp2/lib/sfparse.h | 409 ++++++ + deps/nghttp2/nghttp2.gyp | 7 +- + 36 files changed, 3477 insertions(+), 472 deletions(-) + delete mode 100644 deps/nghttp2/lib/includes/config.h + create mode 100644 deps/nghttp2/lib/nghttp2_extpri.c + create mode 100644 deps/nghttp2/lib/nghttp2_extpri.h + create mode 100644 deps/nghttp2/lib/nghttp2_ratelim.c + create mode 100644 deps/nghttp2/lib/nghttp2_ratelim.h + create mode 100644 deps/nghttp2/lib/nghttp2_time.c + create mode 100644 deps/nghttp2/lib/nghttp2_time.h + create mode 100644 deps/nghttp2/lib/sfparse.c + create mode 100644 deps/nghttp2/lib/sfparse.h + +diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt +index 4dc2fcd..7adba3a 100644 +--- a/deps/nghttp2/lib/CMakeLists.txt ++++ b/deps/nghttp2/lib/CMakeLists.txt +@@ -23,7 +23,11 @@ set(NGHTTP2_SOURCES + nghttp2_mem.c + nghttp2_http.c + nghttp2_rcbuf.c ++ nghttp2_extpri.c ++ nghttp2_ratelim.c ++ nghttp2_time.c + nghttp2_debug.c ++ sfparse.c + ) + + set(NGHTTP2_RES "") +diff --git a/deps/nghttp2/lib/Makefile.am b/deps/nghttp2/lib/Makefile.am +index 1e1f248..c3ace40 100644 +--- a/deps/nghttp2/lib/Makefile.am ++++ b/deps/nghttp2/lib/Makefile.am +@@ -50,7 +50,11 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ + nghttp2_mem.c \ + nghttp2_http.c \ + nghttp2_rcbuf.c \ +- nghttp2_debug.c ++ nghttp2_extpri.c \ ++ nghttp2_ratelim.c \ ++ nghttp2_time.c \ ++ nghttp2_debug.c \ ++ sfparse.c + + HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_frame.h \ +@@ -66,7 +70,11 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_mem.h \ + nghttp2_http.h \ + nghttp2_rcbuf.h \ +- nghttp2_debug.h ++ nghttp2_extpri.h \ ++ nghttp2_ratelim.h \ ++ nghttp2_time.h \ ++ nghttp2_debug.h \ ++ sfparse.h + + libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) + libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \ +diff --git a/deps/nghttp2/lib/Makefile.in b/deps/nghttp2/lib/Makefile.in +index 5653774..0b95613 100644 +--- a/deps/nghttp2/lib/Makefile.in ++++ b/deps/nghttp2/lib/Makefile.in +@@ -1,4 +1,4 @@ +-# Makefile.in generated by automake 1.16.4 from Makefile.am. ++# Makefile.in generated by automake 1.16.5 from Makefile.am. + # @configure_input@ + + # Copyright (C) 1994-2021 Free Software Foundation, Inc. +@@ -107,13 +107,8 @@ host_triplet = @host@ + target_triplet = @target@ + subdir = lib + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ +- $(top_srcdir)/m4/ax_boost_base.m4 \ +- $(top_srcdir)/m4/ax_boost_system.m4 \ +- $(top_srcdir)/m4/ax_boost_thread.m4 \ +- $(top_srcdir)/m4/ax_check_compile_flag.m4 \ ++am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ +- $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +@@ -162,7 +157,8 @@ am__objects_2 = nghttp2_pq.lo nghttp2_map.lo nghttp2_queue.lo \ + nghttp2_hd_huffman.lo nghttp2_hd_huffman_data.lo \ + nghttp2_version.lo nghttp2_priority_spec.lo nghttp2_option.lo \ + nghttp2_callbacks.lo nghttp2_mem.lo nghttp2_http.lo \ +- nghttp2_rcbuf.lo nghttp2_debug.lo ++ nghttp2_rcbuf.lo nghttp2_extpri.lo nghttp2_ratelim.lo \ ++ nghttp2_time.lo nghttp2_debug.lo sfparse.lo + am_libnghttp2_la_OBJECTS = $(am__objects_1) $(am__objects_2) + libnghttp2_la_OBJECTS = $(am_libnghttp2_la_OBJECTS) + AM_V_lt = $(am__v_lt_@AM_V@) +@@ -189,8 +185,9 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp + am__maybe_remake_depfiles = depfiles + am__depfiles_remade = ./$(DEPDIR)/nghttp2_buf.Plo \ + ./$(DEPDIR)/nghttp2_callbacks.Plo \ +- ./$(DEPDIR)/nghttp2_debug.Plo ./$(DEPDIR)/nghttp2_frame.Plo \ +- ./$(DEPDIR)/nghttp2_hd.Plo ./$(DEPDIR)/nghttp2_hd_huffman.Plo \ ++ ./$(DEPDIR)/nghttp2_debug.Plo ./$(DEPDIR)/nghttp2_extpri.Plo \ ++ ./$(DEPDIR)/nghttp2_frame.Plo ./$(DEPDIR)/nghttp2_hd.Plo \ ++ ./$(DEPDIR)/nghttp2_hd_huffman.Plo \ + ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo \ + ./$(DEPDIR)/nghttp2_helper.Plo ./$(DEPDIR)/nghttp2_http.Plo \ + ./$(DEPDIR)/nghttp2_map.Plo ./$(DEPDIR)/nghttp2_mem.Plo \ +@@ -198,9 +195,11 @@ am__depfiles_remade = ./$(DEPDIR)/nghttp2_buf.Plo \ + ./$(DEPDIR)/nghttp2_outbound_item.Plo \ + ./$(DEPDIR)/nghttp2_pq.Plo \ + ./$(DEPDIR)/nghttp2_priority_spec.Plo \ +- ./$(DEPDIR)/nghttp2_queue.Plo ./$(DEPDIR)/nghttp2_rcbuf.Plo \ +- ./$(DEPDIR)/nghttp2_session.Plo ./$(DEPDIR)/nghttp2_stream.Plo \ +- ./$(DEPDIR)/nghttp2_submit.Plo ./$(DEPDIR)/nghttp2_version.Plo ++ ./$(DEPDIR)/nghttp2_queue.Plo ./$(DEPDIR)/nghttp2_ratelim.Plo \ ++ ./$(DEPDIR)/nghttp2_rcbuf.Plo ./$(DEPDIR)/nghttp2_session.Plo \ ++ ./$(DEPDIR)/nghttp2_stream.Plo ./$(DEPDIR)/nghttp2_submit.Plo \ ++ ./$(DEPDIR)/nghttp2_time.Plo ./$(DEPDIR)/nghttp2_version.Plo \ ++ ./$(DEPDIR)/sfparse.Plo + am__mv = mv -f + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +@@ -299,11 +298,6 @@ AUTOCONF = @AUTOCONF@ + AUTOHEADER = @AUTOHEADER@ + AUTOMAKE = @AUTOMAKE@ + AWK = @AWK@ +-BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +-BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +-BOOST_LDFLAGS = @BOOST_LDFLAGS@ +-BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +-BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ + BPFCFLAGS = @BPFCFLAGS@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ +@@ -320,7 +314,6 @@ CXXCPP = @CXXCPP@ + CXXDEPMODE = @CXXDEPMODE@ + CXXFLAGS = @CXXFLAGS@ + CYGPATH_W = @CYGPATH_W@ +-CYTHON = @CYTHON@ + DEFS = @DEFS@ + DEPDIR = @DEPDIR@ + DLLTOOL = @DLLTOOL@ +@@ -336,6 +329,7 @@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ + EXTRACFLAG = @EXTRACFLAG@ + EXTRA_DEFS = @EXTRA_DEFS@ + FGREP = @FGREP@ ++FILECMD = @FILECMD@ + GREP = @GREP@ + HAVE_CXX14 = @HAVE_CXX14@ + INSTALL = @INSTALL@ +@@ -364,8 +358,8 @@ 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_OPENSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ +-LIBNGTCP2_CRYPTO_OPENSSL_LIBS = @LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ ++LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ ++LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ + LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ +@@ -404,15 +398,9 @@ PKG_CONFIG = @PKG_CONFIG@ + PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ + PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ + PYTHON = @PYTHON@ +-PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ + PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +-PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +-PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +-PYTHON_LIBS = @PYTHON_LIBS@ + PYTHON_PLATFORM = @PYTHON_PLATFORM@ +-PYTHON_PLATFORM_SITE_PKG = @PYTHON_PLATFORM_SITE_PKG@ + PYTHON_PREFIX = @PYTHON_PREFIX@ +-PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ + PYTHON_VERSION = @PYTHON_VERSION@ + RANLIB = @RANLIB@ + SED = @SED@ +@@ -523,7 +511,11 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ + nghttp2_mem.c \ + nghttp2_http.c \ + nghttp2_rcbuf.c \ +- nghttp2_debug.c ++ nghttp2_extpri.c \ ++ nghttp2_ratelim.c \ ++ nghttp2_time.c \ ++ nghttp2_debug.c \ ++ sfparse.c + + HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_frame.h \ +@@ -539,7 +531,11 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_mem.h \ + nghttp2_http.h \ + nghttp2_rcbuf.h \ +- nghttp2_debug.h ++ nghttp2_extpri.h \ ++ nghttp2_ratelim.h \ ++ nghttp2_time.h \ ++ nghttp2_debug.h \ ++ sfparse.h + + libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) + libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \ +@@ -628,6 +624,7 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_debug.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_extpri.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ # am--include-marker +@@ -642,11 +639,14 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_ratelim.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_rcbuf.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_time.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sfparse.Plo@am__quote@ # am--include-marker + + $(am__depfiles_remade): + @$(MKDIR_P) $(@D) +@@ -909,6 +909,7 @@ distclean: distclean-recursive + -rm -f ./$(DEPDIR)/nghttp2_buf.Plo + -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo + -rm -f ./$(DEPDIR)/nghttp2_debug.Plo ++ -rm -f ./$(DEPDIR)/nghttp2_extpri.Plo + -rm -f ./$(DEPDIR)/nghttp2_frame.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo +@@ -923,11 +924,14 @@ distclean: distclean-recursive + -rm -f ./$(DEPDIR)/nghttp2_pq.Plo + -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo + -rm -f ./$(DEPDIR)/nghttp2_queue.Plo ++ -rm -f ./$(DEPDIR)/nghttp2_ratelim.Plo + -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo + -rm -f ./$(DEPDIR)/nghttp2_session.Plo + -rm -f ./$(DEPDIR)/nghttp2_stream.Plo + -rm -f ./$(DEPDIR)/nghttp2_submit.Plo ++ -rm -f ./$(DEPDIR)/nghttp2_time.Plo + -rm -f ./$(DEPDIR)/nghttp2_version.Plo ++ -rm -f ./$(DEPDIR)/sfparse.Plo + -rm -f Makefile + distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags +@@ -976,6 +980,7 @@ maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/nghttp2_buf.Plo + -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo + -rm -f ./$(DEPDIR)/nghttp2_debug.Plo ++ -rm -f ./$(DEPDIR)/nghttp2_extpri.Plo + -rm -f ./$(DEPDIR)/nghttp2_frame.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd.Plo + -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo +@@ -990,11 +995,14 @@ maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/nghttp2_pq.Plo + -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo + -rm -f ./$(DEPDIR)/nghttp2_queue.Plo ++ -rm -f ./$(DEPDIR)/nghttp2_ratelim.Plo + -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo + -rm -f ./$(DEPDIR)/nghttp2_session.Plo + -rm -f ./$(DEPDIR)/nghttp2_stream.Plo + -rm -f ./$(DEPDIR)/nghttp2_submit.Plo ++ -rm -f ./$(DEPDIR)/nghttp2_time.Plo + -rm -f ./$(DEPDIR)/nghttp2_version.Plo ++ -rm -f ./$(DEPDIR)/sfparse.Plo + -rm -f Makefile + maintainer-clean-am: distclean-am maintainer-clean-generic + +diff --git a/deps/nghttp2/lib/includes/Makefile.in b/deps/nghttp2/lib/includes/Makefile.in +index 327e523..3de90d7 100644 +--- a/deps/nghttp2/lib/includes/Makefile.in ++++ b/deps/nghttp2/lib/includes/Makefile.in +@@ -1,4 +1,4 @@ +-# Makefile.in generated by automake 1.16.4 from Makefile.am. ++# Makefile.in generated by automake 1.16.5 from Makefile.am. + # @configure_input@ + + # Copyright (C) 1994-2021 Free Software Foundation, Inc. +@@ -114,13 +114,8 @@ host_triplet = @host@ + target_triplet = @target@ + subdir = lib/includes + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ +- $(top_srcdir)/m4/ax_boost_base.m4 \ +- $(top_srcdir)/m4/ax_boost_system.m4 \ +- $(top_srcdir)/m4/ax_boost_thread.m4 \ +- $(top_srcdir)/m4/ax_check_compile_flag.m4 \ ++am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ +- $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +@@ -208,11 +203,6 @@ AUTOCONF = @AUTOCONF@ + AUTOHEADER = @AUTOHEADER@ + AUTOMAKE = @AUTOMAKE@ + AWK = @AWK@ +-BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +-BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +-BOOST_LDFLAGS = @BOOST_LDFLAGS@ +-BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +-BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ + BPFCFLAGS = @BPFCFLAGS@ + CC = @CC@ + CCDEPMODE = @CCDEPMODE@ +@@ -229,7 +219,6 @@ CXXCPP = @CXXCPP@ + CXXDEPMODE = @CXXDEPMODE@ + CXXFLAGS = @CXXFLAGS@ + CYGPATH_W = @CYGPATH_W@ +-CYTHON = @CYTHON@ + DEFS = @DEFS@ + DEPDIR = @DEPDIR@ + DLLTOOL = @DLLTOOL@ +@@ -245,6 +234,7 @@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ + EXTRACFLAG = @EXTRACFLAG@ + EXTRA_DEFS = @EXTRA_DEFS@ + FGREP = @FGREP@ ++FILECMD = @FILECMD@ + GREP = @GREP@ + HAVE_CXX14 = @HAVE_CXX14@ + INSTALL = @INSTALL@ +@@ -273,8 +263,8 @@ 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_OPENSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ +-LIBNGTCP2_CRYPTO_OPENSSL_LIBS = @LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ ++LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ ++LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ + LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ +@@ -313,15 +303,9 @@ PKG_CONFIG = @PKG_CONFIG@ + PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ + PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ + PYTHON = @PYTHON@ +-PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ + PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +-PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +-PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +-PYTHON_LIBS = @PYTHON_LIBS@ + PYTHON_PLATFORM = @PYTHON_PLATFORM@ +-PYTHON_PLATFORM_SITE_PKG = @PYTHON_PLATFORM_SITE_PKG@ + PYTHON_PREFIX = @PYTHON_PREFIX@ +-PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ + PYTHON_VERSION = @PYTHON_VERSION@ + RANLIB = @RANLIB@ + SED = @SED@ +diff --git a/deps/nghttp2/lib/includes/config.h b/deps/nghttp2/lib/includes/config.h +deleted file mode 100644 +index 12a816e..0000000 +--- a/deps/nghttp2/lib/includes/config.h ++++ /dev/null +@@ -1,92 +0,0 @@ +-/* Hint to the compiler that a function never returns */ +-#define NGHTTP2_NORETURN +- +-/* Edited to match src/node.h. */ +-#include +- +-#ifdef _WIN32 +-#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) +-typedef intptr_t ssize_t; +-# define _SSIZE_T_ +-# define _SSIZE_T_DEFINED +-#endif +-#else // !_WIN32 +-# include // size_t, ssize_t +-#endif // _WIN32 +- +-/* Define to 1 if you have the `std::map::emplace`. */ +-#define HAVE_STD_MAP_EMPLACE 1 +- +-/* Define to 1 if you have `libjansson` library. */ +-/* #undef HAVE_JANSSON */ +- +-/* Define to 1 if you have `libxml2` library. */ +-/* #undef HAVE_LIBXML2 */ +- +-/* Define to 1 if you have `spdylay` library. */ +-/* #undef HAVE_SPDYLAY */ +- +-/* Define to 1 if you have `mruby` library. */ +-/* #undef HAVE_MRUBY */ +- +-/* Define to 1 if you have `neverbleed` library. */ +-/* #undef HAVE_NEVERBLEED */ +- +-/* sizeof(int *) */ +-#define SIZEOF_INT_P 4 +- +-/* sizeof(time_t) */ +-#define SIZEOF_TIME_T 8 +- +-/* Define to 1 if you have the `_Exit` function. */ +-#define HAVE__EXIT 1 +- +-/* Define to 1 if you have the `accept4` function. */ +-/* #undef HAVE_ACCEPT4 */ +- +-/* Define to 1 if you have the `initgroups` function. */ +-#define HAVE_DECL_INITGROUPS 0 +- +-/* Define to 1 to enable debug output. */ +-/* #undef DEBUGBUILD */ +- +-/* Define to 1 if you want to disable threads. */ +-/* #undef NOTHREADS */ +- +-/* Define to 1 if you have the header file. */ +-#ifndef _WIN32 +-# define HAVE_ARPA_INET_H 1 +-#endif +- +-/* Define to 1 if you have the header file. */ +-#define HAVE_FCNTL_H 1 +- +-/* Define to 1 if you have the header file. */ +-#define HAVE_INTTYPES_H 1 +- +-/* Define to 1 if you have the header file. */ +-#define HAVE_LIMITS_H 1 +- +-/* Define to 1 if you have the header file. */ +-/* #undef HAVE_NETDB_H */ +- +-/* Define to 1 if you have the header file. */ +-/* #undef HAVE_NETINET_IN_H */ +- +-/* Define to 1 if you have the header file. */ +-/* #undef HAVE_PWD_H */ +- +-/* Define to 1 if you have the header file. */ +-/* #undef HAVE_SYS_SOCKET_H */ +- +-/* Define to 1 if you have the header file. */ +-/* #undef HAVE_SYS_TIME_H */ +- +-/* Define to 1 if you have the header file. */ +-/* #undef HAVE_SYSLOG_H */ +- +-/* Define to 1 if you have the header file. */ +-#define HAVE_TIME_H 1 +- +-/* Define to 1 if you have the header file. */ +-/* #undef HAVE_UNISTD_H */ +diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +index 04321a6..fa22081 100644 +--- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h ++++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +@@ -634,7 +634,11 @@ typedef enum { + * The ORIGIN frame, which is defined by `RFC 8336 + * `_. + */ +- NGHTTP2_ORIGIN = 0x0c ++ NGHTTP2_ORIGIN = 0x0c, ++ /** ++ * The PRIORITY_UPDATE frame, which is defined by :rfc:`9218`. ++ */ ++ NGHTTP2_PRIORITY_UPDATE = 0x10 + } nghttp2_frame_type; + + /** +@@ -703,7 +707,11 @@ typedef enum { + * SETTINGS_ENABLE_CONNECT_PROTOCOL + * (`RFC 8441 `_) + */ +- NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08 ++ NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08, ++ /** ++ * SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`) ++ */ ++ NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09 + } nghttp2_settings_id; + /* Note: If we add SETTINGS, update the capacity of + NGHTTP2_INBOUND_NUM_IV as well */ +@@ -1422,12 +1430,6 @@ typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, + * respectively. The header name/value pairs are emitted via + * :type:`nghttp2_on_header_callback`. + * +- * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be +- * called after stream is closed (see +- * :type:`nghttp2_on_stream_close_callback`). The application should +- * check that stream is still alive using its own stream management or +- * :func:`nghttp2_session_get_stream_user_data()`. +- * + * Only HEADERS and DATA frame can signal the end of incoming data. + * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + * |frame| is the last frame from the remote peer in this stream. +@@ -2693,6 +2695,11 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + * 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. ++ * ++ * 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. + */ + NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, + int val); +@@ -2719,6 +2726,53 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, + NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, + size_t val); + ++/** ++ * @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. ++ * ++ * Client session ignores this option. ++ */ ++NGHTTP2_EXTERN void ++nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, ++ int val); ++ ++/** ++ * @function ++ * ++ * This option, if set to nonzero, turns off RFC 9113 leading and ++ * trailing white spaces validation against HTTP field value. Some ++ * important fields, such as HTTP/2 pseudo header fields, are ++ * validated more strictly and this option does not apply to them. ++ */ ++NGHTTP2_EXTERN void ++nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( ++ nghttp2_option *option, int val); ++ ++/** ++ * @function ++ * ++ * This function sets the rate limit for the incoming stream reset ++ * (RST_STREAM frame). It is server use only. It is a token-bucket ++ * based rate limiter. |burst| specifies the number of tokens that is ++ * initially available. The maximum number of tokens is capped to ++ * this value. |rate| specifies the number of tokens that are ++ * regenerated per second. An incoming RST_STREAM consumes one token. ++ * If there is no token available, GOAWAY is sent to tear down the ++ * connection. |burst| and |rate| default to 1000 and 33 ++ * respectively. ++ */ ++NGHTTP2_EXTERN void ++nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, ++ uint64_t burst, uint64_t rate); ++ + /** + * @function + * +@@ -3589,6 +3643,11 @@ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + * 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: + * +@@ -3632,6 +3691,11 @@ nghttp2_session_change_stream_priority(nghttp2_session *session, + * 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: + * +@@ -3837,6 +3901,11 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + * :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 |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with +@@ -4057,6 +4126,11 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + * :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 |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with +@@ -4184,6 +4258,11 @@ NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + * :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: + * +@@ -4198,6 +4277,61 @@ nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + ++/** ++ * @macro ++ * ++ * :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency ++ * level for :rfc:`9218` extensible priorities. ++ */ ++#define NGHTTP2_EXTPRI_DEFAULT_URGENCY 3 ++ ++/** ++ * @macro ++ * ++ * :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level ++ * for :rfc:`9218` extensible priorities. ++ */ ++#define NGHTTP2_EXTPRI_URGENCY_HIGH 0 ++ ++/** ++ * @macro ++ * ++ * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for ++ * :rfc:`9218` extensible priorities. ++ */ ++#define NGHTTP2_EXTPRI_URGENCY_LOW 7 ++ ++/** ++ * @macro ++ * ++ * :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency ++ * levels for :rfc:`9218` extensible priorities. ++ */ ++#define NGHTTP2_EXTPRI_URGENCY_LEVELS (NGHTTP2_EXTPRI_URGENCY_LOW + 1) ++ ++/** ++ * @struct ++ * ++ * :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities ++ * specification for a stream. ++ */ ++typedef struct nghttp2_extpri { ++ /** ++ * :member:`urgency` is the urgency of a stream, it must be in ++ * [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`, ++ * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the ++ * highest urgency. ++ */ ++ uint32_t urgency; ++ /** ++ * :member:`inc` indicates that a content can be processed ++ * incrementally or not. If inc is 0, it cannot be processed ++ * incrementally. If inc is 1, it can be processed incrementally. ++ * Other value is not permitted. ++ */ ++ int inc; ++} nghttp2_extpri; ++ + /** + * @function + * +@@ -4722,6 +4856,108 @@ NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, + const nghttp2_origin_entry *ov, + size_t nov); + ++/** ++ * @struct ++ * ++ * The payload of PRIORITY_UPDATE frame. PRIORITY_UPDATE frame is a ++ * non-critical extension to HTTP/2. If this frame is received, and ++ * `nghttp2_option_set_user_recv_extension_type()` is not set, and ++ * `nghttp2_option_set_builtin_recv_extension_type()` is set for ++ * :enum:`nghttp2_frame_type.NGHTTP2_PRIORITY_UPDATE`, ++ * ``nghttp2_extension.payload`` will point to this struct. ++ * ++ * It has the following members: ++ */ ++typedef struct { ++ /** ++ * The stream ID of the stream whose priority is updated. ++ */ ++ int32_t stream_id; ++ /** ++ * The pointer to Priority field value. It is not necessarily ++ * NULL-terminated. ++ */ ++ uint8_t *field_value; ++ /** ++ * The length of the :member:`field_value`. ++ */ ++ size_t field_value_len; ++} nghttp2_ext_priority_update; ++ ++/** ++ * @function ++ * ++ * Submits PRIORITY_UPDATE frame. ++ * ++ * PRIORITY_UPDATE frame is a non-critical extension to HTTP/2, and ++ * defined in :rfc:`9218#section-7.1`. ++ * ++ * The |flags| is currently ignored and should be ++ * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. ++ * ++ * The |stream_id| is the ID of stream which is prioritized. The ++ * |field_value| points to the Priority field value. The ++ * |field_value_len| is the length of the Priority field value. ++ * ++ * If this function is called by server, ++ * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` is returned. ++ * ++ * If ++ * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` ++ * of value of 0 is received by a remote endpoint (or it is omitted), ++ * this function does nothing and returns 0. ++ * ++ * This function returns 0 if it succeeds, or one of the following ++ * negative error codes: ++ * ++ * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` ++ * Out of memory ++ * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` ++ * The function is called from server side session ++ * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` ++ * The |field_value_len| is larger than 16380; or |stream_id| is ++ * 0. ++ */ ++NGHTTP2_EXTERN int nghttp2_submit_priority_update(nghttp2_session *session, ++ uint8_t flags, ++ int32_t stream_id, ++ const uint8_t *field_value, ++ size_t field_value_len); ++ ++/** ++ * @function ++ * ++ * Changes the priority of the existing stream denoted by |stream_id|. ++ * The new priority is |extpri|. This function is meant to be used by ++ * server for :rfc:`9218` extensible prioritization scheme. ++ * ++ * If |session| is initialized as client, this function returns ++ * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. For client, use ++ * `nghttp2_submit_priority_update()` instead. ++ * ++ * If :member:`extpri->urgency ` is out of ++ * bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`. ++ * ++ * If |ignore_client_signal| is nonzero, server starts to ignore ++ * client priority signals for this stream. ++ * ++ * If ++ * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` ++ * of value of 1 is not submitted via `nghttp2_submit_settings()`, ++ * this function does nothing and returns 0. ++ * ++ * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` ++ * Out of memory. ++ * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` ++ * The |session| is initialized as client. ++ * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` ++ * |stream_id| is zero; or a stream denoted by |stream_id| is not ++ * found. ++ */ ++NGHTTP2_EXTERN int nghttp2_session_change_extpri_stream_priority( ++ nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri, ++ int ignore_client_signal); ++ + /** + * @function + * +@@ -4833,9 +5069,23 @@ NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 ++ * ++ * This function is considered obsolete, and application should ++ * consider to use `nghttp2_check_header_value_rfc9113()` instead. + */ + NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); + ++/** ++ * @function ++ * ++ * Returns nonzero if HTTP header field value |value| of length |len| ++ * is valid according to ++ * http://tools.ietf.org/html/rfc7230#section-3.2, plus ++ * https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1 ++ */ ++NGHTTP2_EXTERN int nghttp2_check_header_value_rfc9113(const uint8_t *value, ++ size_t len); ++ + /** + * @function + * +diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +index c608251..f56954e 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.47.0" ++#define NGHTTP2_VERSION "1.57.0" + + /** + * @macro +@@ -37,6 +37,6 @@ + * release. This is a 24 bit number with 8 bits for major number, 8 bits + * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. + */ +-#define NGHTTP2_VERSION_NUM 0x012f00 ++#define NGHTTP2_VERSION_NUM 0x013900 + + #endif /* NGHTTP2VER_H */ +diff --git a/deps/nghttp2/lib/nghttp2_extpri.c b/deps/nghttp2/lib/nghttp2_extpri.c +new file mode 100644 +index 0000000..3fd9b78 +--- /dev/null ++++ b/deps/nghttp2/lib/nghttp2_extpri.c +@@ -0,0 +1,35 @@ ++/* ++ * nghttp2 - HTTP/2 C Library ++ * ++ * Copyright (c) 2022 nghttp3 contributors ++ * Copyright (c) 2022 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#include "nghttp2_extpri.h" ++ ++uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri) { ++ return (uint8_t)((uint32_t)extpri->inc << 7 | extpri->urgency); ++} ++ ++void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri) { ++ extpri->urgency = nghttp2_extpri_uint8_urgency(u8extpri); ++ extpri->inc = nghttp2_extpri_uint8_inc(u8extpri); ++} +diff --git a/deps/nghttp2/lib/nghttp2_extpri.h b/deps/nghttp2/lib/nghttp2_extpri.h +new file mode 100644 +index 0000000..23c6ddc +--- /dev/null ++++ b/deps/nghttp2/lib/nghttp2_extpri.h +@@ -0,0 +1,65 @@ ++/* ++ * nghttp2 - HTTP/2 C Library ++ * ++ * Copyright (c) 2022 nghttp3 contributors ++ * Copyright (c) 2022 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#ifndef NGHTTP2_EXTPRI_H ++#define NGHTTP2_EXTPRI_H ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++ ++#include ++ ++/* ++ * NGHTTP2_EXTPRI_INC_MASK is a bit mask to retrieve incremental bit ++ * from a value produced by nghttp2_extpri_to_uint8. ++ */ ++#define NGHTTP2_EXTPRI_INC_MASK (1 << 7) ++ ++/* ++ * nghttp2_extpri_to_uint8 encodes |pri| into uint8_t variable. ++ */ ++uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri); ++ ++/* ++ * nghttp2_extpri_from_uint8 decodes |u8extpri|, which is produced by ++ * nghttp2_extpri_to_uint8, intto |extpri|. ++ */ ++void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri); ++ ++/* ++ * nghttp2_extpri_uint8_urgency extracts urgency from |PRI| which is ++ * supposed to be constructed by nghttp2_extpri_to_uint8. ++ */ ++#define nghttp2_extpri_uint8_urgency(PRI) \ ++ ((uint32_t)((PRI) & ~NGHTTP2_EXTPRI_INC_MASK)) ++ ++/* ++ * nghttp2_extpri_uint8_inc extracts inc from |PRI| which is supposed to ++ * be constructed by nghttp2_extpri_to_uint8. ++ */ ++#define nghttp2_extpri_uint8_inc(PRI) (((PRI)&NGHTTP2_EXTPRI_INC_MASK) != 0) ++ ++#endif /* NGHTTP2_EXTPRI_H */ +diff --git a/deps/nghttp2/lib/nghttp2_frame.c b/deps/nghttp2/lib/nghttp2_frame.c +index 3648b23..77cb463 100644 +--- a/deps/nghttp2/lib/nghttp2_frame.c ++++ b/deps/nghttp2/lib/nghttp2_frame.c +@@ -253,6 +253,31 @@ void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, origin->ov); + } + ++void nghttp2_frame_priority_update_init(nghttp2_extension *frame, ++ int32_t stream_id, uint8_t *field_value, ++ size_t field_value_len) { ++ nghttp2_ext_priority_update *priority_update; ++ ++ nghttp2_frame_hd_init(&frame->hd, 4 + field_value_len, ++ NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0); ++ ++ priority_update = frame->payload; ++ priority_update->stream_id = stream_id; ++ priority_update->field_value = field_value; ++ priority_update->field_value_len = field_value_len; ++} ++ ++void nghttp2_frame_priority_update_free(nghttp2_extension *frame, ++ nghttp2_mem *mem) { ++ nghttp2_ext_priority_update *priority_update; ++ ++ priority_update = frame->payload; ++ if (priority_update == NULL) { ++ return; ++ } ++ nghttp2_mem_free(mem, priority_update->field_value); ++} ++ + size_t nghttp2_frame_priority_len(uint8_t flags) { + if (flags & NGHTTP2_FLAG_PRIORITY) { + return NGHTTP2_PRIORITY_SPECLEN; +@@ -393,8 +418,8 @@ void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); + } + +-int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, +- const uint8_t *payload) { ++void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, ++ const uint8_t *payload) { + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); + } else { +@@ -403,11 +428,9 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + + frame->nva = NULL; + frame->nvlen = 0; +- +- return 0; + } + +-int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { ++void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); +@@ -423,8 +446,6 @@ int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { + nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); + + buf->last += NGHTTP2_PRIORITY_SPECLEN; +- +- return 0; + } + + void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, +@@ -432,8 +453,8 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); + } + +-int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, +- nghttp2_rst_stream *frame) { ++void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, ++ nghttp2_rst_stream *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); +@@ -448,8 +469,6 @@ int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; +- +- return 0; + } + + void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, +@@ -567,16 +586,15 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + return frame_pack_headers_shared(bufs, &frame->hd); + } + +-int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, +- const uint8_t *payload) { ++void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, ++ const uint8_t *payload) { + frame->promised_stream_id = + nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->nva = NULL; + frame->nvlen = 0; +- return 0; + } + +-int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { ++void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); +@@ -591,8 +609,6 @@ int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { + + buf->last = + nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); +- +- return 0; + } + + void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, +@@ -672,8 +688,8 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + return 0; + } + +-int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, +- nghttp2_window_update *frame) { ++void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, ++ nghttp2_window_update *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); +@@ -688,8 +704,6 @@ int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + + nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment); + buf->last += 4; +- +- return 0; + } + + void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, +@@ -698,7 +712,7 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; + } + +-int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { ++void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { + int rv; + nghttp2_buf *buf; + nghttp2_ext_altsvc *altsvc; +@@ -727,8 +741,6 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { + rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len); + + assert(rv == 0); +- +- return 0; + } + + void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, +@@ -876,6 +888,55 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, + return 0; + } + ++void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, ++ nghttp2_extension *frame) { ++ int rv; ++ nghttp2_buf *buf; ++ nghttp2_ext_priority_update *priority_update; ++ ++ /* This is required with --disable-assert. */ ++ (void)rv; ++ ++ priority_update = frame->payload; ++ ++ buf = &bufs->head->buf; ++ ++ assert(nghttp2_buf_avail(buf) >= 4 + priority_update->field_value_len); ++ ++ buf->pos -= NGHTTP2_FRAME_HDLEN; ++ ++ nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); ++ ++ nghttp2_put_uint32be(buf->last, (uint32_t)priority_update->stream_id); ++ buf->last += 4; ++ ++ rv = nghttp2_bufs_add(bufs, priority_update->field_value, ++ priority_update->field_value_len); ++ ++ assert(rv == 0); ++} ++ ++void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, ++ uint8_t *payload, ++ size_t payloadlen) { ++ nghttp2_ext_priority_update *priority_update; ++ ++ assert(payloadlen >= 4); ++ ++ priority_update = frame->payload; ++ ++ priority_update->stream_id = ++ nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; ++ ++ if (payloadlen > 4) { ++ priority_update->field_value = payload + 4; ++ priority_update->field_value_len = payloadlen - 4; ++ } else { ++ priority_update->field_value = NULL; ++ priority_update->field_value_len = 0; ++ } ++} ++ + nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem) { + nghttp2_settings_entry *iv_copy; +@@ -1071,6 +1132,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { + return 0; + } + break; ++ case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: ++ if (iv[i].value != 0 && iv[i].value != 1) { ++ return 0; ++ } ++ break; + } + } + return 1; +@@ -1105,14 +1171,14 @@ static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { + buf->last += trail_padlen; + } + +-int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, +- size_t padlen, int framehd_only) { ++void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, ++ size_t padlen, int framehd_only) { + nghttp2_buf *buf; + + if (padlen == 0) { + DEBUGF("send: padlen = 0, nothing to do\n"); + +- return 0; ++ return; + } + + /* +@@ -1145,6 +1211,4 @@ int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + hd->flags |= NGHTTP2_FLAG_PADDED; + + DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen); +- +- return 0; + } +diff --git a/deps/nghttp2/lib/nghttp2_frame.h b/deps/nghttp2/lib/nghttp2_frame.h +index 3859926..d586688 100644 +--- a/deps/nghttp2/lib/nghttp2_frame.h ++++ b/deps/nghttp2/lib/nghttp2_frame.h +@@ -73,6 +73,7 @@ + typedef union { + nghttp2_ext_altsvc altsvc; + nghttp2_ext_origin origin; ++ nghttp2_ext_priority_update priority_update; + } nghttp2_ext_frame_payload; + + void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); +@@ -142,11 +143,9 @@ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + * Unpacks HEADERS frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block and + * after possible Pad Length field. +- * +- * This function always succeeds and returns 0. + */ +-int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, +- const uint8_t *payload); ++void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, ++ const uint8_t *payload); + + /* + * Packs PRIORITY frame |frame| in wire format and store it in +@@ -154,10 +153,8 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. +- * +- * This function always succeeds and returns 0. + */ +-int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame); ++void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame); + + /* + * Unpacks PRIORITY wire format into |frame|. +@@ -171,11 +168,9 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. +- * +- * This function always succeeds and returns 0. + */ +-int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, +- nghttp2_rst_stream *frame); ++void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, ++ nghttp2_rst_stream *frame); + + /* + * Unpacks RST_STREAM frame byte sequence into |frame|. +@@ -264,15 +259,9 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This + * function only unapcks bytes that come before name/value header + * block and after possible Pad Length field. +- * +- * This function returns 0 if it succeeds or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_PROTO +- * TODO END_HEADERS flag is not set + */ +-int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, +- const uint8_t *payload); ++void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, ++ const uint8_t *payload); + + /* + * Packs PING frame |frame| in wire format and store it in +@@ -280,10 +269,8 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. +- * +- * This function always succeeds and returns 0. + */ +-int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); ++void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); + + /* + * Unpacks PING wire format into |frame|. +@@ -342,11 +329,9 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. +- * +- * This function always succeeds and returns 0. + */ +-int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, +- nghttp2_window_update *frame); ++void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, ++ nghttp2_window_update *frame); + + /* + * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. +@@ -360,17 +345,13 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. +- * +- * This function always succeeds and returns 0. + */ +-int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext); ++void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext); + + /* + * Unpacks ALTSVC wire format into |frame|. The |payload| of + * |payloadlen| bytes contains frame payload. This function assumes + * that frame->payload points to the nghttp2_ext_altsvc object. +- * +- * This function always succeeds and returns 0. + */ + void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, + size_t origin_len, uint8_t *payload, +@@ -423,6 +404,27 @@ int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext); + int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); ++ ++/* ++ * Packs PRIORITY_UPDATE frame |frame| in wire frame format and store ++ * it in |bufs|. ++ * ++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called ++ * before calling this function. ++ */ ++void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, ++ nghttp2_extension *ext); ++ ++/* ++ * Unpacks PRIORITY_UPDATE wire format into |frame|. The |payload| of ++ * |payloadlen| bytes contains frame payload. This function assumes ++ * that frame->payload points to the nghttp2_ext_priority_update ++ * object. ++ */ ++void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, ++ uint8_t *payload, ++ size_t payloadlen); ++ + /* + * Initializes HEADERS frame |frame| with given values. |frame| takes + * ownership of |nva|, so caller must not free it. If |stream_id| is +@@ -538,6 +540,25 @@ void nghttp2_frame_origin_init(nghttp2_extension *frame, + */ + void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem); + ++/* ++ * Initializes PRIORITY_UPDATE frame |frame| with given values. This ++ * function assumes that frame->payload points to ++ * nghttp2_ext_priority_update object. On success, this function ++ * takes ownership of |field_value|, so caller must not free it. ++ */ ++void nghttp2_frame_priority_update_init(nghttp2_extension *frame, ++ int32_t stream_id, uint8_t *field_value, ++ size_t field_value_len); ++ ++/* ++ * Frees up resources under |frame|. This function does not free ++ * nghttp2_ext_priority_update object pointed by frame->payload. This ++ * function only frees field_value pointed by ++ * nghttp2_ext_priority_update.field_value. ++ */ ++void nghttp2_frame_priority_update_free(nghttp2_extension *frame, ++ nghttp2_mem *mem); ++ + /* + * Returns the number of padding bytes after payload. The total + * padding length is given in the |padlen|. The returned value does +@@ -609,16 +630,8 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); + * |padlen| including Pad Length field. The |hd| is the frame header + * for the serialized data. This function fills zeros padding region + * unless framehd_only is nonzero. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory. +- * NGHTTP2_ERR_FRAME_SIZE_ERROR +- * The length of the resulting frame is too large. + */ +-int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, +- size_t padlen, int framehd_only); ++void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, ++ size_t padlen, int framehd_only); + + #endif /* NGHTTP2_FRAME_H */ +diff --git a/deps/nghttp2/lib/nghttp2_hd.c b/deps/nghttp2/lib/nghttp2_hd.c +index 30ee9b8..8a2bda6 100644 +--- a/deps/nghttp2/lib/nghttp2_hd.c ++++ b/deps/nghttp2/lib/nghttp2_hd.c +@@ -269,6 +269,11 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) { + return NGHTTP2_TOKEN_LOCATION; + } + break; ++ case 'y': ++ if (memeq("priorit", name, 7)) { ++ return NGHTTP2_TOKEN_PRIORITY; ++ } ++ break; + } + break; + case 9: +diff --git a/deps/nghttp2/lib/nghttp2_hd.h b/deps/nghttp2/lib/nghttp2_hd.h +index 2674028..6de0052 100644 +--- a/deps/nghttp2/lib/nghttp2_hd.h ++++ b/deps/nghttp2/lib/nghttp2_hd.h +@@ -112,6 +112,7 @@ typedef enum { + NGHTTP2_TOKEN_PROXY_CONNECTION, + NGHTTP2_TOKEN_UPGRADE, + NGHTTP2_TOKEN__PROTOCOL, ++ NGHTTP2_TOKEN_PRIORITY, + } nghttp2_token; + + struct nghttp2_hd_entry; +diff --git a/deps/nghttp2/lib/nghttp2_helper.c b/deps/nghttp2/lib/nghttp2_helper.c +index 588e269..93dd475 100644 +--- a/deps/nghttp2/lib/nghttp2_helper.c ++++ b/deps/nghttp2/lib/nghttp2_helper.c +@@ -507,6 +507,19 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) { + return 1; + } + ++int nghttp2_check_header_value_rfc9113(const uint8_t *value, size_t len) { ++ if (len == 0) { ++ return 1; ++ } ++ ++ if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' || ++ *(value + len - 1) == '\t') { ++ return 0; ++ } ++ ++ return nghttp2_check_header_value(value, len); ++} ++ + /* Generated by genmethodchartbl.py */ + static char VALID_METHOD_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, +diff --git a/deps/nghttp2/lib/nghttp2_http.c b/deps/nghttp2/lib/nghttp2_http.c +index a2bcd2c..ecdeb21 100644 +--- a/deps/nghttp2/lib/nghttp2_http.c ++++ b/deps/nghttp2/lib/nghttp2_http.c +@@ -30,6 +30,8 @@ + + #include "nghttp2_hd.h" + #include "nghttp2_helper.h" ++#include "nghttp2_extpri.h" ++#include "sfparse.h" + + static uint8_t downcase(uint8_t c) { + return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; +@@ -72,25 +74,12 @@ static int64_t parse_uint(const uint8_t *s, size_t len) { + return n; + } + +-static int lws(const uint8_t *s, size_t n) { +- size_t i; +- for (i = 0; i < n; ++i) { +- if (s[i] != ' ' && s[i] != '\t') { +- return 0; +- } +- } +- return 1; +-} +- + static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv, +- int flag) { +- if (stream->http_flags & flag) { +- return 0; +- } +- if (lws(nv->value->base, nv->value->len)) { ++ uint32_t flag) { ++ if ((stream->http_flags & flag) || nv->value->len == 0) { + return 0; + } +- stream->http_flags = (uint16_t)(stream->http_flags | flag); ++ stream->http_flags = stream->http_flags | flag; + return 1; + } + +@@ -114,6 +103,8 @@ static int check_path(nghttp2_stream *stream) { + + static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, + int trailer, int connect_protocol) { ++ nghttp2_extpri extpri; ++ + if (nv->name->base[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { +@@ -212,6 +203,23 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, + return NGHTTP2_ERR_HTTP_HEADER; + } + break; ++ case NGHTTP2_TOKEN_PRIORITY: ++ 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, ++ nv->value->len) == 0) { ++ stream->http_extpri = nghttp2_extpri_to_uint8(&extpri); ++ stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY; ++ } else { ++ stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY; ++ stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY; ++ } ++ } ++ break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; +@@ -329,6 +337,16 @@ static int check_scheme(const uint8_t *value, size_t len) { + return 1; + } + ++static int lws(const uint8_t *s, size_t n) { ++ size_t i; ++ for (i = 0; i < n; ++i) { ++ if (s[i] != ' ' && s[i] != '\t') { ++ return 0; ++ } ++ } ++ return 1; ++} ++ + int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_hd_nv *nv, + int trailer) { +@@ -369,13 +387,37 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + break; + case NGHTTP2_TOKEN__AUTHORITY: + case NGHTTP2_TOKEN_HOST: +- rv = nghttp2_check_authority(nv->value->base, nv->value->len); ++ if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { ++ rv = nghttp2_check_authority(nv->value->base, nv->value->len); ++ } else if ( ++ stream->flags & ++ NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { ++ rv = nghttp2_check_header_value(nv->value->base, nv->value->len); ++ } else { ++ rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len); ++ } + break; + case NGHTTP2_TOKEN__SCHEME: + rv = check_scheme(nv->value->base, nv->value->len); + break; ++ case NGHTTP2_TOKEN__PROTOCOL: ++ /* Check the value consists of just white spaces, which was done ++ in check_pseudo_header before ++ nghttp2_check_header_value_rfc9113 has been introduced. */ ++ if ((stream->flags & ++ NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) && ++ lws(nv->value->base, nv->value->len)) { ++ rv = 0; ++ break; ++ } ++ /* fall through */ + default: +- rv = nghttp2_check_header_value(nv->value->base, nv->value->len); ++ if (stream->flags & ++ NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { ++ rv = nghttp2_check_header_value(nv->value->base, nv->value->len); ++ } else { ++ rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len); ++ } + } + + if (rv == 0) { +@@ -443,16 +485,15 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) { + + if (stream->status_code / 100 == 1) { + /* non-final response */ +- stream->http_flags = +- (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | +- NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); ++ stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | ++ NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; + stream->content_length = -1; + stream->status_code = -1; + return 0; + } + + stream->http_flags = +- (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); ++ stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; + + if (!expect_response_body(stream)) { + stream->content_length = 0; +@@ -537,3 +578,54 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream, + return; + } + } ++ ++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; ++ int rv; ++ ++ sf_parser_init(&sfp, value, valuelen); ++ ++ for (;;) { ++ rv = sf_parser_dict(&sfp, &key, &val); ++ if (rv != 0) { ++ if (rv == SF_ERR_EOF) { ++ break; ++ } ++ ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ ++ if (key.len != 1) { ++ continue; ++ } ++ ++ switch (key.base[0]) { ++ case 'i': ++ if (val.type != SF_TYPE_BOOLEAN) { ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ ++ pri.inc = val.boolean; ++ ++ break; ++ case 'u': ++ if (val.type != SF_TYPE_INTEGER || ++ val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH || ++ NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) { ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ ++ pri.urgency = (uint32_t)val.integer; ++ ++ break; ++ } ++ } ++ ++ *dest = pri; ++ ++ return 0; ++} +diff --git a/deps/nghttp2/lib/nghttp2_http.h b/deps/nghttp2/lib/nghttp2_http.h +index dd057cd..d9992fe 100644 +--- a/deps/nghttp2/lib/nghttp2_http.h ++++ b/deps/nghttp2/lib/nghttp2_http.h +@@ -94,4 +94,7 @@ int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); + void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame); + ++int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, ++ size_t valuelen); ++ + #endif /* NGHTTP2_HTTP_H */ +diff --git a/deps/nghttp2/lib/nghttp2_map.c b/deps/nghttp2/lib/nghttp2_map.c +index e5db168..5f63fc2 100644 +--- a/deps/nghttp2/lib/nghttp2_map.c ++++ b/deps/nghttp2/lib/nghttp2_map.c +@@ -31,21 +31,14 @@ + + #include "nghttp2_helper.h" + +-#define NGHTTP2_INITIAL_TABLE_LENBITS 8 ++#define NGHTTP2_INITIAL_TABLE_LENBITS 4 + +-int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { ++void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { + map->mem = mem; +- map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS; +- map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS; +- map->table = +- nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket)); +- if (map->table == NULL) { +- return NGHTTP2_ERR_NOMEM; +- } +- ++ map->tablelen = 0; ++ map->tablelenbits = 0; ++ map->table = NULL; + map->size = 0; +- +- return 0; + } + + void nghttp2_map_free(nghttp2_map *map) { +@@ -78,6 +71,10 @@ int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr), + uint32_t i; + nghttp2_map_bucket *bkt; + ++ if (map->size == 0) { ++ return 0; ++ } ++ + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + +@@ -223,9 +220,17 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { + + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { +- rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); +- if (rv != 0) { +- return rv; ++ if (map->tablelen) { ++ rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); ++ if (rv != 0) { ++ return rv; ++ } ++ } else { ++ rv = map_resize(map, 1 << NGHTTP2_INITIAL_TABLE_LENBITS, ++ NGHTTP2_INITIAL_TABLE_LENBITS); ++ if (rv != 0) { ++ return rv; ++ } + } + } + +@@ -239,11 +244,18 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { + } + + void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) { +- uint32_t h = hash(key); +- size_t idx = h2idx(h, map->tablelenbits); ++ uint32_t h; ++ size_t idx; + nghttp2_map_bucket *bkt; + size_t d = 0; + ++ if (map->size == 0) { ++ return NULL; ++ } ++ ++ h = hash(key); ++ idx = h2idx(h, map->tablelenbits); ++ + for (;;) { + bkt = &map->table[idx]; + +@@ -262,11 +274,18 @@ void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) { + } + + int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { +- uint32_t h = hash(key); +- size_t idx = h2idx(h, map->tablelenbits), didx; ++ uint32_t h; ++ size_t idx, didx; + nghttp2_map_bucket *bkt; + size_t d = 0; + ++ if (map->size == 0) { ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ ++ h = hash(key); ++ idx = h2idx(h, map->tablelenbits); ++ + for (;;) { + bkt = &map->table[idx]; + +@@ -306,6 +325,10 @@ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { + } + + void nghttp2_map_clear(nghttp2_map *map) { ++ if (map->tablelen == 0) { ++ return; ++ } ++ + memset(map->table, 0, sizeof(*map->table) * map->tablelen); + map->size = 0; + } +diff --git a/deps/nghttp2/lib/nghttp2_map.h b/deps/nghttp2/lib/nghttp2_map.h +index 1419a09..d90245a 100644 +--- a/deps/nghttp2/lib/nghttp2_map.h ++++ b/deps/nghttp2/lib/nghttp2_map.h +@@ -54,14 +54,8 @@ typedef struct nghttp2_map { + + /* + * Initializes the map |map|. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory + */ +-int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); ++void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); + + /* + * Deallocates any resources allocated for |map|. The stored entries +diff --git a/deps/nghttp2/lib/nghttp2_net.h b/deps/nghttp2/lib/nghttp2_net.h +index 582099b..521f981 100644 +--- a/deps/nghttp2/lib/nghttp2_net.h ++++ b/deps/nghttp2/lib/nghttp2_net.h +@@ -53,7 +53,7 @@ + STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; +- *p++ = hostlong >> 24; ++ *p++ = (unsigned char)(hostlong >> 24); + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; +@@ -63,7 +63,7 @@ STIN uint32_t htonl(uint32_t hostlong) { + STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; +- *p++ = hostshort >> 8; ++ *p++ = (unsigned char)(hostshort >> 8); + *p = hostshort & 0xffu; + return res; + } +@@ -71,9 +71,9 @@ STIN uint16_t htons(uint16_t hostshort) { + STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; +- res = *p++ << 24; +- res += *p++ << 16; +- res += *p++ << 8; ++ res = (uint32_t)(*p++ << 24); ++ res += (uint32_t)(*p++ << 16); ++ res += (uint32_t)(*p++ << 8); + res += *p; + return res; + } +@@ -81,7 +81,7 @@ STIN uint32_t ntohl(uint32_t netlong) { + STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; +- res = *p++ << 8; ++ res = (uint16_t)(*p++ << 8); + res += *p; + return res; + } +diff --git a/deps/nghttp2/lib/nghttp2_option.c b/deps/nghttp2/lib/nghttp2_option.c +index 34348e6..43d4e95 100644 +--- a/deps/nghttp2/lib/nghttp2_option.c ++++ b/deps/nghttp2/lib/nghttp2_option.c +@@ -90,6 +90,10 @@ void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, + option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; + option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN; + return; ++ case NGHTTP2_PRIORITY_UPDATE: ++ option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; ++ option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_PRIORITY_UPDATE; ++ return; + default: + return; + } +@@ -126,3 +130,23 @@ void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS; + option->max_settings = val; + } ++ ++void nghttp2_option_set_server_fallback_rfc7540_priorities( ++ nghttp2_option *option, int val) { ++ option->opt_set_mask |= NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES; ++ option->server_fallback_rfc7540_priorities = val; ++} ++ ++void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( ++ nghttp2_option *option, int val) { ++ option->opt_set_mask |= ++ NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; ++ option->no_rfc9113_leading_and_trailing_ws_validation = val; ++} ++ ++void nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, ++ uint64_t burst, uint64_t rate) { ++ option->opt_set_mask |= NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT; ++ option->stream_reset_burst = burst; ++ option->stream_reset_rate = rate; ++} +diff --git a/deps/nghttp2/lib/nghttp2_option.h b/deps/nghttp2/lib/nghttp2_option.h +index 939729f..2259e18 100644 +--- a/deps/nghttp2/lib/nghttp2_option.h ++++ b/deps/nghttp2/lib/nghttp2_option.h +@@ -68,12 +68,20 @@ typedef enum { + NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, + NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11, + NGHTTP2_OPT_MAX_SETTINGS = 1 << 12, ++ NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 13, ++ NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14, ++ NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT = 1 << 15, + } nghttp2_option_flag; + + /** + * Struct to store option values for nghttp2_session. + */ + struct nghttp2_option { ++ /** ++ * NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT ++ */ ++ uint64_t stream_reset_burst; ++ uint64_t stream_reset_rate; + /** + * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH + */ +@@ -127,6 +135,14 @@ struct nghttp2_option { + * NGHTTP2_OPT_NO_CLOSED_STREAMS + */ + int no_closed_streams; ++ /** ++ * NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES ++ */ ++ int server_fallback_rfc7540_priorities; ++ /** ++ * NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION ++ */ ++ int no_rfc9113_leading_and_trailing_ws_validation; + /** + * NGHTTP2_OPT_USER_RECV_EXT_TYPES + */ +diff --git a/deps/nghttp2/lib/nghttp2_outbound_item.c b/deps/nghttp2/lib/nghttp2_outbound_item.c +index f651c80..2a3041d 100644 +--- a/deps/nghttp2/lib/nghttp2_outbound_item.c ++++ b/deps/nghttp2/lib/nghttp2_outbound_item.c +@@ -89,6 +89,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { + case NGHTTP2_ORIGIN: + nghttp2_frame_origin_free(&frame->ext, mem); + break; ++ case NGHTTP2_PRIORITY_UPDATE: ++ nghttp2_frame_priority_update_free(&frame->ext, mem); ++ break; + default: + assert(0); + break; +diff --git a/deps/nghttp2/lib/nghttp2_pq.c b/deps/nghttp2/lib/nghttp2_pq.c +index bebccc7..64353ac 100644 +--- a/deps/nghttp2/lib/nghttp2_pq.c ++++ b/deps/nghttp2/lib/nghttp2_pq.c +@@ -29,13 +29,12 @@ + + #include "nghttp2_helper.h" + +-int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { ++void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { + pq->mem = mem; + pq->capacity = 0; + pq->q = NULL; + pq->length = 0; + pq->less = less; +- return 0; + } + + void nghttp2_pq_free(nghttp2_pq *pq) { +diff --git a/deps/nghttp2/lib/nghttp2_pq.h b/deps/nghttp2/lib/nghttp2_pq.h +index 7b7b739..c8d90ef 100644 +--- a/deps/nghttp2/lib/nghttp2_pq.h ++++ b/deps/nghttp2/lib/nghttp2_pq.h +@@ -55,14 +55,8 @@ typedef struct { + + /* + * Initializes priority queue |pq| with compare function |cmp|. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory. + */ +-int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); ++void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); + + /* + * Deallocates any resources allocated for |pq|. The stored items are +diff --git a/deps/nghttp2/lib/nghttp2_ratelim.c b/deps/nghttp2/lib/nghttp2_ratelim.c +new file mode 100644 +index 0000000..7011655 +--- /dev/null ++++ b/deps/nghttp2/lib/nghttp2_ratelim.c +@@ -0,0 +1,75 @@ ++/* ++ * nghttp2 - HTTP/2 C Library ++ * ++ * Copyright (c) 2023 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#include "nghttp2_ratelim.h" ++#include "nghttp2_helper.h" ++ ++void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate) { ++ rl->val = rl->burst = burst; ++ rl->rate = rate; ++ rl->tstamp = 0; ++} ++ ++void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp) { ++ uint64_t d, gain; ++ ++ if (tstamp == rl->tstamp) { ++ return; ++ } ++ ++ if (tstamp > rl->tstamp) { ++ d = tstamp - rl->tstamp; ++ } else { ++ d = 1; ++ } ++ ++ rl->tstamp = tstamp; ++ ++ if (UINT64_MAX / d < rl->rate) { ++ rl->val = rl->burst; ++ ++ return; ++ } ++ ++ gain = rl->rate * d; ++ ++ if (UINT64_MAX - gain < rl->val) { ++ rl->val = rl->burst; ++ ++ return; ++ } ++ ++ rl->val += gain; ++ rl->val = nghttp2_min(rl->val, rl->burst); ++} ++ ++int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n) { ++ if (rl->val < n) { ++ return -1; ++ } ++ ++ rl->val -= n; ++ ++ return 0; ++} +diff --git a/deps/nghttp2/lib/nghttp2_ratelim.h b/deps/nghttp2/lib/nghttp2_ratelim.h +new file mode 100644 +index 0000000..866ed3f +--- /dev/null ++++ b/deps/nghttp2/lib/nghttp2_ratelim.h +@@ -0,0 +1,57 @@ ++/* ++ * nghttp2 - HTTP/2 C Library ++ * ++ * Copyright (c) 2023 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#ifndef NGHTTP2_RATELIM_H ++#define NGHTTP2_RATELIM_H ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++ ++#include ++ ++typedef struct nghttp2_ratelim { ++ /* burst is the maximum value of val. */ ++ uint64_t burst; ++ /* rate is the amount of value that is regenerated per 1 tstamp. */ ++ uint64_t rate; ++ /* val is the amount of value available to drain. */ ++ uint64_t val; ++ /* tstamp is the last timestamp in second resolution that is known ++ to this object. */ ++ uint64_t tstamp; ++} nghttp2_ratelim; ++ ++/* nghttp2_ratelim_init initializes |rl| with the given parameters. */ ++void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate); ++ ++/* nghttp2_ratelim_update updates rl->val with the current |tstamp| ++ given in second resolution. */ ++void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp); ++ ++/* nghttp2_ratelim_drain drains |n| from rl->val. It returns 0 if it ++ succeeds, or -1. */ ++int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n); ++ ++#endif /* NGHTTP2_RATELIM_H */ +diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c +index 380a47c..ec5024d 100644 +--- a/deps/nghttp2/lib/nghttp2_session.c ++++ b/deps/nghttp2/lib/nghttp2_session.c +@@ -36,6 +36,8 @@ + #include "nghttp2_option.h" + #include "nghttp2_http.h" + #include "nghttp2_pq.h" ++#include "nghttp2_extpri.h" ++#include "nghttp2_time.h" + #include "nghttp2_debug.h" + + /* +@@ -143,6 +145,11 @@ 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; + } +@@ -354,6 +361,14 @@ static void session_inbound_frame_reset(nghttp2_session *session) { + } + nghttp2_frame_origin_free(&iframe->frame.ext, mem); + break; ++ case NGHTTP2_PRIORITY_UPDATE: ++ if ((session->builtin_recv_ext_types & ++ NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) { ++ break; ++ } ++ /* Do not call nghttp2_frame_priority_update_free, because all ++ fields point to sbuf. */ ++ break; + } + } + +@@ -385,6 +400,7 @@ static void init_settings(nghttp2_settings_storage *settings) { + settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; + settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; + settings->max_header_list_size = UINT32_MAX; ++ settings->no_rfc7540_priorities = UINT32_MAX; + } + + static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, +@@ -398,6 +414,21 @@ static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, + aob->state = NGHTTP2_OB_POP_ITEM; + } + ++#define NGHTTP2_STREAM_MAX_CYCLE_GAP ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX) ++ ++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_STREAM_MAX_CYCLE_GAP; ++} ++ + int nghttp2_enable_strict_preface = 1; + + static int session_new(nghttp2_session **session_ptr, +@@ -408,6 +439,7 @@ static int session_new(nghttp2_session **session_ptr, + size_t nbuffer; + size_t max_deflate_dynamic_table_size = + NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; ++ size_t i; + + if (mem == NULL) { + mem = nghttp2_mem_default(); +@@ -442,6 +474,11 @@ static int session_new(nghttp2_session **session_ptr, + (*session_ptr)->pending_local_max_concurrent_stream = + NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; + (*session_ptr)->pending_enable_push = 1; ++ (*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX; ++ ++ nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim, ++ NGHTTP2_DEFAULT_STREAM_RESET_BURST, ++ NGHTTP2_DEFAULT_STREAM_RESET_RATE); + + if (server) { + (*session_ptr)->server = 1; +@@ -527,6 +564,26 @@ static int session_new(nghttp2_session **session_ptr, + option->max_settings) { + (*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) { ++ (*session_ptr)->opt_flags |= ++ NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; ++ } ++ ++ if (option->opt_set_mask & NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT) { ++ nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim, ++ option->stream_reset_burst, ++ option->stream_reset_rate); ++ } + } + + rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, +@@ -538,10 +595,6 @@ static int session_new(nghttp2_session **session_ptr, + if (rv != 0) { + goto fail_hd_inflater; + } +- rv = nghttp2_map_init(&(*session_ptr)->streams, mem); +- if (rv != 0) { +- goto fail_map; +- } + + nbuffer = ((*session_ptr)->max_send_header_block_length + + NGHTTP2_FRAMEBUF_CHUNKLEN - 1) / +@@ -559,6 +612,8 @@ static int session_new(nghttp2_session **session_ptr, + goto fail_aob_framebuf; + } + ++ nghttp2_map_init(&(*session_ptr)->streams, mem); ++ + active_outbound_item_reset(&(*session_ptr)->aob, mem); + + (*session_ptr)->callbacks = *callbacks; +@@ -584,11 +639,13 @@ static int session_new(nghttp2_session **session_ptr, + } + } + ++ for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { ++ nghttp2_pq_init(&(*session_ptr)->sched[i].ob_data, stream_less, mem); ++ } ++ + return 0; + + fail_aob_framebuf: +- nghttp2_map_free(&(*session_ptr)->streams); +-fail_map: + nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); + fail_hd_inflater: + nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); +@@ -735,6 +792,7 @@ static void inflight_settings_del(nghttp2_inflight_settings *settings, + void nghttp2_session_del(nghttp2_session *session) { + nghttp2_mem *mem; + nghttp2_inflight_settings *settings; ++ size_t i; + + if (session == NULL) { + return; +@@ -748,6 +806,9 @@ void nghttp2_session_del(nghttp2_session *session) { + settings = next; + } + ++ 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 +@@ -775,6 +836,8 @@ int nghttp2_session_reprioritize_stream( + 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)) { +@@ -842,6 +905,202 @@ int nghttp2_session_reprioritize_stream( + return 0; + } + ++static uint64_t pq_get_first_cycle(nghttp2_pq *pq) { ++ nghttp2_stream *stream; ++ ++ if (nghttp2_pq_empty(pq)) { ++ return 0; ++ } ++ ++ stream = nghttp2_struct_of(nghttp2_pq_top(pq), nghttp2_stream, pq_entry); ++ return stream->cycle; ++} ++ ++static int session_ob_data_push(nghttp2_session *session, ++ nghttp2_stream *stream) { ++ int rv; ++ uint32_t urgency; ++ 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); ++ inc = nghttp2_extpri_uint8_inc(stream->extpri); ++ ++ assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); ++ ++ pq = &session->sched[urgency].ob_data; ++ ++ stream->cycle = pq_get_first_cycle(pq); ++ if (inc) { ++ stream->cycle += stream->last_writelen; ++ } ++ ++ rv = nghttp2_pq_push(pq, &stream->pq_entry); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ stream->queued = 1; ++ ++ return 0; ++} ++ ++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); ++ ++ assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); ++ ++ nghttp2_pq_remove(&session->sched[urgency].ob_data, &stream->pq_entry); ++ ++ stream->queued = 0; ++} ++ ++static int session_attach_stream_item(nghttp2_session *session, ++ nghttp2_stream *stream, ++ 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; ++ } ++ ++ return session_ob_data_push(session, stream); ++} ++ ++static void session_detach_stream_item(nghttp2_session *session, ++ nghttp2_stream *stream) { ++ nghttp2_stream_detach_item(stream); ++ ++ if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || ++ !stream->queued) { ++ return; ++ } ++ ++ session_ob_data_remove(session, stream); ++} ++ ++static void session_defer_stream_item(nghttp2_session *session, ++ nghttp2_stream *stream, uint8_t flags) { ++ nghttp2_stream_defer_item(stream, flags); ++ ++ if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || ++ !stream->queued) { ++ return; ++ } ++ ++ session_ob_data_remove(session, stream); ++} ++ ++static int session_resume_deferred_stream_item(nghttp2_session *session, ++ nghttp2_stream *stream, ++ uint8_t flags) { ++ int rv; ++ ++ 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)) { ++ return 0; ++ } ++ ++ return session_ob_data_push(session, stream); ++} ++ ++static nghttp2_outbound_item * ++session_sched_get_next_outbound_item(nghttp2_session *session) { ++ size_t i; ++ nghttp2_pq_entry *ent; ++ nghttp2_stream *stream; ++ ++ for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { ++ ent = nghttp2_pq_top(&session->sched[i].ob_data); ++ if (!ent) { ++ continue; ++ } ++ ++ stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry); ++ return stream->item; ++ } ++ ++ return NULL; ++} ++ ++static int session_sched_empty(nghttp2_session *session) { ++ size_t i; ++ ++ for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { ++ if (!nghttp2_pq_empty(&session->sched[i].ob_data)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++static void session_sched_reschedule_stream(nghttp2_session *session, ++ nghttp2_stream *stream) { ++ nghttp2_pq *pq; ++ uint32_t urgency = nghttp2_extpri_uint8_urgency(stream->extpri); ++ int inc = nghttp2_extpri_uint8_inc(stream->extpri); ++ uint64_t penalty = (uint64_t)stream->last_writelen; ++ int rv; ++ ++ (void)rv; ++ ++ assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); ++ ++ pq = &session->sched[urgency].ob_data; ++ ++ if (!inc || nghttp2_pq_size(pq) == 1) { ++ return; ++ } ++ ++ nghttp2_pq_remove(pq, &stream->pq_entry); ++ ++ stream->cycle += penalty; ++ ++ rv = nghttp2_pq_push(pq, &stream->pq_entry); ++ ++ assert(0 == rv); ++} ++ ++static int session_update_stream_priority(nghttp2_session *session, ++ nghttp2_stream *stream, ++ uint8_t u8extpri) { ++ if (stream->extpri == u8extpri) { ++ return 0; ++ } ++ ++ if (stream->queued) { ++ session_ob_data_remove(session, stream); ++ ++ stream->extpri = u8extpri; ++ ++ return session_ob_data_push(session, stream); ++ } ++ ++ stream->extpri = u8extpri; ++ ++ return 0; ++} ++ + int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item) { + /* TODO Return error if stream is not found for the frame requiring +@@ -863,7 +1122,7 @@ int nghttp2_session_add_item(nghttp2_session *session, + return NGHTTP2_ERR_DATA_EXIST; + } + +- rv = nghttp2_stream_attach_item(stream, item); ++ rv = session_attach_stream_item(session, stream, item); + + if (rv != 0) { + return rv; +@@ -1039,13 +1298,27 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + mem = &session->mem; + stream = nghttp2_session_get_stream_raw(session, stream_id); + ++ if (session->opt_flags & ++ NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { ++ flags |= NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; ++ } ++ + if (stream) { + assert(stream->state == NGHTTP2_STREAM_IDLE); +- assert(nghttp2_stream_in_dep_tree(stream)); +- nghttp2_session_detach_idle_stream(session, stream); +- rv = nghttp2_stream_dep_remove(stream); +- if (rv != 0) { +- return NULL; ++ assert((stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || ++ nghttp2_stream_in_dep_tree(stream)); ++ ++ if (nghttp2_stream_in_dep_tree(stream)) { ++ assert(!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)); ++ nghttp2_session_detach_idle_stream(session, stream); ++ 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; ++ } + } + } else { + stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); +@@ -1056,7 +1329,21 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + stream_alloc = 1; + } + +- if (pri_spec->stream_id != 0) { ++ 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 && +@@ -1102,6 +1389,10 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + (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++; ++ } ++ + rv = nghttp2_map_insert(&session->streams, stream_id, stream); + if (rv != 0) { + nghttp2_stream_free(stream); +@@ -1141,6 +1432,10 @@ 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; + } +@@ -1180,11 +1475,7 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + + item = stream->item; + +- rv = nghttp2_stream_detach_item(stream); +- +- if (rv != 0) { +- return rv; +- } ++ session_detach_stream_item(session, stream); + + /* If item is queued, it will be deleted when it is popped + (nghttp2_session_prep_frame() will fail). If session->aob.item +@@ -1230,6 +1521,10 @@ 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)) { +@@ -1784,6 +2079,28 @@ static int session_predicate_origin_send(nghttp2_session *session) { + return 0; + } + ++static int session_predicate_priority_update_send(nghttp2_session *session, ++ int32_t stream_id) { ++ nghttp2_stream *stream; ++ ++ if (session_is_closing(session)) { ++ return NGHTTP2_ERR_SESSION_CLOSING; ++ } ++ ++ stream = nghttp2_session_get_stream(session, stream_id); ++ if (stream == NULL) { ++ return 0; ++ } ++ if (stream->state == NGHTTP2_STREAM_CLOSING) { ++ return NGHTTP2_ERR_STREAM_CLOSING; ++ } ++ if (stream->shut_flags & NGHTTP2_SHUT_RD) { ++ return NGHTTP2_ERR_INVALID_STREAM_STATE; ++ } ++ ++ return 0; ++} ++ + /* Take into account settings max frame size and both connection-level + flow control here */ + static ssize_t +@@ -1899,7 +2216,6 @@ static ssize_t session_call_select_padding(nghttp2_session *session, + frame->push_promise has also padlen in the same position. */ + static int session_headers_add_pad(nghttp2_session *session, + nghttp2_frame *frame) { +- int rv; + ssize_t padded_payloadlen; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; +@@ -1924,11 +2240,7 @@ static int session_headers_add_pad(nghttp2_session *session, + DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n", + padded_payloadlen, padlen); + +- rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); +- +- if (rv != 0) { +- return rv; +- } ++ nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); + + frame->headers.padlen = padlen; + +@@ -2011,13 +2323,7 @@ static int session_prep_frame(nghttp2_session *session, + // Search stream including closed again. + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + if (stream) { +- int rv2; +- +- rv2 = nghttp2_stream_detach_item(stream); +- +- if (nghttp2_is_fatal(rv2)) { +- return rv2; +- } ++ session_detach_stream_item(session, stream); + } + + return rv; +@@ -2032,12 +2338,8 @@ static int session_prep_frame(nghttp2_session *session, + queue when session->remote_window_size > 0 */ + assert(session->remote_window_size > 0); + +- rv = nghttp2_stream_defer_item(stream, +- NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ session_defer_stream_item(session, stream, ++ NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); +@@ -2051,22 +2353,15 @@ static int session_prep_frame(nghttp2_session *session, + return rv; + } + if (rv == NGHTTP2_ERR_DEFERRED) { +- rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ session_defer_stream_item(session, stream, ++ NGHTTP2_STREAM_FLAG_DEFERRED_USER); + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +- rv = nghttp2_stream_detach_item(stream); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ session_detach_stream_item(session, stream); + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); +@@ -2076,13 +2371,7 @@ static int session_prep_frame(nghttp2_session *session, + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (rv != 0) { +- int rv2; +- +- rv2 = nghttp2_stream_detach_item(stream); +- +- if (nghttp2_is_fatal(rv2)) { +- return rv2; +- } ++ session_detach_stream_item(session, stream); + + return rv; + } +@@ -2328,6 +2617,18 @@ static int session_prep_frame(nghttp2_session *session, + } + + return 0; ++ case NGHTTP2_PRIORITY_UPDATE: { ++ nghttp2_ext_priority_update *priority_update = frame->ext.payload; ++ rv = session_predicate_priority_update_send(session, ++ priority_update->stream_id); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ nghttp2_frame_pack_priority_update(&session->aob.framebufs, &frame->ext); ++ ++ return 0; ++ } + default: + /* Unreachable here */ + assert(0); +@@ -2339,6 +2640,8 @@ 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); + } +@@ -2354,7 +2657,12 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) { + } + + if (session->remote_window_size > 0) { +- return nghttp2_stream_next_outbound_item(&session->root); ++ item = nghttp2_stream_next_outbound_item(&session->root); ++ if (item) { ++ return item; ++ } ++ ++ return session_sched_get_next_outbound_item(session); + } + + return NULL; +@@ -2388,7 +2696,12 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) { + } + + if (session->remote_window_size > 0) { +- return nghttp2_stream_next_outbound_item(&session->root); ++ item = nghttp2_stream_next_outbound_item(&session->root); ++ if (item) { ++ return item; ++ } ++ ++ return session_sched_get_next_outbound_item(session); + } + + return NULL; +@@ -2498,10 +2811,20 @@ static int session_close_stream_on_goaway(nghttp2_session *session, + return 0; + } + +-static void reschedule_stream(nghttp2_stream *stream) { ++static void session_reschedule_stream(nghttp2_session *session, ++ nghttp2_stream *stream) { + stream->last_writelen = stream->item->frame.hd.length; + +- nghttp2_stream_reschedule(stream); ++ if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) { ++ nghttp2_stream_reschedule(stream); ++ return; ++ } ++ ++ if (!session->server) { ++ return; ++ } ++ ++ session_sched_reschedule_stream(session, stream); + } + + static int session_update_stream_consumed_size(nghttp2_session *session, +@@ -2550,10 +2873,7 @@ static int session_after_frame_sent1(nghttp2_session *session) { + } + + if (stream && aux_data->eof) { +- rv = nghttp2_stream_detach_item(stream); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ session_detach_stream_item(session, stream); + + /* Call on_frame_send_callback after + nghttp2_stream_detach_item(), so that application can issue +@@ -2675,9 +2995,8 @@ static int session_after_frame_sent1(nghttp2_session *session) { + } + } + case NGHTTP2_PRIORITY: +- if (session->server) { ++ if (session->server || session->pending_no_rfc7540_priorities == 1) { + return 0; +- ; + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); +@@ -2787,17 +3106,8 @@ static int session_after_frame_sent1(nghttp2_session *session) { + /* + * Called after a frame is sent and session_after_frame_sent1. This + * function is responsible to reset session->aob. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory. +- * NGHTTP2_ERR_CALLBACK_FAILURE +- * The callback function failed. + */ +-static int session_after_frame_sent2(nghttp2_session *session) { +- int rv; ++static void session_after_frame_sent2(nghttp2_session *session) { + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; +@@ -2820,13 +3130,13 @@ static int session_after_frame_sent2(nghttp2_session *session) { + DEBUGF("send: next CONTINUATION frame, %zu bytes\n", + nghttp2_buf_len(&framebufs->cur->buf)); + +- return 0; ++ return; + } + } + + active_outbound_item_reset(&session->aob, mem); + +- return 0; ++ return; + } + + /* DATA frame */ +@@ -2840,7 +3150,7 @@ static int session_after_frame_sent2(nghttp2_session *session) { + if (aux_data->eof) { + active_outbound_item_reset(aob, mem); + +- return 0; ++ return; + } + + /* Reset no_copy here because next write may not use this. */ +@@ -2852,22 +3162,18 @@ static int session_after_frame_sent2(nghttp2_session *session) { + further data. */ + if (nghttp2_session_predicate_data_send(session, stream) != 0) { + if (stream) { +- rv = nghttp2_stream_detach_item(stream); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ session_detach_stream_item(session, stream); + } + + active_outbound_item_reset(aob, mem); + +- return 0; ++ return; + } + + aob->item = NULL; + active_outbound_item_reset(&session->aob, mem); + +- return 0; ++ return; + } + + static int session_call_send_data(nghttp2_session *session, +@@ -2940,6 +3246,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + if (rv < 0) { + int32_t opened_stream_id = 0; + uint32_t error_code = NGHTTP2_INTERNAL_ERROR; ++ int rv2 = 0; + + DEBUGF("send: frame preparation failed with %s\n", + nghttp2_strerror(rv)); +@@ -2982,19 +3289,18 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + } + if (opened_stream_id) { + /* careful not to override rv */ +- int rv2; + rv2 = nghttp2_session_close_stream(session, opened_stream_id, + error_code); +- +- if (nghttp2_is_fatal(rv2)) { +- return rv2; +- } + } + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + active_outbound_item_reset(aob, mem); + ++ if (nghttp2_is_fatal(rv2)) { ++ return rv2; ++ } ++ + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* If header compression error occurred, should terminiate + connection. */ +@@ -3098,7 +3404,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + + /* Frame has completely sent */ + if (fast_cb) { +- rv = session_after_frame_sent2(session); ++ session_after_frame_sent2(session); + } else { + rv = session_after_frame_sent1(session); + if (rv < 0) { +@@ -3106,12 +3412,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + assert(nghttp2_is_fatal(rv)); + return rv; + } +- rv = session_after_frame_sent2(session); +- } +- if (rv < 0) { +- /* FATAL */ +- assert(nghttp2_is_fatal(rv)); +- return rv; ++ session_after_frame_sent2(session); + } + /* We have already adjusted the next state */ + break; +@@ -3150,11 +3451,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +- rv = nghttp2_stream_detach_item(stream); +- +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ session_detach_stream_item(session, stream); + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); +@@ -3178,11 +3475,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + assert(nghttp2_is_fatal(rv)); + return rv; + } +- rv = session_after_frame_sent2(session); +- if (rv < 0) { +- assert(nghttp2_is_fatal(rv)); +- return rv; +- } ++ session_after_frame_sent2(session); + + /* We have already adjusted the next state */ + +@@ -3730,6 +4023,21 @@ static int session_end_stream_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; ++ ++ assert(frame->hd.type == NGHTTP2_HEADERS); ++ ++ 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); ++ if (rv != 0) { ++ assert(nghttp2_is_fatal(rv)); ++ return rv; ++ } ++ } ++ + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return 0; + } +@@ -4053,17 +4361,12 @@ int nghttp2_session_on_headers_received(nghttp2_session *session, + } + + static int session_process_headers_frame(nghttp2_session *session) { +- int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + nghttp2_stream *stream; + +- rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); ++ nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); + +- if (rv != 0) { +- return nghttp2_session_terminate_session_with_reason( +- session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack"); +- } + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + frame->headers.cat = NGHTTP2_HCAT_REQUEST; +@@ -4091,6 +4394,8 @@ int nghttp2_session_on_priority_received(nghttp2_session *session, + 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"); +@@ -4148,11 +4453,30 @@ static int session_process_priority_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + ++ assert(!session_no_rfc7540_pri_no_fallback(session)); ++ + nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos); + + return nghttp2_session_on_priority_received(session, frame); + } + ++static int session_update_stream_reset_ratelim(nghttp2_session *session) { ++ if (!session->server || (session->goaway_flags & NGHTTP2_GOAWAY_SUBMITTED)) { ++ return 0; ++ } ++ ++ nghttp2_ratelim_update(&session->stream_reset_ratelim, ++ nghttp2_time_now_sec()); ++ ++ if (nghttp2_ratelim_drain(&session->stream_reset_ratelim, 1) == 0) { ++ return 0; ++ } ++ ++ return nghttp2_session_add_goaway(session, session->last_recv_stream_id, ++ NGHTTP2_INTERNAL_ERROR, NULL, 0, ++ NGHTTP2_GOAWAY_AUX_NONE); ++} ++ + int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; +@@ -4182,7 +4506,8 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + if (nghttp2_is_fatal(rv)) { + return rv; + } +- return 0; ++ ++ return session_update_stream_reset_ratelim(session); + } + + static int session_process_rst_stream_frame(nghttp2_session *session) { +@@ -4214,8 +4539,8 @@ static int update_remote_initial_window_size_func(void *entry, void *ptr) { + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + +- rv = nghttp2_stream_resume_deferred_item( +- stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); ++ rv = session_resume_deferred_stream_item( ++ arg->session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + + if (nghttp2_is_fatal(rv)) { + return rv; +@@ -4259,9 +4584,16 @@ static int update_local_initial_window_size_func(void *entry, void *ptr) { + return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } +- if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && +- stream->window_update_queued == 0 && +- nghttp2_should_send_window_update(stream->local_window_size, ++ ++ if (stream->window_update_queued) { ++ return 0; ++ } ++ ++ if (arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { ++ return session_update_stream_consumed_size(arg->session, stream, 0); ++ } ++ ++ if (nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + + rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, +@@ -4382,6 +4714,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session, + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + session->local_settings.enable_connect_protocol = iv[i].value; + break; ++ case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: ++ session->local_settings.no_rfc7540_priorities = iv[i].value; ++ break; + } + } + +@@ -4540,10 +4875,38 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, + + session->remote_settings.enable_connect_protocol = entry->value; + ++ break; ++ case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: ++ ++ if (entry->value != 0 && entry->value != 1) { ++ return session_handle_invalid_connection( ++ session, frame, NGHTTP2_ERR_PROTO, ++ "SETTINGS: invalid SETTINGS_NO_RFC7540_PRIORITIES"); ++ } ++ ++ if (session->remote_settings.no_rfc7540_priorities != UINT32_MAX && ++ session->remote_settings.no_rfc7540_priorities != entry->value) { ++ return session_handle_invalid_connection( ++ session, frame, NGHTTP2_ERR_PROTO, ++ "SETTINGS: SETTINGS_NO_RFC7540_PRIORITIES cannot be changed"); ++ } ++ ++ session->remote_settings.no_rfc7540_priorities = entry->value; ++ + break; + } + } + ++ 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)) { + rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); + +@@ -4684,17 +5047,11 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session, + } + + static int session_process_push_promise_frame(nghttp2_session *session) { +- int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + +- rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, +- iframe->sbuf.pos); +- +- if (rv != 0) { +- return nghttp2_session_terminate_session_with_reason( +- session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack"); +- } ++ nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, ++ iframe->sbuf.pos); + + return nghttp2_session_on_push_promise_received(session, frame); + } +@@ -4826,8 +5183,8 @@ static int session_on_stream_window_update_received(nghttp2_session *session, + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + +- rv = nghttp2_stream_resume_deferred_item( +- stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); ++ rv = session_resume_deferred_stream_item( ++ session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + + if (nghttp2_is_fatal(rv)) { + return rv; +@@ -4898,6 +5255,80 @@ int nghttp2_session_on_origin_received(nghttp2_session *session, + return session_call_on_frame_received(session, frame); + } + ++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; ++ ++ assert(session->server); ++ ++ priority_update = frame->ext.payload; ++ ++ if (frame->hd.stream_id != 0) { ++ return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, ++ "PRIORITY_UPDATE: stream_id == 0"); ++ } ++ ++ if (nghttp2_session_is_my_stream_id(session, priority_update->stream_id)) { ++ if (session_detect_idle_stream(session, priority_update->stream_id)) { ++ return session_handle_invalid_connection( ++ session, frame, NGHTTP2_ERR_PROTO, ++ "PRIORITY_UPDATE: prioritizing idle push is not allowed"); ++ } ++ ++ /* TODO Ignore priority signal to a push stream for now */ ++ return session_call_on_frame_received(session, frame); ++ } ++ ++ stream = nghttp2_session_get_stream_raw(session, priority_update->stream_id); ++ if (stream) { ++ /* Stream already exists. */ ++ if (stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) { ++ return session_call_on_frame_received(session, frame); ++ } ++ } else if (session_detect_idle_stream(session, priority_update->stream_id)) { ++ if (session->num_idle_streams + session->num_incoming_streams >= ++ session->local_settings.max_concurrent_streams) { ++ return session_handle_invalid_connection( ++ session, frame, NGHTTP2_ERR_PROTO, ++ "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); ++ if (!stream) { ++ return NGHTTP2_ERR_NOMEM; ++ } ++ } else { ++ return session_call_on_frame_received(session, frame); ++ } ++ ++ extpri.urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY; ++ extpri.inc = 0; ++ ++ rv = nghttp2_http_parse_priority(&extpri, priority_update->field_value, ++ priority_update->field_value_len); ++ if (rv != 0) { ++ /* Just ignore field_value if it cannot be parsed. */ ++ return session_call_on_frame_received(session, frame); ++ } ++ ++ rv = session_update_stream_priority(session, stream, ++ nghttp2_extpri_to_uint8(&extpri)); ++ if (rv != 0) { ++ if (nghttp2_is_fatal(rv)) { ++ return rv; ++ } ++ } ++ ++ return session_call_on_frame_received(session, frame); ++} ++ + static int session_process_altsvc_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; +@@ -4932,6 +5363,16 @@ static int session_process_origin_frame(nghttp2_session *session) { + return nghttp2_session_on_origin_received(session, frame); + } + ++static int session_process_priority_update_frame(nghttp2_session *session) { ++ nghttp2_inbound_frame *iframe = &session->iframe; ++ nghttp2_frame *frame = &iframe->frame; ++ ++ nghttp2_frame_unpack_priority_update_payload(&frame->ext, iframe->sbuf.pos, ++ nghttp2_buf_len(&iframe->sbuf)); ++ ++ return nghttp2_session_on_priority_update_received(session, frame); ++} ++ + static int session_process_extension_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; +@@ -5269,6 +5710,7 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: ++ case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: + break; + default: + DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id); +@@ -5429,7 +5871,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { +- return in - first; ++ return (ssize_t)(in - first); + } + + if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || +@@ -5466,7 +5908,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { +- return in - first; ++ return (ssize_t)(in - first); + } + + nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); +@@ -5882,6 +6324,49 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + + iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD; + ++ break; ++ case NGHTTP2_PRIORITY_UPDATE: ++ if ((session->builtin_recv_ext_types & ++ NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) { ++ busy = 1; ++ iframe->state = NGHTTP2_IB_IGN_PAYLOAD; ++ break; ++ } ++ ++ DEBUGF("recv: PRIORITY_UPDATE\n"); ++ ++ iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; ++ iframe->frame.ext.payload = ++ &iframe->ext_frame_payload.priority_update; ++ ++ if (!session->server) { ++ rv = nghttp2_session_terminate_session_with_reason( ++ session, NGHTTP2_PROTOCOL_ERROR, ++ "PRIORITY_UPDATE is received from server"); ++ if (nghttp2_is_fatal(rv)) { ++ return rv; ++ } ++ return (ssize_t)inlen; ++ } ++ ++ if (iframe->payloadleft < 4) { ++ busy = 1; ++ iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; ++ break; ++ } ++ ++ if (!session_no_rfc7540_pri_no_fallback(session) || ++ iframe->payloadleft > sizeof(iframe->raw_sbuf)) { ++ busy = 1; ++ iframe->state = NGHTTP2_IB_IGN_PAYLOAD; ++ break; ++ } ++ ++ busy = 1; ++ ++ iframe->state = NGHTTP2_IB_READ_NBYTE; ++ inbound_frame_set_mark(iframe, iframe->payloadleft); ++ + break; + default: + busy = 1; +@@ -5923,7 +6408,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { +- return in - first; ++ return (ssize_t)(in - first); + } + + switch (iframe->frame.hd.type) { +@@ -5988,13 +6473,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + + break; + case NGHTTP2_PRIORITY: +- rv = session_process_priority_frame(session); +- if (nghttp2_is_fatal(rv)) { +- return rv; +- } ++ 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)) { ++ return rv; ++ } + +- if (iframe->state == NGHTTP2_IB_IGN_ALL) { +- return (ssize_t)inlen; ++ if (iframe->state == NGHTTP2_IB_IGN_ALL) { ++ return (ssize_t)inlen; ++ } + } + + session_inbound_frame_reset(session); +@@ -6150,6 +6638,18 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + + iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD; + ++ break; ++ case NGHTTP2_PRIORITY_UPDATE: ++ DEBUGF("recv: prioritized_stream_id=%d\n", ++ nghttp2_get_uint32(iframe->sbuf.pos) & NGHTTP2_STREAM_ID_MASK); ++ ++ rv = session_process_priority_update_frame(session); ++ if (nghttp2_is_fatal(rv)) { ++ return rv; ++ } ++ ++ session_inbound_frame_reset(session); ++ + break; + } + default: +@@ -6212,7 +6712,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + +- return in - first; ++ return (ssize_t)(in - first); + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { +@@ -6403,7 +6903,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { +- return in - first; ++ return (ssize_t)(in - first); + } + + nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); +@@ -6461,7 +6961,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { +- return in - first; ++ return (ssize_t)(in - first); + } + + /* Pad Length field is subject to flow control */ +@@ -6611,7 +7111,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + 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 in - first; ++ return (ssize_t)(in - first); + } + + if (nghttp2_is_fatal(rv)) { +@@ -6791,7 +7291,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + + assert(in == last); + +- return in - first; ++ return (ssize_t)(in - first); + } + + int nghttp2_session_recv(nghttp2_session *session) { +@@ -6863,7 +7363,8 @@ 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) && ++ ((!nghttp2_pq_empty(&session->root.obq) || ++ !session_sched_empty(session)) && + session->remote_window_size > 0) || + (nghttp2_outbound_queue_top(&session->ob_syn) && + !session_is_outgoing_concurrent_streams_max(session)); +@@ -6962,6 +7463,9 @@ int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + nghttp2_mem_free(mem, item); + return rv; + } ++ ++ session->goaway_flags |= NGHTTP2_GOAWAY_SUBMITTED; ++ + return 0; + } + +@@ -7016,6 +7520,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + int rv; + nghttp2_mem *mem; + nghttp2_inflight_settings *inflight_settings = NULL; ++ uint8_t no_rfc7540_pri = session->pending_no_rfc7540_priorities; + + mem = &session->mem; + +@@ -7033,6 +7538,21 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + ++ for (i = 0; i < niv; ++i) { ++ if (iv[i].settings_id != NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES) { ++ continue; ++ } ++ ++ if (no_rfc7540_pri == UINT8_MAX) { ++ no_rfc7540_pri = (uint8_t)iv[i].value; ++ continue; ++ } ++ ++ if (iv[i].value != (uint32_t)no_rfc7540_pri) { ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ } ++ + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; +@@ -7107,6 +7627,12 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + } + } + ++ if (no_rfc7540_pri == UINT8_MAX) { ++ session->pending_no_rfc7540_priorities = 0; ++ } else { ++ session->pending_no_rfc7540_priorities = no_rfc7540_pri; ++ } ++ + return 0; + } + +@@ -7229,13 +7755,10 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + +- rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, +- aux_data->no_copy); +- if (rv != 0) { +- return rv; +- } ++ nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, ++ aux_data->no_copy); + +- reschedule_stream(stream); ++ session_reschedule_stream(session, stream); + + if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && + (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { +@@ -7309,7 +7832,7 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + +- rv = nghttp2_stream_resume_deferred_item(stream, ++ rv = session_resume_deferred_stream_item(session, stream, + NGHTTP2_STREAM_FLAG_DEFERRED_USER); + + if (nghttp2_is_fatal(rv)) { +@@ -7417,6 +7940,8 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, + return session->remote_settings.max_header_list_size; + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + return session->remote_settings.enable_connect_protocol; ++ case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: ++ return session->remote_settings.no_rfc7540_priorities; + } + + assert(0); +@@ -7440,6 +7965,8 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session, + return session->local_settings.max_header_list_size; + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + return session->local_settings.enable_connect_protocol; ++ case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: ++ return session->local_settings.no_rfc7540_priorities; + } + + assert(0); +@@ -7723,6 +8250,10 @@ int nghttp2_session_change_stream_priority( + 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; + } +@@ -7755,6 +8286,10 @@ int nghttp2_session_create_idle_stream(nghttp2_session *session, + 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; +@@ -7796,3 +8331,38 @@ nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) { + void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) { + session->user_data = user_data; + } ++ ++int nghttp2_session_change_extpri_stream_priority( ++ nghttp2_session *session, int32_t stream_id, ++ const nghttp2_extpri *extpri_in, int ignore_client_signal) { ++ nghttp2_stream *stream; ++ nghttp2_extpri extpri = *extpri_in; ++ ++ if (!session->server) { ++ return NGHTTP2_ERR_INVALID_STATE; ++ } ++ ++ if (session->pending_no_rfc7540_priorities != 1) { ++ return 0; ++ } ++ ++ if (stream_id == 0) { ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ ++ stream = nghttp2_session_get_stream_raw(session, stream_id); ++ if (!stream) { ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ ++ if (extpri.urgency > NGHTTP2_EXTPRI_URGENCY_LOW) { ++ extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW; ++ } ++ ++ if (ignore_client_signal) { ++ stream->flags |= NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES; ++ } ++ ++ return session_update_stream_priority(session, stream, ++ nghttp2_extpri_to_uint8(&extpri)); ++} +diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h +index 907b170..b119329 100644 +--- a/deps/nghttp2/lib/nghttp2_session.h ++++ b/deps/nghttp2/lib/nghttp2_session.h +@@ -39,6 +39,7 @@ + #include "nghttp2_buf.h" + #include "nghttp2_callbacks.h" + #include "nghttp2_mem.h" ++#include "nghttp2_ratelim.h" + + /* The global variable for tests where we want to disable strict + preface handling. */ +@@ -52,7 +53,9 @@ 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_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; + + /* +@@ -62,7 +65,8 @@ typedef enum { + typedef enum { + NGHTTP2_TYPEMASK_NONE = 0, + NGHTTP2_TYPEMASK_ALTSVC = 1 << 0, +- NGHTTP2_TYPEMASK_ORIGIN = 1 << 1 ++ NGHTTP2_TYPEMASK_ORIGIN = 1 << 1, ++ NGHTTP2_TYPEMASK_PRIORITY_UPDATE = 1 << 2 + } nghttp2_typemask; + + typedef enum { +@@ -102,6 +106,10 @@ typedef struct { + /* The default value of maximum number of concurrent streams. */ + #define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu + ++/* The default values for stream reset rate limiter. */ ++#define NGHTTP2_DEFAULT_STREAM_RESET_BURST 1000 ++#define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33 ++ + /* Internal state when receiving incoming frame */ + typedef enum { + /* Receiving frame header */ +@@ -151,10 +159,8 @@ typedef struct { + /* padding length for the current frame */ + size_t padlen; + nghttp2_inbound_state state; +- /* Small buffer. Currently the largest contiguous chunk to buffer +- is frame header. We buffer part of payload, but they are smaller +- than frame header. */ +- uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN]; ++ /* Small fixed sized buffer. */ ++ uint8_t raw_sbuf[32]; + } nghttp2_inbound_frame; + + typedef struct { +@@ -165,6 +171,7 @@ typedef struct { + uint32_t max_frame_size; + uint32_t max_header_list_size; + uint32_t enable_connect_protocol; ++ uint32_t no_rfc7540_priorities; + } nghttp2_settings_storage; + + typedef enum { +@@ -176,7 +183,9 @@ typedef enum { + /* Flag means GOAWAY was sent */ + NGHTTP2_GOAWAY_SENT = 0x4, + /* Flag means GOAWAY was received */ +- NGHTTP2_GOAWAY_RECV = 0x8 ++ NGHTTP2_GOAWAY_RECV = 0x8, ++ /* Flag means GOAWAY has been submitted at least once */ ++ NGHTTP2_GOAWAY_SUBMITTED = 0x10 + } nghttp2_goaway_flag; + + /* nghttp2_inflight_settings stores the SETTINGS entries which local +@@ -202,6 +211,12 @@ struct nghttp2_session { + response) frame, which are subject to + SETTINGS_MAX_CONCURRENT_STREAMS limit. */ + nghttp2_outbound_queue ob_syn; ++ /* Queues for DATA frames which is used when ++ SETTINGS_NO_RFC7540_PRIORITIES is enabled. This implements RFC ++ 9218 extensible prioritization scheme. */ ++ struct { ++ nghttp2_pq ob_data; ++ } sched[NGHTTP2_EXTPRI_URGENCY_LEVELS]; + nghttp2_active_outbound_item aob; + nghttp2_inbound_frame iframe; + nghttp2_hd_deflater hd_deflater; +@@ -227,6 +242,12 @@ struct nghttp2_session { + /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not + considered as in-flight. */ + nghttp2_inflight_settings *inflight_settings_head; ++ /* Stream reset rate limiter. If receiving excessive amount of ++ stream resets, GOAWAY will be sent. */ ++ nghttp2_ratelim stream_reset_ratelim; ++ /* Sequential number across all streams to process streams in ++ FIFO. */ ++ uint64_t stream_seq; + /* The number of outgoing streams. This will be capped by + remote_settings.max_concurrent_streams. */ + size_t num_outgoing_streams; +@@ -328,6 +349,11 @@ struct nghttp2_session { + /* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to + accept :protocol header field before SETTINGS_ACK is received. */ + uint8_t pending_enable_connect_protocol; ++ /* 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 +@@ -773,6 +799,19 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session, + int nghttp2_session_on_origin_received(nghttp2_session *session, + nghttp2_frame *frame); + ++/* ++ * Called when PRIORITY_UPDATE is received, assuming |frame| is ++ * properly initialized. ++ * ++ * This function returns 0 if it succeeds, or one of the following ++ * negative error codes: ++ * ++ * NGHTTP2_ERR_CALLBACK_FAILURE ++ * The callback function failed. ++ */ ++int nghttp2_session_on_priority_update_received(nghttp2_session *session, ++ nghttp2_frame *frame); ++ + /* + * Called when DATA is received, assuming |frame| is properly + * initialized. +diff --git a/deps/nghttp2/lib/nghttp2_stream.c b/deps/nghttp2/lib/nghttp2_stream.c +index f4c80a2..f1951f8 100644 +--- a/deps/nghttp2/lib/nghttp2_stream.c ++++ b/deps/nghttp2/lib/nghttp2_stream.c +@@ -100,6 +100,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + 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) { +@@ -463,14 +465,12 @@ static int stream_update_dep_on_attach_item(nghttp2_stream *stream) { + return 0; + } + +-static int stream_update_dep_on_detach_item(nghttp2_stream *stream) { ++static void stream_update_dep_on_detach_item(nghttp2_stream *stream) { + if (nghttp2_pq_empty(&stream->obq)) { + stream_obq_remove(stream); + } + + validate_tree(stream); +- +- return 0; + } + + int nghttp2_stream_attach_item(nghttp2_stream *stream, +@@ -484,6 +484,10 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream, + + 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. +@@ -497,16 +501,20 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream, + return 0; + } + +-int nghttp2_stream_detach_item(nghttp2_stream *stream) { ++void nghttp2_stream_detach_item(nghttp2_stream *stream) { + DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item); + + stream->item = NULL; + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); + +- return stream_update_dep_on_detach_item(stream); ++ if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { ++ return; ++ } ++ ++ stream_update_dep_on_detach_item(stream); + } + +-int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { ++void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { + assert(stream->item); + + DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id, +@@ -514,7 +522,11 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { + + stream->flags |= flags; + +- return stream_update_dep_on_detach_item(stream); ++ 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) { +@@ -529,6 +541,10 @@ int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) { + return 0; + } + ++ if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { ++ return 0; ++ } ++ + return stream_update_dep_on_attach_item(stream); + } + +diff --git a/deps/nghttp2/lib/nghttp2_stream.h b/deps/nghttp2/lib/nghttp2_stream.h +index 2846c6a..71b9fb1 100644 +--- a/deps/nghttp2/lib/nghttp2_stream.h ++++ b/deps/nghttp2/lib/nghttp2_stream.h +@@ -90,8 +90,15 @@ typedef enum { + NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, + /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and + NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ +- NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c +- ++ 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 ++ validation against a field value is not performed. */ ++ NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 0x40, + } nghttp2_stream_flag; + + /* HTTP related flags to enforce HTTP semantics */ +@@ -132,6 +139,11 @@ typedef enum { + /* set if final response is expected */ + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, + NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15, ++ /* set if priority header field is received */ ++ NGHTTP2_HTTP_FLAG_PRIORITY = 1 << 16, ++ /* set if an error is encountered while parsing priority header ++ field */ ++ NGHTTP2_HTTP_FLAG_BAD_PRIORITY = 1 << 17, + } nghttp2_http_flag; + + struct nghttp2_stream { +@@ -204,7 +216,7 @@ struct nghttp2_stream { + /* status code from remote server */ + int16_t status_code; + /* Bitwise OR of zero or more nghttp2_http_flag values */ +- uint16_t http_flags; ++ uint32_t http_flags; + /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ + uint8_t flags; + /* Bitwise OR of zero or more nghttp2_shut_flag values */ +@@ -218,6 +230,12 @@ struct nghttp2_stream { + this stream. The nonzero does not necessarily mean WINDOW_UPDATE + is not queued. */ + uint8_t window_update_queued; ++ /* extpri is a stream priority produced by nghttp2_extpri_to_uint8 ++ used by RFC 9218 extensible priorities. */ ++ uint8_t extpri; ++ /* http_extpri is a stream priority received in HTTP request header ++ fields and produced by nghttp2_extpri_to_uint8. */ ++ uint8_t http_extpri; + }; + + void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, +@@ -240,14 +258,8 @@ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); + * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates + * the reason of this action. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory + */ +-int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); ++void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); + + /* + * Put back deferred data in this stream to active state. The |flags| +@@ -361,14 +373,8 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream, + /* + * Detaches |stream->item|. This function does not free + * |stream->item|. The caller must free it. +- * +- * This function returns 0 if it succeeds, or one of the following +- * negative error codes: +- * +- * NGHTTP2_ERR_NOMEM +- * Out of memory + */ +-int nghttp2_stream_detach_item(nghttp2_stream *stream); ++void nghttp2_stream_detach_item(nghttp2_stream *stream); + + /* + * Makes the |stream| depend on the |dep_stream|. This dependency is +diff --git a/deps/nghttp2/lib/nghttp2_submit.c b/deps/nghttp2/lib/nghttp2_submit.c +index 92fb03e..f5554eb 100644 +--- a/deps/nghttp2/lib/nghttp2_submit.c ++++ b/deps/nghttp2/lib/nghttp2_submit.c +@@ -196,7 +196,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)) { ++ 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; +@@ -229,6 +230,10 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t 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; + } +@@ -662,6 +667,78 @@ fail_item_malloc: + return rv; + } + ++int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags, ++ int32_t stream_id, ++ const uint8_t *field_value, ++ size_t field_value_len) { ++ nghttp2_mem *mem; ++ uint8_t *buf, *p; ++ nghttp2_outbound_item *item; ++ nghttp2_frame *frame; ++ nghttp2_ext_priority_update *priority_update; ++ int rv; ++ (void)flags; ++ ++ mem = &session->mem; ++ ++ if (session->server) { ++ return NGHTTP2_ERR_INVALID_STATE; ++ } ++ ++ if (session->remote_settings.no_rfc7540_priorities == 0) { ++ return 0; ++ } ++ ++ if (stream_id == 0 || 4 + field_value_len > NGHTTP2_MAX_PAYLOADLEN) { ++ return NGHTTP2_ERR_INVALID_ARGUMENT; ++ } ++ ++ if (field_value_len) { ++ buf = nghttp2_mem_malloc(mem, field_value_len + 1); ++ if (buf == NULL) { ++ return NGHTTP2_ERR_NOMEM; ++ } ++ ++ p = nghttp2_cpymem(buf, field_value, field_value_len); ++ *p = '\0'; ++ } else { ++ buf = NULL; ++ } ++ ++ item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); ++ if (item == NULL) { ++ rv = NGHTTP2_ERR_NOMEM; ++ goto fail_item_malloc; ++ } ++ ++ nghttp2_outbound_item_init(item); ++ ++ item->aux_data.ext.builtin = 1; ++ ++ priority_update = &item->ext_frame_payload.priority_update; ++ ++ frame = &item->frame; ++ frame->ext.payload = priority_update; ++ ++ nghttp2_frame_priority_update_init(&frame->ext, stream_id, buf, ++ field_value_len); ++ ++ rv = nghttp2_session_add_item(session, item); ++ if (rv != 0) { ++ nghttp2_frame_priority_update_free(&frame->ext, mem); ++ nghttp2_mem_free(mem, item); ++ ++ return rv; ++ } ++ ++ return 0; ++ ++fail_item_malloc: ++ free(buf); ++ ++ return rv; ++} ++ + static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, + const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; +@@ -688,7 +765,8 @@ int32_t nghttp2_submit_request(nghttp2_session *session, + return NGHTTP2_ERR_PROTO; + } + +- if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { ++ 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; +diff --git a/deps/nghttp2/lib/nghttp2_time.c b/deps/nghttp2/lib/nghttp2_time.c +new file mode 100644 +index 0000000..2a5f1a6 +--- /dev/null ++++ b/deps/nghttp2/lib/nghttp2_time.c +@@ -0,0 +1,62 @@ ++/* ++ * nghttp2 - HTTP/2 C Library ++ * ++ * Copyright (c) 2023 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#include "nghttp2_time.h" ++ ++#ifdef HAVE_TIME_H ++# include ++#endif /* HAVE_TIME_H */ ++ ++#ifdef HAVE_SYSINFOAPI_H ++# include ++#endif /* HAVE_SYSINFOAPI_H */ ++ ++#ifndef HAVE_GETTICKCOUNT64 ++static uint64_t time_now_sec(void) { ++ time_t t = time(NULL); ++ ++ if (t == -1) { ++ return 0; ++ } ++ ++ return (uint64_t)t; ++} ++#endif /* HAVE_GETTICKCOUNT64 */ ++ ++#ifdef HAVE_CLOCK_GETTIME ++uint64_t nghttp2_time_now_sec(void) { ++ struct timespec tp; ++ int rv = clock_gettime(CLOCK_MONOTONIC, &tp); ++ ++ if (rv == -1) { ++ return time_now_sec(); ++ } ++ ++ return (uint64_t)tp.tv_sec; ++} ++#elif defined(HAVE_GETTICKCOUNT64) ++uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; } ++#else /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */ ++uint64_t nghttp2_time_now_sec(void) { return time_now_sec(); } ++#endif /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */ +diff --git a/deps/nghttp2/lib/nghttp2_time.h b/deps/nghttp2/lib/nghttp2_time.h +new file mode 100644 +index 0000000..03c0bbe +--- /dev/null ++++ b/deps/nghttp2/lib/nghttp2_time.h +@@ -0,0 +1,38 @@ ++/* ++ * nghttp2 - HTTP/2 C Library ++ * ++ * Copyright (c) 2023 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#ifndef NGHTTP2_TIME_H ++#define NGHTTP2_TIME_H ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++ ++#include ++ ++/* nghttp2_time_now_sec returns seconds from implementation-specific ++ timepoint. If it is unable to get seconds, it returns 0. */ ++uint64_t nghttp2_time_now_sec(void); ++ ++#endif /* NGHTTP2_TIME_H */ +diff --git a/deps/nghttp2/lib/sfparse.c b/deps/nghttp2/lib/sfparse.c +new file mode 100644 +index 0000000..efa2850 +--- /dev/null ++++ b/deps/nghttp2/lib/sfparse.c +@@ -0,0 +1,1146 @@ ++/* ++ * sfparse ++ * ++ * Copyright (c) 2023 sfparse contributors ++ * Copyright (c) 2019 nghttp3 contributors ++ * Copyright (c) 2015 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#include "sfparse.h" ++ ++#include ++#include ++#include ++ ++#define SF_STATE_DICT 0x08u ++#define SF_STATE_LIST 0x10u ++#define SF_STATE_ITEM 0x18u ++ ++#define SF_STATE_INNER_LIST 0x04u ++ ++#define SF_STATE_BEFORE 0x00u ++#define SF_STATE_BEFORE_PARAMS 0x01u ++#define SF_STATE_PARAMS 0x02u ++#define SF_STATE_AFTER 0x03u ++ ++#define SF_STATE_OP_MASK 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 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 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 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 SF_STATE_INITIAL 0x00u ++ ++#define DIGIT_CASES \ ++ case '0': \ ++ case '1': \ ++ case '2': \ ++ case '3': \ ++ case '4': \ ++ case '5': \ ++ case '6': \ ++ case '7': \ ++ case '8': \ ++ case '9' ++ ++#define LCALPHA_CASES \ ++ case 'a': \ ++ case 'b': \ ++ case 'c': \ ++ case 'd': \ ++ case 'e': \ ++ case 'f': \ ++ case 'g': \ ++ case 'h': \ ++ case 'i': \ ++ case 'j': \ ++ case 'k': \ ++ case 'l': \ ++ case 'm': \ ++ case 'n': \ ++ case 'o': \ ++ case 'p': \ ++ case 'q': \ ++ case 'r': \ ++ case 's': \ ++ case 't': \ ++ case 'u': \ ++ case 'v': \ ++ case 'w': \ ++ case 'x': \ ++ case 'y': \ ++ case 'z' ++ ++#define UCALPHA_CASES \ ++ case 'A': \ ++ case 'B': \ ++ case 'C': \ ++ case 'D': \ ++ case 'E': \ ++ case 'F': \ ++ case 'G': \ ++ case 'H': \ ++ case 'I': \ ++ case 'J': \ ++ case 'K': \ ++ case 'L': \ ++ case 'M': \ ++ case 'N': \ ++ case 'O': \ ++ case 'P': \ ++ case 'Q': \ ++ case 'R': \ ++ case 'S': \ ++ case 'T': \ ++ case 'U': \ ++ case 'V': \ ++ case 'W': \ ++ case 'X': \ ++ case 'Y': \ ++ case 'Z' ++ ++#define ALPHA_CASES \ ++ UCALPHA_CASES: \ ++ LCALPHA_CASES ++ ++#define X20_21_CASES \ ++ case ' ': \ ++ case '!' ++ ++#define X23_5B_CASES \ ++ case '#': \ ++ case '$': \ ++ case '%': \ ++ case '&': \ ++ case '\'': \ ++ case '(': \ ++ case ')': \ ++ case '*': \ ++ case '+': \ ++ case ',': \ ++ case '-': \ ++ case '.': \ ++ case '/': \ ++ DIGIT_CASES: \ ++ case ':': \ ++ case ';': \ ++ case '<': \ ++ case '=': \ ++ case '>': \ ++ case '?': \ ++ case '@': \ ++ UCALPHA_CASES: \ ++ case '[' ++ ++#define X5D_7E_CASES \ ++ case ']': \ ++ case '^': \ ++ case '_': \ ++ case '`': \ ++ LCALPHA_CASES: \ ++ case '{': \ ++ case '|': \ ++ case '}': \ ++ case '~' ++ ++static int is_ws(uint8_t c) { ++ switch (c) { ++ case ' ': ++ case '\t': ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++static int parser_eof(sf_parser *sfp) { return sfp->pos == sfp->end; } ++ ++static void parser_discard_ows(sf_parser *sfp) { ++ for (; !parser_eof(sfp) && is_ws(*sfp->pos); ++sfp->pos) ++ ; ++} ++ ++static void parser_discard_sp(sf_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; ++ sfp->state |= op; ++} ++ ++static void parser_unset_inner_list_state(sf_parser *sfp) { ++ sfp->state &= ~SF_STATE_INNER_LIST; ++} ++ ++static int parser_key(sf_parser *sfp, sf_vec *dest) { ++ const uint8_t *base; ++ ++ switch (*sfp->pos) { ++ case '*': ++ LCALPHA_CASES: ++ break; ++ default: ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ base = sfp->pos++; ++ ++ for (; !parser_eof(sfp); ++sfp->pos) { ++ switch (*sfp->pos) { ++ case '_': ++ case '-': ++ case '.': ++ case '*': ++ DIGIT_CASES: ++ LCALPHA_CASES: ++ continue; ++ } ++ ++ break; ++ } ++ ++ if (dest) { ++ dest->base = (uint8_t *)base; ++ dest->len = (size_t)(sfp->pos - dest->base); ++ } ++ ++ return 0; ++} ++ ++static int parser_number(sf_parser *sfp, sf_value *dest) { ++ int sign = 1; ++ int64_t value = 0; ++ size_t len = 0; ++ size_t fpos = 0; ++ ++ if (*sfp->pos == '-') { ++ ++sfp->pos; ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ sign = -1; ++ } ++ ++ assert(!parser_eof(sfp)); ++ ++ for (; !parser_eof(sfp); ++sfp->pos) { ++ switch (*sfp->pos) { ++ DIGIT_CASES: ++ if (++len > 15) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ value *= 10; ++ value += *sfp->pos - '0'; ++ ++ continue; ++ } ++ ++ break; ++ } ++ ++ if (len == 0) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ if (parser_eof(sfp) || *sfp->pos != '.') { ++ if (dest) { ++ dest->type = SF_TYPE_INTEGER; ++ dest->flags = SF_VALUE_FLAG_NONE; ++ dest->integer = value * sign; ++ } ++ ++ return 0; ++ } ++ ++ /* decimal */ ++ ++ if (len > 12) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ fpos = len; ++ ++ ++sfp->pos; ++ ++ for (; !parser_eof(sfp); ++sfp->pos) { ++ switch (*sfp->pos) { ++ DIGIT_CASES: ++ if (++len > 15) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ value *= 10; ++ value += *sfp->pos - '0'; ++ ++ continue; ++ } ++ ++ break; ++ } ++ ++ if (fpos == len || len - fpos > 3) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ if (dest) { ++ dest->type = SF_TYPE_DECIMAL; ++ dest->flags = SF_VALUE_FLAG_NONE; ++ dest->decimal.numer = value * sign; ++ ++ switch (len - fpos) { ++ case 1: ++ dest->decimal.denom = 10; ++ ++ break; ++ case 2: ++ dest->decimal.denom = 100; ++ ++ break; ++ case 3: ++ dest->decimal.denom = 1000; ++ ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static int parser_date(sf_parser *sfp, sf_value *dest) { ++ int rv; ++ sf_value val; ++ ++ /* The first byte has already been validated by the caller. */ ++ assert('@' == *sfp->pos); ++ ++ ++sfp->pos; ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ rv = parser_number(sfp, &val); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (val.type != SF_TYPE_INTEGER) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ if (dest) { ++ *dest = val; ++ dest->type = SF_TYPE_DATE; ++ } ++ ++ return 0; ++} ++ ++static int parser_string(sf_parser *sfp, sf_value *dest) { ++ const uint8_t *base; ++ uint32_t flags = SF_VALUE_FLAG_NONE; ++ ++ /* The first byte has already been validated by the caller. */ ++ assert('"' == *sfp->pos); ++ ++ base = ++sfp->pos; ++ ++ for (; !parser_eof(sfp); ++sfp->pos) { ++ switch (*sfp->pos) { ++ X20_21_CASES: ++ X23_5B_CASES: ++ X5D_7E_CASES: ++ break; ++ case '\\': ++ ++sfp->pos; ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ switch (*sfp->pos) { ++ case '"': ++ case '\\': ++ flags = SF_VALUE_FLAG_ESCAPED_STRING; ++ ++ break; ++ default: ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ 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; ++ } ++ ++ ++sfp->pos; ++ ++ return 0; ++ default: ++ return SF_ERR_PARSE_ERROR; ++ } ++ } ++ ++ return SF_ERR_PARSE_ERROR; ++} ++ ++static int parser_token(sf_parser *sfp, sf_value *dest) { ++ const uint8_t *base; ++ ++ /* The first byte has already been validated by the caller. */ ++ base = sfp->pos++; ++ ++ 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: ++ continue; ++ } ++ ++ break; ++ } ++ ++ if (dest) { ++ dest->type = SF_TYPE_TOKEN; ++ dest->flags = SF_VALUE_FLAG_NONE; ++ dest->vec.base = (uint8_t *)base; ++ dest->vec.len = (size_t)(sfp->pos - base); ++ } ++ ++ return 0; ++} ++ ++static int parser_byteseq(sf_parser *sfp, sf_value *dest) { ++ const uint8_t *base; ++ ++ /* The first byte has already been validated by the caller. */ ++ assert(':' == *sfp->pos); ++ ++ base = ++sfp->pos; ++ ++ for (; !parser_eof(sfp); ++sfp->pos) { ++ switch (*sfp->pos) { ++ case '+': ++ case '/': ++ DIGIT_CASES: ++ ALPHA_CASES: ++ continue; ++ case '=': ++ switch ((sfp->pos - base) & 0x3) { ++ case 0: ++ case 1: ++ return SF_ERR_PARSE_ERROR; ++ 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; ++ } ++ ++ 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; ++ } ++ ++ break; ++ } ++ ++ ++sfp->pos; ++ ++ if (parser_eof(sfp) || *sfp->pos != ':') { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ goto fin; ++ case ':': ++ if ((sfp->pos - base) & 0x3) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ goto fin; ++ default: ++ return SF_ERR_PARSE_ERROR; ++ } ++ } ++ ++ return SF_ERR_PARSE_ERROR; ++ ++fin: ++ if (dest) { ++ dest->type = SF_TYPE_BYTESEQ; ++ dest->flags = SF_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; ++} ++ ++static int parser_boolean(sf_parser *sfp, sf_value *dest) { ++ int b; ++ ++ /* The first byte has already been validated by the caller. */ ++ assert('?' == *sfp->pos); ++ ++ ++sfp->pos; ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ switch (*sfp->pos) { ++ case '0': ++ b = 0; ++ ++ break; ++ case '1': ++ b = 1; ++ ++ break; ++ default: ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ ++sfp->pos; ++ ++ if (dest) { ++ dest->type = SF_TYPE_BOOLEAN; ++ dest->flags = SF_VALUE_FLAG_NONE; ++ dest->boolean = b; ++ } ++ ++ return 0; ++} ++ ++static int parser_bare_item(sf_parser *sfp, sf_value *dest) { ++ switch (*sfp->pos) { ++ case '"': ++ return parser_string(sfp, dest); ++ case '-': ++ DIGIT_CASES: ++ return parser_number(sfp, dest); ++ case '@': ++ return parser_date(sfp, dest); ++ case ':': ++ return parser_byteseq(sfp, dest); ++ case '?': ++ return parser_boolean(sfp, dest); ++ case '*': ++ ALPHA_CASES: ++ return parser_token(sfp, dest); ++ default: ++ return SF_ERR_PARSE_ERROR; ++ } ++} ++ ++static int parser_skip_inner_list(sf_parser *sfp); ++ ++int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { ++ int rv; ++ ++ switch (sfp->state & SF_STATE_OP_MASK) { ++ case SF_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); ++ ++ break; ++ case SF_STATE_PARAMS: ++ break; ++ default: ++ assert(0); ++ abort(); ++ } ++ ++ if (parser_eof(sfp) || *sfp->pos != ';') { ++ parser_set_op_state(sfp, SF_STATE_AFTER); ++ ++ return SF_ERR_EOF; ++ } ++ ++ ++sfp->pos; ++ ++ parser_discard_sp(sfp); ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ rv = parser_key(sfp, dest_key); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ if (parser_eof(sfp) || *sfp->pos != '=') { ++ if (dest_value) { ++ dest_value->type = SF_TYPE_BOOLEAN; ++ dest_value->flags = SF_VALUE_FLAG_NONE; ++ dest_value->boolean = 1; ++ } ++ ++ return 0; ++ } ++ ++ ++sfp->pos; ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ return parser_bare_item(sfp, dest_value); ++} ++ ++static int parser_skip_params(sf_parser *sfp) { ++ int rv; ++ ++ for (;;) { ++ rv = sf_parser_param(sfp, NULL, NULL); ++ switch (rv) { ++ case 0: ++ break; ++ case SF_ERR_EOF: ++ return 0; ++ case SF_ERR_PARSE_ERROR: ++ return rv; ++ default: ++ assert(0); ++ abort(); ++ } ++ } ++} ++ ++int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) { ++ int rv; ++ ++ switch (sfp->state & SF_STATE_OP_MASK) { ++ case SF_STATE_BEFORE: ++ parser_discard_sp(sfp); ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ break; ++ case SF_STATE_BEFORE_PARAMS: ++ rv = parser_skip_params(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ /* Technically, we are entering SF_STATE_AFTER, but we will set ++ another state without reading the state. */ ++ /* parser_set_op_state(sfp, SF_STATE_AFTER); */ ++ ++ /* fall through */ ++ case SF_STATE_AFTER: ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ switch (*sfp->pos) { ++ case ' ': ++ parser_discard_sp(sfp); ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ break; ++ case ')': ++ break; ++ default: ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ break; ++ default: ++ assert(0); ++ abort(); ++ } ++ ++ if (*sfp->pos == ')') { ++ ++sfp->pos; ++ ++ parser_unset_inner_list_state(sfp); ++ parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); ++ ++ return SF_ERR_EOF; ++ } ++ ++ rv = parser_bare_item(sfp, dest); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); ++ ++ return 0; ++} ++ ++static int parser_skip_inner_list(sf_parser *sfp) { ++ int rv; ++ ++ for (;;) { ++ rv = sf_parser_inner_list(sfp, NULL); ++ switch (rv) { ++ case 0: ++ break; ++ case SF_ERR_EOF: ++ return 0; ++ case SF_ERR_PARSE_ERROR: ++ return rv; ++ default: ++ assert(0); ++ abort(); ++ } ++ } ++} ++ ++static int parser_next_key_or_item(sf_parser *sfp) { ++ parser_discard_ows(sfp); ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_EOF; ++ } ++ ++ if (*sfp->pos != ',') { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ ++sfp->pos; ++ ++ parser_discard_ows(sfp); ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ return 0; ++} ++ ++static int parser_dict_value(sf_parser *sfp, sf_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->boolean = 1; ++ } ++ ++ sfp->state = SF_STATE_DICT_BEFORE_PARAMS; ++ ++ return 0; ++ } ++ ++ ++sfp->pos; ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ if (*sfp->pos == '(') { ++ if (dest) { ++ dest->type = SF_TYPE_INNER_LIST; ++ dest->flags = SF_VALUE_FLAG_NONE; ++ } ++ ++ ++sfp->pos; ++ ++ sfp->state = SF_STATE_DICT_INNER_LIST_BEFORE; ++ ++ return 0; ++ } ++ ++ rv = parser_bare_item(sfp, dest); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ sfp->state = SF_STATE_DICT_BEFORE_PARAMS; ++ ++ return 0; ++} ++ ++int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { ++ int rv; ++ ++ switch (sfp->state) { ++ case SF_STATE_DICT_INNER_LIST_BEFORE: ++ rv = parser_skip_inner_list(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ /* fall through */ ++ case SF_STATE_DICT_BEFORE_PARAMS: ++ rv = parser_skip_params(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ /* fall through */ ++ case SF_STATE_DICT_AFTER: ++ rv = parser_next_key_or_item(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ break; ++ case SF_STATE_INITIAL: ++ parser_discard_sp(sfp); ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_EOF; ++ } ++ ++ break; ++ default: ++ assert(0); ++ abort(); ++ } ++ ++ rv = parser_key(sfp, dest_key); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ return parser_dict_value(sfp, dest_value); ++} ++ ++int sf_parser_list(sf_parser *sfp, sf_value *dest) { ++ int rv; ++ ++ switch (sfp->state) { ++ case SF_STATE_LIST_INNER_LIST_BEFORE: ++ rv = parser_skip_inner_list(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ /* fall through */ ++ case SF_STATE_LIST_BEFORE_PARAMS: ++ rv = parser_skip_params(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ /* fall through */ ++ case SF_STATE_LIST_AFTER: ++ rv = parser_next_key_or_item(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ break; ++ case SF_STATE_INITIAL: ++ parser_discard_sp(sfp); ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_EOF; ++ } ++ ++ break; ++ default: ++ assert(0); ++ abort(); ++ } ++ ++ if (*sfp->pos == '(') { ++ if (dest) { ++ dest->type = SF_TYPE_INNER_LIST; ++ dest->flags = SF_VALUE_FLAG_NONE; ++ } ++ ++ ++sfp->pos; ++ ++ sfp->state = SF_STATE_LIST_INNER_LIST_BEFORE; ++ ++ return 0; ++ } ++ ++ rv = parser_bare_item(sfp, dest); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ sfp->state = SF_STATE_LIST_BEFORE_PARAMS; ++ ++ return 0; ++} ++ ++int sf_parser_item(sf_parser *sfp, sf_value *dest) { ++ int rv; ++ ++ switch (sfp->state) { ++ case SF_STATE_INITIAL: ++ parser_discard_sp(sfp); ++ ++ if (parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ break; ++ case SF_STATE_ITEM_INNER_LIST_BEFORE: ++ rv = parser_skip_inner_list(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ /* fall through */ ++ case SF_STATE_ITEM_BEFORE_PARAMS: ++ rv = parser_skip_params(sfp); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ /* fall through */ ++ case SF_STATE_ITEM_AFTER: ++ parser_discard_sp(sfp); ++ ++ if (!parser_eof(sfp)) { ++ return SF_ERR_PARSE_ERROR; ++ } ++ ++ return SF_ERR_EOF; ++ default: ++ assert(0); ++ abort(); ++ } ++ ++ if (*sfp->pos == '(') { ++ if (dest) { ++ dest->type = SF_TYPE_INNER_LIST; ++ dest->flags = SF_VALUE_FLAG_NONE; ++ } ++ ++ ++sfp->pos; ++ ++ sfp->state = SF_STATE_ITEM_INNER_LIST_BEFORE; ++ ++ return 0; ++ } ++ ++ rv = parser_bare_item(sfp, dest); ++ if (rv != 0) { ++ return rv; ++ } ++ ++ sfp->state = SF_STATE_ITEM_BEFORE_PARAMS; ++ ++ return 0; ++} ++ ++void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen) { ++ if (datalen == 0) { ++ sfp->pos = sfp->end = NULL; ++ } else { ++ sfp->pos = data; ++ sfp->end = data + datalen; ++ } ++ ++ sfp->state = SF_STATE_INITIAL; ++} ++ ++void sf_unescape(sf_vec *dest, const sf_vec *src) { ++ const uint8_t *p, *q; ++ uint8_t *o; ++ size_t len, slen; ++ ++ if (src->len == 0) { ++ *dest = *src; ++ ++ return; ++ } ++ ++ o = dest->base; ++ p = src->base; ++ len = src->len; ++ ++ for (;;) { ++ q = memchr(p, '\\', len); ++ if (q == NULL) { ++ if (len == src->len) { ++ *dest = *src; ++ ++ return; ++ } ++ ++ memcpy(o, p, len); ++ o += len; ++ ++ break; ++ } ++ ++ slen = (size_t)(q - p); ++ memcpy(o, p, slen); ++ o += slen; ++ ++ p = q + 1; ++ *o++ = *p++; ++ len -= slen + 2; ++ } ++ ++ dest->len = (size_t)(o - dest->base); ++} ++ ++void sf_base64decode(sf_vec *dest, const sf_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, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, ++ 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, ++ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, ++ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, ++ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -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, -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, -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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1}; ++ uint8_t *o; ++ const uint8_t *p, *end; ++ uint32_t n; ++ size_t i; ++ int idx; ++ ++ assert((src->len & 0x3) == 0); ++ ++ if (src->len == 0) { ++ *dest = *src; ++ ++ return; ++ } ++ ++ o = dest->base; ++ p = src->base; ++ end = src->base + src->len; ++ ++ for (; p != end;) { ++ n = 0; ++ ++ for (i = 1; i <= 4; ++i, ++p) { ++ idx = index_tbl[*p]; ++ ++ if (idx == -1) { ++ assert(i > 2); ++ ++ if (i == 3) { ++ assert(*p == '=' && *(p + 1) == '=' && p + 2 == end); ++ ++ *o++ = (uint8_t)(n >> 16); ++ ++ goto fin; ++ } ++ ++ assert(*p == '=' && p + 1 == end); ++ ++ *o++ = (uint8_t)(n >> 16); ++ *o++ = (n >> 8) & 0xffu; ++ ++ goto fin; ++ } ++ ++ n += (uint32_t)(idx << (24 - i * 6)); ++ } ++ ++ *o++ = (uint8_t)(n >> 16); ++ *o++ = (n >> 8) & 0xffu; ++ *o++ = n & 0xffu; ++ } ++ ++fin: ++ dest->len = (size_t)(o - dest->base); ++} +diff --git a/deps/nghttp2/lib/sfparse.h b/deps/nghttp2/lib/sfparse.h +new file mode 100644 +index 0000000..1474db1 +--- /dev/null ++++ b/deps/nghttp2/lib/sfparse.h +@@ -0,0 +1,409 @@ ++/* ++ * sfparse ++ * ++ * Copyright (c) 2023 sfparse contributors ++ * Copyright (c) 2019 nghttp3 contributors ++ * Copyright (c) 2015 nghttp2 contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++#ifndef SFPARSE_H ++#define SFPARSE_H ++ ++/* Define WIN32 when build target is Win32 API (borrowed from ++ libcurl) */ ++#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) ++# define WIN32 ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#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) */ ++# include ++#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ ++#include ++#include ++ ++/** ++ * @enum ++ * ++ * :type:`sf_type` defines value type. ++ */ ++typedef enum sf_type { ++ /** ++ * :enum:`SF_TYPE_BOOLEAN` indicates boolean type. ++ */ ++ SF_TYPE_BOOLEAN, ++ /** ++ * :enum:`SF_TYPE_INTEGER` indicates integer type. ++ */ ++ SF_TYPE_INTEGER, ++ /** ++ * :enum:`SF_TYPE_DECIMAL` indicates decimal type. ++ */ ++ SF_TYPE_DECIMAL, ++ /** ++ * :enum:`SF_TYPE_STRING` indicates string type. ++ */ ++ SF_TYPE_STRING, ++ /** ++ * :enum:`SF_TYPE_TOKEN` indicates token type. ++ */ ++ SF_TYPE_TOKEN, ++ /** ++ * :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type. ++ */ ++ SF_TYPE_BYTESEQ, ++ /** ++ * :enum:`SF_TYPE_INNER_LIST` indicates inner list type. ++ */ ++ SF_TYPE_INNER_LIST, ++ /** ++ * :enum:`SF_TYPE_DATE` indicates date type. ++ */ ++ SF_TYPE_DATE ++} sf_type; ++ ++/** ++ * @macro ++ * ++ * :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has ++ * occurred, and it is not possible to continue the processing. ++ */ ++#define SF_ERR_PARSE_ERROR -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. ++ */ ++#define SF_ERR_EOF -2 ++ ++/** ++ * @struct ++ * ++ * :type:`sf_vec` stores sequence of bytes. ++ */ ++typedef struct sf_vec { ++ /** ++ * :member:`base` points to the beginning of the sequence of bytes. ++ */ ++ uint8_t *base; ++ /** ++ * :member:`len` is the number of bytes contained in this sequence. ++ */ ++ size_t len; ++} sf_vec; ++ ++/** ++ * @macro ++ * ++ * :macro:`SF_VALUE_FLAG_NONE` indicates no flag set. ++ */ ++#define SF_VALUE_FLAG_NONE 0x0u ++ ++/** ++ * @macro ++ * ++ * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string ++ * contains escaped character(s). ++ */ ++#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u ++ ++/** ++ * @struct ++ * ++ * :type:`sf_decimal` contains decimal value. ++ */ ++typedef struct sf_decimal { ++ /** ++ * :member:`numer` contains numerator of the decimal value. ++ */ ++ int64_t numer; ++ /** ++ * :member:`denom` contains denominator of the decimal value. ++ */ ++ int64_t denom; ++} sf_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`. ++ */ ++typedef struct sf_value { ++ /** ++ * :member:`type` is the type of the value contained in this ++ * particular object. ++ */ ++ sf_type type; ++ /** ++ * :member:`flags` is bitwise OR of one or more of ++ * :macro:`SF_VALUE_FLAG_* `. ++ */ ++ uint32_t flags; ++ /** ++ * @anonunion_start ++ * ++ * @sf_value_value ++ */ ++ union { ++ /** ++ * :member:`boolean` contains boolean value if :member:`type` == ++ * :enum:`sf_type.SF_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`. ++ */ ++ int64_t integer; ++ /** ++ * :member:`decimal` contains decimal value if :member:`type` == ++ * :enum:`sf_type.SF_TYPE_DECIMAL`. ++ */ ++ sf_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`. ++ * ++ * 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:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64 ++ * encoded string. To decode this byte string, use ++ * `sf_base64decode`. ++ * ++ * If :member:`vec.len ` == 0, :member:`vec.base ++ * ` is guaranteed to be NULL. ++ */ ++ sf_vec vec; ++ /** ++ * @anonunion_end ++ */ ++ }; ++} sf_value; ++ ++/** ++ * @struct ++ * ++ * :type:`sf_parser` is the Structured Field Values parser. Use ++ * `sf_parser_init` to initialize it. ++ */ ++typedef struct sf_parser { ++ /* all fields are private */ ++ const uint8_t *pos; ++ const uint8_t *end; ++ uint32_t state; ++} sf_parser; ++ ++/** ++ * @function ++ * ++ * `sf_parser_init` initializes |sfp| with the given buffer pointed by ++ * |data| of length |datalen|. ++ */ ++void sf_parser_init(sf_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| ++ * 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 ++ * while parsing field value. ++ */ ++int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_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. ++ * ++ * Caller can optionally read parameters attached to the pair by ++ * calling `sf_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. ++ * ++ * This function returns 0 if it succeeds, or one of the following ++ * negative error codes: ++ * ++ * :macro:`SF_ERR_EOF` ++ * All values in the dictionary have read. ++ * :macro:`SF_ERR_PARSE_ERROR` ++ * It encountered fatal error while parsing field value. ++ */ ++int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); ++ ++/** ++ * @function ++ * ++ * `sf_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`. ++ * ++ * 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. ++ * ++ * This function returns 0 if it succeeds, or one of the following ++ * negative error codes: ++ * ++ * :macro:`SF_ERR_EOF` ++ * All values in the list have read. ++ * :macro:`SF_ERR_PARSE_ERROR` ++ * It encountered fatal error while parsing field value. ++ */ ++int sf_parser_list(sf_parser *sfp, sf_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. ++ * ++ * 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`. ++ * ++ * 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. ++ * ++ * This function returns 0 if it succeeds, or one of the following ++ * negative error codes: ++ * ++ * :macro:`SF_ERR_EOF` ++ * There is nothing left to read. ++ * :macro:`SF_ERR_PARSE_ERROR` ++ * It encountered fatal error while parsing field value. ++ */ ++int sf_parser_item(sf_parser *sfp, sf_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. ++ * ++ * Caller can optionally read parameters attached to the item by ++ * calling `sf_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 ++ * parameters attached to this inner list by calling ++ * `sf_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` ++ * All values in the inner list have read. ++ * :macro:`SF_ERR_PARSE_ERROR` ++ * It encountered fatal error while parsing field value. ++ */ ++int sf_parser_inner_list(sf_parser *sfp, sf_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. ++ * ++ * :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. ++ * ++ * This function sets the length of unescaped string to ++ * :member:`dest->len `. ++ */ ++void sf_unescape(sf_vec *dest, const sf_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. ++ * ++ * :member:`dest->base ` must point to the buffer that ++ * has sufficient space to store the decoded byte string. ++ * ++ * If :member:`src->len ` == 0, |*src| is assigned to ++ * |*dest|. ++ * ++ * This function sets the length of decoded byte string to ++ * :member:`dest->len `. ++ */ ++void sf_base64decode(sf_vec *dest, const sf_vec *src); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* SFPARSE_H */ +diff --git a/deps/nghttp2/nghttp2.gyp b/deps/nghttp2/nghttp2.gyp +index 0dcd034..7b02f39 100644 +--- a/deps/nghttp2/nghttp2.gyp ++++ b/deps/nghttp2/nghttp2.gyp +@@ -12,7 +12,6 @@ + 'defines': [ + 'BUILDING_NGHTTP2', + 'NGHTTP2_STATICLIB', +- 'HAVE_CONFIG_H', + ], + 'conditions': [ + ['OS=="win"', { +@@ -38,6 +37,7 @@ + 'lib/nghttp2_buf.c', + 'lib/nghttp2_callbacks.c', + 'lib/nghttp2_debug.c', ++ 'lib/nghttp2_extpri.c', + 'lib/nghttp2_frame.c', + 'lib/nghttp2_hd.c', + 'lib/nghttp2_hd_huffman.c', +@@ -52,11 +52,14 @@ + 'lib/nghttp2_pq.c', + 'lib/nghttp2_priority_spec.c', + 'lib/nghttp2_queue.c', ++ 'lib/nghttp2_ratelim.c', + 'lib/nghttp2_rcbuf.c', + 'lib/nghttp2_session.c', + 'lib/nghttp2_stream.c', + 'lib/nghttp2_submit.c', +- 'lib/nghttp2_version.c' ++ 'lib/nghttp2_time.c', ++ 'lib/nghttp2_version.c', ++ 'lib/sfparse.c' + ] + } + ] +-- +2.41.0 + diff --git a/SPECS/nodejs.spec b/SPECS/nodejs.spec index 4b65667..2b97697 100644 --- a/SPECS/nodejs.spec +++ b/SPECS/nodejs.spec @@ -30,7 +30,7 @@ # This is used by both the nodejs package and the npm subpackage that # has a separate version - the name is special so that rpmdev-bumpspec # will bump this rather than adding .1 to the end. -%global baserelease 1 +%global baserelease 3 %{?!_pkgdocdir:%global _pkgdocdir %{_docdir}/%{name}-%{version}} @@ -75,7 +75,7 @@ %global libuv_version 1.43.0 # nghttp2 - from deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h -%global nghttp2_version 1.47.0 +%global nghttp2_version 1.57.0 # nghttp3 - from deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h %global nghttp3_major 0 @@ -182,7 +182,8 @@ Source112: https://github.com/WebAssembly/wasi-sdk/archive/wasi-sdk-14/wasi-sdk- # Disable running gyp on bundled deps we don't use Patch1: 0001-Disable-running-gyp-on-shared-deps.patch -Patch3: nodejs-fips-disable-options.patch +Patch2: 0002-disable-fips-options.patch +Patch3: 0003-deps-nghttp2-update-to-1.57.0.patch BuildRequires: make BuildRequires: python3-devel @@ -722,6 +723,12 @@ end %changelog +* Fri Oct 13 2023 Jan Staněk - 1:16.20.2-3 +- Update version of bundled nghttp2 in spec file + +* Thu Oct 12 2023 Jan Staněk - 1:16.20.2-2 +- Update bundled nghttp2 to 1.57.0 (CVE-2023-44487) + * Wed Aug 30 2023 Zuzana Svetlikova - 1:16.20.2-1 - Rebase to 16.20.2 Resolves: CVE-2023-32002 CVE-2023-32006 CVE-2023-32559