From 92af64870168c76276d49b21e3ecaf41b16074ab Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Sun, 8 Nov 2026 00:36:03 +0000 Subject: [PATCH] deps: update nghttp2 to 1.71.0 --- deps/nghttp2/lib/Makefile.in | 43 +-- deps/nghttp2/lib/includes/Makefile.in | 27 +- deps/nghttp2/lib/includes/nghttp2/nghttp2.h | 41 ++- .../nghttp2/lib/includes/nghttp2/nghttp2ver.h | 4 +- deps/nghttp2/lib/nghttp2_frame.c | 22 +- deps/nghttp2/lib/nghttp2_frame.h | 2 + deps/nghttp2/lib/nghttp2_helper.c | 3 +- deps/nghttp2/lib/nghttp2_map.c | 100 +++---- deps/nghttp2/lib/nghttp2_map.h | 6 +- deps/nghttp2/lib/nghttp2_option.c | 6 + deps/nghttp2/lib/nghttp2_option.h | 5 + deps/nghttp2/lib/nghttp2_session.c | 259 +++++++++++++----- deps/nghttp2/lib/nghttp2_session.h | 5 + 13 files changed, 340 insertions(+), 183 deletions(-) diff --git a/deps/nghttp2/lib/Makefile.in b/deps/nghttp2/lib/Makefile.in index 128fb2912582..aebda670702a 100644 --- a/deps/nghttp2/lib/Makefile.in +++ b/deps/nghttp2/lib/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.5 from Makefile.am. +# Makefile.in generated by automake 1.18.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2021 Free Software Foundation, Inc. +# Copyright (C) 1994-2025 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -86,6 +86,8 @@ am__make_running_with_option = \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +am__rm_f = rm -f $(am__rm_f_notfound) +am__rm_rf = rm -rf $(am__rm_f_notfound) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -141,10 +143,9 @@ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ + { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" LTLIBRARIES = $(lib_LTLIBRARIES) @@ -436,8 +437,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ +am__rm_f_notfound = @am__rm_f_notfound@ am__tar = @am__tar@ am__untar = @am__untar@ +am__xargs_n = @am__xargs_n@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -612,15 +615,13 @@ uninstall-libLTLIBRARIES: done clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + -$(am__rm_f) $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ - test -z "$$locs" || { \ - echo rm -f $${locs}; \ - rm -f $${locs}; \ - } + echo rm -f $${locs}; \ + $(am__rm_f) $${locs} libnghttp2.la: $(libnghttp2_la_OBJECTS) $(libnghttp2_la_DEPENDENCIES) $(EXTRA_libnghttp2_la_DEPENDENCIES) $(AM_V_CCLD)$(libnghttp2_la_LINK) -rpath $(libdir) $(libnghttp2_la_OBJECTS) $(libnghttp2_la_LIBADD) $(LIBS) @@ -660,7 +661,7 @@ distclean-compile: $(am__depfiles_remade): @$(MKDIR_P) $(@D) - @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + @: >>$@ am--depfiles: $(am__depfiles_remade) @@ -813,6 +814,7 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -903,9 +905,9 @@ mostlyclean-generic: clean-generic: distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + -$(am__rm_f) $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES) + -$(am__rm_f) $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -916,7 +918,7 @@ clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-recursive - -rm -f ./$(DEPDIR)/nghttp2_alpn.Plo + -rm -f ./$(DEPDIR)/nghttp2_alpn.Plo -rm -f ./$(DEPDIR)/nghttp2_buf.Plo -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo -rm -f ./$(DEPDIR)/nghttp2_debug.Plo @@ -987,7 +989,7 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive - -rm -f ./$(DEPDIR)/nghttp2_alpn.Plo + -rm -f ./$(DEPDIR)/nghttp2_alpn.Plo -rm -f ./$(DEPDIR)/nghttp2_buf.Plo -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo -rm -f ./$(DEPDIR)/nghttp2_debug.Plo @@ -1055,3 +1057,10 @@ uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgconfigDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: + +# Tell GNU make to disable its built-in pattern rules. +%:: %,v +%:: RCS/%,v +%:: RCS/% +%:: s.% +%:: SCCS/s.% diff --git a/deps/nghttp2/lib/includes/Makefile.in b/deps/nghttp2/lib/includes/Makefile.in index 833a90fb3399..1b115bde50e3 100644 --- a/deps/nghttp2/lib/includes/Makefile.in +++ b/deps/nghttp2/lib/includes/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.5 from Makefile.am. +# Makefile.in generated by automake 1.18.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2021 Free Software Foundation, Inc. +# Copyright (C) 1994-2025 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -93,6 +93,8 @@ am__make_running_with_option = \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +am__rm_f = rm -f $(am__rm_f_notfound) +am__rm_rf = rm -rf $(am__rm_f_notfound) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -168,10 +170,9 @@ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ + { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \ } am__installdirs = "$(DESTDIR)$(includedir)" HEADERS = $(nobase_include_HEADERS) @@ -341,8 +342,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ +am__rm_f_notfound = @am__rm_f_notfound@ am__tar = @am__tar@ am__untar = @am__untar@ +am__xargs_n = @am__xargs_n@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -508,6 +511,7 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -572,8 +576,8 @@ mostlyclean-generic: clean-generic: distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -$(am__rm_f) $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -666,3 +670,10 @@ uninstall-am: uninstall-nobase_includeHEADERS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: + +# Tell GNU make to disable its built-in pattern rules. +%:: %,v +%:: RCS/%,v +%:: RCS/% +%:: s.% +%:: SCCS/s.% diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h index 2a494894d2d7..eac691280ba0 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h @@ -465,10 +465,10 @@ typedef enum { /** * Possible flooding by peer was detected in this HTTP/2 session. * Flooding is measured by how many PING and SETTINGS frames with - * ACK flag set are queued for transmission. These frames are - * response for the peer initiated frames, and peer can cause memory - * exhaustion on server side to send these frames forever and does - * not read network. + * ACK flag set are queued for transmission, and the size of the + * outbound queue. These frames are response for the peer initiated + * frames, and peer can cause memory exhaustion on server side to + * send these frames forever and does not read network. */ NGHTTP2_ERR_FLOODED = -904, /** @@ -3258,6 +3258,21 @@ NGHTTP2_EXTERN void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, uint64_t burst, uint64_t rate); +/** + * @function + * + * This function sets the maximum number of frames allowed in the + * outbound queue. The limit is checked after + * `nghttp2_session_mem_recv2()` and `nghttp2_session_recv()`. The + * current queue size (excluding deferred DATA frames) can be obtained + * by `nghttp2_session_get_outbound_queue_size()`. If it exceeds + * |val|, `nghttp2_session_mem_recv2()` and `nghttp2_session_recv()` + * return :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED`. The default + * value is 5000. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_outbound_queue_size(nghttp2_option *option, size_t val); + /** * @function * @@ -4581,6 +4596,8 @@ NGHTTP2_EXTERN int32_t nghttp2_submit_request( * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The |session| is server session. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The required memory for |nva| of length |nvlen| is too large. * * .. warning:: * @@ -4724,7 +4741,8 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. + * The |stream_id| is 0; The required memory for |nva| of length + * |nvlen| is too large. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` * DATA or HEADERS has been already submitted and not fully * processed yet. Normally, this does not happen, but when @@ -4794,7 +4812,8 @@ nghttp2_submit_response2(nghttp2_session *session, int32_t stream_id, * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. + * The |stream_id| is 0; The required memory for |nva| of length + * |nvlen| is too large. */ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, @@ -4860,7 +4879,8 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, * No stream ID is available because maximum stream ID was * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. + * The |stream_id| is 0; The required memory for |nva| of length + * |nvlen| is too large. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` * DATA or HEADERS has been already submitted and not fully * processed yet. This happens if stream denoted by |stream_id| @@ -5087,8 +5107,8 @@ NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, * copies all the elements in the |iv|. * * While updating individual stream's local window size, if the window - * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - * RST_STREAM is issued against such a stream. + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, GOAWAY + * frame is submitted and the connection will be terminated. * * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is * automatically submitted by the library and application could not @@ -5162,7 +5182,8 @@ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0; The |stream_id| does not designate stream - * that peer initiated. + * that peer initiated; The required memory for |nva| of length + * |nvlen| is too large. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` * The stream was already closed; or the |stream_id| is invalid. * diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h index 6c1a73206f3b..c665394e051c 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.70.0" +#define NGHTTP2_VERSION "1.71.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 0x014600 +#define NGHTTP2_VERSION_NUM 0x014700 #endif /* NGHTTP2VER_H */ diff --git a/deps/nghttp2/lib/nghttp2_frame.c b/deps/nghttp2/lib/nghttp2_frame.c index 657607aed54d..25fd025ab060 100644 --- a/deps/nghttp2/lib/nghttp2_frame.c +++ b/deps/nghttp2/lib/nghttp2_frame.c @@ -1048,8 +1048,8 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { size_t i; - uint8_t *data = NULL; - size_t buflen = 0; + uint8_t *data; + size_t buflen; nghttp2_nv *p; if (nvlen == 0) { @@ -1058,18 +1058,32 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, return 0; } + if (sizeof(nghttp2_nv) > SIZE_MAX / nvlen) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + buflen = sizeof(nghttp2_nv) * nvlen; + for (i = 0; i < nvlen; ++i) { /* + 1 for null-termination */ if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) { + if (nva[i].namelen == SIZE_MAX || + buflen > SIZE_MAX - nva[i].namelen - 1) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + buflen += nva[i].namelen + 1; } if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) { + if (nva[i].valuelen == SIZE_MAX || + buflen > SIZE_MAX - nva[i].valuelen - 1) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + buflen += nva[i].valuelen + 1; } } - buflen += sizeof(nghttp2_nv) * nvlen; - *nva_ptr = nghttp2_mem_malloc(mem, buflen); if (*nva_ptr == NULL) { diff --git a/deps/nghttp2/lib/nghttp2_frame.h b/deps/nghttp2/lib/nghttp2_frame.h index c0917c0df4cc..32631de843f8 100644 --- a/deps/nghttp2/lib/nghttp2_frame.h +++ b/deps/nghttp2/lib/nghttp2_frame.h @@ -597,6 +597,8 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); * * NGHTTP2_ERR_NOMEM * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The required memory for |nva| of length |nvlen| is too large. */ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem); diff --git a/deps/nghttp2/lib/nghttp2_helper.c b/deps/nghttp2/lib/nghttp2_helper.c index 70a80bc6faa7..d4b04ff8d70e 100644 --- a/deps/nghttp2/lib/nghttp2_helper.c +++ b/deps/nghttp2/lib/nghttp2_helper.c @@ -247,7 +247,8 @@ int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, int nghttp2_should_send_window_update(int32_t local_window_size, int32_t recv_window_size) { - return recv_window_size > 0 && recv_window_size >= local_window_size / 2; + return recv_window_size > 0 && local_window_size > 0 && + recv_window_size >= local_window_size / 2; } const char *nghttp2_strerror(int error_code) { diff --git a/deps/nghttp2/lib/nghttp2_map.c b/deps/nghttp2/lib/nghttp2_map.c index b1b1c93c03ad..47a6e0a59e2d 100644 --- a/deps/nghttp2/lib/nghttp2_map.c +++ b/deps/nghttp2/lib/nghttp2_map.c @@ -74,20 +74,19 @@ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr), return 0; } -/* Hasher from - https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs - to maximize the output's sensitivity to all input bits. */ -#define NGHTTP2_MAP_HASHER 0xF1357AEA2E62A9C5ULL -/* 64-bit Fibonacci hashing constant, Golden Ratio constant, to get - the high bits with the good distribution. */ -#define NGHTTP2_MAP_FIBO 0x9E3779B97F4A7C15ULL - -static size_t map_index(const nghttp2_map *map, nghttp2_map_key_type key32) { - uint64_t key = (uint64_t)key32; - - key += map->seed; - key *= NGHTTP2_MAP_HASHER; - return (size_t)((key * NGHTTP2_MAP_FIBO) >> (64 - map->hashbits)); +/* 64-bit finalizer (fmix64) from MurmurHash3 to maximize the output's + sensitivity to all input bits. */ +#define NGHTTP2_MAP_HASHER1 0xFF51AFD7ED558CCDULL +#define NGHTTP2_MAP_HASHER2 0xC4CEB9FE1A85EC53ULL + +static size_t map_index(const nghttp2_map *map, nghttp2_map_key_type key) { + key ^= map->seed; + key ^= key >> 33; + key *= NGHTTP2_MAP_HASHER1; + key ^= key >> 33; + key *= NGHTTP2_MAP_HASHER2; + key ^= key >> 33; + return (size_t)(key >> (64 - map->hashbits)); } #ifndef WIN32 @@ -109,17 +108,17 @@ void nghttp2_map_print_distance(const nghttp2_map *map) { } idx = map_index(map, map->keys[i]); - fprintf(stderr, "@%zu key=%d base=%zu distance=%u\n", i, map->keys[i], idx, - map->psl[i] - 1); + fprintf(stderr, "@%zu key=%" PRIu64 " base=%zu distance=%zu\n", i, + map->keys[i], idx, map->psl[i] - 1); } } #endif /* !defined(WIN32) */ static void map_set_entry(nghttp2_map *map, size_t idx, - nghttp2_map_key_type key, void *data, size_t psl) { + nghttp2_map_key_type key, size_t psl, void *data) { map->keys[idx] = key; + map->psl[idx] = psl; map->data[idx] = data; - map->psl[idx] = (uint8_t)psl; } #define NGHTTP2_SWAP(TYPE, A, B) \ @@ -131,15 +130,15 @@ static void map_set_entry(nghttp2_map *map, size_t idx, } while (0) /* - * map_insert inserts |key| and |data| to |map|, and returns the index - * where the pair is stored if it succeeds. Otherwise, it returns one - * of the following negative error codes: + * map_insert inserts |key| and |data| to |map|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: * * NGHTTP2_ERR_INVALID_ARGUMENT * The another data associated to |key| is already present. */ -static nghttp2_ssize map_insert(nghttp2_map *map, nghttp2_map_key_type key, - void *data) { +static int map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { size_t idx = map_index(map, key); size_t mask = ((size_t)1 << map->hashbits) - 1; size_t psl = 1; @@ -149,16 +148,16 @@ static nghttp2_ssize map_insert(nghttp2_map *map, nghttp2_map_key_type key, kpsl = map->psl[idx]; if (kpsl == 0) { - map_set_entry(map, idx, key, data, psl); + map_set_entry(map, idx, key, psl, data); ++map->size; - return (nghttp2_ssize)idx; + return 0; } if (psl > kpsl) { NGHTTP2_SWAP(nghttp2_map_key_type, &key, &map->keys[idx]); + NGHTTP2_SWAP(size_t, &psl, &map->psl[idx]); NGHTTP2_SWAP(void *, &data, &map->data[idx]); - NGHTTP2_SWAP(uint8_t, &psl, &map->psl[idx]); } else if (map->keys[idx] == key) { /* This check ensures that no duplicate keys are inserted. But it is just a waste after first swap or if this function is @@ -180,14 +179,14 @@ static nghttp2_ssize map_insert(nghttp2_map *map, nghttp2_map_key_type key, static int map_resize(nghttp2_map *map, size_t new_hashbits) { size_t i; size_t tablelen; - nghttp2_ssize idx; nghttp2_map new_map = { .mem = map->mem, .seed = map->seed, .hashbits = new_hashbits, }; void *buf; - (void)idx; + int rv; + (void)rv; if (new_hashbits > NGHTTP2_MAP_MAX_HASHBITS) { return NGHTTP2_ERR_NOMEM; @@ -196,16 +195,16 @@ static int map_resize(nghttp2_map *map, size_t new_hashbits) { tablelen = (size_t)1 << new_hashbits; buf = nghttp2_mem_calloc(map->mem, tablelen, - sizeof(nghttp2_map_key_type) + sizeof(void *) + - sizeof(uint8_t)); - if (buf == NULL) { + sizeof(nghttp2_map_key_type) + sizeof(size_t) + + sizeof(void *)); + if (!buf) { return NGHTTP2_ERR_NOMEM; } new_map.keys = buf; - new_map.data = - (void *)((uint8_t *)new_map.keys + tablelen * sizeof(nghttp2_map_key_type)); - new_map.psl = (uint8_t *)new_map.data + tablelen * sizeof(void *); + new_map.psl = (size_t *)(void *)((uint8_t *)new_map.keys + + tablelen * sizeof(nghttp2_map_key_type)); + new_map.data = (void *)((uint8_t *)new_map.psl + tablelen * sizeof(size_t)); if (map->size) { tablelen = (size_t)1 << map->hashbits; @@ -215,31 +214,26 @@ static int map_resize(nghttp2_map *map, size_t new_hashbits) { continue; } - idx = map_insert(&new_map, map->keys[i], map->data[i]); + rv = map_insert(&new_map, map->keys[i], map->data[i]); /* map_insert must not fail because all keys are unique during resize. */ - assert(idx >= 0); + assert(0 == rv); } } nghttp2_mem_free(map->mem, map->keys); map->keys = new_map.keys; - map->data = new_map.data; map->psl = new_map.psl; + map->data = new_map.data; map->hashbits = new_hashbits; return 0; } -/* NGHTTP2_MAX_PSL_RESIZE_THRESH is the maximum psl threshold. If - reached, resize the table. */ -#define NGHTTP2_MAX_PSL_RESIZE_THRESH 128 - int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { int rv; size_t tablelen; - nghttp2_ssize idx; assert(data); @@ -256,27 +250,9 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { if (rv != 0) { return rv; } - - idx = map_insert(map, key, data); - if (idx < 0) { - return (int)idx; - } - - return 0; - } - - idx = map_insert(map, key, data); - if (idx < 0) { - return (int)idx; - } - - /* Resize if psl reaches really large value which is almost - improbable, but just in case. */ - if (map->psl[idx] - 1 < NGHTTP2_MAX_PSL_RESIZE_THRESH) { - return 0; } - return map_resize(map, map->hashbits + 1); + return map_insert(map, key, data); } void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) { @@ -334,7 +310,7 @@ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { break; } - map_set_entry(map, dest, map->keys[idx], map->data[idx], kpsl - 1); + map_set_entry(map, dest, map->keys[idx], kpsl - 1, map->data[idx]); dest = idx; diff --git a/deps/nghttp2/lib/nghttp2_map.h b/deps/nghttp2/lib/nghttp2_map.h index c47b279345de..864ecdbce818 100644 --- a/deps/nghttp2/lib/nghttp2_map.h +++ b/deps/nghttp2/lib/nghttp2_map.h @@ -36,15 +36,15 @@ /* Implementation of unordered map */ -typedef int32_t nghttp2_map_key_type; +typedef uint64_t nghttp2_map_key_type; typedef struct nghttp2_map { nghttp2_map_key_type *keys; - void **data; /* psl is the Probe Sequence Length. 0 has special meaning that the element is not stored at i-th position if psl[i] == 0. Because of this, the actual psl value is psl[i] - 1 if psl[i] > 0. */ - uint8_t *psl; + size_t *psl; + void **data; nghttp2_mem *mem; uint64_t seed; size_t size; diff --git a/deps/nghttp2/lib/nghttp2_option.c b/deps/nghttp2/lib/nghttp2_option.c index 7e44a241ba1e..a1aa2738eb71 100644 --- a/deps/nghttp2/lib/nghttp2_option.c +++ b/deps/nghttp2/lib/nghttp2_option.c @@ -162,3 +162,9 @@ void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, option->glitch_burst = burst; option->glitch_rate = rate; } + +void nghttp2_option_set_max_outbound_queue_size(nghttp2_option *option, + size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_QUEUE_SIZE; + option->max_outbound_queue_size = val; +} diff --git a/deps/nghttp2/lib/nghttp2_option.h b/deps/nghttp2/lib/nghttp2_option.h index db739b609edf..d53dd98f0bea 100644 --- a/deps/nghttp2/lib/nghttp2_option.h +++ b/deps/nghttp2/lib/nghttp2_option.h @@ -73,6 +73,7 @@ #define NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT 0x8000U #define NGHTTP2_OPT_MAX_CONTINUATIONS 0x010000U #define NGHTTP2_OPT_GLITCH_RATE_LIMIT 0x020000U +#define NGHTTP2_OPT_MAX_OUTBOUND_QUEUE_SIZE 0x040000U /** * Struct to store option values for nghttp2_session. @@ -108,6 +109,10 @@ struct nghttp2_option { * NGHTTP2_OPT_MAX_CONTINUATIONS */ size_t max_continuations; + /** + * NGHTTP2_OPT_MAX_OUTBOUND_QUEUE_SIZE + */ + size_t max_outbound_queue_size; /** * Bitwise OR of NGHTTP2_OPT_* values to determine which fields are * specified. diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c index f4c2afc8009f..bd4415f49336 100644 --- a/deps/nghttp2/lib/nghttp2_session.c +++ b/deps/nghttp2/lib/nghttp2_session.c @@ -285,7 +285,8 @@ nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; - stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); + stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, + (nghttp2_map_key_type)stream_id); if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || stream->state == NGHTTP2_STREAM_IDLE) { @@ -297,7 +298,8 @@ nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, int32_t stream_id) { - return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); + return (nghttp2_stream *)nghttp2_map_find(&session->streams, + (nghttp2_map_key_type)stream_id); } static void session_inbound_frame_reset(nghttp2_session *session) { @@ -498,6 +500,8 @@ static int session_new(nghttp2_session **session_ptr, (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS; (*session_ptr)->max_continuations = NGHTTP2_DEFAULT_MAX_CONTINUATIONS; + (*session_ptr)->max_outbound_queue_size = + NGHTTP2_DEFAULT_MAX_OUTBOUND_QUEUE_SIZE; if (option) { if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && @@ -578,6 +582,10 @@ static int session_new(nghttp2_session **session_ptr, nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim, option->glitch_burst, option->glitch_rate); } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_QUEUE_SIZE) { + (*session_ptr)->max_outbound_queue_size = option->max_outbound_queue_size; + } } rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, @@ -1245,7 +1253,8 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, stream_user_data); stream->seq = session->stream_seq++; - rv = nghttp2_map_insert(&session->streams, stream_id, stream); + rv = nghttp2_map_insert(&session->streams, (nghttp2_map_key_type)stream_id, + stream); if (rv != 0) { nghttp2_stream_free(stream); nghttp2_mem_free(mem, stream); @@ -1366,7 +1375,8 @@ void nghttp2_session_destroy_stream(nghttp2_session *session, session_ob_data_remove(session, stream); } - nghttp2_map_remove(&session->streams, stream->stream_id); + nghttp2_map_remove(&session->streams, + (nghttp2_map_key_type)stream->stream_id); nghttp2_stream_free(stream); nghttp2_mem_free(mem, stream); } @@ -2510,13 +2520,6 @@ static int session_after_frame_sent1(nghttp2_session *session) { aux_data = &item->aux_data.data; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - /* We update flow control window after a frame was completely - sent. This is possible because we choose payload length not to - exceed the window */ - session->remote_window_size -= (int32_t)frame->hd.length; - if (stream) { - stream->remote_window_size -= (int32_t)frame->hd.length; - } if (stream && aux_data->eof) { session_detach_stream_item(session, stream); @@ -2823,6 +2826,20 @@ static int session_call_send_data(nghttp2_session *session, } } +static void session_update_remote_window(nghttp2_session *session, + int32_t stream_id, size_t datalen) { + nghttp2_stream *stream; + + session->remote_window_size -= (int32_t)datalen; + + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return; + } + + stream->remote_window_size -= (int32_t)datalen; +} + static nghttp2_ssize nghttp2_session_mem_send_internal(nghttp2_session *session, const uint8_t **data_ptr, int fast_cb) { @@ -3081,6 +3098,9 @@ static nghttp2_ssize nghttp2_session_mem_send_internal(nghttp2_session *session, return 0; } + session_update_remote_window(session, frame->hd.stream_id, + frame->hd.length); + pause = (rv == NGHTTP2_ERR_PAUSE); rv = session_after_frame_sent1(session); @@ -3130,6 +3150,7 @@ nghttp2_ssize nghttp2_session_mem_send2(nghttp2_session *session, const uint8_t **data_ptr) { int rv; nghttp2_ssize len; + const nghttp2_frame *frame; *data_ptr = NULL; @@ -3139,6 +3160,14 @@ nghttp2_ssize nghttp2_session_mem_send2(nghttp2_session *session, } if (session->aob.item) { + frame = &session->aob.item->frame; + + if (frame->hd.type == NGHTTP2_DATA && + (size_t)len == NGHTTP2_FRAME_HDLEN + frame->hd.length) { + session_update_remote_window(session, frame->hd.stream_id, + frame->hd.length); + } + /* We have to call session_after_frame_sent1 here to handle stream closure upon transmission of frames. Otherwise, END_STREAM may be reached to client before we call nghttp2_session_mem_send @@ -3158,6 +3187,7 @@ int nghttp2_session_send(nghttp2_session *session) { nghttp2_ssize datalen; nghttp2_ssize sentlen; nghttp2_bufs *framebufs; + const nghttp2_frame *frame; framebufs = &session->aob.framebufs; @@ -3182,6 +3212,17 @@ int nghttp2_session_send(nghttp2_session *session) { } return NGHTTP2_ERR_CALLBACK_FAILURE; } + + if (sentlen > 0 && session->aob.item) { + frame = &session->aob.item->frame; + + if (frame->hd.type == NGHTTP2_DATA && + (size_t)datalen == NGHTTP2_FRAME_HDLEN + frame->hd.length) { + session_update_remote_window(session, frame->hd.stream_id, + frame->hd.length); + } + } + /* Rewind the offset to the amount of unsent bytes */ framebufs->cur->buf.pos -= datalen - sentlen; } @@ -4022,6 +4063,22 @@ int nghttp2_session_on_headers_received(nghttp2_session *session, return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); } + + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) && + (session->server || stream->status_code / 100 == 2)) { + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (session->iframe.state == NGHTTP2_IB_IGN_ALL) { + return 0; + } + + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_PROTO); + } + if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { if (stream->state == NGHTTP2_STREAM_OPENED) { rv = session_call_on_begin_headers(session, frame); @@ -4199,6 +4256,11 @@ static int update_local_initial_window_size_func(void *entry, void *ptr) { return NGHTTP2_ERR_FLOW_CONTROL; } + if ((int64_t)stream->local_window_size - (int64_t)stream->recv_window_size > + NGHTTP2_MAX_WINDOW_SIZE) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + if (stream->window_update_queued) { return 0; } @@ -4633,6 +4695,13 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session, session, frame, NGHTTP2_ERR_STREAM_CLOSED, "PUSH_PROMISE: stream closed"); } + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) && + stream->status_code / 100 == 2) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "PUSH_PROMISE: not allowed in CONNECT stream"); + } + promised_stream = nghttp2_session_open_stream( session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_RESERVED, NULL); @@ -4899,6 +4968,12 @@ int nghttp2_session_on_priority_update_received(nghttp2_session *session, "PRIORITY_UPDATE: max concurrent streams exceeded"); } + if (session->num_idle_streams + session->num_incoming_streams >= + session->pending_local_max_concurrent_stream) { + /* Ignore priority hints for excessive streams. */ + return session_call_on_frame_received(session, frame); + } + stream = nghttp2_session_open_stream(session, priority_update->stream_id, NGHTTP2_FLAG_NONE, NGHTTP2_STREAM_IDLE, NULL); @@ -5147,7 +5222,8 @@ static int session_update_consumed_size(nghttp2_session *session, int32_t recv_size; int rv; - if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { + if (delta_size > NGHTTP2_MAX_WINDOW_SIZE || + (size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { return nghttp2_session_terminate_session(session, NGHTTP2_FLOW_CONTROL_ERROR); } @@ -5416,6 +5492,42 @@ inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, return (nghttp2_ssize)(readlen); } +static int session_update_recv_window_on_data_hd(nghttp2_session *session) { + const nghttp2_inbound_frame *iframe = &session->iframe; + int32_t stream_id = iframe->frame.hd.stream_id; + size_t datalen = iframe->frame.hd.length; + nghttp2_stream *stream; + int rv; + + if (datalen == 0) { + return 0; + } + + rv = nghttp2_session_update_recv_connection_window_size(session, datalen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return 0; + } + + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return 0; + } + + rv = nghttp2_session_update_recv_stream_window_size( + session, stream, datalen, + !(iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) && + stream->state != NGHTTP2_STREAM_CLOSING); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + static const uint8_t static_in[] = {0}; ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, @@ -5423,8 +5535,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return (ssize_t)nghttp2_session_mem_recv2(session, in, inlen); } -nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, - const uint8_t *in, size_t inlen) { +static nghttp2_ssize session_mem_recv(nghttp2_session *session, + const uint8_t *in, size_t inlen) { const uint8_t *first, *last; nghttp2_inbound_frame *iframe = &session->iframe; size_t readlen; @@ -5563,10 +5675,28 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, DEBUGF("recv: DATA not allowed stream_id=%d\n", iframe->frame.hd.stream_id); + rv = session_update_recv_window_on_data_hd(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + iframe->state = NGHTTP2_IB_IGN_DATA; break; } + rv = session_update_recv_window_on_data_hd(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if (rv < 0) { rv = nghttp2_session_terminate_session_with_reason( @@ -6671,6 +6801,19 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, return NGHTTP2_ERR_TOO_MANY_CONTINUATIONS; } + if (cont_hd.length > session->local_settings.max_frame_size) { + DEBUGF("recv: length is too large %zu > %u\n", cont_hd.length, + session->local_settings.max_frame_size); + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (nghttp2_ssize)inlen; + } + /* CONTINUATION won't bear NGHTTP2_PADDED flag */ iframe->frame.hd.flags = @@ -6711,16 +6854,6 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, return (nghttp2_ssize)(in - first); } - /* Pad Length field is subject to flow control */ - rv = nghttp2_session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (nghttp2_ssize)inlen; - } - /* Pad Length field is consumed immediately */ rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); @@ -6733,21 +6866,6 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, return (nghttp2_ssize)inlen; } - stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); - if (stream) { - rv = nghttp2_session_update_recv_stream_window_size( - session, stream, readlen, - iframe->payloadleft || - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (nghttp2_ssize)inlen; - } - } - busy = 1; padlen = inbound_frame_compute_pad(iframe); @@ -6800,28 +6918,6 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, if (readlen > 0) { nghttp2_ssize data_readlen; - rv = - nghttp2_session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (nghttp2_ssize)inlen; - } - - rv = nghttp2_session_update_recv_stream_window_size( - session, stream, readlen, - iframe->payloadleft || - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (nghttp2_ssize)inlen; - } - data_readlen = inbound_frame_effective_readlen(iframe, iframe->payloadleft, readlen); @@ -6933,18 +7029,6 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, iframe->payloadleft); if (readlen > 0) { - /* Update connection-level flow control window for ignored - DATA frame too */ - rv = - nghttp2_session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (nghttp2_ssize)inlen; - } - if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { /* Ignored DATA is considered as "consumed" immediately. */ rv = session_update_connection_consumed_size(session, readlen); @@ -7094,6 +7178,23 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, return (nghttp2_ssize)(in - first); } +nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + const uint8_t *in, size_t inlen) { + nghttp2_ssize nread; + + nread = session_mem_recv(session, in, inlen); + if (nread < 0 && nghttp2_is_fatal((int)nread)) { + return nread; + } + + if (nghttp2_session_get_outbound_queue_size(session) > + session->max_outbound_queue_size) { + return NGHTTP2_ERR_FLOODED; + } + + return nread; +} + int nghttp2_session_recv(nghttp2_session *session) { uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; while (1) { @@ -7696,13 +7797,13 @@ nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; - int32_t size; + int64_t size; stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return -1; } - size = stream->local_window_size - stream->recv_window_size; + size = (int64_t)stream->local_window_size - (int64_t)stream->recv_window_size; /* size could be negative if local endpoint reduced SETTINGS_INITIAL_WINDOW_SIZE */ @@ -7710,7 +7811,13 @@ int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session, return 0; } - return size; + /* In this case, GOAWAY with NGHTTP2_FLOW_CONTROL_ERROR has already + been queued or sent */ + if (size > NGHTTP2_MAX_WINDOW_SIZE) { + return NGHTTP2_MAX_WINDOW_SIZE; + } + + return (int32_t)size; } int32_t diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h index 1b04464cda8e..c385ddf20d5b 100644 --- a/deps/nghttp2/lib/nghttp2_session.h +++ b/deps/nghttp2/lib/nghttp2_session.h @@ -110,6 +110,9 @@ typedef struct { HEADER frame. */ #define NGHTTP2_DEFAULT_MAX_CONTINUATIONS 8 +/* The default maximum size of the outbound queue. */ +#define NGHTTP2_DEFAULT_MAX_OUTBOUND_QUEUE_SIZE 5000 + /* Internal state when receiving incoming frame */ typedef enum { /* Receiving frame header */ @@ -280,6 +283,8 @@ struct nghttp2_session { /* The number of CONTINUATION frames following an incoming HEADER frame. This variable is reset when END_HEADERS flag is seen. */ size_t num_continuations; + /* The maximum size of the outbound queue. */ + size_t max_outbound_queue_size; /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ uint32_t next_stream_id; /* The last stream ID this session initiated. For client session,