diff options
author | robot-contrib <robot-contrib@yandex-team.ru> | 2022-06-28 21:27:04 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.ru> | 2022-06-28 21:27:04 +0300 |
commit | e10c657371a057bbe8e327938ca361cb7aa9b497 (patch) | |
tree | a461fe86cde0259e83d083ff50ed5ec188efb774 | |
parent | 2907c9b99276f9d2adb27d8e3ee4a31c09ed15ee (diff) | |
download | ydb-e10c657371a057bbe8e327938ca361cb7aa9b497.tar.gz |
Update contrib/libs/nghttp2 to 1.48.0
ref:b582ac8ce36a77f41a7c33b7a5418e0f28488595
27 files changed, 2132 insertions, 99 deletions
diff --git a/contrib/libs/nghttp2/.yandex_meta/devtools.copyrights.report b/contrib/libs/nghttp2/.yandex_meta/devtools.copyrights.report index d334168a2b..624fd29a95 100644 --- a/contrib/libs/nghttp2/.yandex_meta/devtools.copyrights.report +++ b/contrib/libs/nghttp2/.yandex_meta/devtools.copyrights.report @@ -29,6 +29,18 @@ # FILE_INCLUDE - include all file data into licenses text file # ======================= +KEEP COPYRIGHT_SERVICE_LABEL 3782c9399af9718cb85783105b7784ae +BELONGS ya.make + License text: + Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free + Software Foundation, Inc. + Scancode info: + Original SPDX id: COPYRIGHT_SERVICE_LABEL + Score : 100.00 + Match type : COPYRIGHT + Files with this license: + INSTALL [4:5] + KEEP COPYRIGHT_SERVICE_LABEL 3e6f7a2dce9d6519bf3f2a9eae7e5b6b BELONGS ya.make License text: @@ -110,18 +122,6 @@ BELONGS ya.make lib/nghttp2_submit.c [4:4] lib/nghttp2_version.c [4:4] -KEEP COPYRIGHT_SERVICE_LABEL 71499817b900aaf04d853fdffdc657b0 -BELONGS ya.make - License text: - Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software - Foundation, Inc. - Scancode info: - Original SPDX id: COPYRIGHT_SERVICE_LABEL - Score : 100.00 - Match type : COPYRIGHT - Files with this license: - INSTALL [4:5] - KEEP COPYRIGHT_SERVICE_LABEL 7e2d5f433f62e38814cee9a67e1c3cd0 BELONGS ya.make License text: @@ -192,6 +192,19 @@ BELONGS ya.make lib/nghttp2_map.c [4:5] lib/nghttp2_map.h [4:5] +KEEP COPYRIGHT_SERVICE_LABEL bc9503ec48dfbffaed72fd039643c15f +BELONGS ya.make + License text: + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 nghttp2 contributors + Scancode info: + Original SPDX id: COPYRIGHT_SERVICE_LABEL + Score : 100.00 + Match type : COPYRIGHT + Files with this license: + lib/nghttp2_extpri.c [4:5] + lib/nghttp2_extpri.h [4:5] + KEEP COPYRIGHT_SERVICE_LABEL c812bfe524d8bc74ad8cf40a45902020 BELONGS ya.make License text: @@ -204,3 +217,16 @@ BELONGS ya.make Files with this license: lib/nghttp2_map.c [4:5] lib/nghttp2_map.h [4:5] + +KEEP COPYRIGHT_SERVICE_LABEL d89f9db9c9889910b44184610d7947e1 +BELONGS ya.make + License text: + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 nghttp2 contributors + Scancode info: + Original SPDX id: COPYRIGHT_SERVICE_LABEL + Score : 100.00 + Match type : COPYRIGHT + Files with this license: + lib/nghttp2_extpri.c [4:5] + lib/nghttp2_extpri.h [4:5] diff --git a/contrib/libs/nghttp2/.yandex_meta/devtools.licenses.report b/contrib/libs/nghttp2/.yandex_meta/devtools.licenses.report index dec415cb2d..f729a4c5ca 100644 --- a/contrib/libs/nghttp2/.yandex_meta/devtools.licenses.report +++ b/contrib/libs/nghttp2/.yandex_meta/devtools.licenses.report @@ -69,7 +69,7 @@ BELONGS ya.make KEEP MIT a3a8f7feced3937b87cd090ba748e24b BELONGS ya.make -FILE_INCLUDE AUTHORS found in files: lib/includes/nghttp2/nghttp2.h at line 20, lib/includes/nghttp2/nghttp2ver.h at line 20, lib/nghttp2_buf.c at line 20, lib/nghttp2_buf.h at line 20, lib/nghttp2_callbacks.c at line 20, lib/nghttp2_callbacks.h at line 20, lib/nghttp2_debug.c at line 20, lib/nghttp2_debug.h at line 20, lib/nghttp2_frame.c at line 20, lib/nghttp2_frame.h at line 20, lib/nghttp2_hd.c at line 20, lib/nghttp2_hd.h at line 20, lib/nghttp2_hd_huffman.c at line 20, lib/nghttp2_hd_huffman.h at line 20, lib/nghttp2_hd_huffman_data.c at line 20, lib/nghttp2_helper.c at line 20, lib/nghttp2_helper.h at line 20, lib/nghttp2_http.c at line 20, lib/nghttp2_http.h at line 20, lib/nghttp2_int.h at line 20, lib/nghttp2_map.c at line 21, lib/nghttp2_map.h at line 21, lib/nghttp2_mem.c at line 20, lib/nghttp2_mem.h at line 20, lib/nghttp2_net.h at line 20, lib/nghttp2_npn.c at line 20, lib/nghttp2_npn.h at line 20, lib/nghttp2_option.c at line 20, lib/nghttp2_option.h at line 20, lib/nghttp2_outbound_item.c at line 20, lib/nghttp2_outbound_item.h at line 20, lib/nghttp2_pq.c at line 20, lib/nghttp2_pq.h at line 20, lib/nghttp2_priority_spec.c at line 20, lib/nghttp2_priority_spec.h at line 20, lib/nghttp2_queue.c at line 20, lib/nghttp2_queue.h at line 20, lib/nghttp2_rcbuf.c at line 20, lib/nghttp2_rcbuf.h at line 20, lib/nghttp2_session.c at line 20, lib/nghttp2_session.h at line 20, lib/nghttp2_stream.c at line 20, lib/nghttp2_stream.h at line 20, lib/nghttp2_submit.c at line 20, lib/nghttp2_submit.h at line 20, lib/nghttp2_version.c at line 20 +FILE_INCLUDE AUTHORS found in files: lib/includes/nghttp2/nghttp2.h at line 20, lib/includes/nghttp2/nghttp2ver.h at line 20, lib/nghttp2_buf.c at line 20, lib/nghttp2_buf.h at line 20, lib/nghttp2_callbacks.c at line 20, lib/nghttp2_callbacks.h at line 20, lib/nghttp2_debug.c at line 20, lib/nghttp2_debug.h at line 20, lib/nghttp2_extpri.c at line 21, lib/nghttp2_extpri.h at line 21, lib/nghttp2_frame.c at line 20, lib/nghttp2_frame.h at line 20, lib/nghttp2_hd.c at line 20, lib/nghttp2_hd.h at line 20, lib/nghttp2_hd_huffman.c at line 20, lib/nghttp2_hd_huffman.h at line 20, lib/nghttp2_hd_huffman_data.c at line 20, lib/nghttp2_helper.c at line 20, lib/nghttp2_helper.h at line 20, lib/nghttp2_http.c at line 20, lib/nghttp2_http.h at line 20, lib/nghttp2_int.h at line 20, lib/nghttp2_map.c at line 21, lib/nghttp2_map.h at line 21, lib/nghttp2_mem.c at line 20, lib/nghttp2_mem.h at line 20, lib/nghttp2_net.h at line 20, lib/nghttp2_npn.c at line 20, lib/nghttp2_npn.h at line 20, lib/nghttp2_option.c at line 20, lib/nghttp2_option.h at line 20, lib/nghttp2_outbound_item.c at line 20, lib/nghttp2_outbound_item.h at line 20, lib/nghttp2_pq.c at line 20, lib/nghttp2_pq.h at line 20, lib/nghttp2_priority_spec.c at line 20, lib/nghttp2_priority_spec.h at line 20, lib/nghttp2_queue.c at line 20, lib/nghttp2_queue.h at line 20, lib/nghttp2_rcbuf.c at line 20, lib/nghttp2_rcbuf.h at line 20, lib/nghttp2_session.c at line 20, lib/nghttp2_session.h at line 20, lib/nghttp2_stream.c at line 20, lib/nghttp2_stream.h at line 20, lib/nghttp2_submit.c at line 20, lib/nghttp2_submit.h at line 20, lib/nghttp2_version.c at line 20 Note: matched license text is too long. Read it in the source files. Scancode info: Original SPDX id: MIT @@ -85,6 +85,8 @@ FILE_INCLUDE AUTHORS found in files: lib/includes/nghttp2/nghttp2.h at line 20, lib/nghttp2_callbacks.h [6:23] lib/nghttp2_debug.c [6:23] lib/nghttp2_debug.h [6:23] + lib/nghttp2_extpri.c [7:24] + lib/nghttp2_extpri.h [7:24] lib/nghttp2_frame.c [6:23] lib/nghttp2_frame.h [6:23] lib/nghttp2_hd.c [6:23] diff --git a/contrib/libs/nghttp2/.yandex_meta/licenses.list.txt b/contrib/libs/nghttp2/.yandex_meta/licenses.list.txt index 7b1b4f5ebc..83fa41f7b9 100644 --- a/contrib/libs/nghttp2/.yandex_meta/licenses.list.txt +++ b/contrib/libs/nghttp2/.yandex_meta/licenses.list.txt @@ -1,6 +1,6 @@ ====================COPYRIGHT==================== - Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software -Foundation, Inc. + Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +Software Foundation, Inc. ====================COPYRIGHT==================== @@ -37,6 +37,11 @@ Foundation, Inc. ====================COPYRIGHT==================== + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 nghttp2 contributors + + +====================COPYRIGHT==================== Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors diff --git a/contrib/libs/nghttp2/CMakeLists.txt b/contrib/libs/nghttp2/CMakeLists.txt index a3f00757c3..614e867f18 100644 --- a/contrib/libs/nghttp2/CMakeLists.txt +++ b/contrib/libs/nghttp2/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources(contrib-libs-nghttp2 PRIVATE ${CMAKE_SOURCE_DIR}/contrib/libs/nghttp2/lib/nghttp2_buf.c ${CMAKE_SOURCE_DIR}/contrib/libs/nghttp2/lib/nghttp2_callbacks.c ${CMAKE_SOURCE_DIR}/contrib/libs/nghttp2/lib/nghttp2_debug.c + ${CMAKE_SOURCE_DIR}/contrib/libs/nghttp2/lib/nghttp2_extpri.c ${CMAKE_SOURCE_DIR}/contrib/libs/nghttp2/lib/nghttp2_frame.c ${CMAKE_SOURCE_DIR}/contrib/libs/nghttp2/lib/nghttp2_hd.c ${CMAKE_SOURCE_DIR}/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.c diff --git a/contrib/libs/nghttp2/INSTALL b/contrib/libs/nghttp2/INSTALL index 8865734f81..e82fd21de2 100644 --- a/contrib/libs/nghttp2/INSTALL +++ b/contrib/libs/nghttp2/INSTALL @@ -1,8 +1,8 @@ Installation Instructions ************************* - Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software -Foundation, Inc. + Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright @@ -225,7 +225,7 @@ order to use an ANSI C compiler: and if that doesn't work, install pre-built binaries of GCC for HP-UX. - HP-UX 'make' updates targets which have the same time stamps as their + HP-UX 'make' updates targets which have the same timestamps as their prerequisites, which makes it generally unusable when shipped generated files such as 'configure' are involved. Use GNU 'make' instead. diff --git a/contrib/libs/nghttp2/README.rst b/contrib/libs/nghttp2/README.rst index 84433bd0bb..002377a7d7 100644 --- a/contrib/libs/nghttp2/README.rst +++ b/contrib/libs/nghttp2/README.rst @@ -151,11 +151,11 @@ To enable the experimental HTTP/3 support for h2load and nghttpx, the following libraries are required: * `OpenSSL with QUIC support - <https://github.com/quictls/openssl/tree/OpenSSL_1_1_1m+quic>`_; or + <https://github.com/quictls/openssl/tree/OpenSSL_1_1_1p+quic>`_; or `BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit - 36a41bf0bf2dd3176f8780e09c03585351f29963) -* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ >= 0.2.0 -* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 0.2.0 + 27ffcc6e19bbafddf1b59ec0bc6df2904de7eb2c) +* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ >= 0.6.0 +* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 0.4.0 Use ``--enable-http3`` configure option to enable HTTP/3 feature for h2load and nghttpx. @@ -170,7 +170,7 @@ Use ``--with-libbpf`` configure option to build eBPF program. libelf-dev is needed to build libbpf. For Ubuntu 20.04, you can build libbpf from `the source code -<https://github.com/libbpf/libbpf/releases/tag/v0.7.0>`_. nghttpx +<https://github.com/libbpf/libbpf/releases/tag/v0.8.0>`_. nghttpx requires eBPF program for reloading its configuration and hot swapping its executable. @@ -355,7 +355,7 @@ Build custom OpenSSL: .. code-block:: text - $ git clone --depth 1 -b OpenSSL_1_1_1m+quic https://github.com/quictls/openssl + $ git clone --depth 1 -b OpenSSL_1_1_1p+quic https://github.com/quictls/openssl $ cd openssl $ ./config --prefix=$PWD/build --openssldir=/etc/ssl $ make -j$(nproc) @@ -366,7 +366,7 @@ Build nghttp3: .. code-block:: text - $ git clone --depth 1 -b v0.2.0 https://github.com/ngtcp2/nghttp3 + $ git clone --depth 1 -b v0.5.0 https://github.com/ngtcp2/nghttp3 $ cd nghttp3 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only @@ -378,7 +378,7 @@ Build ngtcp2: .. code-block:: text - $ git clone --depth 1 -b v0.2.0 https://github.com/ngtcp2/ngtcp2 + $ git clone --depth 1 -b v0.6.0 https://github.com/ngtcp2/ngtcp2 $ cd ngtcp2 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only \ @@ -392,7 +392,7 @@ from source: .. code-block:: text - $ git clone --depth 1 -b v0.7.0 https://github.com/libbpf/libbpf + $ git clone --depth 1 -b v0.8.0 https://github.com/libbpf/libbpf $ cd libbpf $ PREFIX=$PWD/build make -C src install $ cd .. diff --git a/contrib/libs/nghttp2/config.h b/contrib/libs/nghttp2/config.h index 42f302a5e2..523d459643 100644 --- a/contrib/libs/nghttp2/config.h +++ b/contrib/libs/nghttp2/config.h @@ -259,7 +259,7 @@ #define PACKAGE_NAME "nghttp2" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "nghttp2 1.47.0" +#define PACKAGE_STRING "nghttp2 1.48.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "nghttp2" @@ -268,7 +268,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.47.0" +#define PACKAGE_VERSION "1.48.0" /* The size of `int *', as computed by sizeof. */ #define SIZEOF_INT_P 8 @@ -373,7 +373,7 @@ /* Version number of package */ -#define VERSION "1.47.0" +#define VERSION "1.48.0" /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ diff --git a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h b/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h index 04321a652f..2e87c481bb 100644 --- a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h +++ b/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h @@ -634,7 +634,11 @@ typedef enum { * The ORIGIN frame, which is defined by `RFC 8336 * <https://tools.ietf.org/html/rfc8336>`_. */ - 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 <https://tools.ietf.org/html/rfc8441>`_) */ - 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 */ @@ -2693,6 +2701,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); @@ -2722,6 +2735,24 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, /** * @function * + * This option, if set to nonzero, allows server to fallback to + * :rfc:`7540` priorities if SETTINGS_NO_RFC7540_PRIORITIES was not + * received from client, and server submitted + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * = 1 via `nghttp2_submit_settings()`. Most of the advanced + * functionality for RFC 7540 priorities are still disabled. This + * fallback only enables the minimal feature set of RFC 7540 + * priorities to deal with priority signaling from client. + * + * Client session ignores this option. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, + int val); + +/** + * @function + * * Initializes |*session_ptr| for client use. The all members of * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| * does not store |callbacks|. The |user_data| is an arbitrary user @@ -3589,6 +3620,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 +3668,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 +3878,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 +4103,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 +4235,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: * @@ -4199,6 +4255,61 @@ nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, 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 * * Submits RST_STREAM frame to cancel/reject the stream |stream_id| @@ -4723,6 +4834,108 @@ NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, 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 <nghttp2_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 * * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and diff --git a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2ver.h index c6082518c7..c1a37dd53f 100644 --- a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2ver.h +++ b/contrib/libs/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.48.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 0x013000 #endif /* NGHTTP2VER_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_extpri.c b/contrib/libs/nghttp2/lib/nghttp2_extpri.c new file mode 100644 index 0000000000..3fd9b78163 --- /dev/null +++ b/contrib/libs/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/contrib/libs/nghttp2/lib/nghttp2_extpri.h b/contrib/libs/nghttp2/lib/nghttp2_extpri.h new file mode 100644 index 0000000000..23c6ddc0c0 --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +/* + * 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/contrib/libs/nghttp2/lib/nghttp2_frame.c b/contrib/libs/nghttp2/lib/nghttp2_frame.c index 3648b2389d..35072c15fc 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_frame.c +++ b/contrib/libs/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; @@ -876,6 +901,57 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, return 0; } +int 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); + + return 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 +1147,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; diff --git a/contrib/libs/nghttp2/lib/nghttp2_frame.h b/contrib/libs/nghttp2/lib/nghttp2_frame.h index 3859926ebb..5f6152b745 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_frame.h +++ b/contrib/libs/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); @@ -423,6 +424,31 @@ 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. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, + nghttp2_extension *ext); + +/* + * Unpacks PRIORITY_UPDATE wire format into |frame|. The |payload| of + * |payloadlen| bytes contains frame payload. This function assumes + * that frame->payload points to the nghttp2_ext_priority_update + * object. + * + * This function always succeeds and returns 0. + */ +void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, + uint8_t *payload, + 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 @@ -539,6 +565,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 * not include the Pad Length field. If |padlen| is 0, this function diff --git a/contrib/libs/nghttp2/lib/nghttp2_hd.c b/contrib/libs/nghttp2/lib/nghttp2_hd.c index 30ee9b8892..8a2bda64c1 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_hd.c +++ b/contrib/libs/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/contrib/libs/nghttp2/lib/nghttp2_hd.h b/contrib/libs/nghttp2/lib/nghttp2_hd.h index 267402881f..6de0052aae 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_hd.h +++ b/contrib/libs/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/contrib/libs/nghttp2/lib/nghttp2_http.c b/contrib/libs/nghttp2/lib/nghttp2_http.c index a2bcd2c0a1..e41cbf6849 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_http.c +++ b/contrib/libs/nghttp2/lib/nghttp2_http.c @@ -30,6 +30,7 @@ #include "nghttp2_hd.h" #include "nghttp2_helper.h" +#include "nghttp2_extpri.h" static uint8_t downcase(uint8_t c) { return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; @@ -83,14 +84,14 @@ static int lws(const uint8_t *s, size_t n) { } static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv, - int flag) { + uint32_t flag) { if (stream->http_flags & flag) { return 0; } if (lws(nv->value->base, nv->value->len)) { return 0; } - stream->http_flags = (uint16_t)(stream->http_flags | flag); + stream->http_flags = stream->http_flags | flag; return 1; } @@ -114,6 +115,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 +215,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; @@ -369,7 +389,11 @@ 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 { + rv = nghttp2_check_header_value(nv->value->base, nv->value->len); + } break; case NGHTTP2_TOKEN__SCHEME: rv = check_scheme(nv->value->base, nv->value->len); @@ -443,16 +467,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 +560,715 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream, return; } } + +/* Generated by genchartbl.py */ +static const int SF_KEY_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, + 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, + 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, + 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, + 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_key(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + + if ((*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_KEY_CHARS[*p]; ++p) + ; + + return p - begin; +} + +static ssize_t sf_parse_integer_or_decimal(nghttp2_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int sign = 1; + int64_t value = 0; + int type = NGHTTP2_SF_VALUE_TYPE_INTEGER; + size_t len = 0; + size_t fpos = 0; + size_t i; + + if (*p == '-') { + if (++p == end) { + return -1; + } + + sign = -1; + } + + if (*p < '0' || '9' < *p) { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + value *= 10; + value += *p - '0'; + + if (++len > 15) { + return -1; + } + + break; + case '.': + if (type != NGHTTP2_SF_VALUE_TYPE_INTEGER) { + goto fin; + } + + if (len > 12) { + return -1; + } + fpos = len; + type = NGHTTP2_SF_VALUE_TYPE_DECIMAL; + + break; + default: + goto fin; + }; + } + +fin: + switch (type) { + case NGHTTP2_SF_VALUE_TYPE_INTEGER: + if (dest) { + dest->type = (uint8_t)type; + dest->i = value * sign; + } + + return p - begin; + case NGHTTP2_SF_VALUE_TYPE_DECIMAL: + if (fpos == len || len - fpos > 3) { + return -1; + } + + if (dest) { + dest->type = (uint8_t)type; + dest->d = (double)value; + for (i = len - fpos; i > 0; --i) { + dest->d /= (double)10; + } + dest->d *= sign; + } + + return p - begin; + default: + assert(0); + abort(); + } +} + +/* Generated by genchartbl.py */ +static const int SF_DQUOTE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, + 1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_string(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != '"') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case '\\': + if (++p == end) { + return -1; + } + + switch (*p) { + case '"': + case '\\': + break; + default: + return -1; + } + + break; + case '"': + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_STRING; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); + } + + ++p; + + return p - begin; + default: + if (!SF_DQUOTE_CHARS[*p]) { + return -1; + } + } + } + + return -1; +} + +/* Generated by genchartbl.py */ +static const int SF_TOKEN_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, + 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_token(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_TOKEN_CHARS[*p]; ++p) + ; + + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_TOKEN; + dest->s.base = begin; + dest->s.len = (size_t)(p - begin); + } + + return p - begin; +} + +/* Generated by genchartbl.py */ +static const int SF_BYTESEQ_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */, + 0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static ssize_t sf_parse_byteseq(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != ':') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case ':': + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_BYTESEQ; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); + } + + ++p; + + return p - begin; + default: + if (!SF_BYTESEQ_CHARS[*p]) { + return -1; + } + } + } + + return -1; +} + +static ssize_t sf_parse_boolean(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int b; + + if (*p++ != '?') { + return -1; + } + + if (p == end) { + return -1; + } + + switch (*p++) { + case '0': + b = 0; + break; + case '1': + b = 1; + break; + default: + return -1; + } + + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN; + dest->b = b; + } + + return p - begin; +} + +static ssize_t sf_parse_bare_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + switch (*begin) { + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return sf_parse_integer_or_decimal(dest, begin, end); + case '"': + return sf_parse_string(dest, begin, end); + case '*': + return sf_parse_token(dest, begin, end); + case ':': + return sf_parse_byteseq(dest, begin, end); + case '?': + return sf_parse_boolean(dest, begin, end); + default: + if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) { + return sf_parse_token(dest, begin, end); + } + return -1; + } +} + +#define sf_discard_sp_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ') { \ + break; \ + } \ + } + +static ssize_t sf_parse_params(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + ssize_t slen; + + for (; p != end && *p == ';';) { + ++p; + + sf_discard_sp_end_err(p, end, -1); + + slen = sf_parse_key(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + } else if (++p == end) { + return -1; + } else { + slen = sf_parse_bare_item(NULL, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + } + } + + return p - begin; +} + +static ssize_t sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + ssize_t slen; + + slen = sf_parse_bare_item(dest, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + return p - begin; +} + +ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + return sf_parse_item(dest, begin, end); +} + +static ssize_t sf_parse_inner_list(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + ssize_t slen; + + if (*p++ != '(') { + return -1; + } + + for (;;) { + sf_discard_sp_end_err(p, end, -1); + + if (*p == ')') { + ++p; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (dest) { + dest->type = NGHTTP2_SF_VALUE_TYPE_INNER_LIST; + } + + return p - begin; + } + + slen = sf_parse_item(NULL, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || (*p != ' ' && *p != ')')) { + return -1; + } + } +} + +ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + return sf_parse_inner_list(dest, begin, end); +} + +static ssize_t sf_parse_item_or_inner_list(nghttp2_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + if (*begin == '(') { + return sf_parse_inner_list(dest, begin, end); + } + + return sf_parse_item(dest, begin, end); +} + +#define sf_discard_ows(BEGIN, END) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + goto fin; \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +#define sf_discard_ows_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + size_t valuelen) { + const uint8_t *p = value, *end = value + valuelen; + ssize_t slen; + nghttp2_sf_value val; + nghttp2_extpri pri = *dest; + const uint8_t *key; + size_t keylen; + + for (; p != end && *p == ' '; ++p) + ; + + for (; p != end;) { + slen = sf_parse_key(p, end); + if (slen < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + key = p; + keylen = (size_t)slen; + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + val.type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN; + val.b = 1; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (++p == end) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } else { + slen = sf_parse_item_or_inner_list(&val, p, end); + if (slen < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } + + p += slen; + + if (keylen == 1) { + switch (key[0]) { + case 'i': + if (val.type != NGHTTP2_SF_VALUE_TYPE_BOOLEAN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri.inc = val.b; + + break; + case 'u': + if (val.type != NGHTTP2_SF_VALUE_TYPE_INTEGER || + val.i < NGHTTP2_EXTPRI_URGENCY_HIGH || + NGHTTP2_EXTPRI_URGENCY_LOW < val.i) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri.urgency = (uint32_t)val.i; + + break; + } + } + + sf_discard_ows(p, end); + + if (*p++ != ',') { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + sf_discard_ows_end_err(p, end, NGHTTP2_ERR_INVALID_ARGUMENT); + } + +fin: + *dest = pri; + + return 0; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_http.h b/contrib/libs/nghttp2/lib/nghttp2_http.h index dd057cdb60..0c3a78eeef 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_http.h +++ b/contrib/libs/nghttp2/lib/nghttp2_http.h @@ -94,4 +94,55 @@ int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); void nghttp2_http_record_request_method(nghttp2_stream *stream, nghttp2_frame *frame); +/* + * RFC 8941 Structured Field Values. + */ +typedef enum nghttp2_sf_value_type { + NGHTTP2_SF_VALUE_TYPE_BOOLEAN, + NGHTTP2_SF_VALUE_TYPE_INTEGER, + NGHTTP2_SF_VALUE_TYPE_DECIMAL, + NGHTTP2_SF_VALUE_TYPE_STRING, + NGHTTP2_SF_VALUE_TYPE_TOKEN, + NGHTTP2_SF_VALUE_TYPE_BYTESEQ, + NGHTTP2_SF_VALUE_TYPE_INNER_LIST, +} nghttp2_sf_value_type; + +/* + * nghttp2_sf_value stores Structured Field Values item. For Inner + * List, only type is set to NGHTTP2_SF_VALUE_TYPE_INNER_LIST. + */ +typedef struct nghttp2_sf_value { + uint8_t type; + union { + int b; + int64_t i; + double d; + struct { + const uint8_t *base; + size_t len; + } s; + }; +} nghttp2_sf_value; + +/* + * nghttp2_sf_parse_item parses the input sequence [|begin|, |end|) + * and stores the parsed an Item in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin, + const uint8_t *end); + +/* + * nghttp2_sf_parse_inner_list parses the input sequence [|begin|, |end|) + * and stores the parsed an Inner List in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest, + const uint8_t *begin, const uint8_t *end); + +int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, + size_t valuelen); + #endif /* NGHTTP2_HTTP_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_option.c b/contrib/libs/nghttp2/lib/nghttp2_option.c index 34348e6606..ffd20322ee 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_option.c +++ b/contrib/libs/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,9 @@ 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; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_option.h b/contrib/libs/nghttp2/lib/nghttp2_option.h index 939729fdcd..0400d56dfc 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_option.h +++ b/contrib/libs/nghttp2/lib/nghttp2_option.h @@ -68,6 +68,7 @@ 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_option_flag; /** @@ -128,6 +129,10 @@ struct nghttp2_option { */ int no_closed_streams; /** + * NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES + */ + int server_fallback_rfc7540_priorities; + /** * NGHTTP2_OPT_USER_RECV_EXT_TYPES */ uint8_t user_recv_ext_types[32]; diff --git a/contrib/libs/nghttp2/lib/nghttp2_outbound_item.c b/contrib/libs/nghttp2/lib/nghttp2_outbound_item.c index f651c8029a..2a3041db19 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_outbound_item.c +++ b/contrib/libs/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/contrib/libs/nghttp2/lib/nghttp2_pq.c b/contrib/libs/nghttp2/lib/nghttp2_pq.c index bebccc7606..64353acc96 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_pq.c +++ b/contrib/libs/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/contrib/libs/nghttp2/lib/nghttp2_pq.h b/contrib/libs/nghttp2/lib/nghttp2_pq.h index 7b7b7392f8..c8d90ef2cc 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_pq.h +++ b/contrib/libs/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/contrib/libs/nghttp2/lib/nghttp2_session.c b/contrib/libs/nghttp2/lib/nghttp2_session.c index 380a47c1b1..134b0efb12 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_session.c +++ b/contrib/libs/nghttp2/lib/nghttp2_session.c @@ -36,6 +36,7 @@ #include "nghttp2_option.h" #include "nghttp2_http.h" #include "nghttp2_pq.h" +#include "nghttp2_extpri.h" #include "nghttp2_debug.h" /* @@ -143,6 +144,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 +360,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 +399,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 +413,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 +438,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 +473,7 @@ 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; if (server) { (*session_ptr)->server = 1; @@ -527,6 +559,13 @@ 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; + } } rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, @@ -584,6 +623,10 @@ 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: @@ -735,6 +778,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 +792,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 +822,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 +891,214 @@ 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 int 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; + + return 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 int session_detach_stream_item(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + + rv = nghttp2_stream_detach_item(stream); + if (rv != 0) { + return rv; + } + + if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || + !stream->queued) { + return 0; + } + + return session_ob_data_remove(session, stream); +} + +static int session_defer_stream_item(nghttp2_session *session, + nghttp2_stream *stream, uint8_t flags) { + int rv; + + rv = nghttp2_stream_defer_item(stream, flags); + if (rv != 0) { + return rv; + } + + if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) || + !stream->queued) { + return 0; + } + + 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 +1120,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; @@ -1041,11 +1298,20 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, 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 +1322,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 +1382,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 +1425,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,7 +1468,7 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, item = stream->item; - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (rv != 0) { return rv; @@ -1230,6 +1518,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 +2076,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 @@ -2013,7 +2327,7 @@ static int session_prep_frame(nghttp2_session *session, if (stream) { int rv2; - rv2 = nghttp2_stream_detach_item(stream); + rv2 = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv2)) { return rv2; @@ -2032,7 +2346,7 @@ 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, + rv = session_defer_stream_item(session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); if (nghttp2_is_fatal(rv)) { @@ -2051,7 +2365,8 @@ 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); + rv = session_defer_stream_item(session, stream, + NGHTTP2_STREAM_FLAG_DEFERRED_USER); if (nghttp2_is_fatal(rv)) { return rv; @@ -2062,7 +2377,7 @@ static int session_prep_frame(nghttp2_session *session, return NGHTTP2_ERR_DEFERRED; } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; @@ -2078,7 +2393,7 @@ static int session_prep_frame(nghttp2_session *session, if (rv != 0) { int rv2; - rv2 = nghttp2_stream_detach_item(stream); + rv2 = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv2)) { return rv2; @@ -2328,6 +2643,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 +2666,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 +2683,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 +2722,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 +2837,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,7 +2899,7 @@ static int session_after_frame_sent1(nghttp2_session *session) { } if (stream && aux_data->eof) { - rv = nghttp2_stream_detach_item(stream); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; } @@ -2675,9 +3024,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); @@ -2852,7 +3200,7 @@ 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); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; @@ -3150,7 +3498,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); + rv = session_detach_stream_item(session, stream); if (nghttp2_is_fatal(rv)) { return rv; @@ -3730,6 +4078,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; } @@ -4091,6 +4454,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,6 +4513,8 @@ 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); @@ -4214,8 +4581,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 +4626,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 +4756,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; } } @@ -4541,6 +4918,34 @@ 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; } } @@ -4826,8 +5231,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 +5303,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 +5411,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 +5758,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); @@ -5883,6 +6373,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; @@ -5988,13 +6521,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); @@ -6151,6 +6687,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: /* This is unknown frame */ @@ -6863,7 +7411,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)); @@ -7016,6 +7565,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 +7583,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 +7672,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; } @@ -7235,7 +7806,7 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, return rv; } - 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 +7880,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 +7988,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 +8013,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 +8298,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 +8334,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 +8379,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/contrib/libs/nghttp2/lib/nghttp2_session.h b/contrib/libs/nghttp2/lib/nghttp2_session.h index 907b1704bc..164b5fd957 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_session.h +++ b/contrib/libs/nghttp2/lib/nghttp2_session.h @@ -52,7 +52,8 @@ 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; /* @@ -62,7 +63,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 { @@ -151,10 +153,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 +165,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 { @@ -202,6 +203,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 +234,9 @@ struct nghttp2_session { /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not considered as in-flight. */ nghttp2_inflight_settings *inflight_settings_head; + /* 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 +338,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 @@ -774,6 +789,19 @@ 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/contrib/libs/nghttp2/lib/nghttp2_stream.c b/contrib/libs/nghttp2/lib/nghttp2_stream.c index f4c80a24b5..b3614a0b02 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_stream.c +++ b/contrib/libs/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) { @@ -484,6 +486,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. @@ -503,6 +509,10 @@ int nghttp2_stream_detach_item(nghttp2_stream *stream) { stream->item = NULL; stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); + if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { + return 0; + } + return stream_update_dep_on_detach_item(stream); } @@ -514,6 +524,10 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { stream->flags |= flags; + if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) { + return 0; + } + return stream_update_dep_on_detach_item(stream); } @@ -529,6 +543,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/contrib/libs/nghttp2/lib/nghttp2_stream.h b/contrib/libs/nghttp2/lib/nghttp2_stream.h index 2846c6aab7..0ec77bf071 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_stream.h +++ b/contrib/libs/nghttp2/lib/nghttp2_stream.h @@ -90,8 +90,12 @@ 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, } nghttp2_stream_flag; /* HTTP related flags to enforce HTTP semantics */ @@ -132,6 +136,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 +213,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 +227,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, diff --git a/contrib/libs/nghttp2/lib/nghttp2_submit.c b/contrib/libs/nghttp2/lib/nghttp2_submit.c index 92fb03e8ca..f5554eb564 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_submit.c +++ b/contrib/libs/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; |